feature #27694 [FrameworkBundle][Cache] Allow configuring PDO-based cache pools, with table auto-creation on first use (nicolas-grekas)
This PR was merged into the 4.2-dev branch.
Discussion
----------
[FrameworkBundle][Cache] Allow configuring PDO-based cache pools, with table auto-creation on first use
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
* Allowed configuring PDO-based cache pools via a new `cache.adapter.pdo` abstract service
* added automatic table creation when using Doctrine DBAL with PDO-based backends
Commits
-------
1484117430
[FrameworkBundle][Cache] Allow configuring PDO-based cache pools, with table auto-creation on first use
This commit is contained in:
commit
cbda6a3c72
@ -5,6 +5,7 @@ CHANGELOG
|
|||||||
-----
|
-----
|
||||||
|
|
||||||
* Allowed configuring taggable cache pools via a new `framework.cache.pools.tags` option (bool|service-id)
|
* Allowed configuring taggable cache pools via a new `framework.cache.pools.tags` option (bool|service-id)
|
||||||
|
* Allowed configuring PDO-based cache pools via a new `cache.adapter.pdo` abstract service
|
||||||
* Deprecated auto-injection of the container in AbstractController instances, register them as service subscribers instead
|
* Deprecated auto-injection of the container in AbstractController instances, register them as service subscribers instead
|
||||||
* Deprecated processing of services tagged `security.expression_language_provider` in favor of a new `AddExpressionLanguageProvidersPass` in SecurityBundle.
|
* Deprecated processing of services tagged `security.expression_language_provider` in favor of a new `AddExpressionLanguageProvidersPass` in SecurityBundle.
|
||||||
|
|
||||||
|
@ -867,6 +867,7 @@ class Configuration implements ConfigurationInterface
|
|||||||
->scalarNode('default_psr6_provider')->end()
|
->scalarNode('default_psr6_provider')->end()
|
||||||
->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end()
|
->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end()
|
||||||
->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end()
|
->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end()
|
||||||
|
->scalarNode('default_pdo_provider')->defaultValue('doctrine.dbal.default_connection')->end()
|
||||||
->arrayNode('pools')
|
->arrayNode('pools')
|
||||||
->useAttributeAsKey('name')
|
->useAttributeAsKey('name')
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
|
@ -1553,7 +1553,7 @@ class FrameworkExtension extends Extension
|
|||||||
// Inline any env vars referenced in the parameter
|
// Inline any env vars referenced in the parameter
|
||||||
$container->setParameter('cache.prefix.seed', $container->resolveEnvPlaceholders($container->getParameter('cache.prefix.seed'), true));
|
$container->setParameter('cache.prefix.seed', $container->resolveEnvPlaceholders($container->getParameter('cache.prefix.seed'), true));
|
||||||
}
|
}
|
||||||
foreach (array('doctrine', 'psr6', 'redis', 'memcached') as $name) {
|
foreach (array('doctrine', 'psr6', 'redis', 'memcached', 'pdo') as $name) {
|
||||||
if (isset($config[$name = 'default_'.$name.'_provider'])) {
|
if (isset($config[$name = 'default_'.$name.'_provider'])) {
|
||||||
$container->setAlias('cache.'.$name, new Alias(Compiler\CachePoolPass::getServiceProvider($container, $config[$name]), false));
|
$container->setAlias('cache.'.$name, new Alias(Compiler\CachePoolPass::getServiceProvider($container, $config[$name]), false));
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,19 @@
|
|||||||
</call>
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="cache.adapter.pdo" class="Symfony\Component\Cache\Adapter\PdoAdapter" abstract="true">
|
||||||
|
<tag name="cache.pool" provider="cache.default_pdo_provider" clearer="cache.default_clearer" reset="reset" />
|
||||||
|
<tag name="monolog.logger" channel="cache" />
|
||||||
|
<argument /> <!-- PDO connection service -->
|
||||||
|
<argument /> <!-- namespace -->
|
||||||
|
<argument>0</argument> <!-- default lifetime -->
|
||||||
|
<argument type="collection" /> <!-- table options -->
|
||||||
|
<argument type="service" id="cache.default_marshaller" on-invalid="ignore" />
|
||||||
|
<call method="setLogger">
|
||||||
|
<argument type="service" id="logger" on-invalid="ignore" />
|
||||||
|
</call>
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="cache.adapter.array" class="Symfony\Component\Cache\Adapter\ArrayAdapter" abstract="true">
|
<service id="cache.adapter.array" class="Symfony\Component\Cache\Adapter\ArrayAdapter" abstract="true">
|
||||||
<tag name="cache.pool" clearer="cache.default_clearer" />
|
<tag name="cache.pool" clearer="cache.default_clearer" />
|
||||||
<tag name="monolog.logger" channel="cache" />
|
<tag name="monolog.logger" channel="cache" />
|
||||||
|
@ -267,6 +267,7 @@ class ConfigurationTest extends TestCase
|
|||||||
'directory' => '%kernel.cache_dir%/pools',
|
'directory' => '%kernel.cache_dir%/pools',
|
||||||
'default_redis_provider' => 'redis://localhost',
|
'default_redis_provider' => 'redis://localhost',
|
||||||
'default_memcached_provider' => 'memcached://localhost',
|
'default_memcached_provider' => 'memcached://localhost',
|
||||||
|
'default_pdo_provider' => 'doctrine.dbal.default_connection',
|
||||||
),
|
),
|
||||||
'workflows' => array(
|
'workflows' => array(
|
||||||
'enabled' => false,
|
'enabled' => false,
|
||||||
|
@ -7,6 +7,7 @@ CHANGELOG
|
|||||||
* added `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache
|
* added `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache
|
||||||
* added sub-second expiry accuracy for backends that support it
|
* added sub-second expiry accuracy for backends that support it
|
||||||
* added support for phpredis 4 `compression` and `tcp_keepalive` options
|
* added support for phpredis 4 `compression` and `tcp_keepalive` options
|
||||||
|
* added automatic table creation when using Doctrine DBAL with PDO-based backends
|
||||||
* throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool
|
* throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool
|
||||||
* deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead
|
* deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead
|
||||||
* deprecated the `AbstractAdapter::createSystemCache()` method
|
* deprecated the `AbstractAdapter::createSystemCache()` method
|
||||||
|
@ -33,7 +33,6 @@ class PdoDbalAdapterTest extends AdapterTestCase
|
|||||||
self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
|
self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
|
||||||
|
|
||||||
$pool = new PdoAdapter(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile)));
|
$pool = new PdoAdapter(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile)));
|
||||||
$pool->createTable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tearDownAfterClass()
|
public static function tearDownAfterClass()
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Cache\Traits;
|
|||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
|
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
|
||||||
use Doctrine\DBAL\DBALException;
|
use Doctrine\DBAL\DBALException;
|
||||||
|
use Doctrine\DBAL\Exception\TableNotFoundException;
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
|
||||||
@ -150,7 +151,11 @@ trait PdoTrait
|
|||||||
$deleteSql .= " AND $this->idCol LIKE :namespace";
|
$deleteSql .= " AND $this->idCol LIKE :namespace";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$delete = $this->getConnection()->prepare($deleteSql);
|
$delete = $this->getConnection()->prepare($deleteSql);
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
$delete->bindValue(':time', time(), \PDO::PARAM_INT);
|
$delete->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||||
|
|
||||||
if ('' !== $this->namespace) {
|
if ('' !== $this->namespace) {
|
||||||
@ -229,7 +234,10 @@ trait PdoTrait
|
|||||||
$sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'";
|
$sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$conn->exec($sql);
|
$conn->exec($sql);
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -241,8 +249,11 @@ trait PdoTrait
|
|||||||
{
|
{
|
||||||
$sql = str_pad('', (count($ids) << 1) - 1, '?,');
|
$sql = str_pad('', (count($ids) << 1) - 1, '?,');
|
||||||
$sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)";
|
$sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)";
|
||||||
|
try {
|
||||||
$stmt = $this->getConnection()->prepare($sql);
|
$stmt = $this->getConnection()->prepare($sql);
|
||||||
$stmt->execute(array_values($ids));
|
$stmt->execute(array_values($ids));
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -302,7 +313,14 @@ trait PdoTrait
|
|||||||
|
|
||||||
$now = time();
|
$now = time();
|
||||||
$lifetime = $lifetime ?: null;
|
$lifetime = $lifetime ?: null;
|
||||||
|
try {
|
||||||
$stmt = $conn->prepare($sql);
|
$stmt = $conn->prepare($sql);
|
||||||
|
} catch (TableNotFoundException $e) {
|
||||||
|
if (!$conn->isTransactionActive() || \in_array($this->driver, array('pgsql', 'sqlite', 'sqlsrv'), true)) {
|
||||||
|
$this->createTable();
|
||||||
|
}
|
||||||
|
$stmt = $conn->prepare($sql);
|
||||||
|
}
|
||||||
|
|
||||||
if ('sqlsrv' === $driver || 'oci' === $driver) {
|
if ('sqlsrv' === $driver || 'oci' === $driver) {
|
||||||
$stmt->bindParam(1, $id);
|
$stmt->bindParam(1, $id);
|
||||||
|
@ -28,11 +28,12 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"cache/integration-tests": "dev-master",
|
"cache/integration-tests": "dev-master",
|
||||||
"doctrine/cache": "~1.6",
|
"doctrine/cache": "~1.6",
|
||||||
"doctrine/dbal": "~2.4",
|
"doctrine/dbal": "~2.5",
|
||||||
"predis/predis": "~1.0",
|
"predis/predis": "~1.0",
|
||||||
"symfony/var-dumper": "^4.1.1"
|
"symfony/var-dumper": "^4.1.1"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
|
"doctrine/dbal": "<2.5",
|
||||||
"symfony/var-dumper": "<3.4"
|
"symfony/var-dumper": "<3.4"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
Reference in New Issue
Block a user