Exemple #1
0
 /**
  * Removes expired elements.
  * @return int number of removed elements
  */
 protected function check_ttl()
 {
     if ($this->ttl === 0) {
         return 0;
     }
     $maxtime = cache::now() - $this->ttl;
     $count = 0;
     for ($value = reset($this->store); $value !== false; $value = next($this->store)) {
         if ($value[1] >= $maxtime) {
             // We know that elements are sorted by ttl so no need to continue.
             break;
         }
         $count++;
     }
     if ($count) {
         // Remove first $count elements as they are expired.
         $this->store = array_slice($this->store, $count, null, true);
         if ($this->maxsize !== false) {
             $this->storecount -= $count;
         }
     }
     return $count;
 }
Exemple #2
0
 /**
  * Purges a cache of all information on a given event.
  *
  * @param string $event
  */
 public static function purge_by_event($event)
 {
     $instance = cache_config::instance();
     $invalidationeventset = false;
     $factory = cache_factory::instance();
     foreach ($instance->get_definitions() as $name => $definitionarr) {
         $definition = cache_definition::load($name, $definitionarr);
         if ($definition->invalidates_on_event($event)) {
             // Create the cache.
             $cache = $factory->create_cache($definition);
             // Initialise, in case of a store.
             if ($cache instanceof cache_store) {
                 $cache->initialise($definition);
             }
             // Purge the cache.
             $cache->purge();
             // We need to flag the event in the "Event invalidation" cache if it hasn't already happened.
             if ($invalidationeventset === false) {
                 // Get the event invalidation cache.
                 $cache = cache::make('core', 'eventinvalidation');
                 // Create a key to invalidate all.
                 $data = array('purged' => cache::now());
                 // Set that data back to the cache.
                 $cache->set($event, $data);
                 // This only needs to occur once.
                 $invalidationeventset = true;
             }
         }
     }
 }
 /**
  * Override the cache::construct method.
  *
  * This function gets overriden so that we can process any invalidation events if need be.
  * If the definition doesn't have any invalidation events then this occurs exactly as it would for the cache class.
  * Otherwise we look at the last invalidation time and then check the invalidation data for events that have occured
  * between then now.
  *
  * You should not call this method from your code, instead you should use the cache::make methods.
  *
  * @param cache_definition $definition
  * @param cache_store $store
  * @param cache_loader|cache_data_source $loader
  */
 public function __construct(cache_definition $definition, cache_store $store, $loader = null)
 {
     // First up copy the loadeduserid to the current user id.
     $this->currentuserid = self::$loadeduserid;
     parent::__construct($definition, $store, $loader);
     // This will trigger check tracked user. If this gets removed a call to that will need to be added here in its place.
     $this->set(self::LASTACCESS, cache::now());
     if ($definition->has_invalidation_events()) {
         $lastinvalidation = $this->get('lastsessioninvalidation');
         if ($lastinvalidation === false) {
             // This is a new session, there won't be anything to invalidate. Set the time of the last invalidation and
             // move on.
             $this->set('lastsessioninvalidation', cache::now());
             return;
         } else {
             if ($lastinvalidation == cache::now()) {
                 // We've already invalidated during this request.
                 return;
             }
         }
         // Get the event invalidation cache.
         $cache = cache::make('core', 'eventinvalidation');
         $events = $cache->get_many($definition->get_invalidation_events());
         $todelete = array();
         $purgeall = false;
         // Iterate the returned data for the events.
         foreach ($events as $event => $keys) {
             if ($keys === false) {
                 // No data to be invalidated yet.
                 continue;
             }
             // Look at each key and check the timestamp.
             foreach ($keys as $key => $timestamp) {
                 // If the timestamp of the event is more than or equal to the last invalidation (happened between the last
                 // invalidation and now)then we need to invaliate the key.
                 if ($timestamp >= $lastinvalidation) {
                     if ($key === 'purged') {
                         $purgeall = true;
                         break;
                     } else {
                         $todelete[] = $key;
                     }
                 }
             }
         }
         if ($purgeall) {
             $this->purge();
         } else {
             if (!empty($todelete)) {
                 $todelete = array_unique($todelete);
                 $this->delete_many($todelete);
             }
         }
         // Set the time of the last invalidation.
         $this->set('lastsessioninvalidation', cache::now());
     }
 }
