From d51fba7781a1edabdb531b81d0aaab48e2ff46a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= <schiessle@owncloud.com>
Date: Wed, 9 Jan 2013 17:11:46 +0100
Subject: [PATCH] every 1000s run reduce the number of versions for all files,
 not only for the currently edited file

---
 apps/files_versions/lib/versions.php | 151 ++++++++++++++++++---------
 1 file changed, 104 insertions(+), 47 deletions(-)

diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php
index ccbbf3ab2c..e890715b5a 100644
--- a/apps/files_versions/lib/versions.php
+++ b/apps/files_versions/lib/versions.php
@@ -16,16 +16,6 @@ namespace OCA_Versions;
 
 class Storage {
 
-
-	// config.php configuration:
-	//   - files_versions
-	//   - files_versionsfolder
-	//   - files_versionsmaxfilesize
-	//
-	// todo:
-	//   - finish porting to OC_FilesystemView to enable network transparency
-	//   - add transparent compression. first test if it´s worth it.
-
 	const DEFAULTENABLED=true;
 	const DEFAULTMAXSIZE=50; // unit: percentage; 50% of available disk space/quota
 	
@@ -177,14 +167,14 @@ class Storage {
 
 			$files_view = new \OC_FilesystemView('/'.$uid.'/files');
 			$local_file = $files_view->getLocalFile($filename);
-			$versions_fileview = \OCP\Files::getStorage('files_versions');
 
 			foreach( $matches as $ma ) {
 
 				$i++;
 				$versions[$i]['cur'] = 0;
 				$parts = explode( '.v', $ma );
-				$versions[$i]['version'] = ( end( $parts ) );
+				$versions[$i]['version'] = ( end( $parts ) );
+				$versions[$i]['path'] = $filename;
 				$versions[$i]['size'] = $versions_fileview->filesize($filename.'.v'.$versions[$i]['version']);
 
 				// if file with modified date exists, flag it in array as currently enabled version
@@ -226,6 +216,62 @@ class Storage {
 		}
 
 	}
+	
+	/**
+	 * @brief returns all stored file versions from a given user
+	 * @param $uid id to the user
+	 * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename
+	 */
+	
+	private static function getAllVersions($uid) {
+		if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) {
+			$versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions');
+			$versionsRoot = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath('');
+			
+			$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($versionsRoot), \RecursiveIteratorIterator::CHILD_FIRST);
+			
+			$versions = array();
+			
+			foreach ($iterator as $path) {
+				if ( preg_match('/^.+\.v(\d+)$/', $path, $match) ) {
+					$relpath = substr($path, strlen($versionsRoot)-1);
+					$versions[$match[1].'#'.$relpath] = array('path' => $relpath, 'timestamp' => $match[1]);
+				}
+			}
+			
+			ksort($versions);
+			
+			$i = 0;
+			
+			$result = array();
+			$file_count = array();
+			
+			foreach( $versions as $key => $value ) {
+				$i++;
+				$size = $versions_fileview->filesize($value['path']);
+				$filename = substr($value['path'], 0, -strlen($value['timestamp'])-2);
+
+				$result['all'][$i]['version'] = $value['timestamp'];
+				$result['all'][$i]['path'] = $filename;
+				$result['all'][$i]['size'] = $size;
+				
+				if ( key_exists($filename, $file_count) ) {
+					$c = $file_count[$filename] +1;
+					$file_count[$filename] = $c;
+				} else {
+					$file_count[$filename] = 1;
+					$c = 1;
+				}
+				$filename = substr($value['path'], 0, -strlen($value['timestamp'])-2);
+				$result['by_file'][$filename][$c]['version'] = $value['timestamp'];
+				$result['by_file'][$filename][$c]['path'] = $filename;
+				$result['by_file'][$filename][$c]['size'] = $size;
+				
+			}
+
+			return $result;
+		}
+	}
 
 	/**
 	 * @brief Erase a file's versions which exceed the set quota
@@ -248,56 +294,67 @@ class Storage {
 			$free = $quota-$rootInfo['size']; // remaining free space for user
 
 			if ( $free > 0 ) {
-				$availableSpace = 100* self::DEFAULTMAXSIZE / ($quota-$rootInfo['size']); // how much space can be used for versions
+				$availableSpace = 100* self::DEFAULTMAXSIZE / ($free); // how much space can be used for versions
 			} // otherwise available space negative and we need to reach at least 0 at the end of the expiration process;
-			
-			$versions = Storage::getVersions($filename);
-			$versions = array_reverse($versions);	// newest version first
+
+			// 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'];
+			} else {
+				$all_versions = Storage::getVersions($filename);
+				$versions_by_file[$filename] = $all_versions;
+			}
 			
 			$time = time();
-			$numOfVersions = count($versions);
 			
-			$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'];
-			}
+			foreach ($versions_by_file as $filename => $versions) {
+				$versions = array_reverse($versions);	// newest version first
+			
+				$numOfVersions = count($versions);
+				$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'];
+				}
 						
-			$nextVersion = $versions[0]['version'] - $step;
+				$nextVersion = $versions[0]['version'] - $step;
 			
-			for ($i=1; $i<$numOfVersions; $i++) {
-				if ( $nextInterval == -1 || $versions[$i]['version'] >= $nextInterval ) {
-					if ( $versions[$i]['version'] > $nextVersion ) {
-						//distance between two version to small, delete version
-						$versions_fileview->unlink($filename.'.v'.$versions[$i]['version']);
-						$availableSpace += $versions[$i]['size'];
-					} else {
+				for ($i=1; $i<$numOfVersions; $i++) {
+					if ( $nextInterval == -1 || $versions[$i]['version'] >= $nextInterval ) {
+						if ( $versions[$i]['version'] > $nextVersion ) {
+							//distance between two version to small, delete version
+							$versions_fileview->unlink($versions[$i]['path'].'.v'.$versions[$i]['version']);
+							$availableSpace += $versions[$i]['size'];
+						} else {
+							$nextVersion = $versions[$i]['version'] - $step;
+						}
+					} else { // time to move on to the next interval
+						$interval++;
+						$i--; // need to go one version back to check the same version against the new interval.
+						$step = Storage::$max_versions_per_interval[$interval]['step'];
 						$nextVersion = $versions[$i]['version'] - $step;
-					}
-				} else { // time to move on to the next interval
-					$interval++;
-					$step = Storage::$max_versions_per_interval[$interval]['step'];
-					$nextVersion = $versions[$i]['version'] - $step;
-					if ( Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1 ) {
-						$nextInterval = -1;
-					} else {
-						$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
+						if ( Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] == -1 ) {
+							$nextInterval = -1;
+						} else {
+							$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
+						}
 					}
 				}
 			}
-			
 			// check if enough space is available after versions are rearranged
-			$versions = array_reverse( $versions ); // oldest version first
-			$numOfVersions = count($versions);
+			$numOfVersions = count($all_versions);
 			$i = 0; 
 			while ($availableSpace < 0) {
-				$versions_fileview->unlink($filename.'.v'.$versions[$i]['version']);
-				$availableSpace += $versions[$i]['size'];
+				$versions_fileview->unlink($all_versions[$i]['path'].'.v'.$all_versions[$i]['version']);
+				$availableSpace += $all_versions[$i]['size'];
 				$i++;
 				if ($i = $numOfVersions-2) break; // keep at least the last version
 			}
 		}
 	}
-}
\ No newline at end of file
+}
-- 
GitLab