16

For every instance of every type of entity, I generate a number of caches, named something like: [module_name]__[entity_type]__[entity_id]__[string_depending_on_where_the_cache_came_from]

Now whenever a entity is updated, I want to drop all caches starting with the relevant entity type and id.

How should I store/clear these caches?

Currently I just cache_set(), but that presents a problem when I want to clear, as I don't know the names of all the relevant caches. Is it safe to drop cache entries with a db_delete()?

1
  • If you don't know the names of all the relevant caches, how can you use db_delete()? – kiamlaluno Jul 4 '12 at 17:47
6

To delete entries from a cache, you should use cache_clear_all(). The reason is that the used cache implementation could not use a database table in the active database. That is what happens with the DrupalDatabaseCache class, but it should not be true for every classes.

If you look at _cache_get_object() (the function called by cache_get() and cache_set()), you will notice it contains the following code.

  static $cache_objects; 
  if (!isset($cache_objects[$bin])) {
    $class = variable_get('cache_class_' . $bin);
    if (!isset($class)) {
      $class = variable_get('cache_default_class', 'DrupalDatabaseCache');
    }
    $cache_objects[$bin] = new $class($bin);
  }
  return $cache_objects[$bin];

The class for the cache implementation could be different for each cache bin store, and even the default one could be changed.

Private update status cache system explains exactly why the normal cache functions are not used in _update_cache_clear(), _update_cache_get(), and _update_cache_set(). (Emphasis is mine.)

We specifically do NOT use the core cache API for saving the fetched data about available updates. It is vitally important that this cache is only cleared when we're populating it after successfully fetching new available update data. Usage of the core cache API results in all sorts of potential problems that would result in attempting to fetch available update data all the time, including if a site has a "minimum cache lifetime" (which is both a minimum and a maximum) defined, or if a site uses memcache or another pluggable cache system that assumes volatile caches.

The Update Manager module still uses the {cache_update} table, but instead of using cache_set(), cache_get(), and cache_clear_all(), there are private helper functions that implement these same basic tasks but ensure that the cache is not prematurely cleared, and that the data is always stored in the database, even if memcache or another cache backend is in use.

The Update Manager has specific needs that are necessary because attempting to fetch update information too frequently would cause issues with Drupal.org servers, considering that the Update Manager can potentially fetch update information from any site running Drupal.

In your case, you could use [module_name]__[entity_type]__[entity_id]__[string_depending_on_where_the_cache_came_from] as cache ID for a single cache bin store. In the case you need to delete all the entries for a entity, you could use the following code.

cache_clear_all("{$module}__{$entity_type}__{$entity_id}__", $bin, TRUE);

If you cannot get the value to assign to $module when you are clearing the cache, or you want to delete the cache entry independently from the module for which the data has been cached, then you can use a different cache ID, such as [entity_type]__[entity_id]__[string_depending_on_where_the_cache_came_from], or [entity_type]__[entity_id]__[module_name]__[string_depending_on_where_the_cache_came_from]. cache_clear_all() deletes all the cache entries with a cache ID starting with the string passed as argument, when $wildcard is TRUE, and the cache ID is not '*'. In this case, the cache would be cleared with the following code.

cache_clear_all("{$entity_type}__{$entity_id}__", $bin, TRUE);
8

I can't think of a good reason why dropping the entries manually would cause a problem. This assumes, of course, that you're using MySQL as the backend for your particular cache; although I think the same applies to any other type of cache backend, the method to clear just wouldn't necessarily be a database query.

If you take the core update module as an example, it bypasses the cache_* functions and clears its cache manually:

function _update_cache_clear($cid = NULL, $wildcard = FALSE) {
  if (empty($cid)) {
    db_delete('cache_update')
      // Clear everything except fetch task information because these are used
      // to ensure that the fetch task queue items are not added multiple times.
      ->condition('cid', 'fetch_task::%', 'NOT LIKE')
      ->execute();
  }
  else {
    $query = db_delete('cache_update');
    if ($wildcard) {
      $query->condition('cid', $cid . '%', 'LIKE');
    }
    else {
      $query->condition('cid', $cid);
    }
    $query->execute();
  }
}

I always think "if it's good enough for core, it's good enough for me" :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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