Added server move detection when hashes match or downloading
authorpkanavos <pkanavos@gmail.com>
Tue, 5 Jun 2012 20:42:53 +0000 (23:42 +0300)
committerpkanavos <pkanavos@gmail.com>
Tue, 5 Jun 2012 20:42:53 +0000 (23:42 +0300)
trunk/Pithos.Core/Agents/PollAgent.cs
trunk/Pithos.Core/Agents/StatusAgent.cs
trunk/Pithos.Core/Agents/Uploader.cs
trunk/Pithos.Core/FileState.cs
trunk/Pithos.Network/CloudFilesClient.cs

index ec33aac..14e5d15 100644 (file)
@@ -503,6 +503,7 @@ namespace Pithos.Core.Agents
             //Don't use the tuple info, it may have been deleted\r
             var localInfo = FileInfoExtensions.FromPath(localFilePath);\r
 \r
+\r
             // Local file unchanged? If both C and L are null, make sure it's because \r
             //both the file is missing and the state checksum is not missing\r
             if (tuple.C == tuple.L && (localInfo.Exists || tuple.FileState==null))\r
@@ -512,7 +513,8 @@ namespace Pithos.Core.Agents
                 if (tuple.S == tuple.L)\r
                 {\r
                     // No server changes\r
-                    ;\r
+                    //Has the file been renamed on the server?\r
+                    MoveForServerMove(accountInfo, tuple);\r
                 }\r
                 else\r
                 {\r
@@ -534,17 +536,24 @@ namespace Pithos.Core.Agents
                         {\r
                             //Server file exists\r
                             //downloadServerObject() // Result: L = S\r
-                            StatusKeeper.SetFileState(localFilePath, FileStatus.Modified,\r
+                            //If the file has moved on the server, move it locally before downloading\r
+                            var targetPath=MoveForServerMove(accountInfo,tuple);\r
+\r
+                            StatusKeeper.SetFileState(targetPath, FileStatus.Modified,\r
                                                       FileOverlayStatus.Modified, "");\r
                             NetworkAgent.Downloader.DownloadCloudFile(accountInfo,\r
                                                                             tuple.ObjectInfo,\r
-                                                                            localFilePath, token).Wait(token);\r
+                                                                            targetPath, token).Wait(token);\r
                             //updateRecord( L = S )\r
-                            StatusKeeper.UpdateFileChecksum(localFilePath, tuple.ObjectInfo.ETag,\r
+                            StatusKeeper.UpdateFileChecksum(targetPath, tuple.ObjectInfo.ETag,\r
                                                             tuple.ObjectInfo.X_Object_Hash);\r
 \r
-                            StatusKeeper.SetFileState(localFilePath, FileStatus.Unchanged,\r
+                            StatusKeeper.StoreInfo(targetPath, tuple.ObjectInfo);\r
+\r
+/*\r
+                            StatusKeeper.SetFileState(targetPath, FileStatus.Unchanged,\r
                                                       FileOverlayStatus.Normal, "");\r
+*/\r
                         }\r
                     }\r
                 }\r
@@ -576,10 +585,9 @@ namespace Pithos.Core.Agents
                                                                "Poll", isUnselected);\r
                             NetworkAgent.Uploader.UploadCloudFile(action, token).Wait(token);\r
 \r
-\r
                             //updateRecord( S = C )\r
-                            StatusKeeper.SetFileState(localFilePath, FileStatus.Unchanged,\r
-                                                      FileOverlayStatus.Normal, "");\r
+                            //State updated by the uploader\r
+                            \r
                             if (isUnselected)\r
                             {\r
                                 ProcessChildren(accountInfo, tuple, agent, token);\r
@@ -619,6 +627,30 @@ namespace Pithos.Core.Agents
             }\r
         }\r
 \r
