Skip to content

Commit a6aa08e

Browse files
committed
feature #38296 [lock] Provides default implementation when store does not supports the behavior (jderusse)
This PR was squashed before being merged into the 5.2-dev branch. Discussion ---------- [lock] Provides default implementation when store does not supports the behavior | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | yes | Tickets | / | License | MIT | Doc PR | / All stores does not provide the same behaviors. Some are blocking, some allows shared Locks, ... Issue is: When people use `lock` to use a behavior that is not supported by the store, they get an `UnsuportedException`, but they don't have any way to know if the store supports or not the behavior they want to use. ie: when using the FrameworkBundle ```yaml framework: lock: '%env(LOCK_DSN)%' ``` User (or bundle) can't use safely `$lock->acquire(true)` or `$lock->acquireRead()`. Given it's very easy to provide an fallback implementation to those behavior, this PR - use fallback implementation when store does not support the behavior - deprecate the RetryTillSaveStore (not needed anymore) Commits ------- 575b391 [lock] Provides default implementation when store does not supports the behavior
2 parents 934b125 + 575b391 commit a6aa08e

13 files changed

+73
-112
lines changed

UPGRADE-5.2.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ Lock
4242
----
4343

4444
* `MongoDbStore` does not implement `BlockingStoreInterface` anymore, typehint against `PersistingStoreInterface` instead.
45+
* deprecated `NotSupportedException`, it shouldn't be thrown anymore.
46+
* deprecated `RetryTillSaveStore`, logic has been moved in `Lock` and is not needed anymore.
4547

4648
Mime
4749
----

UPGRADE-6.0.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ Inflector
7777

7878
* The component has been removed, use `EnglishInflector` from the String component instead.
7979

80+
Lock
81+
----
82+
83+
* Removed the `NotSupportedException`. It shouldn't be thrown anymore.
84+
* Removed the `RetryTillSaveStore`. Logic has been moved in `Lock` and is not needed anymore.
85+
8086
Mailer
8187
------
8288

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Lock;
13+
14+
use Symfony\Component\Lock\Exception\LockConflictedException;
15+
16+
/**
17+
* @author Jérémy Derussé <jeremy@derusse.com>
18+
*/
19+
interface BlockingSharedLockStoreInterface extends SharedLockStoreInterface
20+
{
21+
/**
22+
* Waits until a key becomes free for reading, then stores the resource.
23+
*
24+
* @throws LockConflictedException
25+
*/
26+
public function waitAndSaveRead(Key $key);
27+
}

src/Symfony/Component/Lock/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ CHANGELOG
77
* `MongoDbStore` does not implement `BlockingStoreInterface` anymore, typehint against `PersistingStoreInterface` instead.
88
* added support for shared locks
99
* added `NoLock`
10+
* deprecated `NotSupportedException`, it shouldn't be thrown anymore.
11+
* deprecated `RetryTillSaveStore`, logic has been moved in `Lock` and is not needed anymore.
1012

1113
5.1.0
1214
-----

src/Symfony/Component/Lock/Exception/NotSupportedException.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@
1111

1212
namespace Symfony\Component\Lock\Exception;
1313

