Skip to content

Commit

Permalink
add redis storage
Browse files Browse the repository at this point in the history
  • Loading branch information
akurov-lam committed Apr 18, 2021
1 parent e095104 commit bfd3ddc
Show file tree
Hide file tree
Showing 9 changed files with 579 additions and 0 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
},
"require-dev": {
"ext-json": "*",
"ext-redis": "*",
"doctrine/common": "^2.4.1 || ^3.0",
"doctrine/dbal": "^2.3",
"doctrine/doctrine-bundle": "~1.5 || ^2.0",
"doctrine/orm": "~2.4",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"predis/predis": "^1.1",
"symfony/browser-kit": "~2.8 || ~3.0 || ~4.0 || ^5.0",
"symfony/config": "~2.8 || ~3.0 || ~4.0 || ^5.0",
"symfony/dependency-injection": "~2.8 || ~3.0 || ~4.0 || ^5.0",
Expand Down
92 changes: 92 additions & 0 deletions src/Adapters/Redis/AbstractRedisStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

declare(strict_types=1);

namespace Lamoda\Metric\Adapters\Redis;

use Lamoda\Metric\Common\MetricSourceInterface;
use Lamoda\Metric\Storage\MetricStorageInterface;
use Lamoda\Metric\Storage\MutableMetricInterface;

abstract class AbstractRedisStorage implements \IteratorAggregate, MetricStorageInterface
{
/** @var RedisConnectionInterface */
private $redisConnection;

public function __construct(RedisConnectionInterface $redisConnection)
{
$this->redisConnection = $redisConnection;
}

/** {@inheritdoc} */
final public function getIterator(): \Traversable
{
return $this->getMetrics();
}

/** {@inheritdoc} */
final public function receive(MetricSourceInterface $source): void
{
$metrics = [];
foreach ($source->getMetrics() as $metric) {
$metrics[] = new MetricDto(
$metric->getName(),
$metric->resolve(),
$metric->getTags()
);
}
$this->redisConnection->setMetrics($metrics);
}

/** {@inheritdoc} */
final public function getMetrics(): \Traversable
{
$metricsData = $this->redisConnection->getAllMetrics();
foreach ($metricsData as $metricDto) {
yield new MetricWrapper(
$this->redisConnection,
$this->doCreateMetric($metricDto->name, $metricDto->value, $metricDto->tags)
);
}
}

/** {@inheritdoc} */
final public function findMetric(string $name, array $tags = []): ?MutableMetricInterface
{
$value = $this->redisConnection->getMetricValue($name, $tags);
if ($value === null) {
return null;
}

return new MetricWrapper(
$this->redisConnection,
$this->doCreateMetric($name, $value, $tags)
);
}

/** {@inheritdoc} */
final public function createMetric(string $name, float $value, array $tags = []): MutableMetricInterface
{
$metric = new MetricWrapper(
$this->redisConnection,
$this->doCreateMetric($name, 0, $tags)
);
$metric->setValue($value);

return $metric;
}

abstract protected function doCreateMetric(string $name, float $value, array $tags = []): MutableMetricInterface;

/** {@inheritdoc} */
final public function setMetricValue(string $name, float $value, array $tags = []): void
{
$this->redisConnection->setMetrics([new MetricDto($name, $value, $tags)]);
}

/** {@inheritdoc} */
final public function adjustMetricValue(string $name, float $value, array $tags = []): float
{
return $this->redisConnection->adjustMetric($name, $value, $tags);
}
}
23 changes: 23 additions & 0 deletions src/Adapters/Redis/MetricDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Lamoda\Metric\Adapters\Redis;

/** @internal */
final class MetricDto
{
/** @var string */
public $name;
/** @var float */
public $value;
/** @var array */
public $tags;

public function __construct(string $name, float $value, array $tags)
{
$this->name = $name;
$this->value = $value;
$this->tags = $tags;
}
}
54 changes: 54 additions & 0 deletions src/Adapters/Redis/MetricWrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Lamoda\Metric\Adapters\Redis;

use Lamoda\Metric\Storage\MutableMetricInterface;

/** @internal */
final class MetricWrapper implements MutableMetricInterface
{
/** @var MutatorRedisConnectionInterface */
private $redisConnection;
/** @var MutableMetricInterface */
private $metric;

public function __construct(MutatorRedisConnectionInterface $redisConnection, MutableMetricInterface $metric)
{
$this->redisConnection = $redisConnection;
$this->metric = $metric;
}

/** {@inheritdoc} */
public function adjust(float $delta): void
{
$value = $this->redisConnection->adjustMetric($this->getName(), $delta, $this->getTags());
$this->metric->setValue($value);
}

/** {@inheritdoc} */
public function setValue(float $value): void
{
$this->redisConnection->setMetrics([new MetricDto($this->getName(), $value, $this->getTags())]);
$this->metric->setValue($value);
}

/** {@inheritdoc} */
public function getName(): string
{
return $this->metric->getName();
}

/** {@inheritdoc} */
public function resolve(): float
{
return $this->metric->resolve();
}

/** {@inheritdoc} */
public function getTags(): array
{
return $this->metric->getTags();
}
}
15 changes: 15 additions & 0 deletions src/Adapters/Redis/MutatorRedisConnectionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Lamoda\Metric\Adapters\Redis;

interface MutatorRedisConnectionInterface
{
public function adjustMetric(string $key, float $delta, array $tags): float;

/**
* @param MetricDto[] $metricsData
*/
public function setMetrics(array $metricsData): void;
}
97 changes: 97 additions & 0 deletions src/Adapters/Redis/RedisConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

namespace Lamoda\Metric\Adapters\Redis;

/** @internal */
final class RedisConnection implements RedisConnectionInterface
{
private const DEFAULT_METRICS_KEY = '__php_metrics';

/** @var \Predis\Client|\Redis */
private $client;
/** @var string */
private $metricsKey;

/**
* @param \Predis\Client|\Redis $client
* @param string|null $metricsKey
*/
public function __construct($client, ?string $metricsKey = self::DEFAULT_METRICS_KEY)
{
$this->client = $client;
$this->metricsKey = $metricsKey;
}

/** {@inheritdoc} */
public function getAllMetrics(): array
{
$rawMetricsData = $this->client->hgetall($this->metricsKey);
$metrics = [];
foreach ($rawMetricsData as $rawMetricData => $value) {
$metricData = json_decode($rawMetricData, true);
$metrics[] = new MetricDto(
$metricData['name'],
(float) $value,
$this->convertTagsFromStorage($metricData['tags'])
);
}

return $metrics;
}

/** {@inheritdoc} */
public function adjustMetric(string $key, float $delta, array $tags): float
{
return $this->client->hincrbyfloat($this->metricsKey, $this->buildField($key, $tags), $delta);
}

/** {@inheritdoc} */
public function setMetrics(array $metricsData): void
{
$fields = [];
foreach ($metricsData as $metricDto) {
$field = $this->buildField($metricDto->name, $metricDto->tags);
$fields[$field] = $metricDto->value;
}
$this->client->hmset($this->metricsKey, $fields);
}

/** {@inheritdoc} */
public function getMetricValue(string $key, array $tags): ?float
{
$value = $this->client->hget($this->metricsKey, $this->buildField($key, $tags));
if ($value === false) {
return null;
}

return (float) $value;
}

private function buildField(string $name, array $tags)
{
return json_encode([
'name' => $name,
'tags' => $this->convertTagsForStorage($tags),
]);
}

private function convertTagsForStorage(array $tags): string
{
return json_encode($this->normalizeTags($tags));
}

private function convertTagsFromStorage(string $tags): array
{
return json_decode($tags, true);
}

private function normalizeTags(array $tags): array
{
ksort($tags);

return $tags;
}

}
14 changes: 14 additions & 0 deletions src/Adapters/Redis/RedisConnectionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Lamoda\Metric\Adapters\Redis;

/** @internal */
interface RedisConnectionInterface extends MutatorRedisConnectionInterface
{
/**
* @return MetricDto[]
*/
public function getAllMetrics(): array;

public function getMetricValue(string $key, array $tags): ?float;
}
Loading

0 comments on commit bfd3ddc

Please sign in to comment.