Exemple #4
0
 /**
  * Returns true if the data has expired.
  * @return int
  */
 public function has_expired()
 {
     return $this->expires < cache::now();
 }
Exemple #5
0
 /**
  * Purges a cache of all information on a given event.
  *
  * @param string $event
  */
 public static function purge_by_event($event)
 {
     $instance = cache_config::instance();
     $invalidationeventset = false;
     $factory = cache_factory::instance();
     $inuse = $factory->get_caches_in_use();
     foreach ($instance->get_definitions() as $name => $definitionarr) {
         $definition = cache_definition::load($name, $definitionarr);
         if ($definition->invalidates_on_event($event)) {
             // First up check if there is a cache loader for this definition already.
             // If there is we need to invalidate the keys from there.
             $definitionkey = $definition->get_component() . '/' . $definition->get_area();
             if (isset($inuse[$definitionkey])) {
                 $inuse[$definitionkey]->purge();
             } else {
                 cache::make($definition->get_component(), $definition->get_area())->purge();
             }
             // We should only log events for application and session caches.
             // Request caches shouldn't have events as all data is lost at the end of the request.
             // Events should only be logged once of course and likely several definitions are watching so we
             // track its logging with $invalidationeventset.
             $logevent = $invalidationeventset === false && $definition->get_mode() !== cache_store::MODE_REQUEST;
             // We need to flag the event in the "Event invalidation" cache if it hasn't already happened.
             if ($logevent && $invalidationeventset === false) {
                 // Get the event invalidation cache.
                 $cache = cache::make('core', 'eventinvalidation');
                 // Create a key to invalidate all.
                 $data = array('purged' => cache::now());
                 // Set that data back to the cache.
                 $cache->set($event, $data);
                 // This only needs to occur once.
                 $invalidationeventset = true;
             }
         }
     }
 }
Exemple #6
0
 /**
  * Tests application cache event invalidation over a distributed setup.
  */
 public function test_distributed_application_event_invalidation()
 {
     global $CFG;
     // This is going to be an intense wee test.
     // We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a
     // disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally
     // check that it is not picked up.
     $instance = cache_config_phpunittest::instance();
     $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array('crazyevent')));
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     $this->assertTrue($cache->set('testkey1', 'test data 1'));
     $this->assertEquals('test data 1', $cache->get('testkey1'));
     cache_helper::invalidate_by_event('crazyevent', array('testkey1'));
     $this->assertFalse($cache->get('testkey1'));
     // OK data added, data invalidated, and invalidation time has been set.
     // Now we need to manually add back the data and adjust the invalidation time.
     $hash = md5(cache_store::MODE_APPLICATION . '/phpunit/eventinvalidationtest/' . $CFG->wwwroot . 'phpunit');
     $timefile = $CFG->dataroot . "/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las/lastinvalidation-{$hash}.cache";
     // Make sure the file is correct.
     $this->assertTrue(file_exists($timefile));
     $timecont = serialize(cache::now() - 60);
     // Back 60sec in the past to force it to re-invalidate.
     make_writable_directory(dirname($timefile));
     file_put_contents($timefile, $timecont);
     $this->assertTrue(file_exists($timefile));
     $datafile = $CFG->dataroot . "/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes/testkey1-{$hash}.cache";
     $datacont = serialize("test data 1");
     make_writable_directory(dirname($datafile));
     file_put_contents($datafile, $datacont);
     $this->assertTrue(file_exists($datafile));
     // Test 1: Rebuild without the event and test its there.
     cache_factory::reset();
     $instance = cache_config_phpunittest::instance();
     $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true));
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     $this->assertEquals('test data 1', $cache->get('testkey1'));
     // Test 2: Rebuild and test the invalidation of the event via the invalidation cache.
     cache_factory::reset();
     $instance = cache_config_phpunittest::instance();
     $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array('crazyevent')));
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     $this->assertFalse($cache->get('testkey1'));
 }
Exemple #7
0
 /**
  * Checks if the store has a record for the given key and returns true if so.
  *
  * @param string $key
  * @return bool
  */
 public function has($key)
 {
     $filename = $key . '.cache';
     $maxtime = cache::now() - $this->definition->get_ttl();
     if ($this->prescan) {
         return array_key_exists($filename, $this->keys) && $this->keys[$filename] >= $maxtime;
     }
     $file = $this->file_path_for_key($key);
     return file_exists($file) && ($this->definition->get_ttl() == 0 || filemtime($file) >= $maxtime);
 }
