bug #38329 [lock] Fix non-blocking store fallback (jderusse)
This PR was merged into the 5.2-dev branch.
Discussion
----------
[lock] Fix non-blocking store fallback
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | /
| License | MIT
| Doc PR | /
Fix issue introduced in #38328
Tests added to avoid regressions.
Commits
-------
7a80e41cd8
Fix non-blocking store fallback
This commit is contained in:
commit
a429deee62
@ -71,7 +71,8 @@ final class Lock implements SharedLockInterface, LoggerAwareInterface
|
||||
if (!$this->store instanceof BlockingStoreInterface) {
|
||||
while (true) {
|
||||
try {
|
||||
$this->store->wait($this->key);
|
||||
$this->store->save($this->key);
|
||||
break;
|
||||
} catch (LockConflictedException $e) {
|
||||
usleep((100 + random_int(-10, 10)) * 1000);
|
||||
}
|
||||
@ -127,7 +128,18 @@ final class Lock implements SharedLockInterface, LoggerAwareInterface
|
||||
return $this->acquire($blocking);
|
||||
}
|
||||
if ($blocking) {
|
||||
if (!$this->store instanceof BlockingSharedLockStoreInterface) {
|
||||
while (true) {
|
||||
try {
|
||||
$this->store->saveRead($this->key);
|
||||
break;
|
||||
} catch (LockConflictedException $e) {
|
||||
usleep((100 + random_int(-10, 10)) * 1000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->store->waitAndSaveRead($this->key);
|
||||
}
|
||||
} else {
|
||||
$this->store->saveRead($this->key);
|
||||
}
|
||||
|
@ -13,11 +13,13 @@ namespace Symfony\Component\Lock\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Lock\BlockingSharedLockStoreInterface;
|
||||
use Symfony\Component\Lock\BlockingStoreInterface;
|
||||
use Symfony\Component\Lock\Exception\LockConflictedException;
|
||||
use Symfony\Component\Lock\Key;
|
||||
use Symfony\Component\Lock\Lock;
|
||||
use Symfony\Component\Lock\PersistingStoreInterface;
|
||||
use Symfony\Component\Lock\SharedLockStoreInterface;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
@ -40,7 +42,7 @@ class LockTest extends TestCase
|
||||
$this->assertTrue($lock->acquire(false));
|
||||
}
|
||||
|
||||
public function testAcquireNoBlockingStoreInterface()
|
||||
public function testAcquireNoBlockingWithPersistingStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(PersistingStoreInterface::class)->getMock();
|
||||
@ -56,6 +58,44 @@ class LockTest extends TestCase
|
||||
$this->assertTrue($lock->acquire(false));
|
||||
}
|
||||
|
||||
public function testAcquireBlockingWithPersistingStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(PersistingStoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('save');
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(true, false);
|
||||
|
||||
$this->assertTrue($lock->acquire(true));
|
||||
}
|
||||
|
||||
public function testAcquireBlockingRetryWithPersistingStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(PersistingStoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->any())
|
||||
->method('save')
|
||||
->willReturnCallback(static function () {
|
||||
if (1 === random_int(0, 1)) {
|
||||
return;
|
||||
}
|
||||
throw new LockConflictedException('boom');
|
||||
});
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(true, false);
|
||||
|
||||
$this->assertTrue($lock->acquire(true));
|
||||
}
|
||||
|
||||
public function testAcquireReturnsFalse()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
@ -90,7 +130,7 @@ class LockTest extends TestCase
|
||||
$this->assertFalse($lock->acquire(false));
|
||||
}
|
||||
|
||||
public function testAcquireBlocking()
|
||||
public function testAcquireBlockingWithBlockingStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->createMock(BlockingStoreInterface::class);
|
||||
@ -372,4 +412,96 @@ class LockTest extends TestCase
|
||||
yield [[0.1], false];
|
||||
yield [[-0.1, null], false];
|
||||
}
|
||||
|
||||
public function testAcquireReadNoBlockingWithSharedLockStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->createMock(SharedLockStoreInterface::class);
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('saveRead');
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(true, false);
|
||||
|
||||
$this->assertTrue($lock->acquireRead(false));
|
||||
}
|
||||
|
||||
public function testAcquireReadBlockingWithBlockingSharedLockStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->createMock(BlockingSharedLockStoreInterface::class);
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('waitAndSaveRead');
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(true, false);
|
||||
|
||||
$this->assertTrue($lock->acquireRead(true));
|
||||
}
|
||||
|
||||
public function testAcquireReadBlockingWithSharedLockStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->createMock(SharedLockStoreInterface::class);
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->any())
|
||||
->method('saveRead')
|
||||
->willReturnCallback(static function () {
|
||||
if (1 === random_int(0, 1)) {
|
||||
return;
|
||||
}
|
||||
throw new LockConflictedException('boom');
|
||||
});
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(true, false);
|
||||
|
||||
$this->assertTrue($lock->acquireRead(true));
|
||||
}
|
||||
|
||||
public function testAcquireReadBlockingWithBlockingLockStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->createMock(BlockingStoreInterface::class);
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('waitAndSave');
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(true, false);
|
||||
|
||||
$this->assertTrue($lock->acquireRead(true));
|
||||
}
|
||||
|
||||
public function testAcquireReadBlockingWithPersistingStoreInterface()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->createMock(PersistingStoreInterface::class);
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->any())
|
||||
->method('save')
|
||||
->willReturnCallback(static function () {
|
||||
if (1 === random_int(0, 1)) {
|
||||
return;
|
||||
}
|
||||
throw new LockConflictedException('boom');
|
||||
});
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(true, false);
|
||||
|
||||
$this->assertTrue($lock->acquireRead(true));
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user