From e15914316521bb6d9d791926434c9c6ce5b0aa91 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 25 Jun 2013 09:39:01 +0200 Subject: [PATCH] call expire function before writing the new version to make sure to have enough free space --- apps/files_versions/lib/versions.php | 159 ++++++++++++++++----------- 1 file changed, 96 insertions(+), 63 deletions(-) diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index 2f8262475b4..6abf278ddab 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -113,6 +113,18 @@ class Storage { mkdir($versionsFolderName.'/'.$info['dirname'], 0750, true); } + $versionsSize = self::getVersionsSize($uid); + if ( $versionsSize === false || $versionsSize < 0 ) { + $versionsSize = self::calculateSize($uid); + } + + // assumptgion: we need filesize($filename) for the new version + + // some more free space for the modified file which might be + // 1.5 times as large as the current version -> 2.5 + $neededSpace = $files_view->filesize($filename) * 2.5; + + $versionsSize = self::expire($filename, $versionsSize, $neededSpace); + // disable proxy to prevent multiple fopen calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; @@ -123,11 +135,6 @@ class Storage { // reset proxy state \OC_FileProxy::$enabled = $proxyStatus; - $versionsSize = self::getVersionsSize($uid); - if ( $versionsSize === false || $versionsSize < 0 ) { - $versionsSize = self::calculateSize($uid); - } - $versionsSize += $users_view->filesize('files'.$filename); // expire old revisions if necessary @@ -175,12 +182,12 @@ class Storage { if ($files_view->file_exists($newpath)) { return self::store($new_path); } - + $abs_newpath = $versions_view->getLocalFile($newpath); if ( $files_view->is_dir($oldpath) && $versions_view->is_dir($oldpath) ) { $versions_view->rename($oldpath, $newpath); - } else if ( ($versions = Storage::getVersions($uid, $oldpath)) ) { + } else if ( ($versions = Storage::getVersions($uid, $oldpath)) ) { $info=pathinfo($abs_newpath); if(!file_exists($info['dirname'])) mkdir($info['dirname'], 0750, true); foreach ($versions as $v) { @@ -391,7 +398,7 @@ class Storage { /** * @brief Erase a file's versions which exceed the set quota */ - private static function expire($filename, $versionsSize = null) { + private static function expire($filename, $versionsSize = null, $offset = 0) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); @@ -424,76 +431,39 @@ class Storage { $rootInfo = $files_view->getFileInfo('/'); $free = $quota-$rootInfo['size']; // remaining free space for user if ( $free > 0 ) { - $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions + $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - ($versionsSize + $offset); // how much space can be used for versions } else { - $availableSpace = $free-$versionsSize; + $availableSpace = $free - $versionsSize - $offset; } } else { - $availableSpace = $quota; + $availableSpace = $quota - $offset; } // after every 1000s run reduce the number of all versions not only for the current file $random = rand(0, 1000); if ($random == 0) { - $result = Storage::getAllVersions($uid); - $versions_by_file = $result['by_file']; - $all_versions = $result['all']; + $allFiles = true; } else { - $all_versions = Storage::getVersions($uid, $filename); - $versions_by_file[$filename] = $all_versions; + $allFiles = false; } - $time = time(); + $all_versions = Storage::getVersions($uid, $filename); + $versions_by_file[$filename] = $all_versions; - // it is possible to expire versions from more than one file - // iterate through all given files - foreach ($versions_by_file as $filename => $versions) { - $versions = array_reverse($versions); // newest version first + $sizeOfDeletedVersions = self::delOldVersions($versions_by_file, $all_versions, $versions_fileview); + $availableSpace = $availableSpace + $sizeOfDeletedVersions; + $versionsSize = $versionsSize - $sizeOfDeletedVersions; - $interval = 1; - $step = Storage::$max_versions_per_interval[$interval]['step']; - if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) { - $nextInterval = -1; - } else { - $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter']; - } + // if still not enough free space we rearrange the versions from all files + if ($availableSpace < 0 || $allFiles) { + $result = Storage::getAllVersions($uid); + $versions_by_file = $result['by_file']; + $all_versions = $result['all']; - $firstVersion = reset($versions); - $firstKey = key($versions); - $prevTimestamp = $firstVersion['version']; - $nextVersion = $firstVersion['version'] - $step; - $remaining_versions[$firstKey] = $firstVersion; - unset($versions[$firstKey]); - - foreach ($versions as $key => $version) { - $newInterval = true; - while ( $newInterval ) { - if ( $nextInterval == -1 || $version['version'] >= $nextInterval ) { - if ( $version['version'] > $nextVersion ) { - //distance between two version too small, delete version - $versions_fileview->unlink($version['path'].'.v'.$version['version']); - $availableSpace += $version['size']; - $versionsSize -= $version['size']; - unset($all_versions[$key]); // update array with all versions - } else { - $nextVersion = $version['version'] - $step; - } - $newInterval = false; // version checked so we can move to the next one - } else { // time to move on to the next interval - $interval++; - $step = Storage::$max_versions_per_interval[$interval]['step']; - $nextVersion = $prevTimestamp - $step; - if ( Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1 ) { - $nextInterval = -1; - } else { - $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter']; - } - $newInterval = true; // we changed the interval -> check same version with new interval - } - } - $prevTimestamp = $version['version']; - } + $sizeOfDeletedVersions = self::delOldVersions($versions_by_file, $all_versions, $versions_fileview); + $availableSpace = $availableSpace + $sizeOfDeletedVersions; + $versionsSize = $versionsSize - $sizeOfDeletedVersions; } // Check if enough space is available after versions are rearranged. @@ -513,4 +483,67 @@ class Storage { return false; } + + /** + * @brief delete old version from a given list of versions + * + * @param array $versions_by_file list of versions ordered by files + * @param array $all_versions all versions accross multiple files + * @param $versions_fileview OC\Files\View on data/user/files_versions + * @return size of releted versions + */ + private static function delOldVersions($versions_by_file, &$all_versions, $versions_fileview) { + + $time = time(); + $size = 0; + + // delete old versions for every given file + foreach ($versions_by_file as $versions) { + $versions = array_reverse($versions); // newest version first + + $interval = 1; + $step = Storage::$max_versions_per_interval[$interval]['step']; + if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) { + $nextInterval = -1; + } else { + $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter']; + } + + $firstVersion = reset($versions); + $firstKey = key($versions); + $prevTimestamp = $firstVersion['version']; + $nextVersion = $firstVersion['version'] - $step; + unset($versions[$firstKey]); + + foreach ($versions as $key => $version) { + $newInterval = true; + while ($newInterval) { + if ($nextInterval == -1 || $version['version'] >= $nextInterval) { + if ($version['version'] > $nextVersion) { + //distance between two version too small, delete version + $versions_fileview->unlink($version['path'] . '.v' . $version['version']); + $size += $version['size']; + unset($all_versions[$key]); // update array with all versions + } else { + $nextVersion = $version['version'] - $step; + } + $newInterval = false; // version checked so we can move to the next one + } else { // time to move on to the next interval + $interval++; + $step = Storage::$max_versions_per_interval[$interval]['step']; + $nextVersion = $prevTimestamp - $step; + if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1) { + $nextInterval = -1; + } else { + $nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter']; + } + $newInterval = true; // we changed the interval -> check same version with new interval + } + } + $prevTimestamp = $version['version']; + } + } + return $size; + } + } -- 2.39.5