14+
trigger_deprecation('symfony/lock', '5.2', '%s is deprecated, You should stop using it, as it will be removed in 6.0.', NotSupportedException::class);
15+
1416
/**
1517
* NotSupportedException is thrown when an unsupported method is called.
1618
*
1719
* @author Jérémy Derussé <jeremy@derusse.com>
20+
*
21+
* @deprecated since Symfony 5.2
1822
*/
1923
class NotSupportedException extends \LogicException implements ExceptionInterface
2024
{

src/Symfony/Component/Lock/Lock.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
use Symfony\Component\Lock\Exception\LockConflictedException;
2020
use Symfony\Component\Lock\Exception\LockExpiredException;
2121
use Symfony\Component\Lock\Exception\LockReleasingException;
22-
use Symfony\Component\Lock\Exception\NotSupportedException;
2322

2423
/**
2524
* Lock is the default implementation of the LockInterface.
@@ -70,9 +69,16 @@ public function acquire(bool $blocking = false): bool
7069
try {
7170
if ($blocking) {
7271
if (!$this->store instanceof BlockingStoreInterface) {
73-
throw new NotSupportedException(sprintf('The store "%s" does not support blocking locks.', get_debug_type($this->store)));
72+
while (true) {
73+
try {
74+
$this->store->wait($this->key);
75+
} catch (LockConflictedException $e) {
76+
usleep((100 + random_int(-10, 10)) * 1000);
77+
}
78+
}
79+
} else {
80+
$this->store->waitAndSave($this->key);
7481
}
75-
$this->store->waitAndSave($this->key);
7682
} else {
7783
$this->store->save($this->key);
7884
}
@@ -116,7 +122,9 @@ public function acquireRead(bool $blocking = false): bool
116122
{
117123
try {
118124
if (!$this->store instanceof SharedLockStoreInterface) {
119-
throw new NotSupportedException(sprintf('The store "%s" does not support shared locks.', get_debug_type($this->store)));
125+
$this->logger->debug('Store does not support ReadLocks, fallback to WriteLock.', ['resource' => $this->key]);
126+
127+
return $this->acquire($blocking);
120128
}
121129
if ($blocking) {
122130
$this->store->waitAndSaveRead($this->key);
@@ -125,7 +133,7 @@ public function acquireRead(bool $blocking = false): bool
125133
}
126134

127135
$this->dirty = true;
128-
$this->logger->debug('Successfully acquired the "{resource}" lock.', ['resource' => $this->key]);
136+
$this->logger->debug('Successfully acquired the "{resource}" lock for reading.', ['resource' => $this->key]);
129137

130138
if ($this->ttl) {
131139
$this->refresh();

src/Symfony/Component/Lock/SharedLockStoreInterface.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\Lock;
1313

1414
use Symfony\Component\Lock\Exception\LockConflictedException;
15-
use Symfony\Component\Lock\Exception\NotSupportedException;
1615

1716
/**
1817
* @author Jérémy Derussé <jeremy@derusse.com>
@@ -22,15 +21,7 @@ interface SharedLockStoreInterface extends PersistingStoreInterface
2221
/**
2322
* Stores the resource if it's not locked for reading by someone else.
2423
*
25-
* @throws NotSupportedException
2624
* @throws LockConflictedException
2725
*/
2826
public function saveRead(Key $key);
29-
30-
/**
31-
* Waits until a key becomes free for reading, then stores the resource.
32-
*
33-
* @throws LockConflictedException
34-
*/
35-
public function waitAndSaveRead(Key $key);
3627
}

src/Symfony/Component/Lock/Store/BlockingSharedLockStoreTrait.php

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/Symfony/Component/Lock/Store/CombinedStore.php

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Psr\Log\NullLogger;
1717
use Symfony\Component\Lock\Exception\InvalidArgumentException;
1818
use Symfony\Component\Lock\Exception\LockConflictedException;
19-
use Symfony\Component\Lock\Exception\NotSupportedException;
2019
use Symfony\Component\Lock\Key;
2120
use Symfony\Component\Lock\PersistingStoreInterface;
2221
use Symfony\Component\Lock\SharedLockStoreInterface;
@@ -29,7 +28,6 @@
2928
*/
3029
class CombinedStore implements SharedLockStoreInterface, LoggerAwareInterface
3130
{
32-
use BlockingSharedLockStoreTrait;
3331
use ExpiringStoreTrait;
3432
use LoggerAwareTrait;
3533

@@ -97,26 +95,17 @@ public function save(Key $key)
9795

9896
public function saveRead(Key $key)
9997
{
100-
if (null === $this->sharedLockStores) {
101-
$this->sharedLockStores = [];
102-
foreach ($this->stores as $store) {
103-
if ($store instanceof SharedLockStoreInterface) {
104-
$this->sharedLockStores[] = $store;
105-
}
106-
}
107-
}
108-
10998
$successCount = 0;
99+
$failureCount = 0;
110100
$storesCount = \count($this->stores);
111-
$failureCount = $storesCount - \count($this->sharedLockStores);
112101

113-
if (!$this->strategy->canBeMet($failureCount, $storesCount)) {
114-
throw new NotSupportedException(sprintf('The store "%s" does not contains enough compatible store to met the requirements.', get_debug_type($this)));
115-
}
116-
117-
foreach ($this->sharedLockStores as $store) {
102+
foreach ($this->stores as $store) {
118103
try {
119-
$store->saveRead($key);
104+
if ($store instanceof SharedLockStoreInterface) {
105+
$store->saveRead($key);
106+
} else {
107+
$store->save($key);
108+
}
120109
++$successCount;
121110
} catch (\Exception $e) {
122111
$this->logger->debug('One store failed to save the "{resource}" lock.', ['resource' => $key, 'store' => $store, 'exception' => $e]);

src/Symfony/Component/Lock/Store/RedisStore.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
class RedisStore implements SharedLockStoreInterface
3232
{
3333
use ExpiringStoreTrait;
34-
use BlockingSharedLockStoreTrait;
3534

3635
private $redis;
3736
private $initialTtl;

0 commit comments

Comments
 (0)