Skip to content

Commit

Permalink
Merge pull request #444 from perftools/0.19.x-merge-up-into-0.20.x_8S…
Browse files Browse the repository at this point in the history
…BQ6Noa

Merge release 0.19.1 into 0.20.x
  • Loading branch information
glensc authored Jan 16, 2022
2 parents 829febc + 01b9d01 commit 4d79b20
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 94 deletions.
29 changes: 19 additions & 10 deletions .github/workflows/release-on-milestone-closed.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
# https://docs.github.com/en/actions/reference/environment-variables
# https://github.com/laminas/automatic-releases
# https://github.com/perftools/xhgui/settings/secrets/actions

name: "Automatic Releases"

Expand All @@ -8,6 +10,11 @@ on:
types:
- "closed"

env:
GIT_AUTHOR_NAME: "XHGui Release Bot"
# NOTE: The e-mail does not exist
GIT_AUTHOR_EMAIL: "[email protected]"

jobs:
release:
name: "GIT tag, release & create merge-up PR"
Expand All @@ -27,8 +34,8 @@ jobs:
env:
"GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
"GIT_AUTHOR_NAME": ${{ env.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ env.GIT_AUTHOR_EMAIL }}

- name: "Create Merge-Up Pull Request"
uses: "laminas/automatic-releases@v1"
Expand All @@ -37,8 +44,8 @@ jobs:
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
"GIT_AUTHOR_NAME": ${{ env.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ env.GIT_AUTHOR_EMAIL }}

- name: "Create and/or Switch to new Release Branch"
uses: "laminas/automatic-releases@v1"
Expand All @@ -47,8 +54,8 @@ jobs:
env:
"GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
"GIT_AUTHOR_NAME": ${{ env.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ env.GIT_AUTHOR_EMAIL }}

- name: "Bump Changelog Version On Originating Release Branch"
uses: "laminas/automatic-releases@v1"
Expand All @@ -57,8 +64,8 @@ jobs:
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
"GIT_AUTHOR_NAME": ${{ env.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ env.GIT_AUTHOR_EMAIL }}

- name: "Create new milestones"
uses: "laminas/automatic-releases@v1"
Expand All @@ -67,5 +74,7 @@ jobs:
env:
"GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }}
"SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }}
"GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }}
"GIT_AUTHOR_NAME": ${{ env.GIT_AUTHOR_NAME }}
"GIT_AUTHOR_EMAIL": ${{ env.GIT_AUTHOR_EMAIL }}

# vim:ft=yaml:et:ts=2:sw=2
5 changes: 1 addition & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,6 @@ RUN chmod -R a+rX /app

# install composer vendor
FROM php AS build
# extra deps for composer
RUN apk add --no-cache php-phar
WORKDIR /app
ARG COMPOSER_FLAGS="--no-interaction --no-suggest --ansi --no-dev"
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/
Expand Down Expand Up @@ -130,7 +128,6 @@ RUN rm -rf $(pwd)

# build final image
FROM runtime-$BUILD_SOURCE AS runtime

COPY --from=build --chown=www-data /cache ./cache/
COPY --from=build /vendor ./vendor/
COPY --from=build /app ./
COPY --from=build --chown=www-data /cache ./cache/
15 changes: 0 additions & 15 deletions src/Controller/RunController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ public function __construct(App $app, SearcherInterface $searcher)

