]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - classes/Memcached_DataObject.php
RegisterThrottlePlugin tweak for silencing checks: make sure we don't crash during...
[quix0rs-gnu-social.git] / classes / Memcached_DataObject.php
index bc9aaf81feebde6eecd4931f863de93757675015..ccfd886a1d3dc3ce9ce8de594aa1c5287f5d8634 100644 (file)
 
 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
 
-class Memcached_DataObject extends DB_DataObject
+class Memcached_DataObject extends Safe_DataObject
 {
-    /**
-     * Destructor to free global memory resources associated with
-     * this data object when it's unset or goes out of scope.
-     * DB_DataObject doesn't do this yet by itself.
-     */
-
-    function __destruct()
-    {
-        $this->free();
-        if (method_exists('DB_DataObject', '__destruct')) {
-            parent::__destruct();
-        }
-    }
-
-    /**
-     * 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.
@@ -177,12 +128,13 @@ class Memcached_DataObject extends DB_DataObject
     }
 
     static function cacheKey($cls, $k, $v) {
-        if (is_object($cls) || is_object($k) || is_object($v)) {
+        if (is_object($cls) || is_object($k) || (is_object($v) && !($v instanceof DB_DataObject_Cast))) {
             $e = new Exception();
             common_log(LOG_ERR, __METHOD__ . ' object in param: ' .
                 str_replace("\n", " ", $e->getTraceAsString()));
         }
-        return common_cache_key(strtolower($cls).':'.$k.':'.$v);
+        $vstr = self::valueString($v);
+        return common_cache_key(strtolower($cls).':'.$k.':'.$vstr);
     }
 
     static function getcached($cls, $k, $v) {
@@ -237,11 +189,11 @@ class Memcached_DataObject extends DB_DataObject
                        str_replace("\n", " ", $e->getTraceAsString()));
             return false;
         } else {
-               $keys = $this->_allCacheKeys();
+            $keys = $this->_allCacheKeys();
 
-               foreach ($keys as $key) {
-                   $c->set($key, $this);
-               }
+            foreach ($keys as $key) {
+                $c->set($key, $this);
+            }
         }
     }
 
@@ -278,11 +230,12 @@ class Memcached_DataObject extends DB_DataObject
                 if (empty($this->$key)) {
                     continue;
                 }
-                $ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key);
+                $ckeys[] = $this->cacheKey($this->tableName(), $key, self::valueString($this->$key));
             } else if ($type == 'K' || $type == 'N') {
                 $pkey[] = $key;
-                $pval[] = $this->$key;
+                $pval[] = self::valueString($this->$key);
             } else {
+                // Low level exception. No need for i18n as discussed with Brion.
                 throw new Exception("Unknown key type $key => $type for " . $this->tableName());
             }
         }
@@ -330,6 +283,7 @@ class Memcached_DataObject extends DB_DataObject
                     } else if ($type == 'fulltext') {
                         $search_engine = new MySQLSearch($this, $table);
                     } else {
+                        // Low level exception. No need for i18n as discussed with Brion.
                         throw new ServerException('Unknown search type: ' . $type);
                     }
                 } else {
@@ -363,7 +317,7 @@ class Memcached_DataObject extends DB_DataObject
             $cached[] = clone($inst);
         }
         $inst->free();
-        $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry);
+        $c->set($ckey, $cached, Cache::COMPRESSED, $expiry);
         return new ArrayWrapper($cached);
     }
 
@@ -379,6 +333,10 @@ class Memcached_DataObject extends DB_DataObject
      */
     function _query($string)
     {
+        if (common_config('db', 'annotate_queries')) {
+            $string = $this->annotateQuery($string);
+        }
+
         $start = microtime(true);
         $result = parent::_query($string);
         $delta = microtime(true) - $start;
@@ -391,6 +349,70 @@ class Memcached_DataObject extends DB_DataObject
         return $result;
     }
 
+    /**
+     * Find the first caller in the stack trace that's not a
+     * low-level database function and add a comment to the
+     * query string. This should then be visible in process lists
+     * and slow query logs, to help identify problem areas.
+     *
+     * Also marks whether this was a web GET/POST or which daemon
+     * was running it.
+     *
+     * @param string $string SQL query string
+     * @return string SQL query string, with a comment in it
+     */
+    function annotateQuery($string)
+    {
+        $ignore = array('annotateQuery',
+                        '_query',
+                        'query',
+                        'get',
+                        'insert',
+                        'delete',
+                        'update',
+                        'find');
+        $ignoreStatic = array('staticGet',
+                              'pkeyGet',
+                              'cachedQuery');
+        $here = get_class($this); // if we get confused
+        $bt = debug_backtrace();
+
+        // Find the first caller that's not us?
+        foreach ($bt as $frame) {
+            $func = $frame['function'];
+            if (isset($frame['type']) && $frame['type'] == '::') {
+                if (in_array($func, $ignoreStatic)) {
+                    continue;
+                }
+                $here = $frame['class'] . '::' . $func;
+                break;
+            } else if (isset($frame['type']) && $frame['type'] == '->') {
+                if ($frame['object'] === $this && in_array($func, $ignore)) {
+                    continue;
+                }
+                if (in_array($func, $ignoreStatic)) {
+                    continue; // @fixme this shouldn't be needed?
+                }
+                $here = get_class($frame['object']) . '->' . $func;
+                break;
+            }
+            $here = $func;
+            break;
+        }
+
+        if (php_sapi_name() == 'cli') {
+            $context = basename($_SERVER['PHP_SELF']);
+        } else {
+            $context = $_SERVER['REQUEST_METHOD'];
+        }
+
+        // Slip the comment in after the first command,
+        // or DB_DataObject gets confused about handling inserts and such.
+        $parts = explode(' ', $string, 2);
+        $parts[0] .= " /* $context $here */";
+        return implode(' ', $parts);
+    }
+
     // Sanitize a query for logging
     // @fixme don't trim spaces in string literals
     function sanitizeQuery($string)
@@ -430,7 +452,7 @@ class Memcached_DataObject extends DB_DataObject
         //
         // WARNING WARNING if we end up actually using multiple DBs at a time
         // we'll need some fancier logic here.
-        if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS'])) {
+        if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS']) && php_sapi_name() == 'cli') {
             foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $index => $conn) {
                 if (!empty($conn)) {
                     $conn->disconnect();
@@ -507,7 +529,8 @@ class Memcached_DataObject extends DB_DataObject
         }
 
         if (!$dsn) {
-            throw new Exception("No database name / dsn found anywhere");
+            // TRANS: Exception thrown when database name or Data Source Name could not be found.
+            throw new Exception(_("No database name or DSN found anywhere."));
         }
 
         return $dsn;
@@ -547,4 +570,70 @@ class Memcached_DataObject extends DB_DataObject
     {
         common_debug("debugDump: " . common_log_objstring($this));
     }
+
+    function raiseError($message, $type = null, $behaviour = null)
+    {
+        $id = get_class($this);
+        if (!empty($this->id)) {
+            $id .= ':' . $this->id;
+        }
+        if ($message instanceof PEAR_Error) {
+            $message = $message->getMessage();
+        }
+        // Low level exception. No need for i18n as discussed with Brion.
+        throw new ServerException("[$id] DB_DataObject error [$type]: $message");
+    }
+
+    static function cacheGet($keyPart)
+    {
+        $c = self::memcache();
+
+        if (empty($c)) {
+            return false;
+        }
+
+        $cacheKey = common_cache_key($keyPart);
+
+        return $c->get($cacheKey);
+    }
+
+    static function cacheSet($keyPart, $value, $flag=null, $expiry=null)
+    {
+        $c = self::memcache();
+
+        if (empty($c)) {
+            return false;
+        }
+
+        $cacheKey = common_cache_key($keyPart);
+
+        return $c->set($cacheKey, $value, $flag, $expiry);
+    }
+
+    static function valueString($v)
+    {
+        $vstr = null;
+        if (is_object($v) && $v instanceof DB_DataObject_Cast) {
+            switch ($v->type) {
+            case 'date':
+                $vstr = $v->year . '-' . $v->month . '-' . $v->day;
+                break;
+            case 'blob':
+            case 'string':
+            case 'sql':
+            case 'datetime':
+            case 'time':
+                // Low level exception. No need for i18n as discussed with Brion.
+                throw new ServerException("Unhandled DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
+                break;
+            default:
+                // Low level exception. No need for i18n as discussed with Brion.
+                throw new ServerException("Unknown DB_DataObject_Cast type passed as cacheKey value: '$v->type'");
+                break;
+            }
+        } else {
+            $vstr = strval($v);
+        }
+        return $vstr;
+    }
 }