Merge pull request #7897 from owncloud/ext-swiftcaching

Added object cache for Swift ext storage
This commit is contained in:
Vincent Petry 2016-05-24 16:21:30 +02:00
commit e8c3794308

View file

@ -73,6 +73,14 @@ class Swift extends \OC\Files\Storage\Common {
*/ */
private static $tmpFiles = array(); private static $tmpFiles = array();
/**
* Key value cache mapping path to data object. Maps path to
* \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
* paths and path to false for not existing paths.
* @var \OCP\ICache
*/
private $objectCache;
/** /**
* @param string $path * @param string $path
*/ */
@ -96,18 +104,31 @@ class Swift extends \OC\Files\Storage\Common {
* @param string $path * @param string $path
* @return string * @return string
*/ */
private function getContainerName($path) {
$path = trim(trim($this->root, '/') . "/" . $path, '/.');
return str_replace('/', '\\', $path);
}
/** /**
* Fetches an object from the API.
* If the object is cached already or a
* failed "doesn't exist" response was cached,
* that one will be returned.
*
* @param string $path * @param string $path
* @return \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject|bool object
* or false if the object did not exist
*/ */
private function doesObjectExist($path) { private function fetchObject($path) {
if ($this->objectCache->hasKey($path)) {
// might be "false" if object did not exist from last check
return $this->objectCache->get($path);
}
try { try {
$this->getContainer()->getPartialObject($path); $object = $this->getContainer()->getPartialObject($path);
return true; $this->objectCache->set($path, $object);
return $object;
} catch (ClientErrorResponseException $e) {
// this exception happens when the object does not exist, which
// is expected in most cases
$this->objectCache->set($path, false);
return false;
} catch (ClientErrorResponseException $e) { } catch (ClientErrorResponseException $e) {
// Expected response is "404 Not Found", so only log if it isn't // Expected response is "404 Not Found", so only log if it isn't
if ($e->getResponse()->getStatusCode() !== 404) { if ($e->getResponse()->getStatusCode() !== 404) {
@ -117,6 +138,17 @@ class Swift extends \OC\Files\Storage\Common {
} }
} }
/**
* Returns whether the given path exists.
*
* @param string $path
*
* @return bool true if the object exist, false otherwise
*/
private function doesObjectExist($path) {
return $this->fetchObject($path) !== false;
}
public function __construct($params) { public function __construct($params) {
if ((empty($params['key']) and empty($params['password'])) if ((empty($params['key']) and empty($params['password']))
or empty($params['user']) or empty($params['bucket']) or empty($params['user']) or empty($params['bucket'])
@ -144,6 +176,8 @@ class Swift extends \OC\Files\Storage\Common {
} }
$this->params = $params; $this->params = $params;
// FIXME: private class...
$this->objectCache = new \OC\Cache\CappedMemoryCache();
} }
public function mkdir($path) { public function mkdir($path) {
@ -162,6 +196,9 @@ class Swift extends \OC\Files\Storage\Common {
$metadataHeaders = DataObject::stockHeaders(array()); $metadataHeaders = DataObject::stockHeaders(array());
$allHeaders = $customHeaders + $metadataHeaders; $allHeaders = $customHeaders + $metadataHeaders;
$this->getContainer()->uploadObject($path, '', $allHeaders); $this->getContainer()->uploadObject($path, '', $allHeaders);
// invalidate so that the next access gets the real object
// with all properties
$this->objectCache->remove($path);
} catch (Exceptions\CreateUpdateError $e) { } catch (Exceptions\CreateUpdateError $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
return false; return false;
@ -202,6 +239,7 @@ class Swift extends \OC\Files\Storage\Common {
try { try {
$this->getContainer()->dataObject()->setName($path . '/')->delete(); $this->getContainer()->dataObject()->setName($path . '/')->delete();
$this->objectCache->remove($path . '/');
} catch (Exceptions\DeleteError $e) { } catch (Exceptions\DeleteError $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
return false; return false;
@ -256,7 +294,10 @@ class Swift extends \OC\Files\Storage\Common {
try { try {
/** @var DataObject $object */ /** @var DataObject $object */
$object = $this->getContainer()->getPartialObject($path); $object = $this->fetchObject($path);
if (!$object) {
return false;
}
} catch (ClientErrorResponseException $e) { } catch (ClientErrorResponseException $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
return false; return false;
@ -310,8 +351,12 @@ class Swift extends \OC\Files\Storage\Common {
try { try {
$this->getContainer()->dataObject()->setName($path)->delete(); $this->getContainer()->dataObject()->setName($path)->delete();
$this->objectCache->remove($path);
$this->objectCache->remove($path . '/');
} catch (ClientErrorResponseException $e) { } catch (ClientErrorResponseException $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); if ($e->getResponse()->getStatusCode() !== 404) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
}
return false; return false;
} }
@ -391,8 +436,11 @@ class Swift extends \OC\Files\Storage\Common {
$path .= '/'; $path .= '/';
} }
$object = $this->getContainer()->getPartialObject($path); $object = $this->fetchObject($path);
$object->saveMetadata($metadata); if ($object->saveMetadata($metadata)) {
// invalidate target object to force repopulation on fetch
$this->objectCache->remove($path);
}
return true; return true;
} else { } else {
$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path); $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
@ -400,6 +448,8 @@ class Swift extends \OC\Files\Storage\Common {
$metadataHeaders = DataObject::stockHeaders($metadata); $metadataHeaders = DataObject::stockHeaders($metadata);
$allHeaders = $customHeaders + $metadataHeaders; $allHeaders = $customHeaders + $metadataHeaders;
$this->getContainer()->uploadObject($path, '', $allHeaders); $this->getContainer()->uploadObject($path, '', $allHeaders);
// invalidate target object to force repopulation on fetch
$this->objectCache->remove($path);
return true; return true;
} }
} }
@ -415,8 +465,11 @@ class Swift extends \OC\Files\Storage\Common {
$this->unlink($path2); $this->unlink($path2);
try { try {
$source = $this->getContainer()->getPartialObject($path1); $source = $this->fetchObject($path1);
$source->copy($this->bucket . '/' . $path2); $source->copy($this->bucket . '/' . $path2);
// invalidate target object to force repopulation on fetch
$this->objectCache->remove($path2);
$this->objectCache->remove($path2 . '/');
} catch (ClientErrorResponseException $e) { } catch (ClientErrorResponseException $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
return false; return false;
@ -428,8 +481,11 @@ class Swift extends \OC\Files\Storage\Common {
$this->unlink($path2); $this->unlink($path2);
try { try {
$source = $this->getContainer()->getPartialObject($path1 . '/'); $source = $this->fetchObject($path1 . '/');
$source->copy($this->bucket . '/' . $path2 . '/'); $source->copy($this->bucket . '/' . $path2 . '/');
// invalidate target object to force repopulation on fetch
$this->objectCache->remove($path2);
$this->objectCache->remove($path2 . '/');
} catch (ClientErrorResponseException $e) { } catch (ClientErrorResponseException $e) {
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
return false; return false;
@ -461,10 +517,6 @@ class Swift extends \OC\Files\Storage\Common {
$fileType = $this->filetype($path1); $fileType = $this->filetype($path1);
if ($fileType === 'dir' || $fileType === 'file') { if ($fileType === 'dir' || $fileType === 'file') {
// make way
$this->unlink($path2);
// copy // copy
if ($this->copy($path1, $path2) === false) { if ($this->copy($path1, $path2) === false) {
return false; return false;
@ -564,6 +616,8 @@ class Swift extends \OC\Files\Storage\Common {
} }
$fileData = fopen($tmpFile, 'r'); $fileData = fopen($tmpFile, 'r');
$this->getContainer()->uploadObject(self::$tmpFiles[$tmpFile], $fileData); $this->getContainer()->uploadObject(self::$tmpFiles[$tmpFile], $fileData);
// invalidate target object to force repopulation on fetch
$this->objectCache->remove(self::$tmpFiles[$tmpFile]);
unlink($tmpFile); unlink($tmpFile);
} }