Exemple #8
0
 /**
  * Override the cache::construct method.
  *
  * This function gets overriden so that we can process any invalidation events if need be.
  * If the definition doesn't have any invalidation events then this occurs exactly as it would for the cache class.
  * Otherwise we look at the last invalidation time and then check the invalidation data for events that have occured
  * between then now.
  *
  * You should not call this method from your code, instead you should use the cache::make methods.
  *
  * @param cache_definition $definition
  * @param cache_store $store
  * @param cache_loader|cache_data_source $loader
  * @return void
  */
 public function __construct(cache_definition $definition, cache_store $store, $loader = null)
 {
     parent::__construct($definition, $store, $loader);
     if ($definition->has_invalidation_events()) {
         $lastinvalidation = $this->get('lastsessioninvalidation');
         if ($lastinvalidation === false) {
             // This is a new session, there won't be anything to invalidate. Set the time of the last invalidation and
             // move on.
             $this->set('lastsessioninvalidation', cache::now());
             return;
         } else {
             if ($lastinvalidation == cache::now()) {
                 // We've already invalidated during this request.
                 return;
             }
         }
         // Get the event invalidation cache.
         $cache = cache::make('core', 'eventinvalidation');
         $events = $cache->get_many($definition->get_invalidation_events());
         $todelete = array();
         // Iterate the returned data for the events.
         foreach ($events as $event => $keys) {
             if ($keys === false) {
                 // No data to be invalidated yet.
                 continue;
             }
             // Look at each key and check the timestamp.
             foreach ($keys as $key => $timestamp) {
                 // If the timestamp of the event is more than or equal to the last invalidation (happened between the last
                 // invalidation and now)then we need to invaliate the key.
                 if ($timestamp >= $lastinvalidation) {
                     $todelete[] = $key;
                 }
             }
         }
         if (!empty($todelete)) {
             $todelete = array_unique($todelete);
             $this->delete_many($todelete);
         }
         // Set the time of the last invalidation.
         $this->set('lastsessioninvalidation', cache::now());
     }
 }
Exemple #9
0
 /**
  * Tests application cache event invalidation over a distributed setup.
  */
 public function test_distributed_application_event_invalidation()
 {
     global $CFG;
     // This is going to be an intense wee test.
     // We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a
     // disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally
     // check that it is not picked up.
     $instance = cache_config_testing::instance();
     $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array('crazyevent')));
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     $this->assertTrue($cache->set('testkey1', 'test data 1'));
     $this->assertEquals('test data 1', $cache->get('testkey1'));
     cache_helper::invalidate_by_event('crazyevent', array('testkey1'));
     $this->assertFalse($cache->get('testkey1'));
     // OK data added, data invalidated, and invalidation time has been set.
     // Now we need to manually add back the data and adjust the invalidation time.
     $hash = md5(cache_store::MODE_APPLICATION . '/phpunit/eventinvalidationtest/' . $CFG->wwwroot . 'phpunit');
     $timefile = $CFG->dataroot . "/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las-cache/lastinvalidation-{$hash}.cache";
     // Make sure the file is correct.
     $this->assertTrue(file_exists($timefile));
     $timecont = serialize(cache::now() - 60);
     // Back 60sec in the past to force it to re-invalidate.
     make_writable_directory(dirname($timefile));
     file_put_contents($timefile, $timecont);
     $this->assertTrue(file_exists($timefile));
     $datafile = $CFG->dataroot . "/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes-cache/testkey1-{$hash}.cache";
     $datacont = serialize("test data 1");
     make_writable_directory(dirname($datafile));
     file_put_contents($datafile, $datacont);
     $this->assertTrue(file_exists($datafile));
     // Test 1: Rebuild without the event and test its there.
     cache_factory::reset();
     $instance = cache_config_testing::instance();
     $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true));
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     $this->assertEquals('test data 1', $cache->get('testkey1'));
     // Test 2: Rebuild and test the invalidation of the event via the invalidation cache.
     cache_factory::reset();
     $instance = cache_config_testing::instance();
     $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array('crazyevent')));
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     $this->assertFalse($cache->get('testkey1'));
     // Test 3: Verify that an existing lastinvalidation cache file is updated when needed.
     // Make a new cache class.  This should should invalidate testkey2.
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     // Timestamp should have updated to cache::now().
     $this->assertEquals(cache::now(), $cache->get('lastinvalidation'));
     // Set testkey2 data.
     $cache->set('testkey2', 'test data 2');
     // Backdate the event invalidation time by 30 seconds.
     $invalidationcache = cache::make('core', 'eventinvalidation');
     $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 30));
     // Lastinvalidation should already be cache::now().
     $this->assertEquals(cache::now(), $cache->get('lastinvalidation'));
     // Set it to 15 seconds ago so that we know if it changes.
     $cache->set('lastinvalidation', cache::now() - 15);
     // Make a new cache class.  This should not invalidate anything.
     cache_factory::instance()->reset_cache_instances();
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     // Lastinvalidation shouldn't change since it was already newer than invalidation event.
     $this->assertEquals(cache::now() - 15, $cache->get('lastinvalidation'));
     // Now set the event invalidation to newer than the lastinvalidation time.
     $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 5));
     // Make a new cache class.  This should should invalidate testkey2.
     cache_factory::instance()->reset_cache_instances();
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     // Lastinvalidation timestamp should have updated to cache::now().
     $this->assertEquals(cache::now(), $cache->get('lastinvalidation'));
     // Now simulate a purge_by_event 5 seconds ago.
     $invalidationcache = cache::make('core', 'eventinvalidation');
     $invalidationcache->set('crazyevent', array('purged' => cache::now() - 5));
     // Set our lastinvalidation timestamp to 15 seconds ago.
     $cache->set('lastinvalidation', cache::now() - 15);
     // Make a new cache class.  This should invalidate the cache.
     cache_factory::instance()->reset_cache_instances();
     $cache = cache::make('phpunit', 'eventinvalidationtest');
     // Lastinvalidation timestamp should have updated to cache::now().
     $this->assertEquals(cache::now(), $cache->get('lastinvalidation'));
 }
