Skip to content

Commit f0dbd16

Browse files
authored
chore: make random sequence resolver more random (#80)
1 parent 68aa747 commit f0dbd16

8 files changed

+38
-13
lines changed

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Snowflake & Sonyflake algorithm PHP implementation [中文文档](https://github
2828

2929
![file](https://images.godruoyi.com/logos/201908/13/_1565672621_LPW65Pi8cG.png)
3030

31-
Snowflake is a network service that generates unique ID numbers at high scale with simple guarantees.
31+
Snowflake is a network service that generates unique ID numbers at a high scale with simple guarantees.
3232

3333
1. The first bit is an unused sign bit.
3434
2. The second part consists of a 41-bit timestamp (in milliseconds) representing the offset of the current time relative to a certain reference time.
@@ -84,7 +84,7 @@ $snowflake = new \Godruoyi\Snowflake\Snowflake($datacenterId, $workerId);
8484
$snowflake->id();
8585
```
8686

87-
3. Specify start time.
87+
3. Specify the start time.
8888

8989
```php
9090
$snowflake = new \Godruoyi\Snowflake\Snowflake;
@@ -95,7 +95,16 @@ $snowflake->id();
9595

9696
> The maximum value of a 41-bit timestamp (in milliseconds) can represent up to 69 years, so the Snowflake algorithm can run safely for 69 years. In order to make the most of it, we recommend setting a start time.
9797
98-
4. Use Sonyflake
98+
4. Using different sequence number resolvers (optional).
99+
100+
```php
101+
$snowflake = new \Godruoyi\Snowflake\Snowflake;
102+
$snowflake->setSequenceResolver(new \Godruoyi\Snowflake\RandomSequenceResolver);
103+
104+
$snowflake->id();
105+
```
106+
107+
5. Use Sonyflake
99108

100109
```php
101110
$sonyflake = new \Godruoyi\Snowflake\Sonyflake;

src/RandomSequenceResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function sequence(int $currentTime): int
4343
return $this->sequence;
4444
}
4545

46-
$this->sequence = random_int(0, $this->maxSequence);
46+
$this->sequence = crc32(uniqid((string) random_int(0, PHP_INT_MAX), true)) % $this->maxSequence;
4747
$this->lastTimeStamp = $currentTime;
4848

4949
return $this->sequence;

src/Snowflake.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ public function __construct(int $datacenter = 0, int $workerId = 0)
6060
$maxWorkId = -1 ^ (-1 << self::MAX_WORKID_LENGTH);
6161

6262
// If not set datacenter or workid, we will set a default value to use.
63-
$this->datacenter = $datacenter > $maxDataCenter || $datacenter < 0 ? random_int(0, 31) : $datacenter;
64-
$this->workerId = $workerId > $maxWorkId || $workerId < 0 ? random_int(0, 31) : $workerId;
63+
$this->datacenter = $datacenter > $maxDataCenter || $datacenter <= 0 ? random_int(0, 31) : $datacenter;
64+
$this->workerId = $workerId > $maxWorkId || $workerId <= 0 ? random_int(0, 31) : $workerId;
6565
}
6666

6767
/**

tests/FileLockResolverTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
class FileLockResolverTest extends TestCase
1919
{
2020
private static string $mainLockFileDirPath;
21+
2122
private static string $unWriteableFileDirPath;
2223

2324
private FileLockResolver $fileLocker;
2425

2526
public static function setUpBeforeClass(): void
2627
{
27-
self::$mainLockFileDirPath = dirname(__DIR__) . '/.locks';
28-
self::$unWriteableFileDirPath = __DIR__ . '/.locks';
28+
self::$mainLockFileDirPath = dirname(__DIR__).'/.locks';
29+
self::$unWriteableFileDirPath = __DIR__.'/.locks';
2930
}
3031

3132
protected function setUp(): void
@@ -375,7 +376,7 @@ private function touch(string $content = ''): string
375376

376377
private function cleanUpLockFileDirs(): void
377378
{
378-
$glob = self::$mainLockFileDirPath . '/*';
379+
$glob = self::$mainLockFileDirPath.'/*';
379380
$files = glob($glob);
380381
foreach ($files as $file) {
381382
if (is_file($file)) {

tests/PredisSequenceResolverTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
class PredisSequenceResolverTest extends TestCase
2121
{
22-
public function setUp(): void
22+
protected function setUp(): void
2323
{
2424
if (! class_exists('Predis\\Client')) {
2525
$this->markTestSkipped('Predis extension is not installed');

tests/RandomSequenceResolverTest.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function test_basic(): void
2929
$this->assertCount(Snowflake::MAX_SEQUENCE_SIZE, $seqs);
3030
}
3131

32-
public function test_can_generate_unique_id_by_snowflake(): void
32+
public function test_can_generate_unique_id_using_same_instance(): void
3333
{
3434
$snowflake = new Snowflake(1, 1);
3535
$seqs = [];
@@ -40,4 +40,19 @@ public function test_can_generate_unique_id_by_snowflake(): void
4040

4141
$this->assertCount(Snowflake::MAX_SEQUENCE_SIZE, $seqs);
4242
}
43+
44+
public function test_can_generate_unique_id_by_different_instance(): void
45+
{
46+
for ($i = 0; $i < 1000; $i++) {
47+
$ids = [];
48+
49+
for ($i = 0; $i < 100_000; $i++) {
50+
$snowflake = new Snowflake();
51+
$ids[$snowflake->id()] = true;
52+
}
53+
54+
// We expect to have at least 100_000 - 10 unique ids if using random sequence resolver
55+
$this->assertGreaterThanOrEqual(100_000 - 10, count($ids));
56+
}
57+
}
4358
}

tests/RedisSequenceResolverTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
class RedisSequenceResolverTest extends TestCase
1919
{
20-
public function setUp(): void
20+
protected function setUp(): void
2121
{
2222
if (! extension_loaded('redis')) {
2323
$this->markTestSkipped('Redis extension is not installed');

tests/SwooleSequenceResolverTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
class SwooleSequenceResolverTest extends TestCase
1818
{
19-
public function setUp(): void
19+
protected function setUp(): void
2020
{
2121
if (version_compare(PHP_VERSION, '8.4') >= 0) {
2222
$this->markTestSkipped('Swoole does not yet support PHP 8.4');

0 commit comments

Comments
 (0)