diff --git a/CHANGELOG-2.0.md b/CHANGELOG-2.0.md index 7a764b2c4..10fd289e4 100644 --- a/CHANGELOG-2.0.md +++ b/CHANGELOG-2.0.md @@ -2,7 +2,7 @@ ## Fixed -- [#2634](https://github.com/hyperf/hyperf/pull/2634) Fixed bug that `RedisSecondMetaGenerator` will generate the same meta. +- [#2634](https://github.com/hyperf/hyperf/pull/2634) [#2640](https://github.com/hyperf/hyperf/pull/2640) Fixed bug that `RedisSecondMetaGenerator` will generate the same meta. - [#2639](https://github.com/hyperf/hyperf/pull/2639) Fixed exception will not be normalized for json-rpc. # v2.0.14 - 2020-10-12 diff --git a/bin/co-phpunit b/bin/co-phpunit index 4b1e9bfd6..b22aa5794 100755 --- a/bin/co-phpunit +++ b/bin/co-phpunit @@ -2,9 +2,12 @@ function(){ - return Swoole\Coroutine::stats()['coroutine_num'] === 0; -}]); +Swoole\Coroutine::set([ + 'hook_flags' => SWOOLE_HOOK_ALL, + 'exit_condition' => function(){ + return Swoole\Coroutine::stats()['coroutine_num'] === 0; + } +]); Swoole\Coroutine\Run(function () use (&$code) { if (version_compare('7.1.0', PHP_VERSION, '>')) { fwrite( diff --git a/src/snowflake/src/MetaGenerator.php b/src/snowflake/src/MetaGenerator.php index e7c781231..937bc930f 100644 --- a/src/snowflake/src/MetaGenerator.php +++ b/src/snowflake/src/MetaGenerator.php @@ -42,14 +42,14 @@ abstract class MetaGenerator implements MetaGeneratorInterface if ($this->sequence == 0) { $timestamp = $this->getNextTimestamp(); } + } elseif ($timestamp < $this->lastTimestamp) { + $this->clockMovedBackwards($timestamp, $this->lastTimestamp); + $this->sequence = ($this->sequence + 1) % $this->configuration->maxSequence(); + $timestamp = $this->lastTimestamp; } else { $this->sequence = 0; } - if ($timestamp < $this->lastTimestamp) { - $this->clockMovedBackwards($timestamp, $this->lastTimestamp); - } - if ($timestamp < $this->beginTimestamp) { throw new SnowflakeException(sprintf('The beginTimestamp %d is invalid, because it smaller than timestamp %d.', $this->beginTimestamp, $timestamp)); } diff --git a/src/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php b/src/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php index 283bea80c..795d35541 100644 --- a/src/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php +++ b/src/snowflake/src/MetaGenerator/RedisSecondMetaGenerator.php @@ -28,12 +28,11 @@ class RedisSecondMetaGenerator extends RedisMetaGenerator public function getNextTimestamp(): int { - $timestamp = $this->getTimestamp(); - while ($timestamp <= $this->lastTimestamp) { - usleep(1000); - $timestamp = $this->getTimestamp(); - } + return $this->lastTimestamp + 1; + } - return $timestamp; + protected function clockMovedBackwards($timestamp, $lastTimestamp) + { + // Don't throw exception } } diff --git a/src/snowflake/tests/RedisMetaGeneratorTest.php b/src/snowflake/tests/RedisMetaGeneratorTest.php index aae07825f..f07d19165 100644 --- a/src/snowflake/tests/RedisMetaGeneratorTest.php +++ b/src/snowflake/tests/RedisMetaGeneratorTest.php @@ -137,7 +137,7 @@ class RedisMetaGeneratorTest extends TestCase $this->assertSame($meta->getWorkerId(), $userId % 31); } - public function testGetNextTimestamp() + public function testRedisSecondNextTimestamp() { $container = $this->getContainer(); $hConfig = $container->get(ConfigInterface::class); @@ -147,7 +147,43 @@ class RedisMetaGeneratorTest extends TestCase $time = $metaGenerator->getTimestamp(); $nextTime = $metaGenerator->getNextTimestamp(); $this->assertSame($time + 1, $nextTime); - $this->assertSame(time(), $nextTime); + } + + public function testGenerateSameMetaForRedisSecond() + { + $container = $this->getContainer(); + $hConfig = $container->get(ConfigInterface::class); + $config = new SnowflakeConfig(); + $metaGenerator = new RedisSecondMetaGenerator($config, MetaGeneratorInterface::DEFAULT_BEGIN_SECOND, $hConfig); + $generator = new SnowflakeIdGenerator($metaGenerator); + $result = []; + $channel = new Channel(2); + go(function () use (&$result, $generator, $channel) { + try { + for ($i = 0; $i < 4100; ++$i) { + $result[] = $generator->generate(); + } + } catch (\Throwable $exception) { + } finally { + $channel->push(true); + } + }); + + go(function () use (&$result, $generator, $channel) { + try { + for ($i = 0; $i < 900; ++$i) { + $result[] = $generator->generate(); + } + } catch (\Throwable $exception) { + } finally { + $channel->push(true); + } + }); + + $channel->pop(5); + $channel->pop(5); + + $this->assertSame(5000, count(array_unique($result))); } protected function getContainer() @@ -184,6 +220,8 @@ class RedisMetaGeneratorTest extends TestCase ]); $container = Mockery::mock(Container::class); + ApplicationContext::setContainer($container); + $container->shouldReceive('get')->with(ConfigInterface::class)->andReturn($config); $container->shouldReceive('make')->with(Channel::class, Mockery::any())->andReturnUsing(function ($class, $args) { return new Channel($args['size']); @@ -200,7 +238,6 @@ class RedisMetaGeneratorTest extends TestCase return new RedisProxy($factory, $args['pool']); }); - ApplicationContext::setContainer($container); return $container; } }