Commit 0b6f05cf authored by Jeremy's avatar Jeremy

Port to dmemcache.inc and memcache.inc to Drupal 7, patch thanks to chx.

Minimally updated README and INSTALLATION docs.
NOTE: memcache.db.inc and memcache-session.inc are note yet ported.
parent 28b988c1
// $Id$
For instructions on how to compile memcached on Debian Etch, see here:
http://www.lullabot.com/articles/how_install_memcache_debian_etch
http://www.lullabot.com/articles/how_install_memcache_debian_etch
Fedora memcache + drupal walkthrough
......@@ -85,21 +85,16 @@ configure settings.php)
Apply the default configuration to the bottom of your
DRUPAL/sites/default/settings.php file. If you have already configured
$cfg, you will need to add the element to the array instead of appending the
whole $cfg. The example below uses the default localhost:11211 server.
[simon@yoursite memcache]$ tail -3 ../../sites/default/settings.php
$conf = array(
'cache_inc' => './sites/all/modules/memcache/memcache.inc',
);
[simon@yoursite memcache]$
whole $cfg. Don't forget to also include 'cache.inc' and 'memcache.inc'. The
example below uses the default localhost:11211 server.
8) Enable the drupal module from /admin/build/modules (it's in the "Other"
section) Note that the memcache module only provides some debug output for
administrators to help you see what memcache is doing. It is non-essential
to the functioning of your site, and can be turned off on a production site.
$ tail -3 ../../sites/default/settings.php
include_once('./includes/cache.inc');
include_once('./sites/default/modules/memcache/memcache.inc');
$conf['cache_default_class'] = 'MemCacheDrupal';
$
It is, however, *vital* that you enable the module at least once because it runs
hook_install and adds the serialized column to all of your cache tables.
8) Optionally enable the memcache admin drupal module from /admin/build/modules
(it's in the "Other" section).
9) Also see the memcache_admin module (distributed with this package) to monitor
memcache statistics.
See README.txt for more information about configuring Drupal with memcache.
......@@ -5,44 +5,43 @@
These are the broad steps you need to take in order to use this software. Order
is important.
1. Install the memcached binaries on your server. See http://www.lullabot.com/articles/how_install_memcache_debian_etch
2. Install the PECL memcache extension for PHP. This must be version 2.2.1 or higher or you will experience errors.
1. Install the memcached binaries on your server. See
http://www.lullabot.com/articles/how_install_memcache_debian_etch
2. Install the PECL memcache extension for PHP. This must be version 2.2.1 or
higher or you will experience errors.
3. Put your site into offline mode.
4. Download and install the memcache module.
5. If you have previously been running the memcache module, run update.php.
6. Apply the DRUPAL-5-x-cache-serialize.patch from the patches folder that
comes with the module. Version specific, so use DRUPAL-5-6-cache-serialize.patch
if you are running Drupal 5.6.
7. Start at least one instance of memcached on your server.
8. Edit settings.php to configure the servers, clusters and bins that memcache
6. Start at least one instance of memcached on your server.
7. Edit settings.php to configure the servers, clusters and bins that memcache
is supposed to use.
9. Edit settings.php to include either memcache.inc or memcache.db.inc. For
example, $conf['cache_inc'] ='sites/all/modules/memcache/memcache.db.inc';
10. Bring your site back online.
8. Edit settings.php to include cache.inc, memcache.inc. For example:
include_once('./includes/cache.inc');
include_once('./sites/all/modules/memcache/memcache.inc');
9. Edit settings.php to make memcache the default caching class:
$conf['cache_default_class'] = 'MemCacheDrupal';
9. Bring your site back online.
For instructions on 1 and 2 above, please see the INSTALLATION.txt file that
comes with the memcache module download.
Either the memcache.inc or the memcache.db.inc file is intended to be used
instead of cache.inc, utilizing Drupal's pluggable cache system. The .db.inc
variant saves all data to the database as well, so the site will still have
the performance benefits of cache even if you take your memcache offline. The
site should not ever break due to memcache not being available... it is only
a question of whether caching is still available or not. The memcache.inc file
doesn't save any data to the database and thus has the biggest potential for
increasing your site's performance. If you use this file it is important to
have enough memory allocated to memcache to store everything (including the page
cache), otherwise the cache misses will negate the benefit of the cache hits.
Update $conf in settings.php to tell Drupal which cache_inc file to use:
$conf = array(
// The path to wherever memcache.inc is. The easiest is to simply point it
// to the copy in your module's directory.
'cache_inc' => './sites/all/modules/memcache/memcache.inc',
// or
// 'cache_inc' => './sites/all/modules/memcache/memcache.db.inc',
);
At this time, only memcache.inc is available in Drupal 7. The memcache.inc.db
file has not yet been ported.
http://www.lullabot.com/files/memcache-inc.png
In Drupal 7+, you no longer should set cache_inc in settings.php. Instead, you
will have to manually include 'cache.inc' and 'memcache.inc', then update $conf
to tell Drupal to default to memcache for caching:
// the path to the core cache file
include_once('./includes/cache.inc');
// the path to the memcache cache file
include_once('./sites/all/modules/memcache/memcache.inc');
// make MemCacheDrupal the default cache class
$conf['cache_default_class'] = 'MemCacheDrupal';
## SERVERS ##
......@@ -63,7 +62,11 @@ If you have more than one memcached instance running, you need to add two
arrays to $conf; memcache_servers and memcache_bins. The arrays follow this
pattern:
'memcache_servers' => array(host1:port => cluster, host2:port => cluster, hostN:port => cluster)
'memcache_servers' => array(
host1:port => cluster,
host2:port => cluster,
hostN:port => cluster
)
'memcache_bins' => array(bin1 => cluster, bin2 => cluster, binN => cluster)
......@@ -102,8 +105,10 @@ Here is an example configuration that has two clusters, 'default' and
clusters. 'cache_filter' and 'cache_menu' bins goe to 'cluster2'. All other
bins go to 'default'.
include_once('./includes/cache.inc');
include_once('./sites/all/modules/memcache/memcache.inc');
$conf = array(
'cache_inc' => './includes/memcache.inc',
'cache_default_class' = 'MemCacheDrupal',
'memcache_servers' => array('localhost:11211' => 'default',
'localhost:11212' => 'default',
'123.45.67.890:11211' => 'default',
......@@ -125,42 +130,38 @@ $conf = array(
'memcache_key_prefix' => 'something_unique',
);
## PATCHES ##
## SESSIONS ##
The DRUPAL-5-cache-serialize.patch must be applied to your Drupal installation
for this software to work. The patch depends on a column that needs to get added
to all of the existing cache tables for your site. This column has been
introduced in the DRUPAL-6 development branch so this patch is future-safe if
you ever upgrade to DRUPAL-6. To actually add the column to your database, you
need to either install the memcache.module, or, if it is already installed and
you are updating to this version, run update.php. Either installing the module
or running update.php will add the needed column, Uninstalling the module will
remove the column.
Here is a sample config that uses memcache for sessions. Note you MUST have
a session and a users server set up for memcached sessions to work.
## TROUBLESHOOTING ##
PROBLEM:
Warning: require_once(a) [function.require-once]: failed to open stream:
No such file or directory in /includes/bootstrap.inc on line 853
NOTE: Session.inc is not yet ported to Drupal 7.
SOLUTION:
This error occurs after you apply the DRUPAL-5-cache-serialize.patch because
the code in the patch now expects the cached variables to be unserialized
but they are still serialized in the cache. Clear the cache table:
mysql> TRUNCATE cache;
Query OK, 0 rows affected (0.01 sec)
PROBLEM:
Fatal error: Cannot use string offset as an array in includes/menu.inc on line 452
include_once('./includes/cache.inc');
include_once('./sites/all/modules/memcache/memcache.inc');
$conf = array(
'cache_default_class' = 'MemCacheDrupal',
'session_inc' => './sites/all/modules/memcache/memcache-session.inc',
'memcache_servers' => array(
'localhost:11211' => 'default',
'localhost:11212' => 'filter',
'localhost:11213' => 'menu',
'localhost:11214' => 'page',
'localhost:11215' => 'session',
'localhost:11216' => 'users',
),
'memcache_bins' => array(
'cache' => 'default',
'cache_filter' => 'filter',
'cache_menu' => 'menu',
'cache_page' => 'page',
'session' => 'session',
'users' => 'users',
),
);
SOLUTION:
Similar to the error above, this occurs after applying the
DRUPAL-5-cache-serialize.patch due to the conflict between the existing
cached menu and what the patched code is expecting. Clear cache_menu:
mysql> TRUNCATE cache_menu;
Query OK, 0 rows affected (0.33 sec)
## TROUBLESHOOTING ##
PROBLEM:
Error:
......@@ -169,7 +170,11 @@ Failed to set key: Failed to set key: cache_page-......
SOLUTION:
Upgrade your PECL library to PECL package (2.2.1) (or higher).
WARNING:
Zlib compression at the php.ini level and Memcache conflict.
See http://drupal.org/node/273824
## MEMCACHE ADMIN ##
A module offering a UI for memcache is on the way. It will provide stats, a
A module offering a UI for memcache is included. It provides stats, a
way to clear the cache, and an interface to organize servers/bins/clusters.
......@@ -30,14 +30,14 @@ $_memcache_statistics = array('get' => array(), 'set' => array(), 'hit' => array
*
* @return bool
*/
function dmemcache_set($key, $value, $exp = 0, $bin = 'cache') {
function dmemcache_set($key, $value, $exp = 0, $bin = 'cache', $mc = NULL) {
global $_memcache_statistics;
$_memcache_statistics['set'][] = $key;
$_memcache_statistics['bins'][] = $bin;
if ($mc = dmemcache_object($bin)) {
if ($mc || ($mc = dmemcache_object($bin))) {
$full_key = dmemcache_key($key, $bin);
if (!$mc->set($full_key, $value, TRUE, $exp)) {
watchdog('memcache', 'Failed to set key: ' . $full_key, WATCHDOG_ERROR);
if (!$mc->set($full_key, $value, MEMCACHE_COMPRESSED, $exp)) {
return FALSE;
}
else {
return TRUE;
......@@ -54,15 +54,29 @@ function dmemcache_set($key, $value, $exp = 0, $bin = 'cache') {
*
* @return The item which was originally saved or FALSE
*/
function dmemcache_get($key, $bin = 'cache') {
function dmemcache_get($key, $bin = 'cache', $mc = NULL) {
global $_memcache_statistics;
$_memcache_statistics['get'][] = $key;
$_memcache_statistics['bins'][] = $bin;
if ($mc = dmemcache_object($bin)) {
if ($mc || ($mc = dmemcache_object($bin))) {
$full_key = dmemcache_key($key, $bin);
$result = $mc->get($full_key);
if ($result) {
$_memcache_statistics['hit'][] = $key;
// We check $result->expire to see if the object has expired. If so, we
// try and grab a lock. If we get the lock, we return FALSE instead of
// the cached object which should cause it to be rebuilt. If we do not
// get the lock, we return the cached object. The goal here is to avoid
// cache stampedes.
// By default the cache stampede semaphore is held for 15 seconds. This
// can be adjusted by setting the memcache_stampede_semaphore variable.
// TODO: Can we log when a sempahore expires versus being intentionally
// freed to track when this is happening?
if (isset($result->expire) && $result->expire !== CACHE_PERMANENT && $result->expire <= time() && $mc->add($full_key .'_semaphore', '', FALSE, variable_get('memcache_stampede_semaphore', 15))) {
$result = FALSE;
}
else {
$_memcache_statistics['hit'][] = $key;
}
}
return $result;
}
......@@ -76,8 +90,8 @@ function dmemcache_get($key, $bin = 'cache') {
*
* @return Returns TRUE on success or FALSE on failure.
*/
function dmemcache_delete($key, $bin = 'cache') {
if ($mc = dmemcache_object($bin)) {
function dmemcache_delete($key, $bin = 'cache', $mc = NULL) {
if ($mc || ($mc = dmemcache_object($bin))) {
$full_key = dmemcache_key($key, $bin);
return $mc->delete($full_key);
}
......@@ -94,15 +108,32 @@ function dmemcache_delete($key, $bin = 'cache') {
*
* @return Returns TRUE on success or FALSE on failure.
*/
function dmemcache_flush($bin = 'cache') {
if ($mc = dmemcache_object($bin)) {
function dmemcache_flush($bin = 'cache', $mc = NULL) {
if ($mc || ($mc = dmemcache_object($bin))) {
return $mc->flush();
}
}
function dmemcache_stats($bin = 'cache', $type = '') {
// resolve requests for 'default' type to ''
if ($type == 'default') {
$type = '';
}
// resolve requests for 'default' bin to 'cache'.
if ($bin == 'default') {
$bin = 'cache';
}
if ($mc = dmemcache_object($bin)) {
return $mc->getExtendedStats($type);
// The PHP Memcache extension 3.x version throws an error if the stats
// type is NULL or not in {reset, malloc, slabs, cachedump, items, sizes}.
// If $type is 'default', then no parameter should be passed to the
// Memcache memcache_get_extended_stats() function.
if ($type == 'default' || $type == '') {
return $mc->getExtendedStats();
}
else {
return $mc->getExtendedStats($type);
}
}
}
......@@ -120,6 +151,7 @@ function dmemcache_stats($bin = 'cache', $type = '') {
* @return an Memcache object or FALSE.
*/
function dmemcache_object($bin = NULL, $flush = FALSE) {
global $memcache_online;
static $memcacheCache = array(), $memcache_servers, $memcache_bins;
if ($flush) {
......@@ -163,9 +195,13 @@ function dmemcache_object($bin = NULL, $flush = FALSE) {
// This is a server that belongs to this cluster.
if (!$init) {
// First server gets the connect.
if (@$memcache->addServer($host, $port)) {
if (@$memcache->connect($host, $port)) {
// Only set init to true if a connection was made.
$init = TRUE;
$memcache_online = TRUE;
}
else {
$memcache_online = FALSE;
}
}
else {
......@@ -202,5 +238,6 @@ function dmemcache_key($key, $bin = 'cache') {
$prefix = variable_get('memcache_key_prefix', '');
}
$full_key = ($prefix ? $prefix. '-' : '') . $bin . '-' . $key;
return urlencode($full_key);
}
<?php
// $Id$
require_once dirname(__FILE__) . '/dmemcache.inc';
/**
* @file
* User session handling functions.
*
* An alternative to includes/session.inc.
*/
function sess_open($save_path, $session_name) {
......@@ -22,43 +26,91 @@ function sess_read($key) {
// So we are moving session closure before destructing objects.
register_shutdown_function('session_write_close');
// Handle the case of first time visitors and clients that don't store cookies (eg. web crawlers).
// Handle the case of first time visitors and clients that don't store
// cookies (eg. web crawlers).
if (!isset($_COOKIE[session_name()])) {
$user = drupal_anonymous_user();
return '';
}
// Otherwise, if the session is still active, we have a record of the client's session in memcache.
// Otherwise, if the session is still active, we have a record of the
// client's session in memcache.
$session = dmemcache_get($key, 'session');
$user = sess_user_load($session);
// Record whether this session contains data so that in sess_write() it can
// be determined whether to skip a write.
$user->session_data_present_at_load = !empty($session->session);
return $user->session;
}
/**
* Write a session to session storage.
*
* We have the following cases to handle.
* 1. Anonymous user
* 1a. Without session data.
* 1b. With session data.
* 1c. Session saving has been turned off programatically
* (see session_save_session()).
* 1d. Without session data but had session data at the beginning of the request
* (thus a write must be made to clear stored session data).
* 2. Authenticated user.
* 2a. Without session data.
* 2b. With session data.
* 2c. Session saving has been turned off programatically
* (see session_save_session()).
*
* @param $key
* The session ID.
* @param $value
* Any data to store in the session.
* @return
* TRUE.
*/
function sess_write($key, $value) {
global $user;
// If the client doesn't have a session, and one isn't being created ($value), do nothing.
if (empty($_COOKIE[session_name()]) && empty($value)) {
// If the client doesn't have a session, and one isn't being created ($value),
// do nothing. If session saving has been turned off, do nothing.
// Cases 1a, 1c, and 2c are covered here.
if ((empty($_COOKIE[session_name()]) && empty($value)) || !session_save_session()) {
return TRUE;
}
// Prepare the information to be saved
// Prepare the information to be saved.
$session = new stdClass;
$session->sid = $key;
$session->uid = $user->uid;
$session->cache = $user->cache;
$session->hostname = $_SERVER["REMOTE_ADDR"];
$session->cache = isset($user->cache) ? $user->cache : '';
$session->hostname = ip_address();
$session->session = $value;
$session->timestamp = time();
if ($user->uid || $value) {
// If this is an authenticated user, or there is something to save in the
// session, or this is an anonymous user who currently has nothing in the
// session but did have something in session storage, write it to memcache.
// If $user->session_data_present_at_load is not set, the current user
// was created during this request and it's safest to do a write.
// Cases 1b, 1d, 2a, and 2b are covered here.
if ($user->uid || !empty($value) || empty($value) && (!isset($user->session_data_present_at_load) || $user->session_data_present_at_load)) {
dmemcache_set($key, $session, ini_get('session.gc_maxlifetime'), 'session');
dmemcache_set($user->uid, $user, ini_get('session.gc_maxlifetime'), 'users');
if ($user->uid && $user->access < time() - 300) {
db_query("UPDATE {users} SET access = %d WHERE uid = %d", time(), $user->uid);
if ($user->uid && $session->timestamp - $user->access > variable_get('session_write_interval', 360)) {
db_query('UPDATE {users} SET access = %d WHERE uid = %d', $session->timestamp, $user->uid);
// Update the user access time so that the dmemcache_set() call
// caches the updated time.
$user->access = $session->timestamp;
}
// Data stored in session is stored in session memcache; no need
// to duplicate it in users memcache.
unset($user->session);
unset($user->session_data_present_at_load);
// Store the session id so we can locate the session with the user id.
$user->sid = $key;
dmemcache_set($user->uid, $user, ini_get('session.gc_maxlifetime'), 'users');
}
return TRUE;
......@@ -81,7 +133,7 @@ function sess_regenerate() {
session_regenerate_id();
$key = session_id();
// Grab the user's information that is cached with the anonymous key
// Grab the user's information that is cached with the anonymous key.
$info = dmemcache_get($old_session_id, 'session');
// Update it.
......@@ -113,9 +165,14 @@ function sess_destroy_sid($sid) {
}
/**
* End a specific user's session. Not implemented.
* End a specific user's session.
*/
function sess_destroy_uid($uid) {
$user = dmemcache_get($uid, 'users');
if (is_object($user) && isset($user->sid)) {
dmemcache_delete($user->sid, 'session');
}
dmemcache_delete($uid, 'users');
}
function sess_gc($lifetime) {
......@@ -128,14 +185,52 @@ function sess_gc($lifetime) {
return TRUE;
}
/**
* Determine whether to save session data of the current request.
*
* This function allows the caller to temporarily disable writing of session data,
* should the request end while performing potentially dangerous operations, such as
* manipulating the global $user object. See http://drupal.org/node/218104 for usage
*
* @param $status
* Disables writing of session data when FALSE, (re-)enables writing when TRUE.
* @return
* FALSE if writing session data has been disabled. Otherwise, TRUE.
*/
function session_save_session($status = NULL) {
static $save_session = TRUE;
if (isset($status)) {
$save_session = $status;
}
return ($save_session);
}
/**
* Create the user object.
*
* @param $session
* The session object (see sess_write() for the structure).
* @return $user
* The user object.
*/
function sess_user_load($session) {
$user = new stdClass;
// We found the client's session record and they are an authenticated user
if ($session && $session->uid > 0) {
// We found the client's session record and they are an authenticated user.
if ($session && $session->uid != 0) {
$user = dmemcache_get($session->uid, 'users');
if (!$user->uid && $user->uid != 0) {
$user = db_fetch_object(db_query("SELECT u.* FROM {users} u WHERE u.uid = %d", $session->uid));
// If the 'users' memcache bin is unavailable, $user will be NULL.
// If the cached user was not found in the 'users' memcache bin, $user will
// be FALSE.
// In either of these cases, the user must be retrieved from the database.
if (!$user->uid && isset($session->uid) && $session->uid != 0) {
$user = db_fetch_object(db_query('SELECT u.* FROM {users} u WHERE u.uid = %d', $session->uid));
$user = drupal_unpack($user);
// Normally we would join the session and user tables. But we already
// have the session information. So add that in.
$user->sid = $session->sid;
$user->cache = $session->cache;
$user->hostname = $session->hostname;
$user->timestamp = $session->timestamp;
$user->session = empty($session->session) ? '' : $session->session;
// Add roles element to $user
......@@ -147,20 +242,27 @@ function sess_user_load($session) {
}
}
else if ($user->uid) {
// Got a user object from 'users' memcache bin. Mark it in case modules
// want to know that this user was created from memcache.
$user->from_cache = TRUE;
$user->session = empty($session->session) ? '' : $session->session;
}
else {
// We will only get here when the session has a nonzero uid, a user object
// was successfully retrieved from the 'users' bin, and that user
// object's uid is 0. Not sure why this would ever happen. Leaving former
// comment in:
// This is a rare case that we have a session cached, but no session user object cached.
// This usually only happens if you kill memcached and restart it.
$user = drupal_anonymous_user($session->session);
}
}
// We didn't find the client's record (session has expired), or they are an anonymous user.
// We didn't find the client's record (session has expired), or they are an
// anonymous user.
else {
$session = isset($session->session) ? $session->session : '';
$user = drupal_anonymous_user($session);
}
return $user;
}
......@@ -3,35 +3,41 @@
require_once 'dmemcache.inc';
/** Implementation of cache.inc with memcache logic included **/
/**
* @file
* Implementation of cache.inc with memcache logic included.
**/
/**
* Return data from the persistent cache.
* Return data from the persistent cache. Data may be stored as either plain text or as serialized data.
* cache_get will automatically return unserialized objects and arrays.
*
* @param $key
* @param $cid
* The cache ID of the data to retrieve.
* @param $table
* The table $table to store the data in. Valid core values are 'cache_filter',
* 'cache_menu', 'cache_page', or 'cache' for the default cache.
*/
function cache_get($key, $table = 'cache') {
function cache_get($cid, $table = 'cache') {
global $user;
global $memcache_online;
// Garbage collection necessary when enforcing a minimum cache lifetime
$cache_flush = variable_get('cache_flush', 0);
if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= time())) {
// Time to flush old cache data
db_query("DELETE FROM {%s} WHERE expire != %d AND expire <= %d", $table, CACHE_PERMANENT, $cache_flush);
// Reset the variable immediately to prevent a meltdown in heavy load situations.
variable_set('cache_flush', 0);
// Time to flush old cache data
db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire <= %d", CACHE_PERMANENT, $cache_flush);
}
// If we have a memcache hit for this, return it.
if ($cache = dmemcache_get($key, $table)) {
if ($memcache_online !== FALSE && $cache = dmemcache_get($cid, $table)) {
return $cache;
}
// Look for a database cache hit.
if ($cache = db_fetch_object(db_query("SELECT data, created, headers, expire, serialized FROM {%s} WHERE cid = '%s'", $table, $key))) {
if ($cache = db_fetch_object(db_query("SELECT data, created, headers, expire, serialized FROM {". $table ."} WHERE cid = '%s'", $cid))) {
if (isset($cache->data)) {
// If the data is permanent or we're not enforcing a minimum cache lifetime
// always return the cached data.
......@@ -62,7 +68,9 @@ function cache_get($key, $table = 'cache') {
// By calling cache_set with an extra paramater to signify no db storage,
// we can lazy instantiate memcache that just comes online.
cache_set($key, $table, $cache->data, $cache->expire, $cache->headers, FALSE);
if ($memcache_online) {
cache_set($cid, $cache->data, $table, $cache->expire, $cache->headers, FALSE);
}
return $cache;
}
return 0;
......@@ -95,11 +103,13 @@ function cache_get($key, $table = 'cache') {
*
* @param $cid
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically
* serialized before insertion. Strings will be stored as plain text and
* not serialized.
* @param $table
* The table $table to store the data in. Valid core values are 'cache_filter',
* 'cache_menu', 'cache_page', or 'cache'.
* @param $data
* The data to store in the cache. Complex data types must be serialized first.
* @param $expire
* One of the following values:
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
......@@ -115,14 +125,15 @@ function cache_get($key, $table = 'cache') {
* It allows us to do a cache_set and not write to the database, but only
* to memcache.
*/
function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $headers = NULL, $db_storage = TRUE) {
$time = time();
function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL, $db_storage = TRUE) {
global $memcache_online;
$created = time();
// Create new cache object.
$cache = new stdClass;
$cache->cid = $cid;
$cache->data = is_object($data) ? memcache_clone($data) : $data;
$cache->created = $time;
$cache->created = $created;
$cache->expire = $expire;
$cache->headers = $headers;
......@@ -132,19 +143,20 @@ function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $he
$data = serialize($data);
$serialized = 1;
}
// Save to the database
db_query("
INSERT INTO {$table} (data, created, expire, headers, serialized, cid) VALUES (%b, %d, %d, '%s', %d, '%s') ON DUPLICATE KEY
UPDATE data = %b, created = %d, expire = %d, headers = '%s', serialized = %d",
$data, $time, $expire, $headers, $serialized, $cid,
$data, $time, $expire, $headers, $serialized, $cid);
db_query("DELETE FROM {%s} WHERE cid = '%s'", $table, $cid);
db_query("INSERT INTO {%s} (cid, data, expire, created, headers, serialized) VALUES ('%s', %b, %d, %d, '%s', '%s')", $table, $cid, $data, $expire, $created, $headers, $serialized);
}
// Save to memcache
if ($expire == CACHE_TEMPORARY) {
// 2591199 seconds = about 1 month
$expire = variable_get('cache_lifetime', 2591999);
}
dmemcache_set($cid, $cache, $expire, $table);
if ($memcache_online !== FALSE) {
dmemcache_set($cid, $cache, $expire, $table);
}
}
/**
......@@ -152,17 +164,13 @@ function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $he
* Expire data from the cache. If called without arguments, expirable
* entries will be cleared from the cache_page table.
*
* Memcache logic is simpler than the core cache because memcache doesn't have
* a minimum cache lifetime consideration (it handles it internally), and
* doesn't support wildcards. Wildcard flushes result in the entire table
* being flushed.
*
* @param $cid
* If set, the cache ID to delete. Otherwise, all cache entries that can
* expire are deleted from the specified table.
* expire are deleted.
*
* @param $table
* If set, the table delete from.
* If set, the table $table to delete from. Mandatory
* argument if $cid is set.
*
* @param $wildcard
* If set to TRUE, the $cid is treated as a substring
......@@ -172,25 +180,15 @@ function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $he
function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
global $user;
// Default behavior for when cache_clear_all() is called without parameters
// is to clear all of the expirable entries in the block and page caches.
if (!isset($cid) && !isset($table)) {
$cid = '*';
$wildcard = TRUE;
$table = 'cache_page';
}
if (empty($cid) || ($cid == '*' && $wildcard !== TRUE)) {
# don't do anything if cid is unset. this matches the default drupal behavior...
if ($wildcard && $cid != '*') {
if (variable_get('memcache_debug', FALSE)) {
// call watchdog, since you probably didn't want to flush the entire bin.
watchdog('memcache', "illegal wildcard in cache_clear_all - not flushing entire bin. table: $table. cid: $cid", WATCHDOG_WARNING);
}
}
}
else if ($cid == '*' || $wildcard === TRUE) {
dmemcache_flush($table);
}
else {
dmemcache_delete($cid, $table);
// Clear the block cache first, so stale data will
// not end up in the page cache.
cache_clear_all(NULL, 'cache_block');