From 065ef908468898a5c1ea5b0b8345051e5adc91b3 Mon Sep 17 00:00:00 2001 From: Chris Morrell Date: Wed, 10 Jul 2024 09:47:04 -0400 Subject: [PATCH 1/3] Add way to create IDs that are meant for querying a specific timestamp --- src/Contracts/MakesBits.php | 2 ++ src/Factories/GenericFactory.php | 13 ++++++++++ src/Factories/SnowflakeFactory.php | 7 ++++++ src/Factories/SonyflakeFactory.php | 7 ++++++ tests/Unit/CustomTest.php | 20 +++++++++++++-- tests/Unit/SnowflakeTest.php | 40 +++++++++++++++++++++++++++++- tests/Unit/SonyflakeTest.php | 19 +++++++++++++- 7 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/Contracts/MakesBits.php b/src/Contracts/MakesBits.php index 891970a..470c5e1 100644 --- a/src/Contracts/MakesBits.php +++ b/src/Contracts/MakesBits.php @@ -11,6 +11,8 @@ public function make(): Bits; public function makeFromTimestamp(CarbonInterface $timestamp): Bits; + public function firstForTimestamp(CarbonInterface $timestamp): Bits; + public function fromId(int|string $id): Bits; public function coerce(int|string|Bits $value): Bits; diff --git a/src/Factories/GenericFactory.php b/src/Factories/GenericFactory.php index 2e0e8b1..4e0c5fb 100644 --- a/src/Factories/GenericFactory.php +++ b/src/Factories/GenericFactory.php @@ -47,6 +47,19 @@ public function makeFromTimestamp(CarbonInterface $timestamp): Bits return new Bits($values, $this->config); } + public function firstForTimestamp(CarbonInterface $timestamp): Bits + { + $timestamp = $this->diffFromEpoch($timestamp); + + $values = $this->config->organize( + ids: new WorkerIds(...array_map(fn() => 0, $this->ids->ids)), + timestamp: $timestamp, + sequence: 0 + ); + + return new Bits($values, $this->config); + } + public function fromId(int|string $id): Bits { $values = $this->config->parse((int) $id); diff --git a/src/Factories/SnowflakeFactory.php b/src/Factories/SnowflakeFactory.php index 527dfc7..13e91b7 100644 --- a/src/Factories/SnowflakeFactory.php +++ b/src/Factories/SnowflakeFactory.php @@ -46,6 +46,13 @@ public function makeFromTimestamp(CarbonInterface $timestamp): Snowflake return new Snowflake($timestamp, $this->datacenter_id, $this->worker_id, $sequence, $this->config); } + public function firstForTimestamp(CarbonInterface $timestamp): Snowflake + { + $timestamp = $this->diffFromEpoch($timestamp); + + return new Snowflake($timestamp, 0, 0, 0, $this->config); + } + public function fromId(int|string $id): Snowflake { [$_, $timestamp, $datacenter_id, $worker_id, $sequence] = $this->config->parse((int) $id); diff --git a/src/Factories/SonyflakeFactory.php b/src/Factories/SonyflakeFactory.php index 20d1efd..75f58a2 100644 --- a/src/Factories/SonyflakeFactory.php +++ b/src/Factories/SonyflakeFactory.php @@ -45,6 +45,13 @@ public function makeFromTimestamp(CarbonInterface $timestamp): Sonyflake return new Sonyflake($timestamp, $sequence, $this->machine_id, $this->config); } + public function firstForTimestamp(CarbonInterface $timestamp): Sonyflake + { + $timestamp = $this->diffFromEpoch($timestamp); + + return new Sonyflake($timestamp, 0, 0, $this->config); + } + public function fromId(int|string $id): Sonyflake { [$timestamp, $sequence, $machine_id] = $this->config->parse((int) $id); diff --git a/tests/Unit/CustomTest.php b/tests/Unit/CustomTest.php index 43bad8c..6ed60f1 100644 --- a/tests/Unit/CustomTest.php +++ b/tests/Unit/CustomTest.php @@ -194,12 +194,28 @@ public function test_a_bit_can_be_created_for_a_specific_timestamp(): void $factory = $this->getBitsFactory(14); $bits = $factory->makeFromTimestamp(now()->addMicrosecond()); + $this->assertEquals([1, 14, 0], $bits->values->all()); - $this->assertEquals(1, $bits->values[0]); + $bits = $factory->makeFromTimestamp(now()->addMicrosecond()); + $this->assertEquals([1, 14, 1], $bits->values->all()); $bits = $factory->makeFromTimestamp(now()->addMicroseconds(42)); + $this->assertEquals([42, 14, 0], $bits->values->all()); + } + + public function test_a_bit_can_be_created_for_querying_by_a_specific_timestamp(): void + { + Date::setTestNow(now()); + + $factory = $this->getBitsFactory(14); + + $bits = $factory->firstForTimestamp(now()->addMicrosecond()); + + $this->assertEquals([1, 0, 0], $bits->values->all()); + + $bits = $factory->firstForTimestamp(now()->addMicroseconds(42)); - $this->assertEquals(42, $bits->values[0]); + $this->assertEquals([42, 0, 0], $bits->values->all()); } protected function getBitsFactory(int $id, ResolvesSequences $sequence = null): GenericFactory diff --git a/tests/Unit/SnowflakeTest.php b/tests/Unit/SnowflakeTest.php index f73d965..f63aa79 100644 --- a/tests/Unit/SnowflakeTest.php +++ b/tests/Unit/SnowflakeTest.php @@ -172,15 +172,53 @@ public function test_it_sleeps_1ms_when_sequence_limit_is_reached(): void public function test_a_snowflake_can_be_created_for_a_specific_timestamp(): void { - $factory = $this->app->make(MakesSnowflakes::class); + $factory = new SnowflakeFactory( + epoch: now(), + datacenter_id: 1, + worker_id: 15, + config: app(SnowflakesConfig::class), + sequence: new InMemorySequenceResolver(), + ); $snowflake = $factory->makeFromTimestamp($factory->epoch->toImmutable()->addMillisecond()); $this->assertEquals(1, $snowflake->timestamp); + $this->assertEquals(1, $snowflake->datacenter_id); + $this->assertEquals(15, $snowflake->worker_id); + $this->assertEquals(0, $snowflake->sequence); + + $snowflake = $factory->makeFromTimestamp($factory->epoch->toImmutable()->addMillisecond()); + + $this->assertEquals(1, $snowflake->timestamp); + $this->assertEquals(1, $snowflake->datacenter_id); + $this->assertEquals(15, $snowflake->worker_id); + $this->assertEquals(1, $snowflake->sequence); $snowflake = $factory->makeFromTimestamp($factory->epoch->toImmutable()->addMilliseconds(42)); $this->assertEquals(42, $snowflake->timestamp); + $this->assertEquals(1, $snowflake->datacenter_id); + $this->assertEquals(15, $snowflake->worker_id); + $this->assertEquals(0, $snowflake->sequence); + } + + public function test_a_snowflake_can_be_created_for_querying_by_a_specific_timestamp(): void + { + $factory = $this->app->make(MakesSnowflakes::class); + + $snowflake = $factory->firstForTimestamp($factory->epoch->toImmutable()->addMillisecond()); + + $this->assertEquals(1, $snowflake->timestamp); + $this->assertEquals(0, $snowflake->datacenter_id); + $this->assertEquals(0, $snowflake->worker_id); + $this->assertEquals(0, $snowflake->sequence); + + $snowflake = $factory->firstForTimestamp($factory->epoch->toImmutable()->addMilliseconds(42)); + + $this->assertEquals(42, $snowflake->timestamp); + $this->assertEquals(0, $snowflake->datacenter_id); + $this->assertEquals(0, $snowflake->worker_id); + $this->assertEquals(0, $snowflake->sequence); } public function test_a_snowflake_can_be_serialized_to_json(): void diff --git a/tests/Unit/SonyflakeTest.php b/tests/Unit/SonyflakeTest.php index 08fe224..ab147d9 100644 --- a/tests/Unit/SonyflakeTest.php +++ b/tests/Unit/SonyflakeTest.php @@ -149,7 +149,7 @@ public function test_it_sleeps_10ms_when_sequence_limit_is_reached(): void Sleep::assertSlept(fn(CarbonInterval $interval) => (int) $interval->totalMicroseconds === 10000); } - public function test_a_snowflake_can_be_created_for_a_specific_timestamp(): void + public function test_a_sonyflake_can_be_created_for_a_specific_timestamp(): void { $factory = $this->app->make(MakesSonyflakes::class); @@ -161,4 +161,21 @@ public function test_a_snowflake_can_be_created_for_a_specific_timestamp(): void $this->assertEquals(42, $sonyflake->timestamp); } + + public function test_a_sonyflake_can_be_created_for_querying_by_a_specific_timestamp(): void + { + $factory = $this->app->make(MakesSonyflakes::class); + + $sonyflake = $factory->firstForTimestamp($factory->epoch->toImmutable()->addMilliseconds(10)); + + $this->assertEquals(1, $sonyflake->timestamp); + $this->assertEquals(0, $sonyflake->sequence); + $this->assertEquals(0, $sonyflake->machine_id); + + $sonyflake = $factory->firstForTimestamp($factory->epoch->toImmutable()->addMilliseconds(420)); + + $this->assertEquals(42, $sonyflake->timestamp); + $this->assertEquals(0, $sonyflake->sequence); + $this->assertEquals(0, $sonyflake->machine_id); + } } From 5c4dc42245df5425ae96edaf62ebeb7f4a32edab Mon Sep 17 00:00:00 2001 From: Chris Morrell Date: Wed, 10 Jul 2024 14:59:54 -0400 Subject: [PATCH 2/3] Code style --- src/Factories/GenericFactory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Factories/GenericFactory.php b/src/Factories/GenericFactory.php index 4e0c5fb..84f7dd5 100644 --- a/src/Factories/GenericFactory.php +++ b/src/Factories/GenericFactory.php @@ -52,8 +52,8 @@ public function firstForTimestamp(CarbonInterface $timestamp): Bits $timestamp = $this->diffFromEpoch($timestamp); $values = $this->config->organize( - ids: new WorkerIds(...array_map(fn() => 0, $this->ids->ids)), - timestamp: $timestamp, + ids: new WorkerIds(...array_map(fn() => 0, $this->ids->ids)), + timestamp: $timestamp, sequence: 0 ); From bdf5a63b87c06e91f853d9bd7a9563fffec15fc7 Mon Sep 17 00:00:00 2001 From: Chris Morrell Date: Wed, 10 Jul 2024 15:03:43 -0400 Subject: [PATCH 3/3] Update to match new signature --- src/Factories/GenericFactory.php | 2 +- src/Factories/SnowflakeFactory.php | 2 +- src/Factories/SonyflakeFactory.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Factories/GenericFactory.php b/src/Factories/GenericFactory.php index 54a686f..a9a4c3f 100644 --- a/src/Factories/GenericFactory.php +++ b/src/Factories/GenericFactory.php @@ -57,7 +57,7 @@ public function firstForTimestamp(CarbonInterface $timestamp): Bits sequence: 0 ); - return new Bits($values, $this->config); + return new Bits($values, $this->config, $this->epoch); } public function fromId(int|string $id): Bits diff --git a/src/Factories/SnowflakeFactory.php b/src/Factories/SnowflakeFactory.php index 209fb80..d82a5c9 100644 --- a/src/Factories/SnowflakeFactory.php +++ b/src/Factories/SnowflakeFactory.php @@ -50,7 +50,7 @@ public function firstForTimestamp(CarbonInterface $timestamp): Snowflake { $timestamp = $this->diffFromEpoch($timestamp); - return new Snowflake($timestamp, 0, 0, 0, $this->config); + return new Snowflake($timestamp, 0, 0, 0, $this->epoch, $this->config); } public function fromId(int|string $id): Snowflake diff --git a/src/Factories/SonyflakeFactory.php b/src/Factories/SonyflakeFactory.php index 6c74bdb..12c7136 100644 --- a/src/Factories/SonyflakeFactory.php +++ b/src/Factories/SonyflakeFactory.php @@ -49,7 +49,7 @@ public function firstForTimestamp(CarbonInterface $timestamp): Sonyflake { $timestamp = $this->diffFromEpoch($timestamp); - return new Sonyflake($timestamp, 0, 0, $this->config); + return new Sonyflake($timestamp, 0, 0, $this->epoch, $this->config); } public function fromId(int|string $id): Sonyflake