Exemple #10
0
 /**
  * Purges a cache of all information on a given event.
  *
  * @param string $event
  */
 public static function purge_by_event($event)
 {
     $instance = cache_config::instance();
     $invalidationeventset = false;
     $factory = cache_factory::instance();
     foreach ($instance->get_definitions() as $name => $definitionarr) {
         $definition = cache_definition::load($name, $definitionarr);
         if ($definition->invalidates_on_event($event)) {
             // Check if this definition would result in a persistent loader being in use.
             if ($definition->should_be_persistent()) {
                 // There may be a persistent cache loader. Lets purge that first so that any persistent data is removed.
                 $cache = $factory->create_cache_from_definition($definition->get_component(), $definition->get_area());
                 $cache->purge();
             }
             // Get all of the store instances that are in use for this store.
             $stores = $factory->get_store_instances_in_use($definition);
             foreach ($stores as $store) {
                 // Purge each store individually.
                 $store->purge();
             }
             // We need to flag the event in the "Event invalidation" cache if it hasn't already happened.
             if ($invalidationeventset === false) {
                 // Get the event invalidation cache.
                 $cache = cache::make('core', 'eventinvalidation');
                 // Create a key to invalidate all.
                 $data = array('purged' => cache::now());
                 // Set that data back to the cache.
                 $cache->set($event, $data);
                 // This only needs to occur once.
                 $invalidationeventset = true;
             }
         }
     }
 }
Exemple #11
0
 /**
  * Returns true if the store contains records for any of the given keys.
  *
  * @param array $keys
  * @return bool
  */
 public function has_any(array $keys)
 {
     $maxtime = cache::now() - $this->ttl;
     foreach ($keys as $key) {
         if (array_key_exists($key, $this->store) && ($this->ttl == 0 || $this->store[$key][1] >= $maxtime)) {
             return true;
         }
     }
     return false;
 }
Exemple #12
0
 /**
  * Returns true if the store contains records for any of the given keys.
  *
  * @param array $keys
  * @return bool
  */
 public function has_any(array $keys)
 {
     if ($this->ttl != 0) {
         $maxtime = cache::now() - $this->ttl;
     }
     foreach ($keys as $key) {
         if (isset($this->store[$key]) && ($this->ttl == 0 || $this->store[$key][1] >= $maxtime)) {
             return true;
         }
     }
     return false;
 }