Add atomic reservation support in Redis for goal alert deduplication with expiration. Update Cron logic and add unit tests for validation.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
app_require('classes/redis.php');
|
||||
|
||||
use classes\redis;
|
||||
use Predis\Client as PredisClient;
|
||||
|
||||
class RedisAtomicReservationTestClient extends PredisClient
|
||||
{
|
||||
public array $calls = [];
|
||||
|
||||
public function __construct(private mixed $returnValue)
|
||||
{
|
||||
}
|
||||
|
||||
public function set(...$arguments): mixed
|
||||
{
|
||||
$this->calls[] = $arguments;
|
||||
return $this->returnValue;
|
||||
}
|
||||
|
||||
public function disconnect(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
function redis_test_inject_client(redis $redis, PredisClient $client): void
|
||||
{
|
||||
$reflection = new ReflectionClass($redis);
|
||||
$property = $reflection->getProperty('redis');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($redis, $client);
|
||||
}
|
||||
|
||||
it('claims a slot atomically with nx and expiration', function (): void {
|
||||
global $REDIS_CONFIG;
|
||||
|
||||
$REDIS_CONFIG = [
|
||||
'host' => 'redis',
|
||||
'database' => 0,
|
||||
'password' => '',
|
||||
];
|
||||
|
||||
$client = new RedisAtomicReservationTestClient('OK');
|
||||
|
||||
$redis = new redis();
|
||||
redis_test_inject_client($redis, $client);
|
||||
|
||||
expect($redis->set_if_absent_with_expiration('goal_alert_sent:22:2026-03-17', '1', 86400))->toBeTrue();
|
||||
expect($client->calls)->toBe([
|
||||
['goal_alert_sent:22:2026-03-17', '1', 'EX', 86400, 'NX'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns false when the slot is already claimed and clamps ttl to one second', function (): void {
|
||||
global $REDIS_CONFIG;
|
||||
|
||||
$REDIS_CONFIG = [
|
||||
'host' => 'redis',
|
||||
'database' => 0,
|
||||
'password' => '',
|
||||
];
|
||||
|
||||
$client = new RedisAtomicReservationTestClient(null);
|
||||
|
||||
$redis = new redis();
|
||||
redis_test_inject_client($redis, $client);
|
||||
|
||||
expect($redis->set_if_absent_with_expiration('goal_alert_sent:22:2026-03-17', '1', 0))->toBeFalse();
|
||||
expect($client->calls)->toBe([
|
||||
['goal_alert_sent:22:2026-03-17', '1', 'EX', 1, 'NX'],
|
||||
]);
|
||||
});
|
||||
Reference in New Issue
Block a user