Commit bd0770cd authored by Jeremy's avatar Jeremy

Issue #2204075 by Jeremy: standardize/improve statistics collection and display.

parent 6e8eafa8
......@@ -8,8 +8,8 @@
* session-memcache.db.inc
*/
global $_memcache_statistics;
$_memcache_statistics = array();
global $_dmemcache_stats;
$_dmemcache_stats = array('all' => array(), 'ops' => array());
/*
* A memcache API for Drupal.
......@@ -33,20 +33,25 @@ $_memcache_statistics = array();
* @return bool
*/
function dmemcache_set($key, $value, $exp = 0, $bin = 'cache', $mc = NULL) {
global $_memcache_statistics;
$collect_stats = dmemcache_stats_init();
$full_key = dmemcache_key($key, $bin);
if (dmemcache_collect_stats()) {
$_memcache_statistics[] = array('set', $bin, $full_key, '');
}
$rc = FALSE;
if ($mc || ($mc = dmemcache_object($bin))) {
if ($mc instanceof Memcached) {
return $mc->set($full_key, $value, $exp);
$rc = $mc->set($full_key, $value, $exp);
}
else {
return $mc->set($full_key, $value, MEMCACHE_COMPRESSED, $exp);
$rc = $mc->set($full_key, $value, MEMCACHE_COMPRESSED, $exp);
}
}
return FALSE;
if ($collect_stats) {
dmemcache_stats_write('set', $bin, array($full_key => $rc));
}
return $rc;
}
/**
......@@ -71,20 +76,25 @@ function dmemcache_set($key, $value, $exp = 0, $bin = 'cache', $mc = NULL) {
* @return bool
*/
function dmemcache_add($key, $value, $exp = 0, $bin = 'cache', $mc = NULL, $flag = FALSE) {
global $_memcache_statistics;
$collect_stats = dmemcache_stats_init();
$full_key = dmemcache_key($key, $bin);
if (dmemcache_collect_stats()) {
$_memcache_statistics[] = array('add', $bin, $full_key, '');
}
$rc = FALSE;
if ($mc || ($mc = dmemcache_object($bin))) {
if ($mc instanceof Memcached) {
return $mc->add($full_key, $value, $exp);
$rc = $mc->add($full_key, $value, $exp);
}
else {
return $mc->add($full_key, $value, $flag, $exp);
$rc = $mc->add($full_key, $value, $flag, $exp);
}
}
return FALSE;
if ($collect_stats) {
dmemcache_stats_write('add', $bin, array($full_key => $rc));
}
return $rc;
}
/**
......@@ -96,20 +106,17 @@ function dmemcache_add($key, $value, $exp = 0, $bin = 'cache', $mc = NULL, $flag
* @return The item which was originally saved or FALSE
*/
function dmemcache_get($key, $bin = 'cache', $mc = NULL) {
global $_memcache_statistics;
$collect_stats = dmemcache_stats_init();
$result = FALSE;
$full_key = dmemcache_key($key, $bin);
$statistics = array('get', $bin, $full_key);
$success = '0';
if ($mc || $mc = dmemcache_object($bin)) {
$track_errors = ini_set('track_errors', '1');
$php_errormsg = '';
$result = @$mc->get($full_key);
if (dmemcache_collect_stats()) {
$statistics[] = (bool) $result;
$_memcache_statistics[] = $statistics;
}
if (!empty($php_errormsg)) {
register_shutdown_function('watchdog', 'memcache', 'Exception caught in dmemcache_get: !msg', array('!msg' => $php_errormsg), WATCHDOG_WARNING);
......@@ -118,6 +125,10 @@ function dmemcache_get($key, $bin = 'cache', $mc = NULL) {
ini_set('track_errors', $track_errors);
}
if ($collect_stats) {
dmemcache_stats_write('get', $bin, array($full_key => (bool) $result));
}
return $result;
}
......@@ -130,16 +141,20 @@ function dmemcache_get($key, $bin = 'cache', $mc = NULL) {
* @return The item which was originally saved or FALSE
*/
function dmemcache_get_multi($keys, $bin = 'cache', $mc = NULL) {
global $_memcache_statistics;
$collect_stats = dmemcache_stats_init();
$multi_stats = array();
$full_keys = array();
$statistics = array();
foreach ($keys as $key => $cid) {
$full_key = dmemcache_key($cid, $bin);
if (dmemcache_collect_stats()) {
$statistics[$full_key] = array('getMulti', $bin, $full_key);
}
$full_keys[$cid] = $full_key;
if ($collect_stats) {
$multi_stats[$full_key] = FALSE;
}
}
$results = array();
if ($mc || ($mc = dmemcache_object($bin))) {
if ($mc instanceof Memcached) {
......@@ -158,24 +173,28 @@ function dmemcache_get_multi($keys, $bin = 'cache', $mc = NULL) {
ini_set('track_errors', $track_errors);
}
}
if (dmemcache_collect_stats()) {
foreach ($statistics as $key => $values) {
$values[] = isset($results[$key]) ? '1': '0';
$_memcache_statistics[] = $values;
}
}
// If $results is FALSE, convert it to an empty array.
if (!$results) {
$results = array();
}
if ($collect_stats) {
foreach ($multi_stats as $key => $value) {
$multi_stats[$key] = isset($results[$key]) ? TRUE : FALSE;
}
}
// Convert the full keys back to the cid.
$cid_results = array();
$cid_lookup = array_flip($full_keys);
foreach ($results as $key => $value) {
$cid_results[$cid_lookup[$key]] = $value;
}
if ($collect_stats) {
dmemcache_stats_write('getMulti', $bin, $multi_stats);
}
return $cid_results;
}
......@@ -188,15 +207,20 @@ function dmemcache_get_multi($keys, $bin = 'cache', $mc = NULL) {
* @return Returns TRUE on success or FALSE on failure.
*/
function dmemcache_delete($key, $bin = 'cache', $mc = NULL) {
global $_memcache_statistics;
$collect_stats = dmemcache_stats_init();
$full_key = dmemcache_key($key, $bin);
if (dmemcache_collect_stats()) {
$_memcache_statistics[] = array('delete', $bin, $full_key, '');
}
$rc = FALSE;
if ($mc || ($mc = dmemcache_object($bin))) {
return $mc->delete($full_key, 0);
$rc = $mc->delete($full_key, 0);
}
return FALSE;
if ($collect_stats) {
dmemcache_stats_write('delete', $bin, array($full_key => $rc));
}
return $rc;
}
/**
......@@ -210,13 +234,21 @@ function dmemcache_delete($key, $bin = 'cache', $mc = NULL) {
* @return Returns TRUE on success or FALSE on failure.
*/
function dmemcache_flush($bin = 'cache', $mc = NULL) {
global $_memcache_statistics;
$collect_stats = dmemcache_stats_init();
$rc = FALSE;
if (dmemcache_collect_stats()) {
$_memcache_statistics[] = array('flush', $bin, '', '');
}
if ($mc || ($mc = dmemcache_object($bin))) {
return memcache_flush($mc);
$rc = memcache_flush($mc);
}
if ($collect_stats) {
dmemcache_stats_write('flush', $bin, array('' => $rc));
}
return $rc;
}
function dmemcache_stats($stats_bin = 'cache', $stats_type = 'default', $aggregate = FALSE) {
......@@ -543,13 +575,17 @@ function dmemcache_key($key, $bin = 'cache') {
return $full_key;
}
/**
* Checks whether memcache stats need to be collected.
* Collect statistics if enabled.
*
* Optimized function to determine whether or not we should be collecting statistics. Also starts a
* timer to track how long individual memcache operations take.
*
* @return TRUE or FALSE if statistics should be collected.
*/
function dmemcache_collect_stats() {
global $user;
function dmemcache_stats_init() {
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast = &drupal_static(__FUNCTION__, array('variable_checked' => NULL, 'user_access_checked' => NULL));
}
......@@ -557,18 +593,48 @@ function dmemcache_collect_stats() {
$user_access_checked = &$drupal_static_fast['user_access_checked'];
// Confirm DRUPAL_BOOTSTRAP_VARIABLES has been reached. We don't use
// drupal_get_bootstrap_phase() as it's buggy. We can use variable_get()
// here because _drupal_bootstrap_variables() includes module.inc
// immediately after it calls variable_initialize().
// drupal_get_bootstrap_phase() as it's buggy. We can use variable_get() here because
// _drupal_bootstrap_variables() includes module.inc immediately after it calls
// variable_initialize().
if (!isset($variable_checked) && function_exists('module_list')) {
$variable_checked = variable_get('show_memcache_statistics', FALSE);
}
// If statistics are enabled we need to check user access.
if (!empty($variable_checked) && !isset($user_access_checked) && !empty($user) && function_exists('user_access')) {
if (!empty($variable_checked) && !isset($user_access_checked) && !empty($GLOBALS['user']) && function_exists('user_access')) {
// Statistics are enabled and the $user object has been populated, so check
// that the user has access to view them.
$user_access_checked = user_access('access memcache statistics');
}
// Return whether or not statistics are enabled and the user can access them.
return (!isset($variable_checked) || $variable_checked) && (!isset($user_access_checked) || $user_access_checked);
if ((!isset($variable_checked) || $variable_checked) && (!isset($user_access_checked) || $user_access_checked)) {
timer_start('dmemcache');
return TRUE;
}
else {
return FALSE;
}
}
/**
* Save memcache statistics to be displayed at end of page generation.
*
* @param $action The action being performed (get, set, etc...).
* @param $bin The memcache bin the action is being performed in.
* @param $keys The key the action is being performed on, and whether or not it was a success.
*/
function dmemcache_stats_write($action, $bin, $keys) {
global $_dmemcache_stats, $timers;
// Determine how much time elapsed to execute this action.
$time = timer_read('dmemcache');
// Build the 'all' and 'ops' arrays displayed by memcache_admin.module.
foreach ($keys as $key => $success) {
$_dmemcache_stats['all'][] = array(number_format($time, 2), $action, $bin, $key, $success ? 'hit' : 'miss');
if (!isset($_dmemcache_stats['ops'][$action])) {
$_dmemcache_stats['ops'][$action] = array($action, 0, 0, 0);
}
$_dmemcache_stats['ops'][$action][1] += $time;
$success ? $_dmemcache_stats['ops'][$action][2]++ : $_dmemcache_stats['ops'][$action][3]++;
}
// Reset the dmemcache timer for timing the next memcache operation.
unset($timers['dmemcache']);
}
......@@ -528,12 +528,24 @@ function _memcache_admin_get_bin_for_cluster($cluster) {
return $cluster_map[$cluster];
}
function _memcache_admin_stats_percent($a, $b) {
if ($a == 0) {
return 0;
}
else if ($b == 0) {
return 100;
}
else {
return $a / ($a + $b) * 100;
}
}
/**
* See memcache_admin_init() which registers this function as a shutdown function.
* Displays memcache stats in the footer.
*/
function memcache_admin_shutdown() {
global $_memcache_statistics;
global $_dmemcache_stats;
// Don't call theme() during shutdown if the registry has been rebuilt (such
// as when enabling/disabling modules on admin/build/modules) as things break.
......@@ -557,16 +569,33 @@ function memcache_admin_shutdown() {
}
if (variable_get('show_memcache_statistics', FALSE) && function_exists('user_access') && user_access('access memcache statistics')) {
if (!empty($_memcache_statistics)) {
foreach ($_memcache_statistics as $row => $stats) {
$_memcache_statistics[$row][1] = check_plain($stats[1]);
$_memcache_statistics[$row][2] = check_plain($stats[2]);
$output = '';
if (!empty($_dmemcache_stats['ops'])) {
foreach ($_dmemcache_stats['ops'] as $row => $stats) {
$_dmemcache_stats['ops'][$row][0] = check_plain($stats[0]);
$_dmemcache_stats['ops'][$row][1] = number_format($stats[1], 2);
$hits = number_format(_memcache_admin_stats_percent($stats[2], $stats[3]), 1);
$misses = number_format(_memcache_admin_stats_percent($stats[3], $stats[2]), 1);
$_dmemcache_stats['ops'][$row][2] = number_format($stats[2]) . " ($hits%)";
$_dmemcache_stats['ops'][$row][3] = number_format($stats[3]) . " ($misses%)";
}
$variables = array('header' => array(t('operation'), t('total ms'), t('total hits'), t('total misses')),
'rows' => $_dmemcache_stats['ops']);
$output .= theme('table', $variables);
}
if (!empty($_dmemcache_stats['all'])) {
foreach ($_dmemcache_stats['all'] as $row => $stats) {
$_dmemcache_stats['all'][$row][1] = check_plain($stats[1]);
$_dmemcache_stats['all'][$row][2] = check_plain($stats[2]);
$_dmemcache_stats['all'][$row][3] = check_plain($stats[3]);
}
$variables = array('header' => array(t('Operation'), t('Bin'), t('Key'), t('Hit')),
'rows' => $_memcache_statistics);
$output = theme('table', $variables);
$variables = array('header' => array(t('ms'), t('operation'), t('bin'), t('key'), t('status')),
'rows' => $_dmemcache_stats['all']);
$output .= theme('table', $variables);
}
if (!empty($output)) {
// this makes sure all of the HTML is within the <body> even though this <script> is outside it
print '<div id="memcache-devel"><h2>'. t('Memcache statistics'). '</h2>'. $output. '</div>';
}
......
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