Skip to content

Commit

Permalink
Add delayed staleness handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Nevay committed Jul 28, 2022
1 parent 97ea02c commit feae135
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 0 deletions.
71 changes: 71 additions & 0 deletions src/SDK/Metrics/StalenessHandler/DelayedStalenessHandler.php
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();
}
}
}
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();
}
}
}
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());
}
}

0 comments on commit feae135

Please sign in to comment.