Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 858af71

Browse filesBrowse files
committed
feature #21093 [Lock] Create a lock component (jderusse)
This PR was squashed before being merged into the 3.3-dev branch (closes #21093). Discussion ---------- [Lock] Create a lock component | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | they will | Fixed tickets | #20382 | License | MIT | Doc PR | symfony/symfony-docs#7364 This PR aim to add a new component Lock going further than the FileSystem\LockHandler by allowing remote backend (like Redis, memcache, etc) Inspired by ## Usage The simplest way to use lock is to inject an instance of a Lock in your service ```php class MyService { private $lock; public function __construct(LockInterface $lock) { $this->lock = $lock; } public function run() { $this->lock->acquire(true); // If I'm here, no exception had been raised. Lock is acquired try { // do my job } finally { $this->lock->release(); } } } ``` Configured with something like ```yaml services: app.my_service: class: AppBundle\MyService arguments: - app.lock.my_service app.lock.my_service: class: Symfony\Component\Lock\Lock factory: ['@locker', createLock] arguments: ['my_service'] ``` If you need to lock serveral resource on runtime, wou'll nneed to inject the LockFactory. ```php class MyService { private $lockFactory; public function __construct(LockFactoryInterface $lockFactory) { $this->lockFactory = $lockFactory; } public function run() { foreach ($this->items as $item) { $lock = $this->lockFactory->createLock((string) $item); try { $lock->acquire(); } catch (LockConflictedException $e) { continue; } // When I'm here, no exception had been, raised. Lock is acquired try { // do my job } finally { $lock->release(); } } } } ``` Configured with something like ```yaml services: app.my_service: class: AppBundle\MyService arguments: - '@locker' ``` This component allow you to refresh an expirable lock. This is usefull, if you run a long operation split in several small parts. If you lock with a ttl for the overall operatoin time and your process crash, the lock will block everybody for the defined TTL. But thank to the refresh method, you're able to lock for a small TTL, and refresh it between each parts. ```php class MyService { private $lock; public function __construct(LockInterface $lock) { $this->lock = $lock; } public function run() { $this->lock->acquire(true); try { do { $finished = $this->performLongTask(); // Increase the expire date by 300 more seconds $this->lock->refresh(); } while (!$finished) // do my job } finally { $this->lock->release(); } } } ``` ## Naming anc implementation choise ``` $lock->acquire() vs $lock->lock() ``` Choose to use acquire, because this component is full of `lock` Symfony\Component\Lock\Lock::Lock` raised a E_TOO_MANY_LOCK in my head. ``` $lock->acquire(false); $lock->acquire(true); vs $lock->aquire() $lock->waitAndAquire() ``` Not a big fan of flag feature and 2. But I choose to use the blocking flag to offer a simple (and common usecase) implementation ``` $lock = $factory->createLock($key); $lock->acquire(); vs $lock->aquire($key) ``` I choose to a the pool of locks implementation. It allow the user to create 2 instances and use cross lock even in the same process. ``` interface LockInterface final class Lock implements LockInterface vs final class Lock ``` I choose to use a Interface even if there is only one implementaiton to offer an extension point here # TODO ## In this PR * [x] tests * [x] add logs * [x] offer several redis connectors * [x] try other store implementation to validate the architecture/interface ## In other PR * documentation * add configuration in framework bundle * add stop watch in the debug bar * improve the combined store (takes the drift into account and elapsed time between each store) * implement other stores (memcache, ...) * use this component in session manipulation (fixes #4976) Commits ------- 018e0fc [Lock] Create a lock component
2 parents 6327b41 + 018e0fc commit 858af71
Copy full SHA for 858af71

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner

44 files changed

+2976
-0
lines changed

‎composer.json

Copy file name to clipboardExpand all lines: composer.json
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"symfony/inflector": "self.version",
5555
"symfony/intl": "self.version",
5656
"symfony/ldap": "self.version",
57+
"symfony/lock": "self.version",
5758
"symfony/monolog-bridge": "self.version",
5859
"symfony/options-resolver": "self.version",
5960
"symfony/process": "self.version",

‎src/Symfony/Component/Lock/.gitignore

Copy file name to clipboard
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
composer.lock
2+
phpunit.xml
3+
vendor/
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
3.3.0
5+
-----
6+
7+
* added the component
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Exception;
13+
14+
/**
15+
* Base ExceptionInterface for the Lock Component.
16+
*
17+
* @author Jérémy Derussé <jeremy@derusse.com>
18+
*/
19+
interface ExceptionInterface
20+
{
21+
}
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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\Exception;
13+
14+
/**
15+
* @author Jérémy Derussé <jeremy@derusse.com>
16+
*/
17+
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
18+
{
19+
}
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Exception;
13+
14+
/**
15+
* LockAcquiringException is thrown when an issue happens during the acquisition of a lock.
16+
*
17+
* @author Jérémy Derussé <jeremy@derusse.com>
18+
*/
19+
class LockAcquiringException extends \RuntimeException implements ExceptionInterface
20+
{
21+
}
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Exception;
13+
14+
/**
15+
* LockConflictedException is thrown when a lock is acquired by someone else.
16+
*
17+
* @author Jérémy Derussé <jeremy@derusse.com>
18+
*/
19+
class LockConflictedException extends \RuntimeException implements ExceptionInterface
20+
{
21+
}
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Exception;
13+
14+
/**
15+
* LockReleasingException is thrown when an issue happens during the release of a lock.
16+
*
17+
* @author Jérémy Derussé <jeremy@derusse.com>
18+
*/
19+
class LockReleasingException extends \RuntimeException implements ExceptionInterface
20+
{
21+
}
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Exception;
13+
14+
/**
15+
* LockStorageException is thrown when an issue happens during the manipulation of a lock in a store.
16+
*
17+
* @author Jérémy Derussé <jeremy@derusse.com>
18+
*/
19+
class LockStorageException extends \RuntimeException implements ExceptionInterface
20+
{
21+
}
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Exception;
13+
14+
/**
15+
* NotSupportedException is thrown when an unsupported method is called.
16+
*
17+
* @author Jérémy Derussé <jeremy@derusse.com>
18+
*/
19+
class NotSupportedException extends \LogicException implements ExceptionInterface
20+
{
21+
}
+51Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 Psr\Log\LoggerAwareInterface;
15+
use Psr\Log\LoggerAwareTrait;
16+
use Psr\Log\NullLogger;
17+
18+
/**
19+
* Factory provides method to create locks.
20+
*
21+
* @author Jérémy Derussé <jeremy@derusse.com>
22+
*/
23+
class Factory implements LoggerAwareInterface
24+
{
25+
use LoggerAwareTrait;
26+
27+
private $store;
28+
29+
public function __construct(StoreInterface $store)
30+
{
31+
$this->store = $store;
32+
33+
$this->logger = new NullLogger();
34+
}
35+
36+
/**
37+
* Creates a lock for the given resource.
38+
*
39+
* @param string $resource The resource to lock
40+
* @param float $ttl maximum expected lock duration
41+
*
42+
* @return Lock
43+
*/
44+
public function createLock($resource, $ttl = 300.0)
45+
{
46+
$lock = new Lock(new Key($resource), $this->store, $ttl);
47+
$lock->setLogger($this->logger);
48+
49+
return $lock;
50+
}
51+
}

‎src/Symfony/Component/Lock/Key.php

Copy file name to clipboard
+73Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
/**
15+
* Key is a container for the state of the locks in stores.
16+
*
17+
* @author Jérémy Derussé <jeremy@derusse.com>
18+
*/
19+
final class Key
20+
{
21+
private $resource;
22+
private $state = array();
23+
24+
/**
25+
* @param string $resource
26+
*/
27+
public function __construct($resource)
28+
{
29+
$this->resource = (string) $resource;
30+
}
31+
32+
public function __toString()
33+
{
34+
return $this->resource;
35+
}
36+
37+
/**
38+
* @param string $stateKey
39+
*
40+
* @return bool
41+
*/
42+
public function hasState($stateKey)
43+
{
44+
return isset($this->state[$stateKey]);
45+
}
46+
47+
/**
48+
* @param string $stateKey
49+
* @param mixed $state
50+
*/
51+
public function setState($stateKey, $state)
52+
{
53+
$this->state[$stateKey] = $state;
54+
}
55+
56+
/**
57+
* @param string $stateKey
58+
*/
59+
public function removeState($stateKey)
60+
{
61+
unset($this->state[$stateKey]);
62+
}
63+
64+
/**
65+
* @param $stateKey
66+
*
67+
* @return mixed
68+
*/
69+
public function getState($stateKey)
70+
{
71+
return $this->state[$stateKey];
72+
}
73+
}

‎src/Symfony/Component/Lock/LICENSE

Copy file name to clipboard
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2016-2017 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.