From 77f1729eec700431f24c1594a76697843b507406 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 2 Nov 2016 09:41:14 +0800 Subject: [PATCH] MDL-56675 cachestore_memcached: Disable store if not purgeable If the store is shared and the getAllKeys function is broken due to an incompatability between libmemcached and memcached >= 1.4.23, then it is not possible to purge the cache, and we cannot support the plugin. This patch adjusts the isready check to additionally check if the combination of libmemcached and memcached is affected by this issue. --- cache/stores/memcached/lib.php | 52 ++++++++++++++++++- .../stores/memcached/tests/memcached_test.php | 4 ++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/cache/stores/memcached/lib.php b/cache/stores/memcached/lib.php index 56df93771b9..ba7a6d55295 100644 --- a/cache/stores/memcached/lib.php +++ b/cache/stores/memcached/lib.php @@ -229,8 +229,56 @@ class cachestore_memcached extends cache_store implements cache_is_configurable $version = phpversion('memcached'); $this->candeletemulti = ($version && version_compare($version, self::REQUIRED_VERSION, '>=')); - // Test the connection to the main connection. - $this->isready = @$this->connection->set("ping", 'ping', 1); + $this->isready = $this->is_connection_ready(); + } + + /** + * Confirm whether the connection is ready and usable. + * + * @return boolean + */ + public function is_connection_ready() { + if (!@$this->connection->set("ping", 'ping', 1)) { + // Test the connection to the server. + return false; + } + + if ($this->isshared) { + // There is a bug in libmemcached which means that it is not possible to purge the cache in a shared cache + // configuration. + // This issue currently affects: + // - memcached 1.4.23+ with php-memcached <= 2.2.0 + // The following combinations are not affected: + // - memcached <= 1.4.22 with any version of php-memcached + // - any version of memcached with php-memcached >= 3.0.1 + + + // This check is cheapest as it does not involve connecting to the server at all. + $safecombination = false; + $extension = new ReflectionExtension('memcached'); + if ((version_compare($extension->getVersion(), '3.0.1') >= 0)) { + // This is php-memcached version >= 3.0.1 which is a safe combination. + $safecombination = true; + } + + if (!$safecombination && (version_compare($this->connection->getVersion(), '1.4.22') <= 0)) { + // This is memcached server version <= 1.4.22 which is a safe combination. + $safecombination = true; + } + + if (!$safecombination) { + // This is memcached 1.4.23+ and php-memcached < 3.0.1. + // The issue may have been resolved in a subsequent update to any of the three libraries. + // The only way to safely determine if the combination is safe is to call getAllKeys. + // A safe combination will return an array, whilst an affected combination will return false. + // This is the most expensive check. + if (!is_array($this->connection->getAllKeys())) { + return false; + } + } + } + + return true; } /** diff --git a/cache/stores/memcached/tests/memcached_test.php b/cache/stores/memcached/tests/memcached_test.php index bef5cbe2704..db41269cd74 100644 --- a/cache/stores/memcached/tests/memcached_test.php +++ b/cache/stores/memcached/tests/memcached_test.php @@ -288,6 +288,10 @@ class cachestore_memcached_test extends cachestore_tests { $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test'); $cachestore = $this->create_test_cache_with_config($definition, array('isshared' => true)); + if (!$cachestore->is_connection_ready()) { + $this->markTestSkipped('Could not test cachestore_memcached. Connection is not ready.'); + } + $connection = new Memcached(crc32(__METHOD__)); $connection->addServers($this->get_servers(TEST_CACHESTORE_MEMCACHED_TESTSERVERS)); $connection->setOptions(array(