extend usage of in-class cache in Tinebase_Container
authorLars Kneschke <l.kneschke@metaways.de>
Wed, 15 Apr 2015 03:16:57 +0000 (05:16 +0200)
committerLars Kneschke <l.kneschke@metaways.de>
Thu, 16 Apr 2015 07:38:14 +0000 (09:38 +0200)
* added separate per request cache class
* added in-class caching to all important functions in
Tinebase_Container
* made Tinebase_Container::hasGrant cheaper (easier to cache and
simplified database query)
* use Zend_Cache as fallback if Tinebase_Cache_PerRequest::$_usePersistentCache is true

Change-Id: I75fa4abfc1601448cd30496cfca289eb81b8bfaf
Reviewed-on: http://gerrit.tine20.com/customers/1831
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Reviewed-by: Lars Kneschke <l.kneschke@metaways.de>
tests/tine20/Crm/Export/AbstractTest.php
tests/tine20/Filemanager/Controller/DownloadLinkTests.php
tests/tine20/TestCase.php
tests/tine20/Tinebase/ContainerTest.php
tine20/Tinebase/Backend/Sql/Abstract.php
tine20/Tinebase/Cache/PerRequest.php [new file with mode: 0644]
tine20/Tinebase/Container.php

index 2be9d76..4343f49 100644 (file)
@@ -36,6 +36,7 @@ abstract class Crm_Export_AbstractTest extends Crm_AbstractTest
     protected function setUp()
     {
         Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+        Tinebase_Cache_PerRequest::getInstance()->resetCache();
         $this->_json = new Crm_Frontend_Json();
         
         $contact = $this->_getContact();
@@ -60,5 +61,6 @@ abstract class Crm_Export_AbstractTest extends Crm_AbstractTest
     protected function tearDown()
     {
         Tinebase_TransactionManager::getInstance()->rollBack();
+        Tinebase_Cache_PerRequest::getInstance()->resetCache();
     }
 }
index 795cccb..5197732 100644 (file)
@@ -33,6 +33,7 @@ class Filemanager_Controller_DownloadLinkTests extends TestCase
         
         Tinebase_FileSystem::getInstance()->clearStatCache();
         Tinebase_FileSystem::getInstance()->clearDeletedFilesFromFilesystem();
+        Tinebase_Cache_PerRequest::getInstance()->resetCache();
     }
     
     /**
@@ -79,6 +80,9 @@ class Filemanager_Controller_DownloadLinkTests extends TestCase
             'value'    => '/' . Tinebase_Model_Container::TYPE_PERSONAL . '/' . Tinebase_Core::getUser()->accountLoginName
         )));
         $node = Filemanager_Controller_Node::getInstance()->search($filter)->getFirstRecord();
+        
+        $this->assertInstanceOf('Tinebase_Model_Tree_Node', $node);
+        
         return $node;
     }
     
index e4724f6..86d516b 100644 (file)
@@ -108,6 +108,8 @@ abstract class TestCase extends PHPUnit_Framework_TestCase
         Addressbook_Controller_Contact::getInstance()->setGeoDataForContacts(true);
         
         Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
+        
+        Tinebase_Cache_PerRequest::getInstance()->resetCache();
     }
     
     /**
index 424c133..08bb0cf 100644 (file)
@@ -424,7 +424,6 @@ class Tinebase_ContainerTest extends PHPUnit_Framework_TestCase
     
     /**
      * try to get container by acl
-     *
      */
     public function testGetContainerByAcl()
     {
@@ -438,6 +437,18 @@ class Tinebase_ContainerTest extends PHPUnit_Framework_TestCase
             $this->_validatePath($container);
         }
     }
