public function APDQCache::clear in Asynchronous Prefetch Database Query Cache 7
Implements DrupalCacheInterface::clear().
Here, we ensure that cron only runs cache garbage collection at a configurable frequency, defaulting to 24 hours.
Overrides DrupalDatabaseCache::clear
File
- ./
apdqc.cache.inc, line 623 - Extends Drupal's default database cache so async queries happen.
Class
- APDQCache
- A pretty darn quick cache implementation of Drupal's default cache backend.
Code
public function clear($cid = NULL, $wildcard = FALSE) {
if (variable_get('apdqc_call_hook_on_clear', APDQC_CALL_HOOK_ON_CLEAR)) {
$apdqc_cache_clear_alter = module_implements('apdqc_cache_clear_alter');
if (!empty($apdqc_cache_clear_alter)) {
$caller = $this
->getCaller();
// The bin variable is not alterable; use a copy.
$bin = $this->bin;
// Call hook_apdqc_cache_clear_alter().
drupal_alter('apdqc_cache_clear', $cid, $wildcard, $bin, $caller);
}
}
// Use default core logic for this cache clear if inside of a transaction.
if (Database::getConnection()
->inTransaction()) {
// Do not use any prefectched data.
$apdqc_async_data =& drupal_static('apdqc_async_data');
$do_not_use_async_data =& drupal_static('apdqc_async_data_do_not_use_async');
$do_not_run_prefetch_array =& drupal_static('apdqc_run_prefetch_array');
$apdqc_async_data[$this->bin] = array();
$do_not_use_async_data[$this->bin] = TRUE;
$do_not_run_prefetch_array[$this->bin] = TRUE;
$output = parent::clear($cid, $wildcard);
$this
->callCacheClearHooks($cid, $wildcard);
return $output;
}
$gc_frequency = variable_get('cache_garbage_collection_frequency', CACHE_GARBAGE_COLLECTION_FREQUENCY);
$cids = array(
$cid,
);
if ($wildcard || is_null($cid)) {
$cids = array(
'*',
);
}
if (!empty($gc_frequency) && empty($cid)) {
// Use backtrace to check that the clear came from system_cron.
$backtrace = debug_backtrace();
if ($backtrace[2]['function'] == 'system_cron') {
$cache_lifetime = variable_get('cache_lifetime', 0);
$name = 'cache_garbage_collect_' . $this->bin;
$window = max($cache_lifetime, $gc_frequency);
if (flood_is_allowed($name, 1, $window, 'cron')) {
$this
->garbageCollection();
flood_register_event($name, $window, 'cron');
}
}
else {
$this
->garbageCollection();
}
$this
->callCacheClearHooks($cid, $wildcard);
return;
}
if (is_null($cid)) {
// Build query.
$query = Database::getConnection()
->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
if (variable_get('cache_lifetime', 0)) {
// We store the time in the current user's session. We then simulate
// that the cache was flushed for this user by not returning cached
// data that was cached before the timestamp.
$_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME;
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush == 0) {
// This is the first request to clear the cache, start a timer.
variable_set('cache_flush_' . $this->bin, REQUEST_TIME);
}
elseif (REQUEST_TIME > $cache_flush + variable_get('cache_lifetime', 0)) {
// Clear the cache for everyone, cache_lifetime seconds have
// passed since the first request to clear the cache.
$query .= "WHERE (expire <> " . CACHE_PERMANENT . " AND expire < " . REQUEST_TIME . ")";
$result = apdqc_query(array(
$this->bin,
), $cids, $query, array(
'async' => TRUE,
));
if (is_string($result) && $result === 'NO DB') {
// Use core connection if the additional connection to mysql fails.
$output = parent::clear($cid, $wildcard);
$this
->callCacheClearHooks($cid, $wildcard);
return $output;
}
variable_set('cache_flush_' . $this->bin, 0);
}
}
else {
// No minimum cache lifetime, flush all temporary cache entries now.
$query .= "WHERE (expire <> " . CACHE_PERMANENT . " AND expire < " . REQUEST_TIME . ")";
$result = apdqc_query(array(
$this->bin,
), $cids, $query, array(
'async' => TRUE,
));
if (is_string($result) && $result === 'NO DB') {
// Use core connection if an additional connection to mysql fails.
$output = parent::clear($cid, $wildcard);
$this
->callCacheClearHooks($cid, $wildcard);
return $output;
}
}
}
else {
if ($wildcard) {
if ($cid == '*') {
// Check if $this->bin is a cache table before truncating. Other
// cache_clear_all() operations throw a PDO error in this situation,
// so we don't need to verify them first. This ensures that non-cache
// tables cannot be truncated accidentally.
if ($this
->isValidBin()) {
apdqc_truncate_table($this->bin);
}
else {
throw new Exception(t('Invalid or missing cache bin specified: %bin', array(
'%bin' => $this->bin,
)));
}
}
else {
// Build query.
$query = Database::getConnection()
->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
db_delete($this->bin)
->condition('cid', db_like($cid) . '%', 'LIKE')
->execute();
$escaped_cid = apdqc_escape_string($cid);
$query .= " WHERE cid LIKE '{$escaped_cid}%'";
// Run an async query.
$result = apdqc_query(array(
$this->bin,
), $cids, $query, array(
'async' => TRUE,
));
if (is_string($result) && $result === 'NO DB') {
// Use core connection if an additional connection to mysql fails.
$output = parent::clear($cid, $wildcard);
$this
->callCacheClearHooks($cid, $wildcard);
return $output;
}
}
}
elseif (is_array($cid)) {
// Delete in chunks when a large array is passed.
$chunks = array_chunk($cid, 2000);
$last = array_pop($chunks);
$query = Database::getConnection()
->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
foreach ($chunks as $cids) {
$escaped_cids = array();
foreach ($cids as $id) {
$escaped_cids[] = apdqc_escape_string($id);
}
$escaped_cids = "'" . implode("', '", $escaped_cids) . "'";
// Run query.
$result = apdqc_query(array(
$this->bin,
), $cids, $query . " WHERE cid IN ({$escaped_cids})");
if (is_string($result) && $result === 'NO DB') {
// Use core connection if an additional connection to mysql fails.
$output = parent::clear($cid, $wildcard);
$this
->callCacheClearHooks($cid, $wildcard);
return $output;
}
}
$escaped_cids = array();
foreach ($last as $id) {
$escaped_cids[] = apdqc_escape_string($id);
}
$escaped_cids = "'" . implode("', '", $escaped_cids) . "'";
// Run last one as an async query.
$result = apdqc_query(array(
$this->bin,
), $last, $query . " WHERE cid IN ({$escaped_cids})", array(
'async' => TRUE,
));
if (is_string($result) && $result === 'NO DB') {
// Use core connection if an additional connection to mysql fails.
$output = parent::clear($cid, $wildcard);
$this
->callCacheClearHooks($cid, $wildcard);
return $output;
}
}
else {
$query = Database::getConnection()
->prefixTables("DELETE FROM {" . db_escape_table($this->bin) . "} ");
$result = apdqc_query(array(
$this->bin,
), $cids, $query . " WHERE cid = '" . apdqc_escape_string($cid) . "'", array(
'async' => TRUE,
));
if (is_string($result) && $result === 'NO DB') {
// Use core connection if an additional connection to mysql fails.
$output = parent::clear($cid, $wildcard);
$this
->callCacheClearHooks($cid, $wildcard);
return $output;
}
}
}
$this
->callCacheClearHooks($cid, $wildcard);
}