summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorris Jobke <hey@morrisjobke.de>2015-02-12 10:53:00 +0100
committerMorris Jobke <hey@morrisjobke.de>2015-02-12 10:53:00 +0100
commitf4182d2dc9380015a1dadc6be40a731b8092c971 (patch)
treeacdddd75e9f403a42c9d7b7ee9bdf50596b670c9
parent64216971d447cfe45a3b11cc8c6cf2a67426d7c0 (diff)
parent9df18ffe75589ecf45bf6802dae1024ce3f03785 (diff)
downloadnextcloud-server-f4182d2dc9380015a1dadc6be40a731b8092c971.tar.gz
nextcloud-server-f4182d2dc9380015a1dadc6be40a731b8092c971.zip
Merge pull request #10993 from owncloud/scanner-reuse-fileid
Reuse known fileids and cache data in the scanner
-rw-r--r--lib/private/files/cache/cache.php3
-rw-r--r--lib/private/files/cache/scanner.php194
2 files changed, 124 insertions, 73 deletions
diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php
index 06a884072e0..48b2b5c024b 100644
--- a/lib/private/files/cache/cache.php
+++ b/lib/private/files/cache/cache.php
@@ -193,6 +193,9 @@ class Cache {
$file['size'] = $file['unencrypted_size'];
}
$file['permissions'] = (int)$file['permissions'];
+ $file['mtime'] = (int)$file['mtime'];
+ $file['storage_mtime'] = (int)$file['storage_mtime'];
+ $file['size'] = 0 + $file['size'];
}
return $files;
} else {
diff --git a/lib/private/files/cache/scanner.php b/lib/private/files/cache/scanner.php
index 4778a6803ba..d369398af1c 100644
--- a/lib/private/files/cache/scanner.php
+++ b/lib/private/files/cache/scanner.php
@@ -80,7 +80,8 @@ class Scanner extends BasicEmitter {
* @return array an array of metadata of the file
*/
public function getData($path) {
- if (!$this->storage->isReadable($path)) {
+ $permissions = $this->storage->getPermissions($path);
+ if (!$permissions & \OCP\PERMISSION_READ) {
//cant read, nothing we can do
\OCP\Util::writeLog('OC\Files\Cache\Scanner', "!!! Path '$path' is not accessible or present !!!", \OCP\Util::DEBUG);
return null;
@@ -95,7 +96,7 @@ class Scanner extends BasicEmitter {
}
$data['etag'] = $this->storage->getETag($path);
$data['storage_mtime'] = $data['mtime'];
- $data['permissions'] = $this->storage->getPermissions($path);
+ $data['permissions'] = $permissions;
return $data;
}
@@ -104,9 +105,11 @@ class Scanner extends BasicEmitter {
*
* @param string $file
* @param int $reuseExisting
+ * @param int $parentId
+ * @param array | null $cacheData existing data in the cache for the file to be scanned
* @return array an array of metadata of the scanned file
*/
- public function scanFile($file, $reuseExisting = 0) {
+ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null) {
if (!self::isPartialFile($file)
and !Filesystem::isFileBlacklisted($file)
) {
@@ -118,7 +121,9 @@ class Scanner extends BasicEmitter {
if ($parent === '.' or $parent === '/') {
$parent = '';
}
- $parentId = $this->cache->getId($parent);
+ if ($parentId === -1) {
+ $parentId = $this->cache->getId($parent);
+ }
// scan the parent if it's not in the cache (id -1) and the current file is not the root folder
if ($file and $parentId === -1) {
@@ -128,14 +133,18 @@ class Scanner extends BasicEmitter {
if ($parent) {
$data['parent'] = $parentId;
}
- $cacheData = $this->cache->get($file);
- if ($cacheData and $reuseExisting) {
+ if (is_null($cacheData)) {
+ $cacheData = $this->cache->get($file);
+ }
+ if ($cacheData and $reuseExisting and isset($cacheData['fileid'])) {
// prevent empty etag
if (empty($cacheData['etag'])) {
$etag = $data['etag'];
} else {
$etag = $cacheData['etag'];
}
+ $fileId = $cacheData['fileid'];
+ $data['fileid'] = $fileId;
// only reuse data if the file hasn't explicitly changed
if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) {
$data['mtime'] = $cacheData['mtime'];
@@ -150,9 +159,10 @@ class Scanner extends BasicEmitter {
$newData = array_diff_assoc($data, $cacheData);
} else {
$newData = $data;
+ $fileId = -1;
}
if (!empty($newData)) {
- $data['fileid'] = $this->addToCache($file, $newData);
+ $data['fileid'] = $this->addToCache($file, $newData, $fileId);
$this->emit('\OC\Files\Cache\Scanner', 'postScanFile', array($file, $this->storageId));
\OC_Hook::emit('\OC\Files\Cache\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId));
}
@@ -175,13 +185,19 @@ class Scanner extends BasicEmitter {
/**
* @param string $path
* @param array $data
+ * @param int $fileId
* @return int the id of the added file
*/
- protected function addToCache($path, $data) {
+ protected function addToCache($path, $data, $fileId = -1) {
\OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data));
$this->emit('\OC\Files\Cache\Scanner', 'addToCache', array($path, $this->storageId, $data));
if ($this->cacheActive) {
- return $this->cache->put($path, $data);
+ if ($fileId !== -1) {
+ $this->cache->update($fileId, $data);
+ return $fileId;
+ } else {
+ return $this->cache->put($path, $data);
+ }
} else {
return -1;
}
@@ -190,12 +206,17 @@ class Scanner extends BasicEmitter {
/**
* @param string $path
* @param array $data
+ * @param int $fileId
*/
- protected function updateCache($path, $data) {
+ protected function updateCache($path, $data, $fileId = -1) {
\OC_Hook::emit('Scanner', 'addToCache', array('file' => $path, 'data' => $data));
$this->emit('\OC\Files\Cache\Scanner', 'updateCache', array($path, $this->storageId, $data));
if ($this->cacheActive) {
- $this->cache->put($path, $data);
+ if ($fileId !== -1) {
+ $this->cache->update($fileId, $data);
+ } else {
+ $this->cache->put($path, $data);
+ }
}
}
@@ -212,97 +233,124 @@ class Scanner extends BasicEmitter {
$reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG;
}
$data = $this->scanFile($path, $reuse);
- if ($data !== null) {
- $size = $this->scanChildren($path, $recursive, $reuse);
+ if ($data and $data['mimetype'] === 'httpd/unix-directory') {
+ $size = $this->scanChildren($path, $recursive, $reuse, $data);
$data['size'] = $size;
}
return $data;
}
- protected function getExistingChildren($path) {
+ /**
+ * Get the children currently in the cache
+ *
+ * @param int $folderId
+ * @return array[]
+ */
+ protected function getExistingChildren($folderId) {
$existingChildren = array();
- if ($this->cache->inCache($path)) {
- $children = $this->cache->getFolderContents($path);
- foreach ($children as $child) {
- $existingChildren[] = $child['name'];
- }
+ $children = $this->cache->getFolderContentsById($folderId);
+ foreach ($children as $child) {
+ $existingChildren[$child['name']] = $child;
}
return $existingChildren;
}
/**
+ * Get the children from the storage
+ *
+ * @param string $folder
+ * @return string[]
+ */
+ protected function getNewChildren($folder) {
+ $children = array();
+ if ($dh = $this->storage->opendir($folder)) {
+ if (is_resource($dh)) {
+ while (($file = readdir($dh)) !== false) {
+ if (!Filesystem::isIgnoredDir($file)) {
+ $children[] = $file;
+ }
+ }
+ }
+ }
+ return $children;
+ }
+
+ /**
* scan all the files and folders in a folder
*
* @param string $path
* @param bool $recursive
* @param int $reuse
+ * @param array $folderData existing cache data for the folder to be scanned
* @return int the size of the scanned folder or -1 if the size is unknown at this stage
*/
- public function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1) {
+ protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderData = null) {
if ($reuse === -1) {
$reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG;
}
$this->emit('\OC\Files\Cache\Scanner', 'scanFolder', array($path, $this->storageId));
$size = 0;
$childQueue = array();
- $existingChildren = $this->getExistingChildren($path);
- $newChildren = array();
- if ($this->storage->is_dir($path) && ($dh = $this->storage->opendir($path))) {
- $exceptionOccurred = false;
- if ($this->useTransactions) {
- \OC_DB::beginTransaction();
- }
- if (is_resource($dh)) {
- while (($file = readdir($dh)) !== false) {
- $child = ($path) ? $path . '/' . $file : $file;
- if (!Filesystem::isIgnoredDir($file)) {
- $newChildren[] = $file;
- try {
- $data = $this->scanFile($child, $reuse);
- if ($data) {
- if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE) {
- $childQueue[] = $child;
- } else if ($data['size'] === -1) {
- $size = -1;
- } else if ($size !== -1) {
- $size += $data['size'];
- }
- }
- } catch (\Doctrine\DBAL\DBALException $ex) {
- // might happen if inserting duplicate while a scanning
- // process is running in parallel
- // log and ignore
- \OC_Log::write('core', 'Exception while scanning file "' . $child . '": ' . $ex->getMessage(), \OC_Log::DEBUG);
- $exceptionOccurred = true;
- }
+ if (is_array($folderData) and isset($folderData['fileid'])) {
+ $folderId = $folderData['fileid'];
+ } else {
+ $folderId = $this->cache->getId($path);
+ }
+ $existingChildren = $this->getExistingChildren($folderId);
+ $newChildren = $this->getNewChildren($path);
+
+ if ($this->useTransactions) {
+ \OC_DB::beginTransaction();
+ }
+ $exceptionOccurred = false;
+ foreach ($newChildren as $file) {
+ $child = ($path) ? $path . '/' . $file : $file;
+ try {
+ $existingData = isset($existingChildren[$file]) ? $existingChildren[$file] : null;
+ $data = $this->scanFile($child, $reuse, $folderId, $existingData);
+ if ($data) {
+ if ($data['mimetype'] === 'httpd/unix-directory' and $recursive === self::SCAN_RECURSIVE) {
+ $childQueue[$child] = $data;
+ } else if ($data['size'] === -1) {
+ $size = -1;
+ } else if ($size !== -1) {
+ $size += $data['size'];
}
}
+ } catch (\Doctrine\DBAL\DBALException $ex) {
+ // might happen if inserting duplicate while a scanning
+ // process is running in parallel
+ // log and ignore
+ \OC_Log::write('core', 'Exception while scanning file "' . $child . '": ' . $ex->getMessage(), \OC_Log::DEBUG);
+ $exceptionOccurred = true;
}
- $removedChildren = \array_diff($existingChildren, $newChildren);
- foreach ($removedChildren as $childName) {
- $child = ($path) ? $path . '/' . $childName : $childName;
- $this->removeFromCache($child);
- }
- if ($this->useTransactions) {
- \OC_DB::commit();
- }
- if ($exceptionOccurred) {
- // It might happen that the parallel scan process has already
- // inserted mimetypes but those weren't available yet inside the transaction
- // To make sure to have the updated mime types in such cases,
- // we reload them here
- $this->cache->loadMimetypes();
- }
+ }
+ $removedChildren = \array_diff(array_keys($existingChildren), $newChildren);
+ foreach ($removedChildren as $childName) {
+ $child = ($path) ? $path . '/' . $childName : $childName;
+ $this->removeFromCache($child);
+ }
+ if ($this->useTransactions) {
+ \OC_DB::commit();
+ }
+ if ($exceptionOccurred) {
+ // It might happen that the parallel scan process has already
+ // inserted mimetypes but those weren't available yet inside the transaction
+ // To make sure to have the updated mime types in such cases,
+ // we reload them here
+ $this->cache->loadMimetypes();
+ }
- foreach ($childQueue as $child) {
- $childSize = $this->scanChildren($child, self::SCAN_RECURSIVE, $reuse);
- if ($childSize === -1) {
- $size = -1;
- } else if ($size !== -1) {
- $size += $childSize;
- }
+ foreach ($childQueue as $child => $childData) {
+ $childSize = $this->scanChildren($child, self::SCAN_RECURSIVE, $reuse, $childData);
+ if ($childSize === -1) {
+ $size = -1;
+ } else if ($size !== -1) {
+ $size += $childSize;
}
- $this->updateCache($path, array('size' => $size));
+ }
+ if (!is_array($folderData) or !isset($folderData['size']) or $folderData['size'] !== $size) {
+ $this->updateCache($path, array('size' => $size), $folderId);
}
$this->emit('\OC\Files\Cache\Scanner', 'postScanFolder', array($path, $this->storageId));
return $size;