+
+    /**
+     * try to get container by acl with Zend_Cache
+     */
+    public function testGetContainerByAclWithPersistentCaching()
+    {
+        Tinebase_Cache_PerRequest::getInstance()->usePersistentCache(true);
+        $this->testGetContainerByAcl();
+        $oldValue = Tinebase_Cache_PerRequest::getInstance()->usePersistentCache(false);
+
+        $this->assertTrue($oldValue);
+    }
     
     /**
      * test getGrantsOfRecords
index c00bc24..04f77ce 100644 (file)
@@ -1612,4 +1612,58 @@ abstract class Tinebase_Backend_Sql_Abstract extends Tinebase_Backend_Abstract i
         }
         return $result;
     }
+    
+    /**
+     * save value in in-class cache
+     * 
+     * @param string $method
+     * @param string $cacheId
+     * @param string $value
+     * @return Tinebase_Cache_PerRequest
+     */
+    public function saveInClassCache($method, $cacheId, $value)
+    {
+        return Tinebase_Cache_PerRequest::getInstance()->save($this->_getInClassCacheIdentifier(), $method, $cacheId, $value);
+    }
+    
+    /**
+     * load value from in-class cache
+     * 
+     * @param string $method
+     * @param string $cacheId
+     * @return mixed
+     */
+    public function loadFromClassCache($method, $cacheId)
+    {
+        return Tinebase_Cache_PerRequest::getInstance()->load($this->_getInClassCacheIdentifier(), $method, $cacheId);
+    }
+    
+    /**
+     * reset class cache
+     * 
+     * @param string $key
+     * @return Tinebase_Model_Filter_Abstract
+     */
+    public function resetClassCache($method = null)
+    {
+        Tinebase_Cache_PerRequest::getInstance()->resetCache($this->_getInClassCacheIdentifier(), $method);
+        
+        return $this;
+    }
+    
+    /**
+     * return class cache identifier
+     * 
+     * if class extend parent class, you can define the name of the parent class here
+     * 
+     * @return string
+     */
+    public function _getInClassCacheIdentifier()
+    {
+        if (isset($this->_classCacheIdentifier)) {
+            return $this->_classCacheIdentifier;
+        }
+        
+        return get_class($this);
+    }
 }
