summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/cache.inc97
-rw-r--r--modules/field/field.attach.inc34
-rw-r--r--modules/simpletest/tests/cache.test89
3 files changed, 199 insertions, 21 deletions
diff --git a/includes/cache.inc b/includes/cache.inc
index 20e876bcd..1abf74844 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -46,6 +46,21 @@ function cache_get($cid, $bin = 'cache') {
}
/**
+ * Return data from the persistent cache when given an array of cache IDs.
+ *
+ * @param $cids
+ * An array of cache IDs for the data to retrieve. This is passed by
+ * reference, and will have the IDs successfully returned from cache removed.
+ * @param $bin
+ * The cache bin where the data is stored.
+ * @return
+ * An array of the items successfully returned from cache indexed by cid.
+ */
+function cache_get_multiple(array &$cids, $bin = 'cache') {
+ return _cache_get_object($bin)->getMultiple($cids);
+}
+
+/**
* Store data in the persistent cache.
*
* The persistent cache is split up into several cache bins. In the default
@@ -198,6 +213,18 @@ interface DrupalCacheInterface {
function get($cid);
/**
+ * Return data from the persistent cache when given an array of cache IDs.
+ *
+ * @param $cids
+ * An array of cache IDs for the data to retrieve. This is passed by
+ * reference, and will have the IDs successfully returned from cache
+ * removed.
+ * @return
+ * An array of the items successfully returned from cache indexed by cid.
+ */
+ function getMultiple(&$cids);
+
+ /**
* Store data in the persistent cache.
*
* @param $cid
@@ -249,9 +276,41 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
}
function get($cid) {
+ // Garbage collection necessary when enforcing a minimum cache lifetime.
+ $this->garbageCollection($this->bin);
+ $cache = db_query("SELECT data, created, headers, expire, serialized FROM {" . $this->bin . "} WHERE cid = :cid", array(':cid' => $cid))->fetchObject();
+
+ return $this->prepareItem($cache);
+ }
+
+ function getMultiple(&$cids) {
+ // Garbage collection necessary when enforcing a minimum cache lifetime.
+ $this->garbageCollection($this->bin);
+ $query = db_select($this->bin);
+ $query->fields($this->bin, array('cid', 'data', 'created', 'headers', 'expire', 'serialized'));
+ $query->condition($this->bin . '.cid', $cids, 'IN');
+ $result = $query->execute();
+ $cache = array();
+ foreach ($result as $item) {
+ $item = $this->prepareItem($item);
+ if ($item) {
+ $cache[$item->cid] = $item;
+ }
+ }
+ $cids = array_diff($cids, array_keys($cache));
+ return $cache;
+ }
+
+ /**
+ * Garbage collection for get() and getMultiple().
+ *
+ * @param $bin
+ * The bin being requested.
+ */
+ protected function garbageCollection() {
global $user;
- // Garbage collection is necessary when enforcing a minimum cache lifetime.
+ // Garbage collection necessary when enforcing a minimum cache lifetime.
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) {
// Reset the variable immediately to prevent a meltdown in heavy load situations.
@@ -262,13 +321,31 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
->condition('expire', $cache_flush, '<=')
->execute();
}
+ }
- $cache = db_query("SELECT data, created, headers, expire, serialized FROM {" . $this->bin . "} WHERE cid = :cid", array(':cid' => $cid))->fetchObject();
-
+ /**
+ * Prepare a cached item.
+ *
+ * Checks that items are either permanent or did not expire, and unserializes
+ * data as appropriate.
+ *
+ * @param $cache
+ * An item loaded from cache_get() or cache_get_multiple().
+ * @return
+ * The item with data unserialized as appropriate or FALSE if there is no
+ * valid item to load.
+ */
+ protected function prepareItem($cache) {
if (!isset($cache->data)) {
return FALSE;
}
-
+ // If the data is permanent or we are not enforcing a minimum cache lifetime
+ // always return the cached data.
+ if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) {
+ if ($cache->serialized) {
+ $cache->data = unserialize($cache->data);
+ }
+ }
// If enforcing a minimum cache lifetime, validate that the data is
// currently valid for this user before we return it by making sure the cache
// entry was created before the timestamp in the current session's cache
@@ -280,9 +357,6 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
return FALSE;
}
- if ($cache->serialized) {
- $cache->data = unserialize($cache->data);
- }
if (isset($cache->headers)) {
$cache->headers = unserialize($cache->headers);
}
@@ -357,6 +431,15 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
->execute();
}
}
+ elseif (is_array($cid)) {
+ // Delete in chunks when a large array is passed.
+ do {
+ db_delete($this->bin)
+ ->condition('cid', array_splice($cid, 0, 1000), 'IN')
+ ->execute();
+ }
+ while (count($cid));
+ }
else {
db_delete($this->bin)
->condition('cid', $cid)
diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc
index 42984beba..6c68c57a3 100644
--- a/modules/field/field.attach.inc
+++ b/modules/field/field.attach.inc
@@ -399,28 +399,36 @@ function field_attach_load($obj_type, $objects, $age = FIELD_LOAD_CURRENT) {
$info = field_info_fieldable_types($obj_type);
$cacheable = $load_current && $info['cacheable'];
- $queried_objects = array();
+ if (empty($objects)) {
+ return;
+ }
+
+ // Assume all objects will need to be queried. Objects found in the cache
+ // will be removed from the list.
+ $queried_objects = $objects;
- // Fetch avaliable objects from cache.
+ // Fetch available objects from cache, if applicable.
if ($cacheable) {
+ // Build the list of cache entries to retrieve.
+ $cids = array();
+ foreach ($objects as $id => $object) {
+ $cids[] = "field:$obj_type:$id";
+ }
+ $cache = cache_get_multiple($cids, 'cache_field');
+ // Put the cached field values back into the objects and remove them from
+ // the list of objects to query.
foreach ($objects as $id => $object) {
$cid = "field:$obj_type:$id";
- if ($cached = cache_get($cid, 'cache_field')) {
- foreach ($cached->data as $key => $value) {
- $object->$key = $value;
+ if (isset($cache[$cid])) {
+ unset($queried_objects[$id]);
+ foreach ($cache[$cid]->data as $field_name => $values) {
+ $object->$field_name = $values;
}
}
- else {
- $queried_objects[$id] = $objects[$id];
- }
}
}
- else {
- $queried_objects = $objects;
- }
-
- // Fetch other objects from the database.
+ // Fetch other objects from their storage location.
if ($queried_objects) {
// The invoke order is:
// - hook_field_attach_pre_load()
diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test
index 638747bfd..8ba647ba0 100644
--- a/modules/simpletest/tests/cache.test
+++ b/modules/simpletest/tests/cache.test
@@ -162,6 +162,56 @@ class CacheSavingCase extends CacheTestCase {
}
}
+/**
+ * Test cache_get_multiple().
+ */
+class CacheGetMultipleUnitTest extends CacheTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => t('Fetching multiple cache items'),
+ 'description' => t('Confirm that multiple records are fetched correctly.'),
+ 'group' => t('Cache'),
+ );
+ }
+
+ function setUp() {
+ $this->default_bin = 'cache_page';
+ parent::setUp();
+ }
+
+ /**
+ * Test cache_get_multiple().
+ */
+ function testCacheMultiple() {
+ $item1 = $this->randomName(10);
+ $item2 = $this->randomName(10);
+ cache_set('item1', $item1, $this->default_bin);
+ cache_set('item2', $item2, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.'));
+ $this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.'));
+
+ // Fetch both records from the database with cache_get_multiple().
+ $item_ids = array('item1', 'item2');
+ $items = cache_get_multiple($item_ids, $this->default_bin);
+ $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
+ $this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.'));
+
+ // Remove one item from the cache.
+ cache_clear_all('item2', $this->default_bin);
+
+ // Confirm that only one item is returned by cache_get_multiple().
+ $item_ids = array('item1', 'item2');
+ $items = cache_get_multiple($item_ids, $this->default_bin);
+ $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.'));
+ $this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.'));
+ $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.'));
+ }
+}
+
+/**
+ * Test cache clearing methods.
+ */
class CacheClearCase extends CacheTestCase {
public static function getInfo() {
return array(
@@ -224,4 +274,41 @@ class CacheClearCase extends CacheTestCase {
|| $this->checkCacheExists('test_cid_clear2', $this->default_value),
t('Two caches removed after clearing cid substring with wildcard true.'));
}
-} \ No newline at end of file
+
+ /**
+ * Test clearing using an array.
+ */
+ function testClearArray() {
+ // Create three cache entries.
+ cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear2', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear3', $this->default_value),
+ t('Three cache entries were created.'));
+
+ // Clear two entries using an array.
+ cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
+ $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ t('Two cache entries removed after clearing with an array.'));
+
+ $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
+ t('Entry was not cleared from the cache'));
+
+ // Set the cache clear threshold to 2 to confirm that the full bin is cleared
+ // when the threshold is exceeded.
+ variable_set('cache_clear_threshold', 2);
+ cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ t('Two cache entries were created.'));
+ cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
+ $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ || $this->checkCacheExists('test_cid_clear2', $this->default_value)
+ || $this->checkCacheExists('test_cid_clear3', $this->default_value),
+ t('All cache entries removed when the array exceeded the cache clear threshold.'));
+ }
+}