Fixed bug that RedisSecondMetaGenerator will generate the same meta. (#2640)

* Fixed bug that `RedisSecondMetaGenerator` will generate the same meta.

* Update RedisMetaGenerator.php

* Update CHANGELOG-2.0.md
This commit is contained in:
李铭昕 2020-10-12 19:03:31 +08:00 committed by GitHub
parent 044d827d2a
commit 84e19f78de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 17 deletions

View File

@ -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

View File

@ -2,9 +2,12 @@
<?php
$code = 0;
Swoole\Coroutine::set(['exit_condition' => 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(

View File

@ -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));
}

View File

@ -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
}
}

View File

@ -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;
}
}