This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Component
Fabien Potencier 858af7158f 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
-------

018e0fc330 [Lock] Create a lock component
2017-03-22 11:45:21 -07:00
..
Asset Updated to PHPUnit namespaces 2017-02-20 14:56:45 +01:00
BrowserKit bug #21514 301 status code must drop request method to GET. (jlamur) 2017-03-14 13:25:39 -07:00
Cache Merge branch '3.2' 2017-03-17 15:59:00 +01:00
ClassLoader fixed Composer constraints 2017-02-18 11:13:35 -08:00
Config [travis] Master fixes for HHVM 3.18LTS 2017-03-07 20:35:42 +01:00
Console Fix ConsoleLoggerTest 2017-03-22 13:19:17 +01:00
CssSelector Merge branch '3.2' 2017-02-21 11:07:34 +01:00
Debug fixed Composer constraints 2017-02-18 11:13:35 -08:00
DependencyInjection feature #22098 [*Bundle] Add autowiring aliases for common services (nicolas-grekas) 2017-03-21 14:56:35 -07:00
DomCrawler Merge branch '3.2' 2017-02-21 11:07:34 +01:00
Dotenv minor #22087 Fix some invalid phpdoc in the Dotenv class (stof) 2017-03-21 07:24:05 -07:00
EventDispatcher Merge branch '3.2' 2017-03-21 14:44:47 -07:00
ExpressionLanguage Merge branch '3.2' 2017-02-24 05:59:04 -08:00
Filesystem Merge branch '3.2' 2017-03-21 14:44:47 -07:00
Finder Merge branch '3.2' 2017-03-20 11:06:58 +01:00
Form Merge branch '3.2' 2017-03-21 14:44:47 -07:00
HttpFoundation fixed CS 2017-03-05 13:52:44 -08:00
HttpKernel Merge branch '3.2' 2017-03-21 14:44:47 -07:00
Inflector Merge branch '3.2' 2017-02-20 14:49:17 +01:00
Intl Rename TimeZoneTransformer into TimezoneTransformer 2017-03-14 11:55:49 -07:00
Ldap Merge branch '3.2' 2017-02-21 11:07:34 +01:00
Lock [Lock] Create a lock component 2017-03-22 11:45:19 -07:00
OptionsResolver Merge branch '3.2' 2017-03-21 14:44:47 -07:00
Process Merge branch '3.2' 2017-03-04 13:23:47 +01:00
PropertyAccess Merge branch '3.2' 2017-03-21 14:44:47 -07:00
PropertyInfo [PropertyInfo] Move CHANGELOG.md to the root 2017-03-10 18:23:09 +01:00
Routing [DoctrineBridge][Routing] Require PSR-11 container instead of Symfony container 2017-03-12 08:32:31 -07:00
Security Merge branch '3.2' 2017-03-21 14:44:47 -07:00
Serializer Merge branch '3.2' 2017-03-21 14:44:47 -07:00
Stopwatch Merge branch '3.2' 2017-02-18 18:35:19 +01:00
Templating [2.8] Fix issues reported by static analyse 2017-02-28 15:08:20 +01:00
Translation Merge branch '3.2' 2017-03-21 14:44:47 -07:00
Validator Merge branch '3.2' 2017-03-21 14:44:47 -07:00
VarDumper Merge branch '3.2' 2017-03-12 17:08:03 +01:00
Workflow Merge branch '3.2' 2017-03-20 11:06:58 +01:00
Yaml [Yaml] Fix error handling in parser 2017-03-22 11:47:48 +01:00