]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - classes/Memcached_DataObject.php
Merge branch 'inblob' of git@gitorious.org:~evan/statusnet/evans-mainline into inblob
[quix0rs-gnu-social.git] / classes / Memcached_DataObject.php
index 4f81bef75c4fb4da016919fd3baf30c8c7262371..400b05f9776fca066e462e3a812a2a37fc08f7a6 100644 (file)
@@ -19,8 +19,6 @@
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
-
 class Memcached_DataObject extends DB_DataObject
 {
     /**
@@ -37,6 +35,41 @@ class Memcached_DataObject extends DB_DataObject
         }
     }
 
+    /**
+     * Magic function called at serialize() time.
+     *
+     * We use this to drop a couple process-specific references
+     * from DB_DataObject which can cause trouble in future
+     * processes.
+     *
+     * @return array of variable names to include in serialization.
+     */
+    function __sleep()
+    {
+        $vars = array_keys(get_object_vars($this));
+        $skip = array('_DB_resultid', '_link_loaded');
+        return array_diff($vars, $skip);
+    }
+
+    /**
+     * Magic function called at unserialize() time.
+     *
+     * Clean out some process-specific variables which might
+     * be floating around from a previous process's cached
+     * objects.
+     *
+     * Old cached objects may still have them.
+     */
+    function __wakeup()
+    {
+        // Refers to global state info from a previous process.
+        // Clear this out so we don't accidentally break global
+        // state in *this* process.
+        $this->_DB_resultid = null;
+        // We don't have any local DBO refs, so clear these out.
+        $this->_link_loaded = false;
+    }
+
     /**
      * Wrapper for DB_DataObject's static lookup using memcached
      * as backing instead of an in-process cache array.
@@ -57,30 +90,42 @@ class Memcached_DataObject extends DB_DataObject
             unset($i);
         }
         $i = Memcached_DataObject::getcached($cls, $k, $v);
-        if ($i) {
-            return $i;
-        } else {
+        if ($i === false) { // false == cache miss
             $i = DB_DataObject::factory($cls);
             if (empty($i)) {
-                return null;
+                $i = false;
+                return $i;
             }
             $result = $i->get($k, $v);
             if ($result) {
+                // Hit!
                 $i->encache();
-                return $i;
             } else {
-                return null;
+                // save the fact that no such row exists
+                $c = self::memcache();
+                if (!empty($c)) {
+                    $ck = self::cachekey($cls, $k, $v);
+                    $c->set($ck, null);
+                }
+                $i = false;
             }
         }
+        return $i;
     }
 
-    function &pkeyGet($cls, $kv)
+    /**
+     * @fixme Should this return false on lookup fail to match staticGet?
+     */
+    function pkeyGet($cls, $kv)
     {
         $i = Memcached_DataObject::multicache($cls, $kv);
-        if ($i) {
+        if ($i !== false) { // false == cache miss
             return $i;
         } else {
-            $i = new $cls();
+            $i = DB_DataObject::factory($cls);
+            if (empty($i)) {
+                return false;
+            }
             foreach ($kv as $k => $v) {
                 $i->$k = $v;
             }
@@ -88,6 +133,11 @@ class Memcached_DataObject extends DB_DataObject
                 $i->encache();
             } else {
                 $i = null;
+                $c = self::memcache();
+                if (!empty($c)) {
+                    $ck = self::multicacheKey($cls, $kv);
+                    $c->set($ck, null);
+                }
             }
             return $i;
         }
@@ -96,6 +146,9 @@ class Memcached_DataObject extends DB_DataObject
     function insert()
     {
         $result = parent::insert();
+        if ($result) {
+            $this->encache(); // in case of cached negative lookups
+        }
         return $result;
     }
 
@@ -122,7 +175,7 @@ class Memcached_DataObject extends DB_DataObject
     }
 
     static function cacheKey($cls, $k, $v) {
-        if (is_object($cls) || is_object($j) || is_object($v)) {
+        if (is_object($cls) || is_object($k) || is_object($v)) {
             $e = new Exception();
             common_log(LOG_ERR, __METHOD__ . ' object in param: ' .
                 str_replace("\n", " ", $e->getTraceAsString()));
@@ -141,6 +194,17 @@ class Memcached_DataObject extends DB_DataObject
 
     function keyTypes()
     {
+        // ini-based classes return number-indexed arrays. handbuilt
+        // classes return column => keytype. Make this uniform.
+
+        $keys = $this->keys();
+
+        $keyskeys = array_keys($keys);
+
+        if (is_string($keyskeys[0])) {
+            return $keys;
+        }
+
         global $_DB_DATAOBJECT;
         if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
             $this->databaseStructure();
@@ -152,67 +216,90 @@ class Memcached_DataObject extends DB_DataObject
     function encache()
     {
         $c = $this->memcache();
+
         if (!$c) {
             return false;
-        } else {
-            $pkey = array();
-            $pval = array();
-            $types = $this->keyTypes();
-            ksort($types);
-            foreach ($types as $key => $type) {
-                if ($type == 'K') {
-                    $pkey[] = $key;
-                    $pval[] = $this->$key;
-                } else {
-                    $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this);
-                }
-            }
-            # XXX: should work for both compound and scalar pkeys
-            $pvals = implode(',', $pval);
-            $pkeys = implode(',', $pkey);
-            $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this);
+        }
+
+        $keys = $this->_allCacheKeys();
+
+        foreach ($keys as $key) {
+            $c->set($key, $this);
         }
     }
 
     function decache()
     {
         $c = $this->memcache();
+
         if (!$c) {
             return false;
-        } else {
-            $pkey = array();
-            $pval = array();
-            $types = $this->keyTypes();
-            ksort($types);
-            foreach ($types as $key => $type) {
-                if ($type == 'K') {
-                    $pkey[] = $key;
-                    $pval[] = $this->$key;
-                } else {
-                    $c->delete($this->cacheKey($this->tableName(), $key, $this->$key));
+        }
+
+        $keys = $this->_allCacheKeys();
+
+        foreach ($keys as $key) {
+            $c->delete($key, $this);
+        }
+    }
+
+    function _allCacheKeys()
+    {
+        $ckeys = array();
+
+        $types = $this->keyTypes();
+        ksort($types);
+
+        $pkey = array();
+        $pval = array();
+
+        foreach ($types as $key => $type) {
+
+            assert(!empty($key));
+
+            if ($type == 'U') {
+                if (empty($this->$key)) {
+                    continue;
                 }
+                $ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key);
+            } else if ($type == 'K' || $type == 'N') {
+                $pkey[] = $key;
+                $pval[] = $this->$key;
+            } else {
+                throw new Exception("Unknown key type $key => $type for " . $this->tableName());
             }
-            # should work for both compound and scalar pkeys
-            # XXX: comma works for now but may not be safe separator for future keys
-            $pvals = implode(',', $pval);
-            $pkeys = implode(',', $pkey);
-            $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals));
         }
+
+        assert(count($pkey) > 0);
+
+        // XXX: should work for both compound and scalar pkeys
+        $pvals = implode(',', $pval);
+        $pkeys = implode(',', $pkey);
+
+        $ckeys[] = $this->cacheKey($this->tableName(), $pkeys, $pvals);
+
+        return $ckeys;
     }
 
     function multicache($cls, $kv)
     {
         ksort($kv);
-        $c = Memcached_DataObject::memcache();
+        $c = self::memcache();
         if (!$c) {
             return false;
         } else {
-            $pkeys = implode(',', array_keys($kv));
-            $pvals = implode(',', array_values($kv));
-            return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals));
+            return $c->get(self::multicacheKey($cls, $kv));
         }
     }
 