public function index(Request $request, Response $response): void
{
// The list changes whenever new profiles are recorded.
// Generally avoid caching, but allow re-use in browser's bfcache
// and by cache proxies for concurrent requests.
// https://github.com/perftools/xhgui/issues/261
$response->headers->set('Cache-Control', 'public, max-age=0');

$search = [];
$keys = ['date_start', 'date_end', 'server_name', 'url'];
foreach ($keys as $key) {
Expand Down Expand Up @@ -83,15 +77,6 @@ public function index(Request $request, Response $response): void

public function view(Request $request, Response $response): void
{
// Permalink views to a specific run are meant to be public and immutable.
// But limit the cache to only a short period of time (enough to allow
// handling of abuse or other stampedes). This way we don't have to
// deal with any kind of purging system for when profiles are deleted,
// or for after XHGui itself is upgraded and static assets may be
// incompatible etc.
// https://github.com/perftools/xhgui/issues/261
$response->headers->set('Cache-Control', 'public, max-age=60, must-revalidate');

$detailCount = $this->config('detail.count');
$result = $this->searcher->get($request->get('id'));

Expand Down
194 changes: 182 additions & 12 deletions src/Db/PdoRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@

namespace XHGui\Db;

use DateInterval;
use DateTime;
use Generator;
use PDO;
use RuntimeException;
use XHGui\Searcher\SearcherInterface;

class PdoRepository
{
/** @var PDO */
private $pdo;

/** @var string */
private $driverName;

/** @var string */
private $table;

Expand All @@ -22,9 +28,10 @@ class PdoRepository
* @param string $table Table name where Xhgui profiles are stored
* @param string $tableWatch Table name where Xhgui watch functions are stored
*/
public function __construct(PDO $pdo, string $table, string $tableWatch)
public function __construct(PDO $pdo, string $driverName, string $table, string $tableWatch)
{
$this->pdo = $pdo;
$this->driverName = $driverName;
$this->table = sprintf('"%s"', $table);
$this->tableWatches = sprintf('"%s"', $tableWatch);
$this->initSchema();
Expand Down Expand Up @@ -86,20 +93,22 @@ public function getById(string $id): array
return $row;
}

public function countByUrl(string $url): int
public function countByUrl(array $options): int
{
$query = sprintf('
SELECT COUNT(*) AS count
FROM %s
WHERE "simple_url" LIKE :url
', $this->table);
WHERE %s',
$this->table,
$options['where']['conditions']
);
$stmt = $this->pdo->prepare($query);
$stmt->execute(['url' => '%' . $url . '%']);
$stmt->execute($options['where']['params']);

return (int)$stmt->fetchColumn();
}

public function findByUrl(string $url, string $direction, int $skip, int $perPage): Generator
public function findByUrl(array $options = []): Generator
{
$query = sprintf('
SELECT
Expand All @@ -118,16 +127,18 @@ public function findByUrl(string $url, string $direction, int $skip, int $perPag
"main_mu",
"main_pmu"
FROM %s
WHERE "simple_url" LIKE :url
ORDER BY "request_ts" %s
WHERE %s
ORDER BY %s %s
LIMIT %d OFFSET %d',
$this->table,
$direction,
$perPage,
$skip
$options['where']['conditions'],
$options['sort'],
$options['direction'],
$options['perPage'],
$options['skip']
);
$stmt = $this->pdo->prepare($query);
$stmt->execute(['url' => '%' . $url . '%']);
$stmt->execute($options['where']['params']);

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
yield $row;
Expand Down Expand Up @@ -302,4 +313,163 @@ public function truncateWatches()
$this->pdo->exec(sprintf('DELETE FROM %s', $this->tableWatches))
);
}

public function aggregate(array $options)
{
$query = sprintf(
'SELECT
"id",
"request_ts",
"main_wt",
"main_ct",
"main_cpu",
"main_mu",
"main_pmu"
FROM %s
WHERE %s
ORDER BY %s %s',
$this->table,
$options['where']['conditions'],
$options['sort'],
$options['direction']
);
$stmt = $this->pdo->prepare($query);
$stmt->execute($options['where']['params']);

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
yield $row;
}
}

/**
* Convert request data keys into pdo query.
*/
public function buildQuery(array $options): array
{
return [
'where' => $this->buildWhere($options['conditions']),
'sort' => $this->buildSort($options),
'direction' => $this->buildDirection($options),
'perPage' => $options['perPage'] ?? SearcherInterface::DEFAULT_PER_PAGE,
];
}

/**
* build pdo where
*/
public function buildWhere(array $search): array
{
$where = ['conditions' => '1=1', 'params' => []];

if (empty($search)) {
return $where;
}

if (isset($search['limit_custom']) && $search['limit_custom'][0] === 'P') {
$search['limit'] = $search['limit_custom'];
}
$hasLimit = (isset($search['limit']) && $search['limit'] !== -1);

// simple_url equals match
if (isset($search['simple_url'])) {
$where['conditions'] .= ' and simple_url = :simple_url';
$where['params']['simple_url'] = $search['simple_url'];
}

if (isset($search['date_start']) && !$hasLimit) {
$where['conditions'] .= ' and request_date >= :date_start';
$where['params']['date_start'] = $search['date_start'];
}

if (isset($search['date_end']) && !$hasLimit) {
$where['conditions'] .= ' and request_date <= :date_end';
$where['params']['date_end'] = $search['date_end'];
}

if (isset($search['request_start'])) {
$where['conditions'] .= ' and request_ts >= :request_start';
$where['params']['request_start'] = strtotime($search['request_start']);
}

if (isset($search['request_end'])) {
$where['conditions'] .= ' and request_ts <= :request_end';
$where['params']['request_end'] = strtotime($search['request_end']);
}

// TODO need JSON support
if (isset($search['remote_addr'])) {
$where['conditions'] .= ' and SERVER like :remote_addr';
$where['params']['remote_addr'] = '%' . $search['remote_addr'] . '%';
}

if (isset($search['cookie'])) {
$where['conditions'] .= ' and SERVER like :cookie';
$where['params']['cookie'] = '%' . $search['cookie'] . '%';
}

if (isset($search['server_name'])) {
$where['conditions'] .= ' and SERVER like :server_name';
$where['params']['server_name'] = '%' . $search['server_name'] . '%';
}

if ($hasLimit && $search['limit'][0] === 'P') {
$date = new DateTime();
try {
$date->sub(new DateInterval($search['limit']));
$where['conditions'] .= ' and request_ts >= :limit_start';
$where['params']['limit_start'] = $date->getTimestamp();
} catch (\Exception $e) {
$where['conditions'] .= ' and request_ts >= :limit_start';
$where['params']['limit_start'] = time() + 86400;
}
}

// fuzzy match
if (isset($search['url'])) {
$where['conditions'] .= ' and url like :url';
$where['params']['url'] = '%' . $search['url'] . '%';
}

return $where;
}

/**
* build pdo order sort
*/
private function buildSort(array $options): string
{
if (!isset($options['sort'])) {
return 'request_ts';
}

$valid = ['time', 'wt', 'mu', 'cpu', 'pmu'];
if (isset($options['sort'])) {
if ($options['sort'] === 'time') {
return 'request_ts';
}

if (in_array($options['sort'], $valid, true)) {
return 'main_' . $options['sort'];
}
}

return $options['sort'];
}

/**
* build pdo order direction
*/
private function buildDirection(array $options): string
{
if (!isset($options['direction'])) {
return SearcherInterface::DEFAULT_DIRECTION;
}

$valid = ['desc', 'asc'];
if (in_array($options['direction'], $valid, true)) {
return $options['direction'];
}

return 'desc';
}
}
Loading

0 comments on commit 4d79b20

Please sign in to comment.