/** * Test purging the apcu cache store. */ public function test_purge() { if (!cachestore_apcu::are_requirements_met()) { $this->markTestSkipped('Could not test cachestore_apcu. Requirements are not met.'); } $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_apcu', 'phpunit_test'); $instance = cachestore_apcu::initialise_unit_test_instance($definition); // Test a simple purge return. $this->assertTrue($instance->purge()); // Test purge works. $this->assertTrue($instance->set('test', 'monster')); $this->assertSame('monster', $instance->get('test')); $this->assertTrue($instance->purge()); $this->assertFalse($instance->get('test')); // Test purge with custom data. $this->assertTrue($instance->set('test', 'monster')); $this->assertSame('monster', $instance->get('test')); $this->assertTrue(apcu_store('test', 'pirate', 180)); $this->assertSame('monster', $instance->get('test')); $this->assertTrue(apcu_exists('test')); $this->assertSame('pirate', apcu_fetch('test')); // Purge and check that our data is gone but the the custom data is still there. $this->assertTrue($instance->purge()); $this->assertFalse($instance->get('test')); $this->assertTrue(apcu_exists('test')); $this->assertSame('pirate', apcu_fetch('test')); }
/** * Run the unit tests for the store. */ public function test_test_instance() { $class = $this->get_class_name(); $modes = $class::get_supported_modes(); if ($modes & cache_store::MODE_APPLICATION) { $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, $class, 'phpunit_test'); $instance = new $class($class . '_test', $class::unit_test_configuration()); if (!$instance->is_ready()) { $this->markTestSkipped('Could not test ' . $class . '. No test instance configured for application caches.'); } else { $instance->initialise($definition); $this->run_tests($instance); } } if ($modes & cache_store::MODE_SESSION) { $definition = cache_definition::load_adhoc(cache_store::MODE_SESSION, $class, 'phpunit_test'); $instance = new $class($class . '_test', $class::unit_test_configuration()); if (!$instance->is_ready()) { $this->markTestSkipped('Could not test ' . $class . '. No test instance configured for session caches.'); } else { $instance->initialise($definition); $this->run_tests($instance); } } if ($modes & cache_store::MODE_REQUEST) { $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $class, 'phpunit_test'); $instance = new $class($class . '_test', $class::unit_test_configuration()); if (!$instance->is_ready()) { $this->markTestSkipped('Could not test ' . $class . '. No test instance configured for request caches.'); } else { $instance->initialise($definition); $this->run_tests($instance); } } }
/** * Run the unit tests for the store. */ public function test_test_instance() { $class = $this->get_class_name(); if (!class_exists($class) || !method_exists($class, 'initialise_test_instance') || !$class::are_requirements_met()) { $this->markTestSkipped('Could not test ' . $class . '. Requirements are not met.'); } $modes = $class::get_supported_modes(); if ($modes & cache_store::MODE_APPLICATION) { $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, $class, 'phpunit_test'); $instance = $class::initialise_unit_test_instance($definition); if (!$instance) { $this->markTestSkipped('Could not test ' . $class . '. No test instance configured for application caches.'); } else { $this->run_tests($instance); } } if ($modes & cache_store::MODE_SESSION) { $definition = cache_definition::load_adhoc(cache_store::MODE_SESSION, $class, 'phpunit_test'); $instance = $class::initialise_unit_test_instance($definition); if (!$instance) { $this->markTestSkipped('Could not test ' . $class . '. No test instance configured for session caches.'); } else { $this->run_tests($instance); } } if ($modes & cache_store::MODE_REQUEST) { $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $class, 'phpunit_test'); $instance = $class::initialise_unit_test_instance($definition); if (!$instance) { $this->markTestSkipped('Could not test ' . $class . '. No test instance configured for request caches.'); } else { $this->run_tests($instance); } } }
/** * Sets a key value pair into the static acceleration array. * * @param string $key The parsed key * @param mixed $data * @return bool */ protected function static_acceleration_set($key, $data) { if ($this->staticaccelerationsize !== false && isset($this->staticaccelerationkeys[$key])) { $this->staticaccelerationcount--; unset($this->staticaccelerationkeys[$key]); } // We serialize anything that's not; // 1. A known scalar safe value. // 2. A definition that says it's simpledata. We trust it that it doesn't contain dangerous references. // 3. An object that handles dereferencing by itself. if (is_scalar($data) || $this->definition->uses_simple_data() || $data instanceof cache_cached_object) { $this->staticaccelerationarray[$key]['data'] = $data; $this->staticaccelerationarray[$key]['serialized'] = false; } else { $this->staticaccelerationarray[$key]['data'] = serialize($data); $this->staticaccelerationarray[$key]['serialized'] = true; } if ($this->staticaccelerationsize !== false) { $this->staticaccelerationcount++; $this->staticaccelerationkeys[$key] = $key; if ($this->staticaccelerationcount > $this->staticaccelerationsize) { $dropkey = array_shift($this->staticaccelerationkeys); unset($this->staticaccelerationarray[$dropkey]); $this->staticaccelerationcount--; } } return true; }
/** * Sets an item in the cache given its key and data value. * * @param string $key The key to use. * @param mixed $data The data to set. * @return bool True if the operation was a success false otherwise. */ public function set($key, $data) { if ($this->encode) { // We must serialise this data. $data = serialize($data); } return $this->connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl()); }
/** * Sets many items in the cache in a single transaction. * * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two * keys, 'key' and 'value'. * @return int The number of items successfully set. It is up to the developer to check this matches the number of items * sent ... if they care that is. */ public function set_many(array $keyvaluearray) { $store = array(); foreach ($keyvaluearray as $pair) { $store[$this->prepare_key($pair['key'])] = $pair['value']; } $result = apcu_store($store, null, $this->definition->get_ttl()); return count($keyvaluearray) - count($result); }
/** * Sets many items in the cache in a single transaction. * * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two * keys, 'key' and 'value'. * @return int The number of items successfully set. It is up to the developer to check this matches the number of items * sent ... if they care that is. */ public function set_many(array $keyvaluearray) { $count = 0; foreach ($keyvaluearray as $pair) { if ($this->connection->set($this->parse_key($pair['key']), $pair['value'], MEMCACHE_COMPRESSED, $this->definition->get_ttl())) { $count++; } } return $count; }
/** * Sets an item in the cache given its key and data value. * * @param string $key The key to use. * @param mixed $data The data to set. * @return bool True if the operation was a success false otherwise. */ public function set($key, $data) { if ($this->clustered) { $status = true; foreach ($this->setconnections as $connection) { $status = $connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl()) && $status; } return $status; } return $this->connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl()); }
/** * Sets many items in the cache in a single transaction. * * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two * keys, 'key' and 'value'. * @return int The number of items successfully set. It is up to the developer to check this matches the number of items * sent ... if they care that is. */ public function set_many(array $keyvaluearray) { $pairs = array(); foreach ($keyvaluearray as $pair) { $pairs[$pair['key']] = $pair['value']; } if ($this->connection->setMulti($pairs, $this->definition->get_ttl())) { return count($keyvaluearray); } return 0; }
public function test_different_caches_have_different_prefixes() { $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_apcu', 'phpunit_test'); $instance = cachestore_apcu::initialise_unit_test_instance($definition); $definition2 = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_apcu', 'phpunit_test2'); $instance2 = cachestore_apcu::initialise_unit_test_instance($definition2); $instance->set('test1', 1); $this->assertFalse($instance2->get('test1')); $instance2->purge(); $this->assertSame(1, $instance->get('test1')); }
protected function setUp() { if (!defined('CACHESTORE_REDIS_TEST_SERVER')) { $this->markTestSkipped('Must define CACHESTORE_REDIS_TEST_SERVER to test Redis cache store'); } if (!cachestore_redis::are_requirements_met()) { $this->markTestSkipped('Requirements for Redis cache store are not met'); } $this->store = new cachestore_redis('test', array('server' => CACHESTORE_REDIS_TEST_SERVER, 'prefix' => 'phpunit')); $this->store->initialise(cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'foo_bar', 'baz')); }
/** * Parses the key turning it into a string (or array is required) suitable to be passed to the cache store. * * @param string|int $key As passed to get|set|delete etc. * @return string|array String unless the store supports multi-identifiers in which case an array if returned. */ protected function parse_key($key) { // First up if the store supports multiple keys we'll go with that. if ($this->store->supports_multiple_identifiers()) { $result = $this->definition->generate_multi_key_parts(); $result['key'] = $key; return $result; } // If not we need to generate a hash and to for that we use the cache_helper. return cache_helper::hash_key($key, $this->definition); }
/** * Creates the required cachestore for the tests to run against Redis. * * @return cachestore_redis */ protected function create_cachestore_redis() { /** @var cache_definition $definition */ $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_redis', 'phpunit_test'); $store = new cachestore_redis('Test', cachestore_redis::unit_test_configuration()); $store->initialise($definition); $this->store = $store; if (!$store) { $this->markTestSkipped(); } return $store; }
/** * Returns the item from the static acceleration array if it exists there. * * @param string $key The parsed key * @return mixed|false The data from the static acceleration array or false if it wasn't there. */ protected function get_from_persist_cache($key) { // This method of checking if an array was supplied is faster than is_array. if ($key === (array) $key) { $key = $key['key']; } // This isset check is faster than array_key_exists but will return false // for null values, meaning null values will come from backing store not // the static acceleration array. We think this okay because null usage should be // very rare (see comment in MDL-39472). if (!$this->staticacceleration || !isset($this->staticaccelerationarray[$key])) { $result = false; } else { $data = $this->staticaccelerationarray[$key]; if (!$this->has_a_ttl() || !$data instanceof cache_ttl_wrapper) { if ($data instanceof cache_cached_object) { $data = $data->restore_object(); } $result = $data; } else { if ($data->has_expired()) { $this->delete_from_persist_cache($key); $result = false; } else { if ($data instanceof cache_cached_object) { $data = $data->restore_object(); } $result = $data->data; } } } if ($result) { if ($this->perfdebug) { cache_helper::record_cache_hit('** static acceleration **', $this->definition->get_id()); } if ($this->staticaccelerationsize > 1 && $this->staticaccelerationcount > 1) { // Check to see if this is the last item on the static acceleration keys array. if (end($this->staticaccelerationkeys) !== $key) { // It isn't the last item. // Move the item to the end of the array so that it is last to be removed. unset($this->staticaccelerationkeys[$key]); $this->staticaccelerationkeys[$key] = $key; } } return $result; } else { if ($this->perfdebug) { cache_helper::record_cache_miss('** static acceleration **', $this->definition->get_id()); } return false; } }
/** * A small additional test to make sure definitions that hash a hash starting with a number work OK */ public function test_collection_name() { // This generates a definition that has a hash starting with a number. MDL-46208. $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_mongodb', 'abc'); $instance = cachestore_mongodb::initialise_test_instance($definition); if (!$instance) { $this->markTestSkipped(); } $this->assertTrue($instance->set(1, 'alpha')); $this->assertTrue($instance->set(2, 'beta')); $this->assertEquals('alpha', $instance->get(1)); $this->assertEquals('beta', $instance->get(2)); $this->assertEquals(array(1 => 'alpha', 2 => 'beta'), $instance->get_many(array(1, 2))); }
/** * Sets an item in the cache given its key and data value. * * @param string $key The key to use. * @param mixed $data The data to set. * @return bool True if the operation was a success false otherwise. */ public function set($key, $data) { if ($this->encode) { // We must serialise this data. $data = serialize($data); } if ($this->clustered) { $status = true; foreach ($this->setconnections as $connection) { $status = $connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl()) && $status; } return $status; } return $this->connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl()); }
/** * Testing cachestore_file::get with prescan enabled and with * deleting the cache between the prescan and the call to get. * * The deleting of cache simulates some other process purging * the cache. */ public function test_cache_get_with_prescan_and_purge() { global $CFG; $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, 'cachestore_file', 'phpunit_test'); $name = 'File test'; $path = make_cache_directory('cachestore_file_test'); $cache = new cachestore_file($name, array('path' => $path, 'prescan' => true)); $cache->initialise($definition); $cache->set('testing', 'value'); $path = make_cache_directory('cachestore_file_test'); $cache = new cachestore_file($name, array('path' => $path, 'prescan' => true)); $cache->initialise($definition); // Let's pretend that some other process purged caches. remove_dir($CFG->cachedir . '/cachestore_file_test', true); make_cache_directory('cachestore_file_test'); $cache->get('testing'); }
/** * Sets many items in the cache in a single transaction. * * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two * keys, 'key' and 'value'. * @return int The number of items successfully set. It is up to the developer to check this matches the number of items * sent ... if they care that is. */ public function set_many(array $keyvaluearray) { $pairs = array(); foreach ($keyvaluearray as $pair) { $pairs[$pair['key']] = $pair['value']; } $status = true; if ($this->clustered) { foreach ($this->setconnections as $connection) { $status = $connection->setMulti($pairs, $this->definition->get_ttl()) && $status; } } else { $status = $this->connection->setMulti($pairs, $this->definition->get_ttl()); } if ($status) { return count($keyvaluearray); } return 0; }
/** * Tests the valid keys to ensure they work. */ public function test_valid_keys() { $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test'); $instance = cachestore_memcached::initialise_test_instance($definition); if (!$instance) { // Something prevented memcached store to be inited (extension, TEST_CACHESTORE_MEMCACHED_TESTSERVERS...). $this->markTestSkipped(); } $keys = array('abc', 'ABC', '123', 'aB1', '1aB', 'a-1', '1-a', '-a1', 'a1-', 'a_1', '1_a', '_a1', 'a1_'); foreach ($keys as $key) { $this->assertTrue($instance->set($key, $key), "Failed to set key `{$key}`"); } foreach ($keys as $key) { $this->assertEquals($key, $instance->get($key), "Failed to get key `{$key}`"); } $values = $instance->get_many($keys); foreach ($values as $key => $value) { $this->assertEquals($key, $value); } }
/** * Test the maxsize option. */ public function test_maxsize() { $defid = 'phpunit/testmaxsize'; $config = cache_config_phpunittest::instance(); $config->phpunit_add_definition($defid, array('mode' => cache_store::MODE_REQUEST, 'component' => 'phpunit', 'area' => 'testmaxsize', 'maxsize' => 3)); $definition = cache_definition::load($defid, $config->get_definition_by_id($defid)); $instance = cachestore_static::initialise_test_instance($definition); $this->assertTrue($instance->set('key1', 'value1')); $this->assertTrue($instance->set('key2', 'value2')); $this->assertTrue($instance->set('key3', 'value3')); $this->assertTrue($instance->has('key1')); $this->assertTrue($instance->has('key2')); $this->assertTrue($instance->has('key3')); $this->assertTrue($instance->set('key4', 'value4')); $this->assertTrue($instance->set('key5', 'value5')); $this->assertFalse($instance->has('key1')); $this->assertFalse($instance->has('key2')); $this->assertTrue($instance->has('key3')); $this->assertTrue($instance->has('key4')); $this->assertTrue($instance->has('key5')); $this->assertFalse($instance->get('key1')); $this->assertFalse($instance->get('key2')); $this->assertEquals('value3', $instance->get('key3')); $this->assertEquals('value4', $instance->get('key4')); $this->assertEquals('value5', $instance->get('key5')); // Test adding one more. $this->assertTrue($instance->set('key6', 'value6')); $this->assertFalse($instance->get('key3')); // Test reducing and then adding to make sure we don't lost one. $this->assertTrue($instance->delete('key6')); $this->assertTrue($instance->set('key7', 'value7')); $this->assertEquals('value4', $instance->get('key4')); // Set the same key three times to make sure it doesn't count overrides. for ($i = 0; $i < 3; $i++) { $this->assertTrue($instance->set('key8', 'value8')); } $this->assertEquals('value7', $instance->get('key7'), 'Overrides are incorrectly incrementing size'); // Test adding many. $this->assertEquals(3, $instance->set_many(array(array('key' => 'keyA', 'value' => 'valueA'), array('key' => 'keyB', 'value' => 'valueB'), array('key' => 'keyC', 'value' => 'valueC')))); $this->assertEquals(array('key4' => false, 'key5' => false, 'key6' => false, 'key7' => false, 'keyA' => 'valueA', 'keyB' => 'valueB', 'keyC' => 'valueC'), $instance->get_many(array('key4', 'key5', 'key6', 'key7', 'keyA', 'keyB', 'keyC'))); }
/** * Returns the item from the persist cache if it exists there. * * @param string $key The parsed key * @return mixed|false The data from the persist cache or false if it wasn't there. */ protected function get_from_persist_cache($key) { if (is_array($key)) { $key = $key['key']; } if (!$this->persist || !array_key_exists($key, $this->persistcache)) { $result = false; } else { $data = $this->persistcache[$key]; if (!$this->has_a_ttl() || !$data instanceof cache_ttl_wrapper) { if ($data instanceof cache_cached_object) { $data = $data->restore_object(); } $result = $data; } else { if ($data->has_expired()) { $this->delete_from_persist_cache($key); $result = false; } else { if ($data instanceof cache_cached_object) { $data = $data->restore_object(); } $result = $data->data; } } } if ($result) { if ($this->perfdebug) { cache_helper::record_cache_hit('** static persist **', $this->definition->get_id()); } return $result; } else { if ($this->perfdebug) { cache_helper::record_cache_miss('** static persist **', $this->definition->get_id()); } return false; } }
/** * Returns an array of stores that would meet the requirements for every definition. * * These stores would be 100% suitable to map as defaults for cache modes. * * @return array[] An array of stores, keys are the store names. */ public static function get_stores_suitable_for_mode_default() { $factory = cache_factory::instance(); $config = $factory->create_config_instance(); $requirements = 0; foreach ($config->get_definitions() as $definition) { $definition = cache_definition::load($definition['component'] . '/' . $definition['area'], $definition); $requirements = $requirements | $definition->get_requirements_bin(); } $stores = array(); foreach ($config->get_all_stores() as $name => $store) { if (!empty($store['features']) && $store['features'] & $requirements) { $stores[$name] = $store; } } return $stores; }
/** * Initialises the cache. * * Once this has been done the cache is all set to be used. * * @param cache_definition $definition */ public function initialise(cache_definition $definition) { $this->storeid = $definition->generate_definition_hash(); $this->store =& self::register_store_id($this->name . '-' . $definition->get_id()); $this->ttl = $definition->get_ttl(); $maxsize = $definition->get_maxsize(); if ($maxsize !== null) { // Must be a positive int. $this->maxsize = abs((int) $maxsize); $this->storecount = count($this->store); } $this->check_ttl(); }
/** * Tests that memcached cache store flushes entire cache when it is using a dedicated cache. */ public function test_dedicated_cache() { if (!cachestore_memcached::are_requirements_met() || !defined('TEST_CACHESTORE_MEMCACHED_TESTSERVERS')) { $this->markTestSkipped('Could not test cachestore_memcached. Requirements are not met.'); } $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test'); $cachestore = $this->create_test_cache_with_config($definition, array('isshared' => false)); $connection = new Memcached(crc32(__METHOD__)); $connection->addServers($this->get_servers(TEST_CACHESTORE_MEMCACHED_TESTSERVERS)); $connection->setOptions(array(Memcached::OPT_COMPRESSION => true, Memcached::OPT_SERIALIZER => Memcached::SERIALIZER_PHP, Memcached::OPT_PREFIX_KEY => 'phpunit_', Memcached::OPT_BUFFER_WRITES => false)); // We must flush first to make sure nothing is there. $connection->flush(); // Test the cachestore. $this->assertFalse($cachestore->get('test')); $this->assertTrue($cachestore->set('test', 'cachestore')); $this->assertSame('cachestore', $cachestore->get('test')); // Test the connection. $this->assertFalse($connection->get('test')); $this->assertEquals(Memcached::RES_NOTFOUND, $connection->getResultCode()); $this->assertTrue($connection->set('test', 'connection')); $this->assertSame('connection', $connection->get('test')); // Test both again and make sure the values are correct. $this->assertSame('cachestore', $cachestore->get('test')); $this->assertSame('connection', $connection->get('test')); // Purge the cachestore and check the connection was also purged. $this->assertTrue($cachestore->purge()); $this->assertFalse($cachestore->get('test')); $this->assertFalse($connection->get('test')); }
/** * Hashes a descriptive key to make it shorter and still unique. * @param string|int $key * @param cache_definition $definition * @return string */ public static function hash_key($key, cache_definition $definition) { if ($definition->uses_simple_keys()) { if (debugging() && preg_match('#[^a-zA-Z0-9_]#', $key)) { throw new coding_exception('Cache definition ' . $definition->get_id() . ' requires simple keys. Invalid key provided.', $key); } // We put the key first so that we can be sure the start of the key changes. return (string) $key . '-' . $definition->generate_single_key_prefix(); } $key = $definition->generate_single_key_prefix() . '-' . $key; return sha1($key); }
/** * Creates a definition instance or returns the existing one if it has already been created. * @param string $component * @param string $area * @param string $aggregate * @return cache_definition */ public function create_definition($component, $area, $aggregate = null) { $id = $component . '/' . $area; if ($aggregate) { $id .= '::' . $aggregate; } if (!array_key_exists($id, $this->definitions)) { // This is the first time this definition has been requested. if ($this->is_initialising()) { // We're initialising the cache right now. Don't try to create another config instance. // We'll just use an ad-hoc cache for the time being. $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area); } else { // Load all the known definitions and find the desired one. $instance = $this->create_config_instance(); $definition = $instance->get_definition_by_id($id); if (!$definition) { // Oh-oh the definition doesn't exist. // There are several things that could be going on here. // We may be installing/upgrading a site and have hit a definition that hasn't been used before. // Of the developer may be trying to use a newly created definition. if ($this->is_updating()) { // The cache is presently initialising and the requested cache definition has not been found. // This means that the cache initialisation has requested something from a cache (I had recursive nightmares about this). // To serve this purpose and avoid errors we are going to make use of an ad-hoc cache rather than // search for the definition which would possibly cause an infitite loop trying to initialise the cache. $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area); if ($aggregate !== null) { // If you get here you deserve a warning. We have to use an ad-hoc cache here, so we can't find the definition and therefor // can't find any information about the datasource or any of its aggregated. // Best of luck. debugging('An unknown cache was requested during development with an aggregate that could not be loaded. Ad-hoc cache used instead.', DEBUG_DEVELOPER); $aggregate = null; } } else { // Either a typo of the developer has just created the definition and is using it for the first time. $this->reset(); $instance = $this->create_config_instance(true); $instance->update_definitions(); $definition = $instance->get_definition_by_id($id); if (!$definition) { throw new coding_exception('The requested cache definition does not exist.' . $id, $id); } else { if (!$this->is_disabled()) { debugging('Cache definitions reparsed causing cache reset in order to locate definition. You should bump the version number to ensure definitions are reprocessed.', DEBUG_DEVELOPER); } } $definition = cache_definition::load($id, $definition, $aggregate); } } else { $definition = cache_definition::load($id, $definition, $aggregate); } } $this->definitions[$id] = $definition; } return $this->definitions[$id]; }
/** * Initialises the store instance for a definition. * @param cache_definition $definition */ public function initialise(cache_definition $definition) { // If the definition isn't using static acceleration then we need to be store data here. // The reasoning behind this is that: // - If the definition is using static acceleration then the cache loader is going to // store things in its static array. // - If the definition is not using static acceleration then the cache loader won't try to store anything // and we will need to store it here in order to make sure it is accessible. if ($definition->get_mode() !== self::MODE_APPLICATION) { // Neither the request cache nor the session cache provide static acceleration. $this->persist = true; } else { $this->persist = !$definition->use_static_acceleration(); } }
/** * Initialises the store instance for use. * * Once this has been done the cache is all set to be used. * * @param cache_definition $definition * @throws coding_exception */ public function initialise(cache_definition $definition) { if ($this->is_initialised()) { throw new coding_exception('This mongodb instance has already been initialised.'); } $this->database = $this->connection->selectDB($this->databasename); $this->definitionhash = 'm' . $definition->generate_definition_hash(); $this->collection = $this->database->selectCollection($this->definitionhash); $options = array('name' => 'idx_key'); if ($this->legacymongo) { $options['safe'] = $this->usesafe; } else { $options['w'] = $this->usesafe ? 1 : 0; } $this->collection->ensureIndex(array('key' => 1), $options); }
/** * Gets all of the stores that are to be used for the given definition. * * @param cache_definition $definition * @return array */ public function get_stores_for_definition(cache_definition $definition) { // Check if MUC has been disabled. $factory = cache_factory::instance(); if ($factory->stores_disabled()) { // Yip its been disabled. // To facilitate this we are going to always return an empty array of stores to use. // This will force all cache instances to use the cachestore_dummy. // MUC will still be used essentially so that code using it will still continue to function but because no cache stores // are being used interaction with MUC will be purely based around a static var. return array(); } $availablestores = $this->get_stores($definition->get_mode(), $definition->get_requirements_bin()); $stores = array(); $id = $definition->get_id(); // Now get any mappings and give them priority. foreach ($this->configdefinitionmappings as $mapping) { if ($mapping['definition'] !== $id) { continue; } $storename = $mapping['store']; if (!array_key_exists($storename, $availablestores)) { continue; } if (array_key_exists($storename, $stores)) { $store = $stores[$storename]; unset($stores[$storename]); $stores[$storename] = $store; } else { $stores[$storename] = $availablestores[$storename]; } } if (empty($stores) && !$definition->is_for_mappings_only()) { $mode = $definition->get_mode(); // Load the default stores. foreach ($this->configmodemappings as $mapping) { if ($mapping['mode'] === $mode && array_key_exists($mapping['store'], $availablestores)) { $store = $availablestores[$mapping['store']]; if (empty($store['mappingsonly'])) { $stores[$mapping['store']] = $store; } } } } return $stores; }
/** * Tests the clustering feature. */ public function test_clustered() { $this->resetAfterTest(true); if (!defined('TEST_CACHESTORE_MEMCACHE_TESTSERVERS')) { $this->markTestSkipped(); } $testservers = explode("\n", trim(TEST_CACHESTORE_MEMCACHE_TESTSERVERS)); if (count($testservers) < 2) { $this->markTestSkipped(); } // User the first server as our primary. set_config('testservers', $testservers[0], 'cachestore_memcache'); set_config('testsetservers', TEST_CACHESTORE_MEMCACHE_TESTSERVERS, 'cachestore_memcache'); set_config('testclustered', true, 'cachestore_memcache'); // First and instance that we can use to test the second server. $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcache', 'phpunit_test'); $instance = cachestore_memcache::initialise_test_instance($definition); if (!$instance) { $this->markTestSkipped(); } // Now we are going to setup a connection to each independent server. set_config('testclustered', false, 'cachestore_memcache'); set_config('testsetservers', '', 'cachestore_memcache'); $checkinstances = array(); foreach ($testservers as $testserver) { set_config('testservers', $testserver, 'cachestore_memcache'); $checkinstance = cachestore_memcache::initialise_test_instance($definition); if (!$checkinstance) { $this->markTestSkipped(); } $checkinstances[] = $checkinstance; } $keys = array('abc', 'ABC', '123', 'aB1', '1aB', 'a-1', '1-a', '-a1', 'a1-', 'a_1', '1_a', '_a1', 'a1_'); // Set each key. foreach ($keys as $key) { $this->assertTrue($instance->set($key, $key), "Failed to set key `{$key}`"); } // Check each key. foreach ($keys as $key) { $this->assertEquals($key, $instance->get($key), "Failed to get key `{$key}`"); foreach ($checkinstances as $id => $checkinstance) { $this->assertEquals($key, $checkinstance->get($key), "Failed to get key `{$key}` from server {$id}"); } } // Reset a key. $this->assertTrue($instance->set($keys[0], 'New'), "Failed to reset key `{$key}`"); $this->assertEquals('New', $instance->get($keys[0]), "Failed to get reset key `{$key}`"); foreach ($checkinstances as $id => $checkinstance) { $this->assertEquals('New', $checkinstance->get($keys[0]), "Failed to get reset key `{$key}` from server {$id}"); } // Delete and check that we can't retrieve. foreach ($keys as $key) { $this->assertTrue($instance->delete($key), "Failed to delete key `{$key}`"); $this->assertFalse($instance->get($key), "Retrieved deleted key `{$key}`"); foreach ($checkinstances as $id => $checkinstance) { $this->assertFalse($checkinstance->get($key), "Retrieved deleted key `{$key}` from server {$id}"); } } // Try set many, and check that count is correct. $many = array(); foreach ($keys as $key) { $many[] = array('key' => $key, 'value' => $key); } $returncount = $instance->set_many($many); $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match'); // Check keys retrieved with get_many. $values = $instance->get_many($keys); foreach ($keys as $key) { $this->assertTrue(isset($values[$key]), "Failed to get_many key `{$key}`"); $this->assertEquals($key, $values[$key], "Failed to match get_many key `{$key}`"); } foreach ($checkinstances as $id => $checkinstance) { $values = $checkinstance->get_many($keys); foreach ($keys as $key) { $this->assertTrue(isset($values[$key]), "Failed to get_many key `{$key}` from server {$id}"); $this->assertEquals($key, $values[$key], "Failed to get_many key `{$key}` from server {$id}"); } } // Delete many, make sure count matches. $returncount = $instance->delete_many($keys); $this->assertEquals(count($many), $returncount, 'Delete many count didn\'t match'); // Check that each key was deleted. foreach ($keys as $key) { $this->assertFalse($instance->get($key), "Retrieved many deleted key `{$key}`"); foreach ($checkinstances as $id => $checkinstance) { $this->assertFalse($checkinstance->get($key), "Retrieved many deleted key `{$key}` from server {$id}"); } } // Set the keys again. $returncount = $instance->set_many($many); $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match'); // Purge. $this->assertTrue($instance->purge(), 'Failure to purge'); // Delete and check that we can't retrieve. foreach ($keys as $key) { $this->assertFalse($instance->get($key), "Retrieved purged key `{$key}`"); foreach ($checkinstances as $id => $checkinstance) { $this->assertFalse($checkinstance->get($key), "Retrieved purged key `{$key}` from server 2"); } } }