+    static function multicacheKey($cls, $kv)
+    {
+        ksort($kv);
+        $pkeys = implode(',', array_keys($kv));
+        $pvals = implode(',', array_values($kv));
+        return self::cacheKey($cls, $pkeys, $pvals);
+    }
+
     function getSearchEngine($table)
     {
         require_once INSTALLDIR.'/lib/search_engines.php';
@@ -247,7 +334,8 @@ class Memcached_DataObject extends DB_DataObject
         $key_part = common_keyize($cls).':'.md5($qry);
         $ckey = common_cache_key($key_part);
         $stored = $c->get($ckey);
-        if ($stored) {
+
+        if ($stored !== false) {
             return new ArrayWrapper($stored);
         }
 
@@ -262,18 +350,6 @@ class Memcached_DataObject extends DB_DataObject
         return new ArrayWrapper($cached);
     }
 
-    function cleanup()
-    {
-        global $_DB_DATAOBJECT;
-
-        if (isset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid])) {
-            unset($_DB_DATAOBJECT['RESULTFIELDS'][$this->_DB_resultid]);
-        }
-        if (isset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid])) {
-            unset($_DB_DATAOBJECT['RESULTS'][$this->_DB_resultid]);
-        }
-    }
-
     // We overload so that 'SET NAMES "utf8"' is called for
     // each connection