mirror of
https://github.com/nextcloud/server.git
synced 2026-06-13 10:40:40 -04:00
feat(snowflake): allows to generate Snowflake IDs matching a timestamp
Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
This commit is contained in:
parent
3956e292b4
commit
28d32d8fff
3 changed files with 50 additions and 0 deletions
|
|
@ -43,6 +43,23 @@ final readonly class SnowflakeGenerator implements ISnowflakeGenerator {
|
|||
return $this->nextId();
|
||||
}
|
||||
|
||||
return $this->packSnowflakeId($seconds, $milliseconds, $serverId, $isCli, $sequenceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return minimal snowflake ID for a given timestamp
|
||||
*
|
||||
* Not a real snowflake ID!
|
||||
* Only use it for comparisons. For example get all snowflake IDs generated before $timestamp
|
||||
*
|
||||
* @since 34.0.1
|
||||
*/
|
||||
#[Override]
|
||||
public function minForTimeId(int $timestamp): string {
|
||||
return $this->packSnowflakeId($timestamp - self::TS_OFFSET, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private function packSnowflakeId($seconds, $milliseconds, $serverId, $isCli, $sequenceId): string {
|
||||
if (PHP_INT_SIZE === 8) {
|
||||
$firstHalf = $seconds & 0x7FFFFFFF;
|
||||
$secondHalf = (($milliseconds & 0x3FF) << 22) | ($serverId << 13) | ($isCli << 12) | $sequenceId;
|
||||
|
|
|
|||
|
|
@ -42,4 +42,18 @@ interface ISnowflakeGenerator {
|
|||
* @since 33.0
|
||||
*/
|
||||
public function nextId(): string;
|
||||
|
||||
/**
|
||||
* Return the smallest possible Snowflake ID for a given timestamp
|
||||
*
|
||||
* Not a real snowflake ID!
|
||||
* Only use it for comparisons. Examples:
|
||||
* - find all Snowflake IDs generated from a given $timestamp
|
||||
* Look for `>= minForTimeId($timestamp)`
|
||||
* - delete all Snowflake IDs generated before a given $timestamp
|
||||
* Delete where `id < minForTimeId($timestamp)`
|
||||
*
|
||||
* @since 34.0.1
|
||||
*/
|
||||
public function minForTimeId(int $timestamp): string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,25 @@ class GeneratorTest extends TestCase {
|
|||
$this->assertEquals($this->serverInfo->getServerId(), $data->getServerId());
|
||||
}
|
||||
|
||||
public function testMinForTime(): void {
|
||||
$generator = new SnowflakeGenerator(new TimeFactory(), $this->sequence, $this->serverInfo);
|
||||
$now = time();
|
||||
$snowflakeId = $generator->minForTimeId($now);
|
||||
$data = $this->decoder->decode($snowflakeId);
|
||||
|
||||
$this->assertIsString($snowflakeId);
|
||||
|
||||
// Check timestamp
|
||||
$this->assertEquals($now - ISnowflakeGenerator::TS_OFFSET, $data->getSeconds());
|
||||
|
||||
// Check all other fields are at zero
|
||||
$this->assertEquals(0, $data->getMilliseconds());
|
||||
$this->assertEquals(0, $data->getServerId());
|
||||
$this->assertEquals(0, $data->getSequenceId());
|
||||
$this->assertFalse($data->isCli());
|
||||
$this->assertEquals(0, $data->getServerId());
|
||||
}
|
||||
|
||||
#[DataProvider('provideSnowflakeData')]
|
||||
public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, int $expectedMilliseconds): void {
|
||||
$dt = new \DateTimeImmutable($date);
|
||||
|
|
|
|||
Loading…
Reference in a new issue