diff --git a/tine20/Tinebase/Cache/PerRequest.php b/tine20/Tinebase/Cache/PerRequest.php
new file mode 100644 (file)
index 0000000..e662aaf
--- /dev/null
@@ -0,0 +1,189 @@
+<?php
+/**
+ * Tine 2.0
+ * 
+ * @package     Tinebase
+ * @subpackage  Cache
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright   Copyright (c) 2015-2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Lars Kneschke <l.kneschke@metaways.de>
+ */
+
+/**
+ * per request in memory cache class
+ * 
+ * a central place to manage in class caches
+ * 
+ * @package     Tinebase
+ * @subpackage  Cache
+ */
+class Tinebase_Cache_PerRequest 
+{
+    /**
+     * the cache
+     *
+     * @var array
+     */
+    protected $_inMemoryCache = array();
+
+    /**
+     * use Zend_Cache as fallback
+     *
+     * @var bool
+     *
+     * TODO allow to configure this
+     */
+    protected $_usePersistentCache = false;
+    
+    /**
+     * holds the instance of the singleton
+     *
+     * @var Tinebase_Cache_PerRequest
+     */
+    private static $_instance = NULL;
+    
+    /**
+     * the constructor
+     *
+     * don't use the constructor. use the singleton 
+     */
+    private function __construct()
+    {
+    }
+    
+    /**
+     * don't clone. Use the singleton.
+     *
+     */
+    private function __clone() 
+    {
+    }
+    
+    /**
+     * the singleton pattern
+     *
+     * @return Tinebase_Cache_PerRequest
+     */
+    public static function getInstance() 
+    {
+        if (self::$_instance === NULL) {
+            self::$_instance = new Tinebase_Cache_PerRequest();
+        }
+        
+        return self::$_instance;
+    }
+
+    /**
+     * set/get persistent cache usage
+     *
+     * @param  boolean optional
+     * @return boolean
+     */
+    public function usePersistentCache()
+    {
+        $value = (func_num_args() === 1) ? (bool) func_get_arg(0) : NULL;
+        $currValue = $this->_usePersistentCache;
+        if ($value !== NULL) {
+            $this->_usePersistentCache = $value;
+        }
+
+        return $currValue;
+    }
+
+    /**
+     * save entry in cache
+     * 
+     * @param string $class
+     * @param string $method
+     * @param string $cacheId
+     * @param string $value
+     * @return Tinebase_Cache_PerRequest
+     */
+    public function save($class, $method, $cacheId, $value)
+    {
+        $cacheId = sha1($cacheId);
+        
+        $this->_inMemoryCache[$class][$method][$cacheId] = $value;
+
+        if ($this->_usePersistentCache) {
+            Tinebase_Core::getCache()->save($value, $cacheId);
+        }
+        
+        return $this;
+    }
+    
+    /**
+     * load entry from cache
+     * 
+     * @param string $class
+     * @param string $method
+     * @param string $cacheId
+     * @throws Tinebase_Exception_NotFound
+     * @return mixed
+     */
+    public function load($class, $method, $cacheId)
+    {
+        $cacheId = sha1($cacheId);
+        
+        if (!isset($this->_inMemoryCache[$class]) ||
+            !isset($this->_inMemoryCache[$class][$method]) ||
+            !(isset($this->_inMemoryCache[$class][$method][$cacheId]) || array_key_exists($cacheId, $this->_inMemoryCache[$class][$method]))
+        ) {
+
+            if ($this->_usePersistentCache) {
+                // TODO better use Tinebase_Core::getCache()->test() ?
+                $value = Tinebase_Core::getCache()->load($cacheId);
+                if ($value !== false) {
+                    $this->_inMemoryCache[$class][$method][$cacheId] = $value;
+                    return $value;
+                }
+            }
+
+            throw new Tinebase_Exception_NotFound('cacheId not found');
+        };
+        
+        return $this->_inMemoryCache[$class][$method][$cacheId];
+    }
+    
+    /**
+     * reset cache
+     * 
+     * @param string $class
+     * @param string $method
+     * @param string $cacheId
+     * @return Tinebase_Cache_PerRequest
+     */
+    public function resetCache($class = null, $method = null, $cacheId = null)
+    {
+        $cacheId = $cacheId ? sha1($cacheId) : $cacheId;
+        
+        if (empty($class)) {
+            $this->_inMemoryCache = array();
+            
+            return $this;
+        }
+        
+        if (empty($method)) {
+            $this->_inMemoryCache[$class] = array();
+            
+            return $this;
+        }
+        
+        if (empty($cacheId)) {
+            $this->_inMemoryCache[$class][$method] = array();
+            
+            return $this;
+        } else if ($this->_usePersistentCache) {
+            Tinebase_Core::getCache()->remove($cacheId);
+        }
+        
+        if (isset($this->_inMemoryCache[$class]) &&
+            isset($this->_inMemoryCache[$class][$method]) &&
+            (isset($this->_inMemoryCache[$class][$method][$cacheId]) || array_key_exists($cacheId, $this->_inMemoryCache[$class][$method]))
+        ) {
+            unset($this->_inMemoryCache[$class][$method][$cacheId]);
+        };
+        
+        return $this;
+    }
+}
\ No newline at end of file
index fb2ea5c..298d594 100644 (file)
@@ -80,12 +80,6 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      */
     protected $_defaultCountCol = 'id';
     
