Commit 28e67ceb by Scott

Ensure unique prefix for cache keys

parent 618372e6
...@@ -76,6 +76,13 @@ interface Q2A_Storage_CacheDriver ...@@ -76,6 +76,13 @@ interface Q2A_Storage_CacheDriver
public function getError(); public function getError();
/** /**
* Get the prefix used for all cache keys.
*
* @return string
*/
public function getKeyPrefix();
/**
* Get current statistics for the cache. * Get current statistics for the cache.
* *
* @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes. * @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes.
......
...@@ -36,6 +36,7 @@ class Q2A_Storage_CacheFactory ...@@ -36,6 +36,7 @@ class Q2A_Storage_CacheFactory
if (self::$cacheDriver === null) { if (self::$cacheDriver === null) {
$config = array( $config = array(
'enabled' => (int) qa_opt('caching_enabled') === 1, 'enabled' => (int) qa_opt('caching_enabled') === 1,
'keyprefix' => QA_FINAL_MYSQL_DATABASE . '.' . QA_MYSQL_TABLE_PREFIX . '.',
'dir' => defined('QA_CACHE_DIRECTORY') ? QA_CACHE_DIRECTORY : null, 'dir' => defined('QA_CACHE_DIRECTORY') ? QA_CACHE_DIRECTORY : null,
); );
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver
{ {
private $enabled = false; private $enabled = false;
private $keyPrefix = '';
private $error; private $error;
private $cacheDir; private $cacheDir;
...@@ -39,6 +40,10 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver ...@@ -39,6 +40,10 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver
return; return;
} }
if (isset($config['keyprefix'])) {
$this->keyPrefix = $config['keyprefix'];
}
if (isset($config['dir'])) { if (isset($config['dir'])) {
$this->cacheDir = realpath($config['dir']); $this->cacheDir = realpath($config['dir']);
...@@ -67,14 +72,15 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver ...@@ -67,14 +72,15 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver
return null; return null;
} }
$file = $this->getFilename($key); $fullKey = $this->keyPrefix . $key;
$file = $this->getFilename($fullKey);
if (is_readable($file)) { if (is_readable($file)) {
$lines = file($file, FILE_IGNORE_NEW_LINES); $lines = file($file, FILE_IGNORE_NEW_LINES);
$actualKey = array_shift($lines); $actualKey = array_shift($lines);
// double check this is the correct data // double check this is the correct data
if ($key === $actualKey) { if ($fullKey === $actualKey) {
$expiry = array_shift($lines); $expiry = array_shift($lines);
if (is_numeric($expiry) && time() < $expiry) { if (is_numeric($expiry) && time() < $expiry) {
...@@ -103,13 +109,14 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver ...@@ -103,13 +109,14 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver
{ {
$success = false; $success = false;
$ttl = (int) $ttl; $ttl = (int) $ttl;
$fullKey = $this->keyPrefix . $key;
if ($this->enabled && $ttl > 0) { if ($this->enabled && $ttl > 0) {
$encData = serialize($data); $encData = serialize($data);
$expiry = time() + ($ttl * 60); $expiry = time() + ($ttl * 60);
$cache = $key . "\n" . $expiry . "\n" . $encData; $cache = $fullKey . "\n" . $expiry . "\n" . $encData;
$file = $this->getFilename($key); $file = $this->getFilename($fullKey);
$dir = dirname($file); $dir = dirname($file);
if (is_dir($dir) || mkdir($dir, 0777, true)) { if (is_dir($dir) || mkdir($dir, 0777, true)) {
$success = @file_put_contents($file, $cache) !== false; $success = @file_put_contents($file, $cache) !== false;
...@@ -127,10 +134,10 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver ...@@ -127,10 +134,10 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver
*/ */
public function delete($key) public function delete($key)
{ {
if ($this->enabled) { $fullKey = $this->keyPrefix . $key;
$file = $this->getFilename($key);
$dir = dirname($key);
if ($this->enabled) {
$file = $this->getFilename($fullKey);
return $this->deleteFile($file); return $this->deleteFile($file);
} }
...@@ -209,6 +216,16 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver ...@@ -209,6 +216,16 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver
} }
/** /**
* Get the prefix used for all cache keys.
*
* @return string
*/
public function getKeyPrefix()
{
return $this->keyPrefix;
}
/**
* Get current statistics for the cache. * Get current statistics for the cache.
* *
* @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes. * @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes.
...@@ -255,13 +272,13 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver ...@@ -255,13 +272,13 @@ class Q2A_Storage_FileCacheDriver implements Q2A_Storage_CacheDriver
/** /**
* Generates filename for cache key, of the form `1/23/123abc` * Generates filename for cache key, of the form `1/23/123abc`
* @param string $key The unique cache key. * @param string $key The unique cache key (including prefix).
* *
* @return string * @return string
*/ */
private function getFilename($key) private function getFilename($fullKey)
{ {
$filename = sha1($key); $filename = sha1($fullKey);
return $this->cacheDir . '/' . substr($filename, 0, 1) . '/' . substr($filename, 1, 2) . '/' . $filename; return $this->cacheDir . '/' . substr($filename, 0, 1) . '/' . substr($filename, 1, 2) . '/' . $filename;
} }
} }
...@@ -27,6 +27,7 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver ...@@ -27,6 +27,7 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver
{ {
private $memcached; private $memcached;
private $enabled = false; private $enabled = false;
private $keyPrefix = '';
private $error; private $error;
private $flushed = false; private $flushed = false;
...@@ -45,10 +46,14 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver ...@@ -45,10 +46,14 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver
return; return;
} }
if (isset($config['keyprefix'])) {
$this->keyPrefix = $config['keyprefix'];
}
if (extension_loaded('memcached')) { if (extension_loaded('memcached')) {
$this->memcached = new Memcached; $this->memcached = new Memcached;
$this->memcached->addServer(self::HOST, self::PORT); $this->memcached->addServer(self::HOST, self::PORT);
if ($this->memcached->set('q2a.test', 'TEST')) { if ($this->memcached->set($this->keyPrefix . 'test', 'TEST')) {
$this->enabled = true; $this->enabled = true;
} else { } else {
$this->setMemcachedError(); $this->setMemcachedError();
...@@ -70,7 +75,7 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver ...@@ -70,7 +75,7 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver
return null; return null;
} }
$result = $this->memcached->get($key); $result = $this->memcached->get($this->keyPrefix . $key);
if ($result === false) { if ($result === false) {
$this->setMemcachedError(); $this->setMemcachedError();
...@@ -96,7 +101,7 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver ...@@ -96,7 +101,7 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver
$ttl = (int) $ttl; $ttl = (int) $ttl;
$expiry = time() + ($ttl * 60); $expiry = time() + ($ttl * 60);
$success = $this->memcached->set($key, $data, $expiry); $success = $this->memcached->set($this->keyPrefix . $key, $data, $expiry);
if (!$success) { if (!$success) {
$this->setMemcachedError(); $this->setMemcachedError();
...@@ -117,7 +122,7 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver ...@@ -117,7 +122,7 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver
return false; return false;
} }
$success = $this->memcached->delete($key); $success = $this->memcached->delete($this->keyPrefix . $key);
if (!$success) { if (!$success) {
$this->setMemcachedError(); $this->setMemcachedError();
...@@ -170,6 +175,16 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver ...@@ -170,6 +175,16 @@ class Q2A_Storage_MemcachedDriver implements Q2A_Storage_CacheDriver
} }
/** /**
* Get the prefix used for all cache keys.
*
* @return string
*/
public function getKeyPrefix()
{
return $this->keyPrefix;
}
/**
* Get current statistics for the cache. * Get current statistics for the cache.
* *
* @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes. * @return array Array of stats: 'files' => number of files, 'size' => total file size in bytes.
......
...@@ -42,7 +42,7 @@ $pagestate = qa_get_state(); ...@@ -42,7 +42,7 @@ $pagestate = qa_get_state();
// Get information about this question // Get information about this question
$cacheDriver = Q2A_Storage_CacheFactory::getCacheDriver(); $cacheDriver = Q2A_Storage_CacheFactory::getCacheDriver();
$cacheKey = "q2a.question:$questionid"; $cacheKey = "question:$questionid";
$useCache = $userid === null && $cacheDriver->isEnabled() && !qa_is_http_post() && empty($pagestate); $useCache = $userid === null && $cacheDriver->isEnabled() && !qa_is_http_post() && empty($pagestate);
$saveCache = false; $saveCache = false;
......
...@@ -524,7 +524,7 @@ function qa_db_single_select($selectspec) ...@@ -524,7 +524,7 @@ function qa_db_single_select($selectspec)
// check for cached results // check for cached results
if (isset($selectspec['caching'])) { if (isset($selectspec['caching'])) {
$cacheDriver = Q2A_Storage_CacheFactory::getCacheDriver(); $cacheDriver = Q2A_Storage_CacheFactory::getCacheDriver();
$cacheKey = 'q2a.query:' . $selectspec['caching']['key']; $cacheKey = 'query:' . $selectspec['caching']['key'];
if ($cacheDriver->isEnabled()) { if ($cacheDriver->isEnabled()) {
$queryData = $cacheDriver->get($cacheKey); $queryData = $cacheDriver->get($cacheKey);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment