From 86c1d510f1c192880920085fd7b77d65f3f848ad Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Wed, 9 May 2012 19:01:51 +0200 Subject: [PATCH] Add select() method to query objects from cache; Use locks to avoid multiple threads synching the same folder simultaneously --- plugins/libkolab/SQL/mysql.sql | 2 +- plugins/libkolab/lib/kolab_storage_cache.php | 72 ++++++++++++++++++- plugins/libkolab/lib/kolab_storage_folder.php | 35 +++++++++ plugins/libkolab/libkolab.php | 2 - 4 files changed, 106 insertions(+), 5 deletions(-) diff --git a/plugins/libkolab/SQL/mysql.sql b/plugins/libkolab/SQL/mysql.sql index 376eb4fd..5f49e92c 100644 --- a/plugins/libkolab/SQL/mysql.sql +++ b/plugins/libkolab/SQL/mysql.sql @@ -6,7 +6,7 @@ * @licence GNU AGPL **/ -CREATE TABLE `kolab_cache` ( +CREATE TABLE IF NOT EXISTS `kolab_cache` ( `resource` VARCHAR(255) CHARACTER SET ascii NOT NULL, `type` VARCHAR(32) CHARACTER SET ascii NOT NULL, `msguid` BIGINT UNSIGNED NOT NULL, diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php index 9f80691c..65aa4cb7 100644 --- a/plugins/libkolab/lib/kolab_storage_cache.php +++ b/plugins/libkolab/lib/kolab_storage_cache.php @@ -81,6 +81,9 @@ class kolab_storage_cache if ($this->synched) return; + // lock synchronization for this folder or wait if locked + $this->_sync_lock(); + // synchronize IMAP mailbox cache $this->imap->folder_sync($this->folder->name); @@ -92,8 +95,9 @@ class kolab_storage_cache if ($this->ready) { // read cache index $sql_result = $this->db->query( - "SELECT msguid, uid FROM kolab_cache WHERE resource=?", - $this->resource_uri + "SELECT msguid, uid FROM kolab_cache WHERE resource=? AND type<>?", + $this->resource_uri, + 'lock' ); $old_index = array(); @@ -120,6 +124,9 @@ class kolab_storage_cache } } + // remove lock + $this->_sync_unlock(); + $this->synched = time(); } @@ -349,6 +356,7 @@ class kolab_storage_cache $this->db->quote($param[2]) ); } + return $sql_where; } @@ -400,6 +408,11 @@ class kolab_storage_cache // database runs in server's timezone so using date() is what we want $sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']); $sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']); + + // extend date range for recurring events + if ($object['recurrence']) { + $sql_data['dtend'] = date('Y-m-d H:i:s', $object['recurrence']['UNTIL'] ?: strtotime('now + 2 years')); + } } if ($object['_formatobj']) @@ -453,6 +466,61 @@ class kolab_storage_cache return $object; } + /** + * Check lock record for this folder and wait if locked or set lock + */ + private function _sync_lock() + { + if (!$this->ready) + return; + + $sql_arr = $this->db->fetch_assoc($this->db->query( + "SELECT msguid AS locked FROM kolab_cache ". + "WHERE resource=? AND type=?", + $this->resource_uri, + 'lock' + )); + + // create lock record if not exists + if (!$sql_arr) { + $this->db->query( + "INSERT INTO kolab_cache (resource, type, msguid, uid, data, xml)". + " VALUES (?, ?, ?, '', '', '')", + $this->resource_uri, + 'lock', + time() + ); + } + // wait if locked (expire locks after 10 minutes) + else if (intval($sql_arr['locked']) > 0 && (time() - $sql_arr['locked']) < 600) { + usleep(500000); + return $this->_sync_lock(); + } + // set lock + else { + $this->db->query( + "UPDATE kolab_cache SET msguid=? ". + "WHERE resource=? AND type=?", + time(), + $this->resource_uri, + 'lock' + ); + } + } + + /** + * Remove lock for this folder + */ + private function _sync_unlock() + { + $this->db->query( + "UPDATE kolab_cache SET msguid=0 ". + "WHERE resource=? AND type=?", + $this->resource_uri, + 'lock' + ); + } + /** * Resolve an object UID into an IMAP message UID * diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index c55a34fd..eed7b08a 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -317,6 +317,41 @@ class kolab_storage_folder } + /** + * Select *some* Kolab objects matching the given query + * + * @param array Pseudo-SQL query as list of filter parameter triplets + * triplet: array('', '', '') + * @return array List of Kolab data objects (each represented as hash array) + */ + public function select($query = array()) + { + // check query argument + if (empty($query)) + return $this->get_objects(); + + $type = null; + foreach ($query as $i => $param) { + if ($param[0] == 'type') { + $type = $param[2]; + } + else if (($param[0] == 'dtstart' || $param[0] == 'dtend') && is_numeric($param[2])) { + $query[$i][2] = date('Y-m-d H:i:s', $param[2]); + } + } + + // add type selector if not in $query + if (!$type) + $query[] = array('type','=',$this->type); + + // synchronize caches + $this->cache->synchronize(); + + // fetch objects from cache + return $this->cache->select($query); + } + + /** * Getter for a single Kolab object, identified by its UID * diff --git a/plugins/libkolab/libkolab.php b/plugins/libkolab/libkolab.php index 3d0709b4..fd911be4 100644 --- a/plugins/libkolab/libkolab.php +++ b/plugins/libkolab/libkolab.php @@ -63,9 +63,7 @@ class libkolab extends rcube_plugin */ function storage_init($p) { - $rcmail = rcmail::get_instance(); $p['fetch_headers'] = trim($p['fetch_headers'] .' X-KOLAB-TYPE'); - return $p; }