-    protected $_classCache = array(
-        'getContainerByACL' => array(),
-        'getContainerById'  => array(),
-        'hasGrant'          => array()
-    );
-
     /**
      * cache timeout for ACL related cache entries (in seconds)
      * 
@@ -376,7 +370,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
     {
         $select = $this->_getSelect();
         $select->where($this->_db->quoteIdentifier('uuid') . ' = ' . $this->_db->quoteIdentifier('name'));
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         $rows = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
         
         $result = new Tinebase_Record_RecordSet('Tinebase_Model_Container', $rows, TRUE);
@@ -412,8 +406,10 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         
         $classCacheId = convertCacheId($accountId . $applicationId . implode('', (array)$grant) . (int)$onlyIds . (int)$ignoreACL);
         
-        if (isset($this->_classCache[__FUNCTION__][$classCacheId])) {
-            return $this->_classCache[__FUNCTION__][$classCacheId];
+        try {
+            return $this->loadFromClassCache(__FUNCTION__, $classCacheId);
+        } catch (Tinebase_Exception_NotFound $tenf) {
+            // continue...
         }
         
         $select = $this->_getSelect($onlyIds ? 'id' : '*')
@@ -432,7 +428,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         
         $this->addGrantsSql($select, $accountId, $grant);
         
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         
         if ($onlyIds) {
             $result = $stmt->fetchAll(Zend_Db::FETCH_COLUMN);
@@ -451,7 +447,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             }
         }
         
-        $this->_classCache[__FUNCTION__][$classCacheId] = $result;
+        $this->saveInClassCache(__FUNCTION__, $classCacheId, $result);
         
         return $result;
     }
@@ -474,8 +470,10 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         $classCacheId = $containerId . 'd' . (int)$_getDeleted;
         $cacheId      = 'getContainerById' . $classCacheId;
         
-        if (isset($this->_classCache[__FUNCTION__][$classCacheId])) {
-            return $this->_classCache[__FUNCTION__][$classCacheId];
+        try {
+            return $this->loadFromClassCache(__FUNCTION__, $classCacheId);
+        } catch (Tinebase_Exception_NotFound $tenf) {
+            // continue...
         }
         
         // load from cache
@@ -487,7 +485,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             $cache->save($result, $cacheId, array('container'));
         }
         
-        $this->_classCache[__FUNCTION__][$classCacheId] = $result;
+        $this->saveInClassCache(__FUNCTION__, $classCacheId, $result);
         
         return $result;
     }
@@ -544,7 +542,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             $select->where("{$this->_db->quoteIdentifier('owner.account_id')} = ?", $ownerId);
         }
         
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         $containersData = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
         
         if (count($containersData) == 0) {
@@ -578,7 +576,22 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         $ownerId     = Tinebase_Model_User::convertUserIdToInt($_owner);
         $grant       = $_ignoreACL ? '*' : $_grant;
         $application = Tinebase_Application::getInstance()->getApplicationByName($meta['appName']);
-
+        
+        $classCacheId = convertCacheId(
+            $accountId .
+            $application->getId() .
+            ($meta['recordClass'] ? $meta['recordClass'] : null) .
+            $ownerId .
+            implode('', (array)$grant) .
+            (int)$_ignoreACL
+        );
+        
+        try {
+            return $this->loadFromClassCache(__FUNCTION__, $classCacheId);
+        } catch (Tinebase_Exception_NotFound $tenf) {
+            // continue...
+        }
+        
         $select = $this->_db->select()
             ->distinct()
             ->from(array('owner' => SQL_TABLE_PREFIX . 'container_acl'), array())
@@ -607,7 +620,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             $select->where("{$this->_db->quoteIdentifier('container.model')} = ?", $meta['recordClass']);
         }
         
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         $containersData = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
         
         // if no containers where found, maybe something went wrong when creating the initial folder
@@ -621,6 +634,8 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         }
 
         $containers = new Tinebase_Record_RecordSet('Tinebase_Model_Container', $containersData, TRUE);
+        
+        $this->saveInClassCache(__FUNCTION__, $classCacheId, $containers);
 
         return $containers;
     }
@@ -751,9 +766,23 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      */
     public function getSharedContainer($_accountId, $_application, $_grant, $_ignoreACL = FALSE)
     {
+        $accountId   = Tinebase_Model_User::convertUserIdToInt($_accountId);
         $application = Tinebase_Application::getInstance()->getApplicationByName($_application);
         $grant       = $_ignoreACL ? '*' : $_grant;
         
+        $classCacheId = convertCacheId(
+            $accountId .
+            $application->getId() .
+            implode('', (array)$grant) .
+            (int)$_ignoreACL
+        );
+        
+        try {
+            return $this->loadFromClassCache(__FUNCTION__, $classCacheId);
+        } catch (Tinebase_Exception_NotFound $tenf) {
+            // continue...
+        }
+        
         $select = $this->_getSelect()
             ->distinct()
             ->join(array(
@@ -767,12 +796,14 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             
             ->order('container.name');
         
-        $this->addGrantsSql($select, $_accountId, $grant);
+        $this->addGrantsSql($select, $accountId, $grant);
+        
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         
-        $stmt = $this->_db->query($select);
-
         $containers = new Tinebase_Record_RecordSet('Tinebase_Model_Container', $stmt->fetchAll(Zend_Db::FETCH_ASSOC), TRUE);
         
+        $this->saveInClassCache(__FUNCTION__, $classCacheId, $containers);
+        
         Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
             . ' Found ' . count($containers) . ' shared container(s) in application ' . $application->name);
         
@@ -828,7 +859,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             
         $this->addGrantsSql($select, $accountId, $grant);
         
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         $containerIds = $stmt->fetchAll(Zend_Db::FETCH_COLUMN);
         
         // no container ids found / can stop here
@@ -855,7 +886,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             ->where("{$this->_db->quoteIdentifier('container_acl.account_grant')} = ?", Tinebase_Model_Grants::GRANT_ADMIN)
             ->where("{$this->_db->quoteIdentifier('accounts.status')} = ?", 'enabled');
             
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         $accountIds = $stmt->fetchAll(Zend_Db::FETCH_COLUMN);
         
         return $accountIds;
@@ -872,8 +903,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      */
     public function getOtherUsersContainer($_accountId, $_application, $_grant, $_ignoreACL = FALSE)
     {
-        $containerData = $this->_getOtherUsersContainerData($_accountId, $_application, $_grant, $_ignoreACL);
-        $result = new Tinebase_Record_RecordSet('Tinebase_Model_Container', $containerData, TRUE);
+        $result = $this->_getOtherUsersContainerData($_accountId, $_application, $_grant, $_ignoreACL);
         
         return $result;
     }
@@ -885,7 +915,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      * @param   string|Tinebase_Model_Application   $_application
      * @param   array|string                        $_grant
      * @param   bool                                $_ignoreACL
-     * @return  array of array of containerData
+     * @return  Tinebase_Record_RecordSet set of Tinebase_Model_Container
      */
     protected function _getOtherUsersContainerData($_accountId, $_application, $_grant, $_ignoreACL = FALSE)
     {
@@ -893,6 +923,19 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         $application = Tinebase_Application::getInstance()->getApplicationByName($_application);
         $grant       = $_ignoreACL ? '*' : $_grant;
         
+        $classCacheId = convertCacheId(
+            $accountId .
+            $application->getId() .
+            implode('', (array)$grant) .
+            (int)$_ignoreACL
+        );
+        
+        try {
+            return $this->loadFromClassCache(__FUNCTION__, $classCacheId);
+        } catch (Tinebase_Exception_NotFound $tenf) {
+            // continue...
+        }
+        
         $select = $this->_db->select()
             ->from(array('owner' => SQL_TABLE_PREFIX . 'container_acl'), array('account_id'))
             ->join(array(
@@ -929,10 +972,13 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         
         Tinebase_Backend_Sql_Abstract::traitGroup($select);
         
-        $stmt = $this->_db->query($select);
-        $containersData = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
+        
+        $containers = new Tinebase_Record_RecordSet('Tinebase_Model_Container', $stmt->fetchAll(Zend_Db::FETCH_ASSOC), TRUE);
         
-        return $containersData;
+        $this->saveInClassCache(__FUNCTION__, $classCacheId, $containers);
+        
+        return $containers;
     }
     
     /**
@@ -1066,23 +1112,6 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
     }
     
     /**
-     * reset class cache
-     * 
-     * @param string $key
-     * @return Tinebase_Container
-     */
-    public function resetClassCache($key = null)
-    {
-        foreach ($this->_classCache as $cacheKey => $cacheValue) {
-            if ($key === null || $key === $cacheKey) {
-                $this->_classCache[$cacheKey] = array();
-            }
-        }
-        
-        return $this;
-    }
-    
-    /**
      * set container color, if the user has the required right
      *
      * @param   int|Tinebase_Model_Container $_containerId
@@ -1115,7 +1144,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
      * @param   array|string                        $_grant
      * @return  boolean
      */
-    public function hasGrant($_accountId, $_containerId, $_grant) 
+    public function hasGrant($_accountId, $_containerId, $_grant)
     {
         $accountId = Tinebase_Model_User::convertUserIdToInt($_accountId);
         
@@ -1130,36 +1159,32 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
             . ' account: ' . $accountId . ' / containerId: ' . $containerId . ' / grant:' . implode('/', (array)$_grant));
         
-        // always bring values in the same order for $classCacheId 
-        if (is_array($_grant)) {
-            sort($_grant);
-        }
-        
-        $classCacheId = convertCacheId($accountId . $containerId . implode('', (array)$_grant));
+        $classCacheId = convertCacheId($accountId . $containerId);
         
-        if (isset($this->_classCache[__FUNCTION__][$classCacheId]) || array_key_exists($classCacheId, $this->_classCache[__FUNCTION__])) {
-            return $this->_classCache[__FUNCTION__][$classCacheId];
+        try {
+            $allGrants = $this->loadFromClassCache(__FUNCTION__, $classCacheId);
+        } catch (Tinebase_Exception_NotFound $tenf) {
+            // NOTE: some tests ask for already deleted container ;-)
+            $select = $this->_getSelect(array(), true)
+                ->distinct()
+                ->where("{$this->_db->quoteIdentifier('container.id')} = ?", $containerId)
+                ->join(array(
+                    /* table  */ 'container_acl' => SQL_TABLE_PREFIX . 'container_acl'), 
+                    /* on     */ "{$this->_db->quoteIdentifier('container_acl.container_id')} = {$this->_db->quoteIdentifier('container.id')}",
+                    /* select */ array('container_acl.account_grant')
+                );
+                
+            $this->addGrantsSql($select, $accountId, '*');
+            
+            $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
+            
+            $allGrants = $stmt->fetchAll(Zend_Db::FETCH_COLUMN);
+            $this->saveInClassCache(__FUNCTION__, $classCacheId, $allGrants);
         }
         
-        // NOTE: some tests ask for already deleted container ;-)
-        $select = $this->_getSelect(self::IDCOL, true)
-            ->where("{$this->_db->quoteIdentifier('container.id')} = ?", $containerId)
-            ->join(array(
-                /* table  */ 'container_acl' => SQL_TABLE_PREFIX . 'container_acl'), 
-                /* on     */ "{$this->_db->quoteIdentifier('container_acl.container_id')} = {$this->_db->quoteIdentifier('container.id')}",
-                /* select */ array()
-            );
-        
-        $this->addGrantsSql($select, $accountId, $_grant);
+        $matchingGrants = array_intersect((array)$_grant, $allGrants);
         
-        $stmt = $this->_db->query($select);
-        
-        $grants = $stmt->fetchAll(Zend_Db::FETCH_COLUMN);
-        $result = ! empty($grants);
-        
-        $this->_classCache[__FUNCTION__][$classCacheId] = $result;
-        
-        return $result;
+        return !!count($matchingGrants);
     }
     
     /**
@@ -1182,10 +1207,10 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         
         Tinebase_Backend_Sql_Abstract::traitGroup($select);
         
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
 
         $grantsData = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
-
+        
         foreach($grantsData as $grantData) {
             $givenGrants = explode(',', $grantData['account_grants']);
             foreach($givenGrants as $grant) {
@@ -1247,12 +1272,13 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
             
             Tinebase_Backend_Sql_Abstract::traitGroup($select);
             
-            $stmt = $this->_db->query($select);
+            $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
             $rows = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
             $grants = $this->_getGrantsFromArray($rows, $accountId, $_grantModel);
             
             $cache->save($grants, $cacheKey, array('container'), self::ACL_CACHE_TIMEOUT);
         }
+        
         return $grants;
     }
     
@@ -1311,7 +1337,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         }
         
         if (empty($containerIds)) {
-            return;
+            return array();
         }
         
         $accountId = $_accountId instanceof Tinebase_Record_Abstract
@@ -1331,15 +1357,10 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         
         Tinebase_Backend_Sql_Abstract::traitGroup($select);
         
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         $rows = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
         
-        if (empty($rows)) {
-            return;
-        }
-        
         $containers = array();
-        
         // add results to container ids and get grants array
         foreach ($rows as $row) {
             // NOTE id is non-ambiguous
@@ -1663,7 +1684,7 @@ class Tinebase_Container extends Tinebase_Backend_Sql_Abstract
         
         $select = $this->_getSelect(array('id', 'content_seq'), TRUE);
         $select->where($this->_db->quoteInto($this->_db->quoteIdentifier('id') . ' IN (?)', (array) $containerIds));
-        $stmt = $this->_db->query($select);
+        $stmt = $this->_db->query('/*' . __FUNCTION__ . '*/' . $select);
         $result = $stmt->fetchAll();
         foreach ($result as $key => $value) {
             $result[$value['id']] = $value['content_seq'];