+        private string MoveForServerMove(AccountInfo accountInfo, StateTuple tuple)\r
+        {\r
+            var relativePath = tuple.ObjectInfo.RelativeUrlToFilePath(accountInfo.UserName);\r
+            var serverPath = Path.Combine(accountInfo.AccountPath, relativePath);\r
+\r
+            if (tuple.FilePath == serverPath) return serverPath;\r
+\r
+            if (tuple.FileInfo.Exists)\r
+            {                    \r
+                var fi = tuple.FileInfo as FileInfo;\r
+                if (fi != null)\r
+                    fi.MoveTo(serverPath);\r
+                var di = tuple.FileInfo as DirectoryInfo;\r
+                if (di != null)\r
+                    di.MoveTo(serverPath);\r
+                StatusKeeper.StoreInfo(serverPath, tuple.ObjectInfo);\r
+            }\r
+            else\r
+            {\r
+                Debug.Assert(false, "File does not exist");\r
+            }\r
+            return serverPath;\r
+        }\r
+\r
         private void DeleteCloudFile(AccountInfo accountInfo, StateTuple tuple)\r
         {\r
             StatusKeeper.SetFileState(tuple.FilePath, FileStatus.Deleted,\r
@@ -647,43 +679,56 @@ namespace Pithos.Core.Agents
             IEnumerable<Tuple<FileSystemInfo, string>> files, \r
             IEnumerable<FileState> states)\r
         {\r
-            var dct = new Dictionary<string, StateTuple>();\r
+            var tuplesByPath = new Dictionary<string, StateTuple>();\r
             foreach (var file in files)\r
             {\r
                 var fsInfo = file.Item1;\r
                 var fileHash = fsInfo is DirectoryInfo? MERKLE_EMPTY:file.Item2;\r
 \r
-                dct[fsInfo.FullName] = new StateTuple {FileInfo = fsInfo, MD5 = fileHash};\r
+                tuplesByPath[fsInfo.FullName] = new StateTuple {FileInfo = fsInfo, MD5 = fileHash};\r
             }\r
             foreach (var state in states)\r
             {\r
                 StateTuple hashTuple;\r
-                if (dct.TryGetValue(state.FilePath, out hashTuple))\r
+                if (tuplesByPath.TryGetValue(state.FilePath, out hashTuple))\r
                 {\r
                     hashTuple.FileState = state;\r
                 }\r
                 else\r
                 {\r
                     var fsInfo = FileInfoExtensions.FromPath(state.FilePath);\r
-                    dct[state.FilePath] = new StateTuple {FileInfo = fsInfo, FileState = state};\r
+                    tuplesByPath[state.FilePath] = new StateTuple {FileInfo = fsInfo, FileState = state};\r
                 }\r
             }\r
+\r
+            var tuplesByID = tuplesByPath.Values\r
+                .Where(tuple => tuple.FileState != null && tuple.FileState.ObjectID!=null)\r
+                .ToDictionary(tuple=>tuple.FileState.ObjectID,tuple=>tuple);//new Dictionary<Guid, StateTuple>();\r
+\r
             foreach (var info in infos)\r
             {\r
                 StateTuple hashTuple;\r
                 var filePath = info.Item1;\r
                 var objectInfo = info.Item2;\r
-                if (dct.TryGetValue(filePath, out hashTuple))\r
+                var objectID = objectInfo.UUID;\r
+\r
+                if (tuplesByID.TryGetValue(objectID, out hashTuple))\r
+                {\r
+                    hashTuple.ObjectInfo = objectInfo;                    \r
+                }\r
+                else if (tuplesByPath.TryGetValue(filePath, out hashTuple))\r
                 {\r
                     hashTuple.ObjectInfo = objectInfo;\r
                 }\r
                 else\r
                 {\r
                     var fsInfo = FileInfoExtensions.FromPath(filePath);\r
-                    dct[filePath] = new StateTuple {FileInfo = fsInfo, ObjectInfo = objectInfo};\r
+                    var tuple = new StateTuple {FileInfo = fsInfo, ObjectInfo = objectInfo};\r
+                    tuplesByPath[filePath] = tuple;\r
+                    tuplesByID[objectInfo.UUID] = tuple;\r
                 }\r
             }\r
-            return dct.Values;\r
+            return tuplesByPath.Values;\r
         }\r
 \r
         /// <summary>\r
index b95645d..4a38675 100644 (file)
@@ -661,9 +661,14 @@ namespace Pithos.Core.Agents
                 using (var connection = GetConnection())
                 using (var command = new SQLiteCommand(connection))
                 {
-                    if (StateExists(path, connection))
+                    //If the ID exists, update the status
+                    if (StateExistsByID(objectInfo.UUID,connection))
                         command.CommandText =
-                            "update FileState set FileStatus= :fileStatus where FilePath = :path  COLLATE NOCASE ";
+                            "update FileState set FilePath=:path,FileStatus= :fileStatus, Checksum=:checksum, ShortHash=:shortHash,Version=:version,VersionTimeStamp=:versionTimeStamp where ObjectID = :objectID  ";                        
+                    else if (StateExists(path, connection))
+                        //If the ID doesn't exist, try to update using the path, and store the ID as well.
+                        command.CommandText =
+                            "update FileState set FileStatus= :fileStatus, ObjectID=:objectID, Checksum=:checksum, ShortHash=:shortHash,Version=:version,VersionTimeStamp=:versionTimeStamp where FilePath = :path  COLLATE NOCASE ";
                     else
                     {
                         command.CommandText =
@@ -702,6 +707,17 @@ namespace Pithos.Core.Agents
 
         }
 
+        private bool StateExistsByID(string objectId,SQLiteConnection connection)
+        {
+            using (var command = new SQLiteCommand("Select count(*) from FileState where ObjectId=:id", connection))
+            {
+                command.Parameters.AddWithValue("id", objectId);
+                var result = command.ExecuteScalar();
+                return ((long)result >= 1);
+            }
+
+        }
+
         public void SetFileStatus(string path, FileStatus status)
         {
             if (String.IsNullOrWhiteSpace(path))
index 136bbbf..ea5f429 100644 (file)
@@ -183,8 +183,15 @@ namespace Pithos.Core.Agents
 \r
                                 await UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash,cancellationToken);\r
                             }\r
-                            //If everything succeeds, change the file and overlay status to normal\r
-                            StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");\r
+                            var currentInfo=client.GetObjectInfo(cloudFile.Account, cloudFile.Container, cloudFile.Name);\r
+                            //If there is no stored ObjectID in the file state, add it\r
+                            if (action.FileState == null || action.FileState.ObjectID == null)\r
+                            {\r
+                                StatusKeeper.StoreInfo(fullFileName,currentInfo);\r
+                            }\r
+                            else                             \r
+                                //If everything succeeds, change the file and overlay status to normal\r
+                                StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");\r
                         }\r
                         catch (WebException exc)\r
                         {\r
index 5e2ef27..833881c 100644 (file)
@@ -73,6 +73,7 @@ namespace Pithos.Core
 
         
         //[Property(Unique = true, UniqueKey = "IX_FileState_ObjectID")]
+        [Property]
         public string ObjectID { get; set; }
 
         [Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
index 61f1641..a1e0b99 100644 (file)
@@ -789,6 +789,7 @@ namespace Pithos.Network
                                                    Container = container,
                                                    Name = objectName,
                                                    ETag = client.GetHeaderValue("ETag"),
+                                                   UUID=client.GetHeaderValue("X-Object-UUID"),
                                                    X_Object_Hash = client.GetHeaderValue("X-Object-Hash"),
                                                    Content_Type = client.GetHeaderValue("Content-Type"),
                                                    Bytes = Convert.ToInt64(client.GetHeaderValue("Content-Length",true)),