From 9dd8d3c417bb2bb5bb002cc877a60f5c95ece2eb Mon Sep 17 00:00:00 2001 From: cdujeu Date: Sat, 25 Jun 2016 10:44:38 +0200 Subject: [PATCH] When using APC as cache driver and when in CLI context, we need to forward delete instructions to server. Group all instructions inside a unique http call at the end of the process. --- .../core.cache/AbstractCacheDriver.php | 133 ++++++++++++++---- .../plugins/core.cache/CoreCacheLoader.php | 29 ++++ core/src/plugins/core.cache/manifest.xml | 5 + 3 files changed, 140 insertions(+), 27 deletions(-) diff --git a/core/src/plugins/core.cache/AbstractCacheDriver.php b/core/src/plugins/core.cache/AbstractCacheDriver.php index 7a2544e636..02eec24145 100644 --- a/core/src/plugins/core.cache/AbstractCacheDriver.php +++ b/core/src/plugins/core.cache/AbstractCacheDriver.php @@ -26,11 +26,17 @@ define('AJXP_CACHE_SERVICE_NS_NODES', 'nodes'); use Doctrine\Common\Cache; +use Psr\Http\Message\ResponseInterface; use Pydio\Access\Core\Model\AJXP_Node; +use Pydio\Cache\Doctrine\Ext\PydioApcuCache; use Pydio\Core\PluginFramework\Plugin; use Pydio\Cache\Doctrine\Ext\PatternClearableCache; +use Pydio\Core\Services\ConfService; +use GuzzleHttp\Client; +use Pydio\Core\Utils\ApplicationState; +use Pydio\Log\Core\Logger; /** * @package AjaXplorer_Plugins @@ -55,6 +61,16 @@ abstract class AbstractCacheDriver extends Plugin */ protected $namespacedCaches = array(); + /** + * Keep a pointer to an http client if necessary + * @var Client + */ + protected $httpClient; + + /** + * @var array + */ + private $httpDeletion = []; /** * @param string $namespace @@ -104,29 +120,6 @@ public static function computeFullRepositoryId($repositoryId, $cacheType){ return $ID = $cacheType."://".$repositoryId."/"; } - /** - * @param string $namespace - * @return bool - */ - public function supportsPatternDelete($namespace) - { - $cacheDriver = $this->getCacheDriver($namespace); - return $cacheDriver instanceof PatternClearableCache; - } - - /** - * @param string $namespace - * @param string $id - * @return bool - */ - public function deleteKeyStartingWith($namespace, $id){ - $cacheDriver = $this->getCacheDriver($namespace); - if(!($cacheDriver instanceof PatternClearableCache)){ - return false; - } - return $cacheDriver->deleteKeysStartingWith($id); - } - /** * Fetches an entry from the cache. * @@ -194,11 +187,17 @@ public function save($namespace, $id, $data, $lifeTime = 0){ * @return bool TRUE if the entry was successfully deleted, FALSE otherwise. */ public function delete($namespace, $id){ + $cacheDriver = $this->getCacheDriver($namespace); + if($this->requiresHttpForwarding($cacheDriver)){ + $this->httpDeletion[$namespace.$id.'key'] = ["namespace"=>$namespace, "key" => $id]; + return true; + } if (isset($cacheDriver) && $cacheDriver->contains($id)) { - $result = $cacheDriver->delete($id); - return $result; + Logger::debug("CacheDriver::Http", "Clear Key ".$id, ["namespace" => $namespace]); + $result = $cacheDriver->delete($id); + return $result; } return false; @@ -212,23 +211,103 @@ public function delete($namespace, $id){ * */ public function deleteAll($namespace){ + $cacheDriver = $this->getCacheDriver($namespace); + if($this->requiresHttpForwarding($cacheDriver)){ + $this->httpDeletion[$namespace.'all'] = ["namespace"=>$namespace, "all" => "true"]; + return true; + } if (isset($cacheDriver)) { - $result = $cacheDriver->deleteAll(); - return $result; + Logger::debug("CacheDriver::Http", "Clear All", ["namespace" => $namespace]); + $result = $cacheDriver->deleteAll(); + return $result; } return false; } + /** + * @param string $namespace + * @return bool + */ + public function supportsPatternDelete($namespace) + { + $cacheDriver = $this->getCacheDriver($namespace); + return $cacheDriver instanceof PatternClearableCache; + } + + /** + * @param string $namespace + * @param string $id + * @return bool + */ + public function deleteKeyStartingWith($namespace, $id){ + + $cacheDriver = $this->getCacheDriver($namespace); + if($this->requiresHttpForwarding($cacheDriver)){ + $this->httpDeletion[$namespace.$id.'pattern'] = ["namespace"=>$namespace, "pattern" => $id]; + return true; + } + + if(!($cacheDriver instanceof PatternClearableCache)){ + return false; + } + Logger::debug("CacheDriver::Http", "Clear Pattern ".$id, ["namespace" => $namespace]); + return $cacheDriver->deleteKeysStartingWith($id); + } + + + /** + * @return array + */ public function listNamespaces(){ return [AJXP_CACHE_SERVICE_NS_SHARED, AJXP_CACHE_SERVICE_NS_NODES]; } + /** + * @param $namespace + * @return array|null + */ public function getStats($namespace){ $cacheDriver = $this->getCacheDriver($namespace); return $cacheDriver->getStats(); } + /** + * @param CacheProvider + * @return bool + */ + protected function requiresHttpForwarding($cacheDriver){ + if(!empty($cacheDriver) && $cacheDriver instanceof PydioApcuCache && ConfService::currentContextIsCommandLine()){ + return true; + } + return false; + } + + /** + * @return Client + */ + protected function getHttpClient(){ + if(!isSet($this->httpClient)){ + $this->httpClient = new Client([ + 'base_url' => ApplicationState::detectServerURL(true) + ]); + } + return $this->httpClient; + } + + public function __destruct() + { + if(count($this->httpDeletion)){ + Logger::debug("CacheDriver::CLI", "Sending cache clear to http", $this->httpDeletion); + $this->getHttpClient()->post('/?get_action=clear_cache_key', [ + 'body' => [ + 'data' => json_encode($this->httpDeletion) + ] + ]); + + } + } + } \ No newline at end of file diff --git a/core/src/plugins/core.cache/CoreCacheLoader.php b/core/src/plugins/core.cache/CoreCacheLoader.php index ce3dd053d2..0c9e243abf 100644 --- a/core/src/plugins/core.cache/CoreCacheLoader.php +++ b/core/src/plugins/core.cache/CoreCacheLoader.php @@ -33,6 +33,7 @@ use Pydio\Access\Core\Model\AJXP_Node; use Pydio\Core\Utils\Vars\InputFilter; +use Pydio\Log\Core\Logger; use Zend\Diactoros\Response\JsonResponse; defined('AJXP_EXEC') or die( 'Access not allowed'); @@ -238,5 +239,33 @@ public function clearCacheByNS(ServerRequestInterface $requestInterface, Respons } + /** + * Service to clear a cache key or a pattern + * @param ServerRequestInterface $requestInterface + * @param ResponseInterface $responseInterface + */ + public function clearCacheKey(ServerRequestInterface $requestInterface, ResponseInterface &$responseInterface){ + $cacheDriver = ConfService::getCacheDriverImpl(); + if($cacheDriver === null) { + $responseInterface = new JsonResponse(["result" => "NOCACHEFOUND"]); + return; + } + $params = $requestInterface->getParsedBody(); + $data = json_decode($params["data"], true); + foreach($data as $key => $params){ + $ns = $params['namespace']; + if(isSet($params["key"])){ + Logger::info("CoreCacheLoader", "Clear Key ".$params["key"], $params); + $cacheDriver->delete($ns, $params["key"]); + }else if(isSet($params["pattern"])){ + Logger::info("CoreCacheLoader", "Clear Pattern ".$params["pattern"], $params); + $cacheDriver->deleteKeyStartingWith($ns, $params["pattern"]); + }else if(isSet($params["all"])) { + Logger::info("CoreCacheLoader", "Clear All ", $params); + $cacheDriver->deleteAll($ns); + } + } + $responseInterface = new JsonResponse(["result" => "SUCCESS"]); + } } diff --git a/core/src/plugins/core.cache/manifest.xml b/core/src/plugins/core.cache/manifest.xml index aa08606584..ae4328dfca 100644 --- a/core/src/plugins/core.cache/manifest.xml +++ b/core/src/plugins/core.cache/manifest.xml @@ -24,6 +24,11 @@ + + + + +