feature #34043 [Lock] Add missing lock connection string in FrameworkExtension (jderusse)
This PR was merged into the 4.4 branch.
Discussion
----------
[Lock] Add missing lock connection string in FrameworkExtension
| Q | A
| ------------- | ---
| Branch? | 4.4
| Bug fix? | no
| New feature? | yes
| Deprecations? | yes
| Tickets | -
| License | MIT
| Doc PR | TODO
This PR adds support to missing DSN in Lock component
```
framework:
lock: sqlite:/tmp/db
lock: mysql:host=localhost;dbname=test
lock: zookeeper://localhost:2181
```
The PR also removes intermediate "internal" services `.lock_connection.*` in favor of factory `StoreFactory::createStore`. Which remove duplicate code.
This PR also deprecate unused services `lock.store.*` and `lock.store.*.abstract`
Commits
-------
2db24cf582
Add missing lock connection string in FrameworkExtension
This commit is contained in:
commit
380e0fc984
@ -162,6 +162,8 @@ Lock
|
|||||||
* Deprecated `Symfony\Component\Lock\StoreInterface` in favor of `Symfony\Component\Lock\BlockingStoreInterface` and
|
* Deprecated `Symfony\Component\Lock\StoreInterface` in favor of `Symfony\Component\Lock\BlockingStoreInterface` and
|
||||||
`Symfony\Component\Lock\PersistingStoreInterface`.
|
`Symfony\Component\Lock\PersistingStoreInterface`.
|
||||||
* `Factory` is deprecated, use `LockFactory` instead
|
* `Factory` is deprecated, use `LockFactory` instead
|
||||||
|
* Deprecated services `lock.store.flock`, `lock.store.semaphore`, `lock.store.memcached.abstract` and `lock.store.redis.abstract`,
|
||||||
|
use `StoreFactory::createStore` instead.
|
||||||
|
|
||||||
Messenger
|
Messenger
|
||||||
---------
|
---------
|
||||||
|
@ -28,7 +28,6 @@ use Symfony\Bundle\FrameworkBundle\Routing\RouteLoaderInterface;
|
|||||||
use Symfony\Bundle\FullStack;
|
use Symfony\Bundle\FullStack;
|
||||||
use Symfony\Component\Asset\PackageInterface;
|
use Symfony\Component\Asset\PackageInterface;
|
||||||
use Symfony\Component\BrowserKit\AbstractBrowser;
|
use Symfony\Component\BrowserKit\AbstractBrowser;
|
||||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
|
||||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||||
use Symfony\Component\Cache\Adapter\ChainAdapter;
|
use Symfony\Component\Cache\Adapter\ChainAdapter;
|
||||||
@ -75,7 +74,6 @@ use Symfony\Component\Lock\Lock;
|
|||||||
use Symfony\Component\Lock\LockFactory;
|
use Symfony\Component\Lock\LockFactory;
|
||||||
use Symfony\Component\Lock\LockInterface;
|
use Symfony\Component\Lock\LockInterface;
|
||||||
use Symfony\Component\Lock\PersistingStoreInterface;
|
use Symfony\Component\Lock\PersistingStoreInterface;
|
||||||
use Symfony\Component\Lock\Store\FlockStore;
|
|
||||||
use Symfony\Component\Lock\Store\StoreFactory;
|
use Symfony\Component\Lock\Store\StoreFactory;
|
||||||
use Symfony\Component\Lock\StoreInterface;
|
use Symfony\Component\Lock\StoreInterface;
|
||||||
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory;
|
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory;
|
||||||
@ -1621,42 +1619,13 @@ class FrameworkExtension extends Extension
|
|||||||
$storeDefinitions = [];
|
$storeDefinitions = [];
|
||||||
foreach ($resourceStores as $storeDsn) {
|
foreach ($resourceStores as $storeDsn) {
|
||||||
$storeDsn = $container->resolveEnvPlaceholders($storeDsn, null, $usedEnvs);
|
$storeDsn = $container->resolveEnvPlaceholders($storeDsn, null, $usedEnvs);
|
||||||
switch (true) {
|
$storeDefinition = new Definition(PersistingStoreInterface::class);
|
||||||
case 'flock' === $storeDsn:
|
$storeDefinition->setFactory([StoreFactory::class, 'createStore']);
|
||||||
$storeDefinition = new Reference('lock.store.flock');
|
$storeDefinition->setArguments([$storeDsn]);
|
||||||
break;
|
|
||||||
case 0 === strpos($storeDsn, 'flock://'):
|
|
||||||
$flockPath = substr($storeDsn, 8);
|
|
||||||
|
|
||||||
$storeDefinitionId = '.lock.flock.store.'.$container->hash($storeDsn);
|
$container->setDefinition($storeDefinitionId = '.lock.'.$resourceName.'.store.'.$container->hash($storeDsn), $storeDefinition);
|
||||||
$container->register($storeDefinitionId, FlockStore::class)->addArgument($flockPath);
|
|
||||||
|
|
||||||
$storeDefinition = new Reference($storeDefinitionId);
|
$storeDefinition = new Reference($storeDefinitionId);
|
||||||
break;
|
|
||||||
case 'semaphore' === $storeDsn:
|
|
||||||
$storeDefinition = new Reference('lock.store.semaphore');
|
|
||||||
break;
|
|
||||||
case $usedEnvs || preg_match('#^[a-z]++://#', $storeDsn):
|
|
||||||
if (!$container->hasDefinition($connectionDefinitionId = '.lock_connection.'.$container->hash($storeDsn))) {
|
|
||||||
$connectionDefinition = new Definition(\stdClass::class);
|
|
||||||
$connectionDefinition->setPublic(false);
|
|
||||||
$connectionDefinition->setFactory([AbstractAdapter::class, 'createConnection']);
|
|
||||||
$connectionDefinition->setArguments([$storeDsn, ['lazy' => true]]);
|
|
||||||
$container->setDefinition($connectionDefinitionId, $connectionDefinition);
|
|
||||||
}
|
|
||||||
|
|
||||||
$storeDefinition = new Definition(PersistingStoreInterface::class);
|
|
||||||
$storeDefinition->setPublic(false);
|
|
||||||
$storeDefinition->setFactory([StoreFactory::class, 'createStore']);
|
|
||||||
$storeDefinition->setArguments([new Reference($connectionDefinitionId)]);
|
|
||||||
|
|
||||||
$container->setDefinition($storeDefinitionId = '.lock.'.$resourceName.'.store.'.$container->hash($storeDsn), $storeDefinition);
|
|
||||||
|
|
||||||
$storeDefinition = new Reference($storeDefinitionId);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new InvalidArgumentException(sprintf('Lock store DSN "%s" is not valid in resource "%s"', $storeDsn, $resourceName));
|
|
||||||
}
|
|
||||||
|
|
||||||
$storeDefinitions[] = $storeDefinition;
|
$storeDefinitions[] = $storeDefinition;
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,22 @@
|
|||||||
<services>
|
<services>
|
||||||
<defaults public="false" />
|
<defaults public="false" />
|
||||||
|
|
||||||
<service id="lock.store.flock" class="Symfony\Component\Lock\Store\FlockStore" />
|
<service id="lock.store.flock" class="Symfony\Component\Lock\Store\FlockStore">
|
||||||
|
<deprecated>The "%service_id%" service is deprecated since Symfony 4.4 and will be removed in 5.0.</deprecated>
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="lock.store.semaphore" class="Symfony\Component\Lock\Store\SemaphoreStore" />
|
<service id="lock.store.semaphore" class="Symfony\Component\Lock\Store\SemaphoreStore">
|
||||||
|
<deprecated>The "%service_id%" service is deprecated since Symfony 4.4 and will be removed in 5.0.</deprecated>
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="lock.store.memcached.abstract" class="Symfony\Component\Lock\Store\MemcachedStore" abstract="true">
|
<service id="lock.store.memcached.abstract" class="Symfony\Component\Lock\Store\MemcachedStore" abstract="true">
|
||||||
<argument /> <!-- Memcached connection service -->
|
<argument /> <!-- Memcached connection service -->
|
||||||
|
<deprecated>The "%service_id%" service is deprecated since Symfony 4.4 and will be removed in 5.0.</deprecated>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="lock.store.redis.abstract" class="Symfony\Component\Lock\Store\RedisStore" abstract="true">
|
<service id="lock.store.redis.abstract" class="Symfony\Component\Lock\Store\RedisStore" abstract="true">
|
||||||
<argument /> <!-- Redis connection service -->
|
<argument /> <!-- Redis connection service -->
|
||||||
|
<deprecated>The "%service_id%" service is deprecated since Symfony 4.4 and will be removed in 5.0.</deprecated>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="lock.store.combined.abstract" class="Symfony\Component\Lock\Store\CombinedStore" abstract="true">
|
<service id="lock.store.combined.abstract" class="Symfony\Component\Lock\Store\CombinedStore" abstract="true">
|
||||||
|
@ -7,7 +7,10 @@ CHANGELOG
|
|||||||
* added InvalidTtlException
|
* added InvalidTtlException
|
||||||
* deprecated `StoreInterface` in favor of `BlockingStoreInterface` and `PersistingStoreInterface`
|
* deprecated `StoreInterface` in favor of `BlockingStoreInterface` and `PersistingStoreInterface`
|
||||||
* `Factory` is deprecated, use `LockFactory` instead
|
* `Factory` is deprecated, use `LockFactory` instead
|
||||||
|
* `StoreFactory::createStore` allows PDO and Zookeeper DSN.
|
||||||
|
* deprecated services `lock.store.flock`, `lock.store.semaphore`, `lock.store.memcached.abstract` and `lock.store.redis.abstract`,
|
||||||
|
use `StoreFactory::createStore` instead.
|
||||||
|
|
||||||
4.2.0
|
4.2.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -58,8 +58,24 @@ class StoreFactory
|
|||||||
return new FlockStore(substr($connection, 8));
|
return new FlockStore(substr($connection, 8));
|
||||||
case 'semaphore' === $connection:
|
case 'semaphore' === $connection:
|
||||||
return new SemaphoreStore();
|
return new SemaphoreStore();
|
||||||
case class_exists(AbstractAdapter::class) && preg_match('#^[a-z]++://#', $connection):
|
case 0 === strpos($connection, 'redis://') && class_exists(AbstractAdapter::class):
|
||||||
return static::createStore(AbstractAdapter::createConnection($connection));
|
case 0 === strpos($connection, 'rediss://') && class_exists(AbstractAdapter::class):
|
||||||
|
return new RedisStore(AbstractAdapter::createConnection($connection, ['lazy' => true]));
|
||||||
|
case 0 === strpos($connection, 'memcached://') && class_exists(AbstractAdapter::class):
|
||||||
|
return new MemcachedStore(AbstractAdapter::createConnection($connection, ['lazy' => true]));
|
||||||
|
case 0 === strpos($connection, 'sqlite:'):
|
||||||
|
case 0 === strpos($connection, 'mysql:'):
|
||||||
|
case 0 === strpos($connection, 'pgsql:'):
|
||||||
|
case 0 === strpos($connection, 'oci:'):
|
||||||
|
case 0 === strpos($connection, 'sqlsrv:'):
|
||||||
|
case 0 === strpos($connection, 'sqlite3://'):
|
||||||
|
case 0 === strpos($connection, 'mysql2://'):
|
||||||
|
case 0 === strpos($connection, 'postgres://'):
|
||||||
|
case 0 === strpos($connection, 'postgresql://'):
|
||||||
|
case 0 === strpos($connection, 'mssql://'):
|
||||||
|
return new PdoStore($connection);
|
||||||
|
case 0 === strpos($connection, 'zookeeper://'):
|
||||||
|
return new ZookeeperStore(ZookeeperStore::createConnection($connection));
|
||||||
default:
|
default:
|
||||||
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
|
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Lock\Store;
|
namespace Symfony\Component\Lock\Store;
|
||||||
|
|
||||||
|
use Symfony\Component\Lock\Exception\InvalidArgumentException;
|
||||||
use Symfony\Component\Lock\Exception\LockAcquiringException;
|
use Symfony\Component\Lock\Exception\LockAcquiringException;
|
||||||
use Symfony\Component\Lock\Exception\LockConflictedException;
|
use Symfony\Component\Lock\Exception\LockConflictedException;
|
||||||
use Symfony\Component\Lock\Exception\LockReleasingException;
|
use Symfony\Component\Lock\Exception\LockReleasingException;
|
||||||
@ -34,6 +35,24 @@ class ZookeeperStore implements StoreInterface
|
|||||||
$this->zookeeper = $zookeeper;
|
$this->zookeeper = $zookeeper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function createConnection(string $dsn): \Zookeeper
|
||||||
|
{
|
||||||
|
if (0 !== strpos($dsn, 'zookeeper:')) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.', $dsn));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $params = parse_url($dsn)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Invalid Zookeeper DSN: %s.', $dsn));
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = $params['host'] ?? '';
|
||||||
|
if (isset($params['port'])) {
|
||||||
|
$host .= ':'.$params['port'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new \Zookeeper($host);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -16,6 +16,7 @@ use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
|||||||
use Symfony\Component\Cache\Traits\RedisProxy;
|
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||||
use Symfony\Component\Lock\Store\FlockStore;
|
use Symfony\Component\Lock\Store\FlockStore;
|
||||||
use Symfony\Component\Lock\Store\MemcachedStore;
|
use Symfony\Component\Lock\Store\MemcachedStore;
|
||||||
|
use Symfony\Component\Lock\Store\PdoStore;
|
||||||
use Symfony\Component\Lock\Store\RedisStore;
|
use Symfony\Component\Lock\Store\RedisStore;
|
||||||
use Symfony\Component\Lock\Store\SemaphoreStore;
|
use Symfony\Component\Lock\Store\SemaphoreStore;
|
||||||
use Symfony\Component\Lock\Store\StoreFactory;
|
use Symfony\Component\Lock\Store\StoreFactory;
|
||||||
@ -50,6 +51,7 @@ class StoreFactoryTest extends TestCase
|
|||||||
}
|
}
|
||||||
if (class_exists(\Zookeeper::class)) {
|
if (class_exists(\Zookeeper::class)) {
|
||||||
yield [$this->createMock(\Zookeeper::class), ZookeeperStore::class];
|
yield [$this->createMock(\Zookeeper::class), ZookeeperStore::class];
|
||||||
|
yield ['zookeeper://localhost:2181', ZookeeperStore::class];
|
||||||
}
|
}
|
||||||
if (\extension_loaded('sysvsem')) {
|
if (\extension_loaded('sysvsem')) {
|
||||||
yield ['semaphore', SemaphoreStore::class];
|
yield ['semaphore', SemaphoreStore::class];
|
||||||
@ -57,6 +59,26 @@ class StoreFactoryTest extends TestCase
|
|||||||
if (class_exists(\Memcached::class) && class_exists(AbstractAdapter::class)) {
|
if (class_exists(\Memcached::class) && class_exists(AbstractAdapter::class)) {
|
||||||
yield ['memcached://server.com', MemcachedStore::class];
|
yield ['memcached://server.com', MemcachedStore::class];
|
||||||
}
|
}
|
||||||
|
if (class_exists(\Redis::class) && class_exists(AbstractAdapter::class)) {
|
||||||
|
yield ['redis://localhost', RedisStore::class];
|
||||||
|
}
|
||||||
|
if (class_exists(\PDO::class)) {
|
||||||
|
yield ['sqlite:/tmp/sqlite.db', PdoStore::class];
|
||||||
|
yield ['sqlite::memory:', PdoStore::class];
|
||||||
|
yield ['mysql:host=localhost;dbname=test;', PdoStore::class];
|
||||||
|
yield ['pgsql:host=localhost;dbname=test;', PdoStore::class];
|
||||||
|
yield ['oci:host=localhost;dbname=test;', PdoStore::class];
|
||||||
|
yield ['sqlsrv:server=localhost;Database=test', PdoStore::class];
|
||||||
|
yield ['mysql://server.com/test', PdoStore::class];
|
||||||
|
yield ['mysql2://server.com/test', PdoStore::class];
|
||||||
|
yield ['pgsql://server.com/test', PdoStore::class];
|
||||||
|
yield ['postgres://server.com/test', PdoStore::class];
|
||||||
|
yield ['postgresql://server.com/test', PdoStore::class];
|
||||||
|
yield ['sqlite:///tmp/test', PdoStore::class];
|
||||||
|
yield ['sqlite3:///tmp/test', PdoStore::class];
|
||||||
|
yield ['oci:///server.com/test', PdoStore::class];
|
||||||
|
yield ['mssql:///server.com/test', PdoStore::class];
|
||||||
|
}
|
||||||
|
|
||||||
yield ['flock', FlockStore::class];
|
yield ['flock', FlockStore::class];
|
||||||
yield ['flock://'.sys_get_temp_dir(), FlockStore::class];
|
yield ['flock://'.sys_get_temp_dir(), FlockStore::class];
|
||||||
|
@ -30,11 +30,26 @@ class ZookeeperStoreTest extends AbstractStoreTest
|
|||||||
{
|
{
|
||||||
$zookeeper_server = getenv('ZOOKEEPER_HOST').':2181';
|
$zookeeper_server = getenv('ZOOKEEPER_HOST').':2181';
|
||||||
|
|
||||||
$zookeeper = new \Zookeeper(implode(',', [$zookeeper_server]));
|
$zookeeper = new \Zookeeper($zookeeper_server);
|
||||||
|
|
||||||
return StoreFactory::createStore($zookeeper);
|
return StoreFactory::createStore($zookeeper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideValidConnectionString
|
||||||
|
*/
|
||||||
|
public function testCreateConnection(string $connectionString)
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf(\Zookeeper::class, ZookeeperStore::createConnection($connectionString));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideValidConnectionString(): iterable
|
||||||
|
{
|
||||||
|
yield 'single host' => ['zookeeper://localhost:2181'];
|
||||||
|
yield 'single multiple host' => ['zookeeper://localhost:2181,localhost:2181'];
|
||||||
|
yield 'with extra attributes' => ['zookeeper://localhost:2181/path?option=value'];
|
||||||
|
}
|
||||||
|
|
||||||
public function testSaveSucceedsWhenPathContainsMoreThanOneNode()
|
public function testSaveSucceedsWhenPathContainsMoreThanOneNode()
|
||||||
{
|
{
|
||||||
$store = $this->getStore();
|
$store = $this->getStore();
|
||||||
|
Reference in New Issue
Block a user