-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
223 additions
and
0 deletions.
There are no files selected for viewing
71 changes: 71 additions & 0 deletions
71
src/SDK/Metrics/StalenessHandler/DelayedStalenessHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace OpenTelemetry\SDK\Metrics\StalenessHandler; | ||
|
||
use function assert; | ||
use Closure; | ||
use OpenTelemetry\SDK\Metrics\ReferenceCounterInterface; | ||
use OpenTelemetry\SDK\Metrics\StalenessHandlerInterface; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
final class DelayedStalenessHandler implements StalenessHandlerInterface, ReferenceCounterInterface | ||
{ | ||
private Closure $stale; | ||
private Closure $freshen; | ||
|
||
/** @var Closure[]|null */ | ||
private ?array $onStale = []; | ||
private int $count = 0; | ||
|
||
public function __construct(Closure $stale, Closure $freshen) | ||
{ | ||
$this->stale = $stale; | ||
$this->freshen = $freshen; | ||
} | ||
|
||
public function acquire(bool $persistent = false): void | ||
{ | ||
if ($this->count === 0) { | ||
($this->freshen)($this); | ||
} | ||
|
||
$this->count++; | ||
|
||
if ($persistent) { | ||
$this->onStale = null; | ||
} | ||
} | ||
|
||
public function release(): void | ||
{ | ||
if (--$this->count || $this->onStale === null) { | ||
return; | ||
} | ||
|
||
($this->stale)($this); | ||
} | ||
|
||
public function onStale(Closure $callback): void | ||
{ | ||
if ($this->onStale === null) { | ||
return; | ||
} | ||
|
||
$this->onStale[] = $callback; | ||
} | ||
|
||
public function triggerStale(): void | ||
{ | ||
assert($this->onStale !== null); | ||
|
||
$callbacks = $this->onStale; | ||
$this->onStale = []; | ||
foreach ($callbacks as $callback) { | ||
$callback(); | ||
} | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/SDK/Metrics/StalenessHandler/DelayedStalenessHandlerFactory.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace OpenTelemetry\SDK\Metrics\StalenessHandler; | ||
|
||
use ArrayAccess; | ||
use Closure; | ||
use OpenTelemetry\SDK\Common\Time\ClockInterface; | ||
use OpenTelemetry\SDK\Common\Util\WeakMap; | ||
use OpenTelemetry\SDK\Metrics\StalenessHandlerFactoryInterface; | ||
use OpenTelemetry\SDK\Metrics\StalenessHandlerInterface; | ||
use Traversable; | ||
|
||
final class DelayedStalenessHandlerFactory implements StalenessHandlerFactoryInterface | ||
{ | ||
private ClockInterface $clock; | ||
private int $nanoDelay; | ||
|
||
private Closure $stale; | ||
private Closure $freshen; | ||
|
||
/** @var ArrayAccess<DelayedStalenessHandler, int>&Traversable<DelayedStalenessHandler, int> */ | ||
private $staleHandlers; | ||
|
||
/** | ||
* @param float $delay delay in seconds | ||
*/ | ||
public function __construct(ClockInterface $clock, float $delay) | ||
{ | ||
$this->clock = $clock; | ||
$this->nanoDelay = (int) ($delay * 1e9); | ||
|
||
$this->stale = function (DelayedStalenessHandler $handler): void { | ||
$this->staleHandlers[$handler] = $this->clock->now(); | ||
}; | ||
$this->freshen = function (DelayedStalenessHandler $handler): void { | ||
unset($this->staleHandlers[$handler]); | ||
}; | ||
|
||
$this->staleHandlers = WeakMap::create(); | ||
} | ||
|
||
public function create(): StalenessHandlerInterface | ||
{ | ||
$this->triggerStaleHandlers(); | ||
|
||
return new DelayedStalenessHandler($this->stale, $this->freshen); | ||
} | ||
|
||
private function triggerStaleHandlers(): void | ||
{ | ||
$expired = $this->clock->now() - $this->nanoDelay; | ||
foreach ($this->staleHandlers as $handler => $timestamp) { | ||
if ($timestamp > $expired) { | ||
break; | ||
} | ||
|
||
/** @var DelayedStalenessHandler $handler */ | ||
unset($this->staleHandlers[$handler]); | ||
$handler->triggerStale(); | ||
} | ||
} | ||
} |
88 changes: 88 additions & 0 deletions
88
tests/Unit/SDK/Metrics/StalenessHandler/DelayedStalenessHandlerTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace OpenTelemetry\Tests\Unit\SDK\Metrics\StalenessHandler; | ||
|
||
use OpenTelemetry\SDK\Metrics\StalenessHandler\DelayedStalenessHandlerFactory; | ||
use OpenTelemetry\Tests\Unit\SDK\Util\TestClock; | ||
use PHPUnit\Framework\TestCase; | ||
use stdClass; | ||
use WeakReference; | ||
|
||
/** | ||
* @covers \OpenTelemetry\SDK\Metrics\StalenessHandler\DelayedStalenessHandler | ||
* @covers \OpenTelemetry\SDK\Metrics\StalenessHandler\DelayedStalenessHandlerFactory | ||
*/ | ||
final class DelayedStalenessHandlerTest extends TestCase | ||
{ | ||
public function test_on_stale(): void | ||
{ | ||
$called = false; | ||
$clock = new TestClock(); | ||
$factory = new DelayedStalenessHandlerFactory($clock, .15); | ||
$handler = $factory->create(); | ||
$handler->onStale(static function () use (&$called): void { | ||
$called = true; | ||
}); | ||
|
||
$handler->acquire(); | ||
$handler->acquire(); | ||
$handler->release(); | ||
$handler->release(); | ||
|
||
$clock->advance(100_000_000); | ||
$factory->create(); | ||
$this->assertFalse($called); | ||
|
||
$clock->advance(100_000_000); | ||
$factory->create(); | ||
/** @phpstan-ignore-next-line */ | ||
$this->assertTrue($called); | ||
} | ||
|
||
public function test_on_stale_acquire_does_not_trigger_callbacks(): void | ||
{ | ||
$called = false; | ||
$clock = new TestClock(); | ||
$factory = new DelayedStalenessHandlerFactory($clock, .15); | ||
$handler = $factory->create(); | ||
$handler->onStale(static function () use (&$called): void { | ||
$called = true; | ||
}); | ||
|
||
$handler->acquire(); | ||
$handler->release(); | ||
|
||
$clock->advance(100_000_000); | ||
$factory->create(); | ||
$this->assertFalse($called); | ||
|
||
$clock->advance(100_000_000); | ||
$handler->acquire(); | ||
$factory->create(); | ||
$this->assertFalse($called); | ||
} | ||
|
||
public function test_releases_callbacks_on_persistent_acquire(): void | ||
{ | ||
$handler = (new DelayedStalenessHandlerFactory(new TestClock(), 0))->create(); | ||
|
||
$object = new stdClass(); | ||
$reference = WeakReference::create($object); | ||
/** @phpstan-ignore-next-line */ | ||
$handler->onStale(static function () use ($object): void { | ||
}); | ||
$handler->acquire(true); | ||
$object = null; | ||
$this->assertNull($reference->get()); | ||
|
||
$object = new stdClass(); | ||
$reference = WeakReference::create($object); | ||
/** @phpstan-ignore-next-line */ | ||
$handler->onStale(static function () use ($object): void { | ||
}); | ||
$object = null; | ||
$this->assertNull($reference->get()); | ||
} | ||
} |