From 99aa78b6fa7ffacbf0bb5319bfa4fece90de8d42 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Tue, 23 Oct 2012 23:30:04 -0400 Subject: [PATCH] Built in caching for primary key queries Selects done against a primary key will automatically cache to Memcached (haven't tried it, but it should fail gracefully) indexed by the model name and the primary key ([NAMESPACE-]MODEL-PKEY). Any updates or deletes against the same primary key will purge the cache automatically. The major caveat here is the case of mass updates which would result in stale data. As it stands the data is being cached for a mere 5 minutes, so this multiple row update scenario would be short lived but ideally, I'll be pushing back the time to live on the cache and/or making it something that's configurable. If you have to do mass updates, you're probably doing them with a cronjob and should just be flushing all of the cache in that scenario (as it would be nearly impossible to detect the affected keys and purge them all). --- classes/Cache.php | 14 ++++--- classes/Model.php | 83 ++++++++++++++++++++++------------------ jar.php | 97 +++++++++++++++++++++++++++-------------------- 3 files changed, 110 insertions(+), 84 deletions(-) diff --git a/classes/Cache.php b/classes/Cache.php index 6591311..5d5d255 100644 --- a/classes/Cache.php +++ b/classes/Cache.php @@ -24,7 +24,9 @@ * don't entirely remember specifics, but the reason for not using Memcached() * was due to an unexplainable bug in the version in the repository for Ubuntu * 10.04 LTS. Memcached() does support more of the memcached protocol and will - * eventually be what PICKLES uses. + * eventually be what PICKLES uses. Keys are forced to be uppercase for + * consistencies sake as I've been burned by the case sensitivity due to typos + * in my code. * * Requires php5-memcache * @@ -155,7 +157,7 @@ class Cache extends Object { if ($this->open()) { - return $this->connection->get($this->namespace . $key); + return $this->connection->get(strtoupper($this->namespace . $key)); } return false; @@ -177,9 +179,11 @@ class Cache extends Object */ public function set($key, $value, $expire = 300) { + $key = strtoupper($key); + if ($this->open()) { - return $this->connection->set($this->namespace . $key, $value, 0, $expire); + return $this->connection->set(strtoupper($this->namespace . $key), $value, 0, $expire); } return false; @@ -197,7 +201,7 @@ class Cache extends Object { if ($this->open()) { - return $this->connection->delete($this->namespace . $key); + return $this->connection->delete(strtoupper($this->namespace . $key)); } return false; @@ -216,7 +220,7 @@ class Cache extends Object { if ($this->open()) { - return $this->connection->increment($this->namespace . $key); + return $this->connection->increment(strtoupper($this->namespace . $key)); } return false; diff --git a/classes/Model.php b/classes/Model.php index 34fa09d..43bd82c 100644 --- a/classes/Model.php +++ b/classes/Model.php @@ -26,6 +26,14 @@ class Model extends Object { // {{{ Properties + /** + * Model Name + * + * @access private + * @var string + */ + private $model = null; + /** * Database Object * @@ -76,14 +84,6 @@ class Model extends Object */ private $input_parameters = array(); - /** - * Datasource - * - * @access protected - * @var string - */ - protected $datasource; - /** * Insert Priority * @@ -298,13 +298,16 @@ class Model extends Object // Runs the parent constructor so we have the config parent::__construct(); - // Gets an instance of the cache and database - // @todo Datasource has no way of being set - $this->db = Database::getInstance($this->datasource != '' ? $this->datasource : null); - $this->caching = $this->db->cache; + // Gets an instance of the database and check which it is + $this->db = Database::getInstance(); $this->mysql = ($this->db->driver == 'pdo_mysql'); $this->postgresql = ($this->db->driver == 'pdo_pgsql'); + // Sets up the cache object and grabs the class name to use in our cache keys + $this->cache = Cache::getInstance(); + $this->model = get_class($this); + + // Default column mapping $columns = array( 'id' => 'id', 'created_at' => 'created_at', @@ -342,12 +345,7 @@ class Model extends Object } } - $this->db->columns = $columns; - - if ($this->caching) - { - $this->cache = Cache::getInstance(); - } + $this->columns = $columns; // Takes a snapshot of the [non-object] object properties foreach ($this as $variable => $value) @@ -409,6 +407,7 @@ class Model extends Object } elseif (ctype_digit((string)$type_or_parameters)) { + $cache_key = $this->model . '-' . $type_or_parameters; $parameters = array($this->columns['id'] => $type_or_parameters); if ($this->columns['is_deleted']) @@ -417,10 +416,10 @@ class Model extends Object } $this->loadParameters($parameters); - $cache_key = 'PICKLES-' . $this->datasource . '-' . $this->table . '-' . $type_or_parameters; } elseif (ctype_digit((string)$parameters)) { + $cache_key = $this->model . '-' . $parameters; $parameters = array($this->columns['id'] => $parameters); if ($this->columns['is_deleted']) @@ -462,7 +461,7 @@ class Model extends Object if (isset($cache_key)) { - //$cached = $this->cache->get($cache_key); + $cached = $this->cache->get($cache_key); } if (isset($cached) && $cached) @@ -471,11 +470,14 @@ class Model extends Object } else { - $this->records = $this->db->fetch(implode(' ', $this->sql), (count($this->input_parameters) == 0 ? null : $this->input_parameters)); + $this->records = $this->db->fetch( + implode(' ', $this->sql), + (count($this->input_parameters) == 0 ? null : $this->input_parameters) + ); if (isset($cache_key)) { - //$this->cache->set($cache_key, $this->records); + $this->cache->set($cache_key, $this->records); } } @@ -600,7 +602,7 @@ class Model extends Object { $format = (stripos($columns, 'USE ') === false); - $this->sql[] = ($format == true ? 'USE INDEX (' : '') . $columns . ($format == true ? ')' : ''); + $this->sql[] = ($format ? 'USE INDEX (' : '') . $columns . ($format ? ')' : ''); } } } @@ -608,7 +610,7 @@ class Model extends Object { $format = (stripos($this->hints, 'USE ') === false); - $this->sql[] = ($format == true ? 'USE INDEX (' : '') . $this->hints . ($format == true ? ')' : ''); + $this->sql[] = ($format ? 'USE INDEX (' : '') . $this->hints . ($format ? ')' : ''); } } @@ -1063,7 +1065,7 @@ class Model extends Object } else { - if ($update === true) + if ($update == true) { $sql = 'UPDATE'; } @@ -1092,13 +1094,13 @@ class Model extends Object $sql .= ' INTO'; } - $sql .= ' ' . $this->table . ($update === true ? ' SET ' : ' '); + $sql .= ' ' . $this->table . ($update ? ' SET ' : ' '); } $input_parameters = null; // Limits the columns being updated - $record = ($update === true ? array_diff_assoc($this->record, isset($this->original[$this->index]) ? $this->original[$this->index] : array()) : $this->record); + $record = ($update ? array_diff_assoc($this->record, isset($this->original[$this->index]) ? $this->original[$this->index] : array()) : $this->record); // Makes sure there's something to INSERT or UPDATE if (count($record) > 0) @@ -1110,7 +1112,7 @@ class Model extends Object { if ($column != $this->columns['id']) { - if ($update === true) + if ($update == true) { if ($input_parameters != null) { @@ -1129,7 +1131,7 @@ class Model extends Object } // If it's an UPDATE tack on the ID - if ($update === true) + if ($update == true) { if ($this->columns['updated_at'] != false) { @@ -1155,11 +1157,6 @@ class Model extends Object $sql .= ' WHERE ' . $this->columns['id'] . ' = ?' . ($this->mysql ? ' LIMIT 1' : '') . ';'; $input_parameters[] = $this->record[$this->columns['id']]; - - if ($this->caching) - { - //$this->cache->delete('PICKLES-' . $this->datasource . '-' . $this->table . '-' . $this->record[$this->columns['id']]); - } } else { @@ -1187,7 +1184,7 @@ class Model extends Object } // Executes the query - if ($this->postgresql && $update === false) + if ($this->postgresql && $update == false) { $results = $this->db->fetch($sql, $input_parameters); @@ -1195,7 +1192,15 @@ class Model extends Object } else { - return $this->db->execute($sql, $input_parameters); + $results = $this->db->execute($sql, $input_parameters); + + // Clears the cache + if ($update) + { + $this->cache->delete($this->model . '-' . $this->record[$this->columns['id']]); + } + + return $results; } } } @@ -1241,8 +1246,12 @@ class Model extends Object } $input_parameters[] = $this->record[$this->columns['id']]; + $results = $this->db->execute($sql, $input_parameters); - return $this->db->execute($sql, $input_parameters); + // Clears the cache + $this->cache->delete($this->model . '-' . $this->record[$this->columns['id']]); + + return $results; } else { diff --git a/jar.php b/jar.php index beaeab5..24d7386 100755 --- a/jar.php +++ b/jar.php @@ -457,7 +457,9 @@ class Browser * don't entirely remember specifics, but the reason for not using Memcached() * was due to an unexplainable bug in the version in the repository for Ubuntu * 10.04 LTS. Memcached() does support more of the memcached protocol and will - * eventually be what PICKLES uses. + * eventually be what PICKLES uses. Keys are forced to be uppercase for + * consistencies sake as I've been burned by the case sensitivity due to typos + * in my code. * * Requires php5-memcache * @@ -588,7 +590,7 @@ class Cache extends Object { if ($this->open()) { - return $this->connection->get($this->namespace . $key); + return $this->connection->get(strtoupper($this->namespace . $key)); } return false; @@ -610,9 +612,11 @@ class Cache extends Object */ public function set($key, $value, $expire = 300) { + $key = strtoupper($key); + if ($this->open()) { - return $this->connection->set($this->namespace . $key, $value, 0, $expire); + return $this->connection->set(strtoupper($this->namespace . $key), $value, 0, $expire); } return false; @@ -630,7 +634,7 @@ class Cache extends Object { if ($this->open()) { - return $this->connection->delete($this->namespace . $key); + return $this->connection->delete(strtoupper($this->namespace . $key)); } return false; @@ -649,7 +653,7 @@ class Cache extends Object { if ($this->open()) { - return $this->connection->increment($this->namespace . $key); + return $this->connection->increment(strtoupper($this->namespace . $key)); } return false; @@ -4036,6 +4040,14 @@ class Model extends Object { // {{{ Properties + /** + * Model Name + * + * @access private + * @var string + */ + private $model = null; + /** * Database Object * @@ -4086,14 +4098,6 @@ class Model extends Object */ private $input_parameters = array(); - /** - * Datasource - * - * @access protected - * @var string - */ - protected $datasource; - /** * Insert Priority * @@ -4308,13 +4312,16 @@ class Model extends Object // Runs the parent constructor so we have the config parent::__construct(); - // Gets an instance of the cache and database - // @todo Datasource has no way of being set - $this->db = Database::getInstance($this->datasource != '' ? $this->datasource : null); - $this->caching = $this->db->cache; + // Gets an instance of the database and check which it is + $this->db = Database::getInstance(); $this->mysql = ($this->db->driver == 'pdo_mysql'); $this->postgresql = ($this->db->driver == 'pdo_pgsql'); + // Sets up the cache object and grabs the class name to use in our cache keys + $this->cache = Cache::getInstance(); + $this->model = get_class($this); + + // Default column mapping $columns = array( 'id' => 'id', 'created_at' => 'created_at', @@ -4352,12 +4359,7 @@ class Model extends Object } } - $this->db->columns = $columns; - - if ($this->caching) - { - $this->cache = Cache::getInstance(); - } + $this->columns = $columns; // Takes a snapshot of the [non-object] object properties foreach ($this as $variable => $value) @@ -4419,6 +4421,7 @@ class Model extends Object } elseif (ctype_digit((string)$type_or_parameters)) { + $cache_key = $this->model . '-' . $type_or_parameters; $parameters = array($this->columns['id'] => $type_or_parameters); if ($this->columns['is_deleted']) @@ -4427,10 +4430,10 @@ class Model extends Object } $this->loadParameters($parameters); - $cache_key = 'PICKLES-' . $this->datasource . '-' . $this->table . '-' . $type_or_parameters; } elseif (ctype_digit((string)$parameters)) { + $cache_key = $this->model . '-' . $parameters; $parameters = array($this->columns['id'] => $parameters); if ($this->columns['is_deleted']) @@ -4472,7 +4475,7 @@ class Model extends Object if (isset($cache_key)) { - //$cached = $this->cache->get($cache_key); + $cached = $this->cache->get($cache_key); } if (isset($cached) && $cached) @@ -4481,11 +4484,14 @@ class Model extends Object } else { - $this->records = $this->db->fetch(implode(' ', $this->sql), (count($this->input_parameters) == 0 ? null : $this->input_parameters)); + $this->records = $this->db->fetch( + implode(' ', $this->sql), + (count($this->input_parameters) == 0 ? null : $this->input_parameters) + ); if (isset($cache_key)) { - //$this->cache->set($cache_key, $this->records); + $this->cache->set($cache_key, $this->records); } } @@ -4610,7 +4616,7 @@ class Model extends Object { $format = (stripos($columns, 'USE ') === false); - $this->sql[] = ($format == true ? 'USE INDEX (' : '') . $columns . ($format == true ? ')' : ''); + $this->sql[] = ($format ? 'USE INDEX (' : '') . $columns . ($format ? ')' : ''); } } } @@ -4618,7 +4624,7 @@ class Model extends Object { $format = (stripos($this->hints, 'USE ') === false); - $this->sql[] = ($format == true ? 'USE INDEX (' : '') . $this->hints . ($format == true ? ')' : ''); + $this->sql[] = ($format ? 'USE INDEX (' : '') . $this->hints . ($format ? ')' : ''); } } @@ -5073,7 +5079,7 @@ class Model extends Object } else { - if ($update === true) + if ($update == true) { $sql = 'UPDATE'; } @@ -5102,13 +5108,13 @@ class Model extends Object $sql .= ' INTO'; } - $sql .= ' ' . $this->table . ($update === true ? ' SET ' : ' '); + $sql .= ' ' . $this->table . ($update ? ' SET ' : ' '); } $input_parameters = null; // Limits the columns being updated - $record = ($update === true ? array_diff_assoc($this->record, isset($this->original[$this->index]) ? $this->original[$this->index] : array()) : $this->record); + $record = ($update ? array_diff_assoc($this->record, isset($this->original[$this->index]) ? $this->original[$this->index] : array()) : $this->record); // Makes sure there's something to INSERT or UPDATE if (count($record) > 0) @@ -5120,7 +5126,7 @@ class Model extends Object { if ($column != $this->columns['id']) { - if ($update === true) + if ($update == true) { if ($input_parameters != null) { @@ -5139,7 +5145,7 @@ class Model extends Object } // If it's an UPDATE tack on the ID - if ($update === true) + if ($update == true) { if ($this->columns['updated_at'] != false) { @@ -5165,11 +5171,6 @@ class Model extends Object $sql .= ' WHERE ' . $this->columns['id'] . ' = ?' . ($this->mysql ? ' LIMIT 1' : '') . ';'; $input_parameters[] = $this->record[$this->columns['id']]; - - if ($this->caching) - { - //$this->cache->delete('PICKLES-' . $this->datasource . '-' . $this->table . '-' . $this->record[$this->columns['id']]); - } } else { @@ -5197,7 +5198,7 @@ class Model extends Object } // Executes the query - if ($this->postgresql && $update === false) + if ($this->postgresql && $update == false) { $results = $this->db->fetch($sql, $input_parameters); @@ -5205,7 +5206,15 @@ class Model extends Object } else { - return $this->db->execute($sql, $input_parameters); + $results = $this->db->execute($sql, $input_parameters); + + // Clears the cache + if ($update) + { + $this->cache->delete($this->model . '-' . $this->record[$this->columns['id']]); + } + + return $results; } } } @@ -5251,8 +5260,12 @@ class Model extends Object } $input_parameters[] = $this->record[$this->columns['id']]; + $results = $this->db->execute($sql, $input_parameters); - return $this->db->execute($sql, $input_parameters); + // Clears the cache + $this->cache->delete($this->model . '-' . $this->record[$this->columns['id']]); + + return $results; } else {