Skip to content

Commit 2e8e0e4

Browse files
authored
Merge pull request #9 from reactphp-parallel/add-metrics
Add Metrics
2 parents 16a3087 + 845f6fb commit 2e8e0e4

File tree

7 files changed

+113
-60
lines changed

7 files changed

+113
-60
lines changed

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ $infinite->run(function () {
4242
$loop->run();
4343
```
4444

45+
## Metrics
46+
47+
This package supports metrics through [`wyrihaximus/metrics`](https://github.com/wyrihaximus/php-metrics):
48+
49+
```php
50+
use React\EventLoop\Factory;
51+
use ReactParallel\EventLoop\EventLoopBridge;
52+
use ReactParallel\EventLoop\Metrics as EventLoopMetrics;
53+
use ReactParallel\Pool\Infinite\Infinite;
54+
use ReactParallel\Pool\Infinite\Metrics;
55+
use WyriHaximus\Metrics\Configuration;
56+
use WyriHaximus\Metrics\InMemory\Registry;
57+
58+
$loop = Factory::create();
59+
$registry = new Registry(Configuration::create());
60+
$eventLoopBridge = (new EventLoopBridge($loop))->withMetrics(EventLoopMetrics::create($registry));
61+
$finite = (new Infinite($loop, $eventLoopBridge, 1.3))->withMetrics(Metrics::create($registry));
62+
```
63+
4564
## License ##
4665

4766
Copyright 2020 [Cees-Jan Kiewiet](http://wyrihaximus.net/)

composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
"react/promise": "^2.7",
2121
"wyrihaximus/async-test-utilities": "^3.2",
2222
"wyrihaximus/constants": "^1.5",
23-
"wyrihaximus/pool-info": "^1.0",
24-
"wyrihaximus/ticking-promise": "^1.6"
23+
"wyrihaximus/metrics": "^1.0",
24+
"wyrihaximus/pool-info": "^1.0"
2525
},
2626
"require-dev": {
2727
"bruli/php-value-objects": "^2.0",

composer.lock

+1-52
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

etc/qa/phpstan.neon

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ parameters:
22
checkMissingIterableValueType: false
33
ignoreErrors:
44
- '#Call to an undefined method React\\Promise\\PromiseInterface::always\(\).#'
5+
- '#Only numeric types are allowed in -, float\|int\|null given on the right side.#'
56
ergebnis:
67
classesAllowedToBeExtended:
78
- ReactParallel\Tests\AbstractPoolTest

src/Infinite.php

+34-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use ReactParallel\Contracts\LowLevelPoolInterface;
1515
use ReactParallel\EventLoop\EventLoopBridge;
1616
use ReactParallel\Runtime\Runtime;
17+
use WyriHaximus\Metrics\Label;
1718
use WyriHaximus\PoolInfo\Info;
1819

1920
use function array_key_exists;
@@ -22,6 +23,7 @@
2223
use function count;
2324
use function dirname;
2425
use function file_exists;
26+
use function hrtime;
2527
use function is_string;
2628
use function React\Promise\reject;
2729
use function spl_object_hash;
@@ -51,6 +53,8 @@ final class Infinite implements LowLevelPoolInterface
5153

5254
private float $ttl;
5355

56+
private ?Metrics $metrics = null;
57+
5458
/** @var GroupInterface[] */
5559
private array $groups = [];
5660

@@ -71,6 +75,14 @@ public function __construct(LoopInterface $loop, EventLoopBridge $eventLoopBridg
7175
$this->eventLoopBridge = $eventLoopBridge;
7276
}
7377

78+
public function withMetrics(Metrics $metrics): self
79+
{
80+
$self = clone $this;
81+
$self->metrics = $metrics;
82+
83+
return $self;
84+
}
85+
7486
/**
7587
* @param mixed[] $args
7688
*/
@@ -89,8 +101,21 @@ public function run(Closure $callable, array $args = []): PromiseInterface
89101

90102
$resolve($this->getIdleRuntime());
91103
}))->then(function (Runtime $runtime) use ($callable, $args): PromiseInterface {
104+
$time = null;
105+
if ($this->metrics instanceof Metrics) {
106+
$this->metrics->threads()->gauge(new Label('state', 'busy'))->incr();
107+
$this->metrics->threads()->gauge(new Label('state', 'idle'))->dcr();
108+
$time = hrtime(true);
109+
}
110+
92111
/** @psalm-suppress UndefinedInterfaceMethod */
93-
return $runtime->run($callable, $args)->always(function () use ($runtime): void {
112+
return $runtime->run($callable, $args)->always(function () use ($runtime, $time): void {
113+
if ($this->metrics instanceof Metrics) {
114+
$this->metrics->executionTime()->summary()->observe((hrtime(true) - $time) / 1e+9);
115+
$this->metrics->threads()->gauge(new Label('state', 'idle'))->incr();
116+
$this->metrics->threads()->gauge(new Label('state', 'busy'))->dcr();
117+
}
118+
94119
if ($this->ttl >= 0.1) {
95120
$this->addRuntimeToIdleList($runtime);
96121
$this->startTtlTimer($runtime);
@@ -182,6 +207,10 @@ private function spawnRuntime(): Runtime
182207
$runtime = new Runtime($this->eventLoopBridge, $this->autoload);
183208
$this->runtimes[spl_object_hash($runtime)] = $runtime;
184209

210+
if ($this->metrics instanceof Metrics) {
211+
$this->metrics->threads()->gauge(new Label('state', 'idle'))->incr();
212+
}
213+
185214
return $runtime;
186215
}
187216

@@ -205,6 +234,10 @@ private function closeRuntime(string $hash): void
205234
unset($this->idleRuntimes[$hash]);
206235
}
207236

237+
if ($this->metrics instanceof Metrics) {
238+
$this->metrics->threads()->gauge(new Label('state', 'idle'))->dcr();
239+
}
240+
208241
if (! array_key_exists($hash, $this->ttlTimers)) {
209242
return;
210243
}

src/Metrics.php

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ReactParallel\Pool\Infinite;
6+
7+
use WyriHaximus\Metrics\Factory as MetricsFactory;
8+
use WyriHaximus\Metrics\Label\Name;
9+
use WyriHaximus\Metrics\Registry;
10+
11+
final class Metrics
12+
{
13+
private Registry\Gauges $threads;
14+
private Registry\Summaries $executionTime;
15+
16+
public function __construct(
17+
Registry\Gauges $threads,
18+
Registry\Summaries $executionTime
19+
) {
20+
$this->threads = $threads;
21+
$this->executionTime = $executionTime;
22+
}
23+
24+
public static function create(Registry $registry): self
25+
{
26+
return new self(
27+
$registry->gauge(
28+
'react_parallel_pool_infinite_threads',
29+
'Currently active or idle thread count',
30+
new Name('state')
31+
),
32+
$registry->summary(
33+
'react_parallel_pool_infinite_execution_time',
34+
'Thread call execution time',
35+
MetricsFactory::defaultQuantiles()
36+
),
37+
);
38+
}
39+
40+
public function threads(): Registry\Gauges
41+
{
42+
return $this->threads;
43+
}
44+
45+
public function executionTime(): Registry\Summaries
46+
{
47+
return $this->executionTime;
48+
}
49+
}

tests/InfiniteTest.php

+7-5
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
namespace ReactParallel\Tests\Pool\Infinite;
44

5+
use WyriHaximus\Metrics\Factory as MetricsFactory;
56
use React\EventLoop\Factory;
67
use React\EventLoop\LoopInterface;
78
use React\Promise\PromiseInterface;
89
use ReactParallel\Contracts\PoolInterface;
910
use ReactParallel\EventLoop\EventLoopBridge;
1011
use ReactParallel\Pool\Infinite\Infinite;
12+
use ReactParallel\Pool\Infinite\Metrics;
1113
use ReactParallel\Tests\AbstractPoolTest;
1214
use WyriHaximus\PoolInfo\Info;
1315
use WyriHaximus\PoolInfo\PoolInfoInterface;
@@ -28,7 +30,7 @@ final class InfiniteTest extends AbstractPoolTest
2830
public function withAZeroTTLThreadsShouldBeKilledOffImmidetally(): void
2931
{
3032
$loop = Factory::create();
31-
$pool = new Infinite($loop, new EventLoopBridge($loop), 0.0);
33+
$pool = $pool = (new Infinite($loop, new EventLoopBridge($loop), 0.0))->withMetrics(Metrics::create(MetricsFactory::create()));
3234

3335
self::assertSame([
3436
Info::TOTAL => 0,
@@ -74,7 +76,7 @@ public function withAZeroTTLThreadsShouldBeKilledOffImmidetally(): void
7476
public function withAnAlmostZeroTTLThreadsShouldNotBeKilledOffImmidetally(): void
7577
{
7678
$loop = Factory::create();
77-
$pool = new Infinite($loop, new EventLoopBridge($loop), 5);
79+
$pool = $pool = (new Infinite($loop, new EventLoopBridge($loop), 5))->withMetrics(Metrics::create(MetricsFactory::create()));
7880

7981
self::assertSame([
8082
Info::TOTAL => 0,
@@ -141,12 +143,12 @@ public function withAnAlmostZeroTTLThreadsShouldNotBeKilledOffImmidetally(): voi
141143
private function poolFactory(): PoolInfoInterface
142144
{
143145
$loop = Factory::create();
144-
return new Infinite($loop, new EventLoopBridge($loop), 5);
146+
return $pool = (new Infinite($loop, new EventLoopBridge($loop), 5))->withMetrics(Metrics::create(MetricsFactory::create()));
145147
}
146148

147149
protected function createPool(LoopInterface $loop): PoolInterface
148150
{
149-
return new Infinite($loop, new EventLoopBridge($loop), 5);
151+
return $pool = (new Infinite($loop, new EventLoopBridge($loop), 5))->withMetrics(Metrics::create(MetricsFactory::create()));
150152
}
151153

152154
/**
@@ -155,7 +157,7 @@ protected function createPool(LoopInterface $loop): PoolInterface
155157
public function aquireLock(): void
156158
{
157159
$loop = Factory::create();
158-
$pool = new Infinite($loop, new EventLoopBridge($loop), 5);
160+
$pool = (new Infinite($loop, new EventLoopBridge($loop), 5))->withMetrics(Metrics::create(MetricsFactory::create()));
159161

160162
$group = $pool->acquireGroup();
161163
self::assertFalse($pool->close());

0 commit comments

Comments
 (0)