1

cache_get always returns an object and I'm a bit confused by its values. There's probably a short generic function that can handle this for once and for all.

$cache = cache_get('key');
if ($cache) { 
  // doesn't work, cache_get always returns an object
}

if (!$cache || ($cache->created < time() - $cache->expire)) {
  // cache is expired? I'm not sure
}

Just checking for the data member doesn't cut it.

I had seen the issue reported on d.o (in 2009) but that doesn't really help me fix it on any live site. So I'm looking for a function that will still make cache work for me without resorting to storing the cache myself in the database.

1

1 Answer 1

3

TL;DR Don't, unless you have to. Drupal has long history of optimization and even if it sometimes serves you outdated cache, it's not without a cause. It's to prevent overhead of constant cache wipes. If your application is time-critical for some reasons, read below.


As you can see in the code that does the actual work:

function getMultiple(&$cids) {
  try {
    // Garbage collection necessary when enforcing a minimum cache lifetime.
    $this->garbageCollection($this->bin);

    // When serving cached pages, the overhead of using db_select() was found
    // to add around 30% overhead to the request. Since $this->bin is a
    // variable, this means the call to db_query() here uses a concatenated
    // string. This is highly discouraged under any other circumstances, and
    // is used here only due to the performance overhead we would incur
    // otherwise. When serving an uncached page, the overhead of using
    // db_select() is a much smaller proportion of the request.
    $result = db_query('SELECT cid, data, created, expire, serialized FROM {' . db_escape_table($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
    $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;
  }
  catch (Exception $e) {
    // If the database is never going to be available, cache requests should
    // return FALSE in order to allow exception handling to occur.
    return array();
  }
}

There is no test built in1, and no limit that makes Core return only unexpired nodes.

  • The way I prefer if I can, is to delete/hide table rows on database level, for example by creating a proxy view that never returns outdated records in the first place, but usually this is inconvenient. Don't try unless you know what you're doing.

  • The Drupal-only way would be to add comparison in PHP, like that:

    if (isset($cache->data) and time() < $cache->expire) {
    
  • If your cache is relatively long, you can, and for performance reasons probably should omit testing their expire date on each call and leave it to Drupal's core. It will allow minimum cache lifetime to work with your data, for example. That's how core is doing it.1


1 There are some tests in prepareItem method, but for performance reasons they do not guarantee that outdated cache items will not be returned - see actual code for details.

3
  • 1
    I think the big takeaway is that the standard Drupal cache backend exforces a minimum lifetime for non-temporary and non-permanent entries, and not an exact one. In fact, this is stated in the API docs for cache_set: "$expire: One of the following values: ... A Unix timestamp: Indicates that the item should be kept at least until the given time, after which it behaves like CACHE_TEMPORARY." (emphasis mine). In addition, constant pruning of the cache tables will ruin the effectiveness of MySQL query caching, as writes/deletes to a table invalidate all entries in that table.
    – mpdonadio
    Commented Feb 11, 2014 at 13:43
  • @MPD I agree with that. On the other hand there are situations when refreshing data at the exact moment is vital. Especially if it's a government project or other project that must comply with specific laws. Also in case of contests. Probably there are other reasons, too. I assumed that's the case here.
    – Mołot
    Commented Feb 11, 2014 at 13:54
  • @MPD better now?
    – Mołot
    Commented Feb 11, 2014 at 14:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.