Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

dmllib: introducing the rcache_* functions for record cache handling

Abstracted Sam's initial work with $record_cache into a series of functions
that abstract things _just enough_ that we can use an internal
in-memory-array implementation or something that is shared across
processes, like memcached or the turckmmcache/eaccelerator caches.

Also
- added hit/miss stats tracking and reporting
- removed max entries limiting as it was buggy - var names mismatches
  and not counting unset()s
  • Loading branch information...
commit 6c2f585f85c9ea6fd0757bff1cc6c939ba9de7a0 1 parent adb61bc
martinlanghoff authored
Showing with 160 additions and 54 deletions.
  1. +150 −51 lib/dmllib.php
  2. +10 −3 lib/moodlelib.php
View
201 lib/dmllib.php
@@ -39,8 +39,11 @@
$empty_rs_cache = array(); // Keeps copies of the recordsets used in one invocation
$metadata_cache = array(); // Keeps copies of the MetaColumns() for each table used in one invocations
-$record_cache = array(); // Keeps copies of all simple get_record results from one invocation
-$record_cache_size = 0; // Count of get_record results stored in this invocation
+
+$rcache = new StdClass; // Cache simple get_record results
+$rcache->data = array();
+$rcache->hits = 0;
+$rcache->misses = 0;
/// FUNCTIONS FOR DATABASE HANDLING ////////////////////////////////
@@ -379,15 +382,16 @@ function count_records_sql($sql) {
*/
function get_record($table, $field1, $value1, $field2='', $value2='', $field3='', $value3='', $fields='*') {
- global $CFG, $record_cache, $record_cache_count;
+ global $CFG;
// Check to see whether this record is eligible for caching (fields=*, only condition is id)
$docache = false;
if (!empty($CFG->enablerecordcache) && $field1=='id' && !$field2 && !$field3 && $fields=='*') {
$docache = true;
// If it's in the cache, return it
- if (!empty($record_cache[$table][$value1])) {
- return $record_cache[$table][$value1];
+ $cached = rcache_get($table, $value1);
+ if (!empty($cached)) {
+ return $cached;
}
}
@@ -395,13 +399,10 @@ function get_record($table, $field1, $value1, $field2='', $value2='', $field3=''
$record = get_record_sql('SELECT '.$fields.' FROM '. $CFG->prefix . $table .' '. $select);
- // If we're caching records, store this one (supposing we got something - we don't cache failures)
- if ($record && $docache && $record_cache_count<$CFG->enablerecordcache) {
- $record_cache[$table][$value1] = $record;
- // We only cache records up to a limit. This is to prevent memory usage becoming
- // unreasonably high for pages which do things like, load every student record in
- // a course, or some such.
- $record_cache_count++;
+ // If we're caching records, store this one
+ // (supposing we got something - we don't cache failures)
+ if ($record && $docache) {
+ rcache_set($table, $value1, $record);
}
return $record;
@@ -1036,26 +1037,19 @@ function get_fieldset_sql($sql) {
*/
function set_field($table, $newfield, $newvalue, $field1, $value1, $field2='', $value2='', $field3='', $value3='') {
- global $CFG, $record_cache;
+ global $CFG;
- // Clear record_cache based on the parameters passed (individual record or whole table)
+ // Clear record_cache based on the parameters passed
+ // (individual record or whole table)
if (!empty($CFG->enablerecordcache)) {
if ($field1 == 'id') {
- if (isset($record_cache[$table][$value1])) {
- unset($record_cache[$table][$value1]);
- }
+ rcache_unset($table, $value1);
} else if ($field2 == 'id') {
- if (isset($record_cache[$table][$value2])) {
- unset($record_cache[$table][$value2]);
- }
+ rcache_unset($table, $value1);
} else if ($field3 == 'id') {
- if (isset($record_cache[$table][$value3])) {
- unset($record_cache[$table][$value3]);
- }
+ rcache_unset($table, $value1);
} else {
- if (isset($record_cache[$table])) {
- unset($record_cache[$table]);
- }
+ rcache_unset_table($table);
}
}
@@ -1078,7 +1072,7 @@ function set_field($table, $newfield, $newvalue, $field1, $value1, $field2='', $
*/
function set_field_select($table, $newfield, $newvalue, $select, $localcall = false) {
- global $db, $CFG, $record_cache;
+ global $db, $CFG;
if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
@@ -1087,11 +1081,10 @@ function set_field_select($table, $newfield, $newvalue, $select, $localcall = fa
$select = 'WHERE ' . $select;
}
- // Clear record_cache based on the parameters passed (individual record or whole table)
+ // Clear record_cache based on the parameters passed
+ // (individual record or whole table)
if (!empty($CFG->enablerecordcache)) {
- if (isset($record_cache[$table])) {
- unset($record_cache[$table]);
- }
+ rcache_unset_table($table);
}
}
@@ -1147,26 +1140,19 @@ function set_field_select($table, $newfield, $newvalue, $select, $localcall = fa
*/
function delete_records($table, $field1='', $value1='', $field2='', $value2='', $field3='', $value3='') {
- global $db, $CFG, $record_cache;
+ global $db, $CFG;
- // Clear record_cache based on the parameters passed (individual record or whole table)
+ // Clear record_cache based on the parameters passed
+ // (individual record or whole table)
if (!empty($CFG->enablerecordcache)) {
if ($field1 == 'id') {
- if (isset($record_cache[$table][$value1])) {
- unset($record_cache[$table][$value1]);
- }
+ rcache_unset($table, $value1);
} else if ($field2 == 'id') {
- if (isset($record_cache[$table][$value2])) {
- unset($record_cache[$table][$value2]);
- }
+ rcache_unset($table, $value2);
} else if ($field3 == 'id') {
- if (isset($record_cache[$table][$value3])) {
- unset($record_cache[$table][$value3]);
- }
+ rcache_unset($table, $value3);
} else {
- if (isset($record_cache[$table])) {
- unset($record_cache[$table]);
- }
+ rcache_unset_table($table);
}
}
@@ -1189,11 +1175,11 @@ function delete_records($table, $field1='', $value1='', $field2='', $value2='',
*/
function delete_records_select($table, $select='') {
- global $CFG, $db, $record_cache;
+ global $CFG, $db;
// Clear record_cache (whole table)
- if (!empty($CFG->enablerecordcache) && isset($record_cache[$table])) {
- unset($record_cache[$table]);
+ if (!empty($CFG->enablerecordcache)) {
+ rcache_unset_table($table);
}
if (defined('MDL_PERFDB')) { global $PERF ; $PERF->dbqueries++; };
@@ -1401,15 +1387,15 @@ function insert_record($table, $dataobject, $returnid=true, $primarykey='id') {
*/
function update_record($table, $dataobject) {
- global $db, $CFG, $record_cache;
+ global $db, $CFG;
if (! isset($dataobject->id) ) {
return false;
}
// Remove this record from record cache since it will change
- if (!empty($CFG->enablerecordcache) && isset($record_cache[$table][$dataobject->id])) {
- unset($record_cache[$table][$dataobject->id]);
+ if (!empty($CFG->enablerecordcache)) {
+ rcache_unset($table, $dataobject->id);
}
/// Temporary hack as part of phasing out all access to obsolete user tables XXX
@@ -2079,4 +2065,117 @@ function db_update_lobs ($table, $sqlcondition, &$clobs, &$blobs) {
return $status;
}
+
+function rcache_set($table, $id, $rec) {
+ global $CFG, $rcache;
+
+ $rcache->data[$table][$id] = $rec;
+ return true;
+}
+
+function rcache_unset($table, $id) {
+ global $CFG, $rcache;
+
+ if (isset($rcache->data[$table][$id])) {
+ unset($rcache->data[$table][$id]);
+ }
+ return true;
+}
+
+/**
+ * Get cached record if available. ONLY use if you
+ * are trying to get the cached record and will NOT
+ * fetch it yourself if not cached.
+ *
+ * Use rcache_getforfill() if you are going to fetch
+ * the record if not cached...
+ *
+ * This function is private and must not be used outside dmllib at all
+ *
+ * @param $table string
+ * @param $id integer
+ * @return mixed object-like record on cache hit, false otherwise
+ */
+function rcache_get($table, $id) {
+ global $CFG, $rcache;
+
+ if (isset($rcache->data[$table][$id])) {
+ $rcache->hits++;
+ return $rcache->data[$table][$id];
+ } else {
+ $rcache->misses++;
+ return false;
+ }
+}
+
+/**
+ * In the simple case, this function will
+ * get cached record if available. If the record
+ * is not available, it will try to get an exclusive
+ * lock that announces that this process will fetch
+ * the record and populate the cache.
+ *
+ * If we fail to get the lock -- this means another
+ * process is going to fetch the rec and fill the cache
+ * so we wait (block) for a few microseconds while we wait for
+ * the cache to be filled or the lock to timeout.
+ *
+ * If you get a false from this call, you _must_ fetch the
+ * rec from DB and populate the cache ASAP or indicate that
+ * you won't by calling rcache_releaseforfill().
+ *
+ * This technique forces serialisation and so helps deal
+ * with thundering herd scenarios where a lot of clients
+ * ask the for the same idempotent (and costly) operation.
+ * The implementation is based on suggestions in this message
+ * http://marc.theaimsgroup.com/?l=git&m=116562052506776&w=2
+ *
+ * This function is private and must not be used outside dmllib at all
+ *
+ * @param $table string
+ * @param $id integer
+ * @return mixed object-like record on cache hit, false otherwise
+ */
+function rcache_getforfill($table, $id) {
+ global $CFG, $rcache;
+
+ return rcache_get($table, $id);
+}
+
+/**
+ * Release the exclusive lock obtained by
+ * rcache_getforfill(). See rcache_getforfill()
+ * for more details.
+ *
+ * This function is private and must not be used outside dmllib at all
+ *
+ * @param $table string
+ * @param $id integer
+ * @return bool
+ */
+function rcache_releaseforfill($table, $id) {
+ global $CFG, $rcache;
+
+ return true;
+}
+
+/**
+ * Remove or invalidate all rcache entries related to
+ * a table. Not all caching mechanisms cluster entries
+ * by table so in those cases we use alternative strategies.
+ *
+ * This function is private and must not be used outside dmllib at all
+ *
+ * @param $table string the table to invalidate records for
+ * @return bool
+ */
+function rcache_unset_table ($table) {
+ global $CFG, $rcache;
+ if (isset($rcache->data[$table])) {
+ unset($rcache->data[$table]);
+ }
+ return true;
+}
+
+
?>
View
13 lib/moodlelib.php
@@ -6466,7 +6466,7 @@ function array_is_nested($array) {
***
**/
function get_performance_info() {
- global $CFG, $PERF;
+ global $CFG, $PERF, $rcache;
$info = array();
$info['html'] = ''; // holds userfriendly HTML representation
@@ -6529,10 +6529,17 @@ function get_performance_info() {
if (!empty($server_load)) {
$info['serverload'] = $server_load;
$info['html'] .= '<span class="serverload">Load average: '.$info['serverload'].'</span> ';
- $info['txt'] .= 'serverload: '.$info['serverload'];
+ $info['txt'] .= "serverload: {$info['serverload']} ";
}
-
+ if (isset($rcache->hits) && isset($rcache->misses)) {
+ $info['rcachehits'] = $rcache->hits;
+ $info['rcachemisses'] = $rcache->misses;
+ $info['html'] .= '<span class="rcache">Record cache hit/miss ratio : '.
+ ($rcache->hits / $rcache->misses) . "({$rcache->hits}/{$rcache->misses})</span> ";
+ $info['txt'] .= 'rcache: '. ($rcache->hits / $rcache->misses) .
+ "({$rcache->hits}/{$rcache->misses}) ";
+ }
$info['html'] = '<div class="performanceinfo">'.$info['html'].'</div>';
return $info;
}
Please sign in to comment.
Something went wrong with that request. Please try again.