Skip to content

Commit 28d32d8

Browse files
committed
feat(snowflake): allows to generate Snowflake IDs matching a timestamp
Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
1 parent 3956e29 commit 28d32d8

3 files changed

Lines changed: 50 additions & 0 deletions

File tree

lib/private/Snowflake/SnowflakeGenerator.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ public function nextId(): string {
4343
return $this->nextId();
4444
}
4545

46+
return $this->packSnowflakeId($seconds, $milliseconds, $serverId, $isCli, $sequenceId);
47+
}
48+
49+
/**
50+
* Return minimal snowflake ID for a given timestamp
51+
*
52+
* Not a real snowflake ID!
53+
* Only use it for comparisons. For example get all snowflake IDs generated before $timestamp
54+
*
55+
* @since 34.0.1
56+
*/
57+
#[Override]
58+
public function minForTimeId(int $timestamp): string {
59+
return $this->packSnowflakeId($timestamp - self::TS_OFFSET, 0, 0, 0, 0);
60+
}
61+
62+
private function packSnowflakeId($seconds, $milliseconds, $serverId, $isCli, $sequenceId): string {
4663
if (PHP_INT_SIZE === 8) {
4764
$firstHalf = $seconds & 0x7FFFFFFF;
4865
$secondHalf = (($milliseconds & 0x3FF) << 22) | ($serverId << 13) | ($isCli << 12) | $sequenceId;

lib/public/Snowflake/ISnowflakeGenerator.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,18 @@ interface ISnowflakeGenerator {
4242
* @since 33.0
4343
*/
4444
public function nextId(): string;
45+
46+
/**
47+
* Return the smallest possible Snowflake ID for a given timestamp
48+
*
49+
* Not a real snowflake ID!
50+
* Only use it for comparisons. Examples:
51+
* - find all Snowflake IDs generated from a given $timestamp
52+
* Look for `>= minForTimeId($timestamp)`
53+
* - delete all Snowflake IDs generated before a given $timestamp
54+
* Delete where `id < minForTimeId($timestamp)`
55+
*
56+
* @since 34.0.1
57+
*/
58+
public function minForTimeId(int $timestamp): string;
4559
}

tests/lib/Snowflake/GeneratorTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,25 @@ public function testGenerator(): void {
6666
$this->assertEquals($this->serverInfo->getServerId(), $data->getServerId());
6767
}
6868

69+
public function testMinForTime(): void {
70+
$generator = new SnowflakeGenerator(new TimeFactory(), $this->sequence, $this->serverInfo);
71+
$now = time();
72+
$snowflakeId = $generator->minForTimeId($now);
73+
$data = $this->decoder->decode($snowflakeId);
74+
75+
$this->assertIsString($snowflakeId);
76+
77+
// Check timestamp
78+
$this->assertEquals($now - ISnowflakeGenerator::TS_OFFSET, $data->getSeconds());
79+
80+
// Check all other fields are at zero
81+
$this->assertEquals(0, $data->getMilliseconds());
82+
$this->assertEquals(0, $data->getServerId());
83+
$this->assertEquals(0, $data->getSequenceId());
84+
$this->assertFalse($data->isCli());
85+
$this->assertEquals(0, $data->getServerId());
86+
}
87+
6988
#[DataProvider('provideSnowflakeData')]
7089
public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, int $expectedMilliseconds): void {
7190
$dt = new \DateTimeImmutable($date);

0 commit comments

Comments
 (0)