feature #28598 [Cache] support configuring multiple Memcached servers in one DSN (nicolas-grekas)
This PR was merged into the 4.2-dev branch.
Discussion
----------
[Cache] support configuring multiple Memcached servers in one DSN
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #27855
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/issues/10402
Useful to reconfigure dynamically an array of memcached servers (eg removing a dead one or adding a new one).
DSN format is e.g. `memcached://localhost?host[foo.bar]=3`.
To ease generating the DSN programmatically, it works also with `memcached:?host[localhost]&host[localhost:12345]&host[/some/memcached.sock:]=3`.
The key of the "host" parameter is a "host:port" pair, the value is the weight of the "host:port" pair.
Sockets need to be specified with the trailing `:` (as shown in the last example).
Commits
-------
8e0605ac18
[Cache] support configuring multiple Memcached servers in one DSN
This commit is contained in:
commit
e6deb092fc
@ -133,7 +133,7 @@ class CachePoolPass implements CompilerPassInterface
|
||||
{
|
||||
$container->resolveEnvPlaceholders($name, null, $usedEnvs);
|
||||
|
||||
if ($usedEnvs || preg_match('#^[a-z]++://#', $name)) {
|
||||
if ($usedEnvs || preg_match('#^[a-z]++:#', $name)) {
|
||||
$dsn = $name;
|
||||
|
||||
if (!$container->hasDefinition($name = '.cache_connection.'.ContainerBuilder::hash($dsn))) {
|
||||
|
@ -151,7 +151,7 @@ abstract class AbstractAdapter implements AdapterInterface, CacheInterface, Logg
|
||||
if (0 === strpos($dsn, 'redis://')) {
|
||||
return RedisAdapter::createConnection($dsn, $options);
|
||||
}
|
||||
if (0 === strpos($dsn, 'memcached://')) {
|
||||
if (0 === strpos($dsn, 'memcached:')) {
|
||||
return MemcachedAdapter::createConnection($dsn, $options);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ CHANGELOG
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* added support for configuring multiple Memcached servers in one DSN
|
||||
* added `MarshallerInterface` and `DefaultMarshaller` to allow changing the serializer and provide one that automatically uses igbinary when available
|
||||
* 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
|
||||
|
@ -192,4 +192,46 @@ class MemcachedAdapterTest extends AdapterTestCase
|
||||
array(\Memcached::OPT_SOCKET_RECV_SIZE => 1, \Memcached::OPT_SOCKET_SEND_SIZE => 2, \Memcached::OPT_RETRY_TIMEOUT => 8),
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultiServerDsn()
|
||||
{
|
||||
$dsn = 'memcached:?host[localhost]&host[localhost:12345]&host[/some/memcached.sock:]=3';
|
||||
$client = MemcachedAdapter::createConnection($dsn);
|
||||
|
||||
$expected = array(
|
||||
0 => array(
|
||||
'host' => 'localhost',
|
||||
'port' => 11211,
|
||||
'type' => 'TCP',
|
||||
),
|
||||
1 => array(
|
||||
'host' => 'localhost',
|
||||
'port' => 12345,
|
||||
'type' => 'TCP',
|
||||
),
|
||||
2 => array(
|
||||
'host' => '/some/memcached.sock',
|
||||
'port' => 0,
|
||||
'type' => 'SOCKET',
|
||||
),
|
||||
);
|
||||
$this->assertSame($expected, $client->getServerList());
|
||||
|
||||
$dsn = 'memcached://localhost?host[foo.bar]=3';
|
||||
$client = MemcachedAdapter::createConnection($dsn);
|
||||
|
||||
$expected = array(
|
||||
0 => array(
|
||||
'host' => 'localhost',
|
||||
'port' => 11211,
|
||||
'type' => 'TCP',
|
||||
),
|
||||
1 => array(
|
||||
'host' => 'foo.bar',
|
||||
'port' => 11211,
|
||||
'type' => 'TCP',
|
||||
),
|
||||
);
|
||||
$this->assertSame($expected, $client->getServerList());
|
||||
}
|
||||
}
|
||||
|
@ -99,19 +99,43 @@ trait MemcachedTrait
|
||||
if (\is_array($dsn)) {
|
||||
continue;
|
||||
}
|
||||
if (0 !== strpos($dsn, 'memcached://')) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s does not start with "memcached://"', $dsn));
|
||||
if (0 !== strpos($dsn, 'memcached:')) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s does not start with "memcached:"', $dsn));
|
||||
}
|
||||
$params = preg_replace_callback('#^memcached://(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) {
|
||||
if (!empty($m[1])) {
|
||||
list($username, $password) = explode(':', $m[1], 2) + array(1 => null);
|
||||
$params = preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) {
|
||||
if (!empty($m[2])) {
|
||||
list($username, $password) = explode(':', $m[2], 2) + array(1 => null);
|
||||
}
|
||||
|
||||
return 'file://';
|
||||
return 'file:'.($m[1] ?? '');
|
||||
}, $dsn);
|
||||
if (false === $params = parse_url($params)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
|
||||
}
|
||||
$query = $hosts = array();
|
||||
if (isset($params['query'])) {
|
||||
parse_str($params['query'], $query);
|
||||
|
||||
if (isset($query['host'])) {
|
||||
if (!\is_array($hosts = $query['host'])) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
|
||||
}
|
||||
foreach ($hosts as $host => $weight) {
|
||||
if (false === $port = strrpos($host, ':')) {
|
||||
$hosts[$host] = array($host, 11211, (int) $weight);
|
||||
} else {
|
||||
$hosts[$host] = array(substr($host, 0, $port), (int) substr($host, 1 + $port), (int) $weight);
|
||||
}
|
||||
}
|
||||
$hosts = array_values($hosts);
|
||||
unset($query['host']);
|
||||
}
|
||||
if ($hosts && !isset($params['host']) && !isset($params['path'])) {
|
||||
unset($servers[$i]);
|
||||
$servers = array_merge($servers, $hosts);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!isset($params['host']) && !isset($params['path'])) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
|
||||
}
|
||||
@ -124,13 +148,16 @@ trait MemcachedTrait
|
||||
'port' => isset($params['host']) ? 11211 : null,
|
||||
'weight' => 0,
|
||||
);
|
||||
if (isset($params['query'])) {
|
||||
parse_str($params['query'], $query);
|
||||
if ($query) {
|
||||
$params += $query;
|
||||
$options = $query + $options;
|
||||
}
|
||||
|
||||
$servers[$i] = array($params['host'], $params['port'], $params['weight']);
|
||||
|
||||
if ($hosts) {
|
||||
$servers = array_merge($servers, $hosts);
|
||||
}
|
||||
}
|
||||
|
||||
// set client's options
|
||||
|
Reference in New Issue
Block a user