Ejemplo n.º 1
0
        private PathScanResults <string> DoWork(Models.Backup backup, CancellationToken cancellationToken)
        {
            PathScanResults <string> results = new PathScanResults <string>();

            BackupedFileRepository daoBackupedFile = new BackupedFileRepository();

            // Load pending `BackupedFiles` from `Backup`.
            IList <Models.BackupedFile> pendingFiles = daoBackupedFile.GetByBackupAndStatus(backup,
                                                                                                                    // 1. We don't want to resume a file that FAILED.
                                                                                                                    // 2. We don't want to resume a file that was CANCELED.
                                                                                                                    // Theoretically, a CANCELED file should never be present in a backup that is still RUNNING,
                                                                                                                    // unless the backup was being CANCELED, and the PlanExecutor was terminated by some reason
                                                                                                                    // after it updated the files as CANCELED but before it got the chance to update the backup
                                                                                                                    // itself as CANCELED.
                                                                                            TransferStatus.STOPPED, // The transfer didn't begin.
                                                                                            TransferStatus.RUNNING  // The transfer did begin but did not complete.
                                                                                            );

            cancellationToken.ThrowIfCancellationRequested();

            // Convert them to a list of paths.
            results.Files = pendingFiles.ToLinkedList <string, Models.BackupedFile>(p => p.File.Path);

            return(results);
        }
Ejemplo n.º 2
0
        // May throw System.SystemException
        private void PopulateFile(BackupPlanPathNode pathNode)
        {
            Assert.AreEqual(EntryType.FILE, pathNode.Type);

#if true
            BackupedFileRepository     dao           = new BackupedFileRepository();
            IList <BackupedFile>       backupedFiles = dao.GetCompletedByStorageAccountAndPath(pathNode.StorageAccount, pathNode.Path);
            IEnumerable <IFileVersion> versions      =
                from f in backupedFiles
                select new FileVersion {
                Name = f.VersionName, Version = f.Version
            };
#else
            // *** DO NOT USE THIS APPROACH BECAUSE IT WILL NOT SHOW A BACKUP VERSION THAT HAS JUST BEEN CREATED! ***
            // I know it's a problem related to the NHibernate caching mechanism, but I don't want to deal with it right now. Sorry! :-)
            IList <BackupedFile>       backupedFiles = pathNode.PlanFile.Versions;
            IEnumerable <IFileVersion> versions      =
                from f in backupedFiles
                where f.TransferStatus == Storage.TransferStatus.COMPLETED
                select new FileVersion {
                Name = f.Backup.VersionName, Version = f.Backup.Version
            };
#endif
            foreach (IFileVersion version in versions)
            {
                BackupPlanTreeNode versionNode = AddFileVersionNode(pathNode, version);
            }
        }
Ejemplo n.º 3
0
        protected void RegisterResultsEventHandlers(Models.Restore restore, TransferResults results)
        {
            RestoredFileRepository daoRestoredFile = new RestoredFileRepository();
            BackupedFileRepository daoBackupedFile = new BackupedFileRepository();

            results.Failed += (object sender, TransferFileProgressArgs args) =>
            {
                Models.RestoredFile restoredFile = daoRestoredFile.GetByRestoreAndPath(restore, args.FilePath);
                restoredFile.TransferStatus = TransferStatus.FAILED;
                restoredFile.UpdatedAt      = DateTime.UtcNow;
                daoRestoredFile.Update(restoredFile);

                var message = string.Format("Failed {0} - {1}", args.FilePath, args.Exception != null ? args.Exception.Message : "Unknown reason");
                Warn(message);
                //StatusInfo.Update(BackupStatusLevel.ERROR, message);
                OnUpdate(new RestoreOperationEvent {
                    Status = RestoreOperationStatus.Updated, Message = message, TransferStatus = TransferStatus.FAILED
                });
            };
            results.Canceled += (object sender, TransferFileProgressArgs args) =>
            {
                Models.RestoredFile restoredFile = daoRestoredFile.GetByRestoreAndPath(restore, args.FilePath);
                restoredFile.TransferStatus = TransferStatus.CANCELED;
                restoredFile.UpdatedAt      = DateTime.UtcNow;
                daoRestoredFile.Update(restoredFile);

                var message = string.Format("Canceled {0} - {1}", args.FilePath, args.Exception != null ? args.Exception.Message : "Unknown reason");
                Warn(message);
                //StatusInfo.Update(BackupStatusLevel.ERROR, message);
                OnUpdate(new RestoreOperationEvent {
                    Status = RestoreOperationStatus.Updated, Message = message, TransferStatus = TransferStatus.CANCELED
                });
            };
            results.Completed += (object sender, TransferFileProgressArgs args) =>
            {
                Models.RestoredFile restoredFile = daoRestoredFile.GetByRestoreAndPath(restore, args.FilePath);
                restoredFile.TransferStatus = TransferStatus.COMPLETED;
                restoredFile.UpdatedAt      = DateTime.UtcNow;
                daoRestoredFile.Update(restoredFile);

                // Only set original modified date if the restored file is the latest version whose transfer is completed,
                // otherwise, keep the date the OS/filesystem gave it.
                bool isLatestVersion = daoBackupedFile.IsLatestVersion(restoredFile.BackupedFile);
                if (isLatestVersion)
                {
                    // Set original LastWriteTime so this file won't be erroneously included in the next Backup.
                    FileManager.SafeSetFileLastWriteTimeUtc(restoredFile.File.Path, restoredFile.BackupedFile.FileLastWrittenAt);
                }
                else
                {
                    // Keep the original LastWriteTime so this file will be included in the next backup.
                }

                var message = string.Format("Completed {0}", args.FilePath);
                Info(message);
                OnUpdate(new RestoreOperationEvent {
                    Status = RestoreOperationStatus.Updated, Message = message, TransferStatus = TransferStatus.COMPLETED
                });
            };
            results.Started += (object sender, TransferFileProgressArgs args) =>
            {
                Models.RestoredFile restoredFile = daoRestoredFile.GetByRestoreAndPath(restore, args.FilePath);
                restoredFile.TransferStatus = TransferStatus.RUNNING;
                restoredFile.UpdatedAt      = DateTime.UtcNow;
                daoRestoredFile.Update(restoredFile);

                var message = string.Format("Started {0}", args.FilePath);
                Info(message);
                OnUpdate(new RestoreOperationEvent {
                    Status = RestoreOperationStatus.Updated, Message = message
                });
            };
            results.Progress += (object sender, TransferFileProgressArgs args) =>
            {
#if DEBUG
                var message = string.Format("Progress {0}% {1} ({2}/{3} bytes)",
                                            args.PercentDone, args.FilePath, args.TransferredBytes, args.TotalBytes);
                //Info(message);
#endif
                OnUpdate(new RestoreOperationEvent {
                    Status = RestoreOperationStatus.Updated, Message = null
                });
            };
        }
Ejemplo n.º 4
0
        private void Save(ISession session)
        {
            Assert.IsFalse(IsSaved);

            BatchProcessor               batchProcessor        = new BatchProcessor();
            BackupRepository             daoBackup             = new BackupRepository(session);
            BackupPlanFileRepository     daoBackupPlanFile     = new BackupPlanFileRepository(session);
            BackupedFileRepository       daoBackupedFile       = new BackupedFileRepository(session);
            BackupPlanPathNodeRepository daoBackupPlanPathNode = new BackupPlanPathNodeRepository(session);

#if false
            var FilesToTrack          = SuppliedFiles.Union(ChangeSet.DeletedFiles);
            var FilesToInsertOrUpdate =
                from f in FilesToTrack
                where
                // Keep it so we'll later add or update a `BackupedFile`.
                ((f.LastStatus == Models.BackupFileStatus.ADDED || f.LastStatus == Models.BackupFileStatus.MODIFIED))
                // Keep it if `LastStatus` is different from `PreviousLastStatus`.
                || ((f.LastStatus == Models.BackupFileStatus.REMOVED || f.LastStatus == Models.BackupFileStatus.DELETED) && (f.LastStatus != f.PreviousLastStatus))
                // Skip all UNCHANGED files.
                select f;
#else
            var FilesToTrack          = SuppliedFiles;
            var FilesToInsertOrUpdate =
                from f in FilesToTrack
                where
                // Keep it so we'll later add or update a `BackupedFile`.
                ((f.LastStatus == Models.BackupFileStatus.ADDED || f.LastStatus == Models.BackupFileStatus.MODIFIED))
                // Skip all UNCHANGED/DELETED/REMOVED files.
                select f;
#endif

            BlockPerfStats stats = new BlockPerfStats();

            using (ITransaction tx = session.BeginTransaction())
            {
                try
                {
                    // ------------------------------------------------------------------------------------

                    stats.Begin("STEP 1");

                    BackupPlanPathNodeCreator pathNodeCreator = new BackupPlanPathNodeCreator(daoBackupPlanPathNode, tx);

                    // 1 - Split path into its components and INSERT new path nodes if they don't exist yet.
                    foreach (Models.BackupPlanFile entry in FilesToInsertOrUpdate)
                    {
                        // Throw if the operation was canceled.
                        CancellationToken.ThrowIfCancellationRequested();

                        try
                        {
                            entry.PathNode = pathNodeCreator.CreateOrUpdatePathNodes(Backup.BackupPlan.StorageAccount, entry);
                        }
                        catch (Exception ex)
                        {
                            string message = string.Format("BUG: Failed to create/update {0} => {1}",
                                                           typeof(Models.BackupPlanPathNode).Name,
                                                           CustomJsonSerializer.SerializeObject(entry, 1));

                            Results.OnError(this, message);
                            logger.Log(LogLevel.Error, ex, message);

                            throw;
                        }

                        batchProcessor.ProcessBatch(session);
                    }

                    batchProcessor.ProcessBatch(session, true);
                    stats.End();

                    // ------------------------------------------------------------------------------------

                    stats.Begin("STEP 2");

                    // 2 - Insert/Update `BackupPlanFile`s as necessary.
                    foreach (Models.BackupPlanFile entry in FilesToInsertOrUpdate)
                    {
                        // Throw if the operation was canceled.
                        CancellationToken.ThrowIfCancellationRequested();

                        // IMPORTANT: It's important that we guarantee the referenced `BackupPlanFile` has a valid `Id`
                        // before we reference it elsewhere, otherwise NHibernate won't have a valid value to put on
                        // the `backup_plan_file_id` column.
                        try
                        {
                            daoBackupPlanFile.InsertOrUpdate(tx, entry);                             // Guarantee it's saved
                        }
                        catch (Exception ex)
                        {
                            string message = string.Format("BUG: Failed to insert/update {0} => {1}",
                                                           typeof(Models.BackupPlanFile).Name,
                                                           CustomJsonSerializer.SerializeObject(entry, 1));

                            Results.OnError(this, message);
                            logger.Log(LogLevel.Error, ex, message);

                            logger.Error("Dump of failed object: {0}", entry.DumpMe());
                            throw;
                        }

                        batchProcessor.ProcessBatch(session);
                    }

                    batchProcessor.ProcessBatch(session, true);
                    stats.End();

                    // ------------------------------------------------------------------------------------

                    stats.Begin("STEP 3");

                    // 3 - Insert/Update `BackupedFile`s as necessary and add them to the `Backup`.
                    //List<Models.BackupedFile> backupedFiles = new List<Models.BackupedFile>(FilesToInsertOrUpdate.Count());

                    foreach (Models.BackupPlanFile entry in FilesToInsertOrUpdate)
                    {
                        // Throw if the operation was canceled.
                        CancellationToken.ThrowIfCancellationRequested();

                        Models.BackupedFile backupedFile = daoBackupedFile.GetByBackupAndPath(Backup, entry.Path);
                        if (backupedFile == null)                         // If we're resuming, this should already exist.
                        {
                            // Create `BackupedFile`.
                            backupedFile = new Models.BackupedFile(Backup, entry);
                        }
                        backupedFile.FileSize          = entry.LastSize;
                        backupedFile.FileStatus        = entry.LastStatus;
                        backupedFile.FileLastWrittenAt = entry.LastWrittenAt;
                        backupedFile.FileLastChecksum  = entry.LastChecksum;
                        switch (entry.LastStatus)
                        {
                        default:
                            backupedFile.TransferStatus = default(TransferStatus);
                            break;

                        case Models.BackupFileStatus.REMOVED:
                        case Models.BackupFileStatus.DELETED:
                            backupedFile.TransferStatus = TransferStatus.COMPLETED;
                            break;
                        }
                        backupedFile.UpdatedAt = DateTime.UtcNow;

                        try
                        {
                            daoBackupedFile.InsertOrUpdate(tx, backupedFile);
                        }
                        catch (Exception ex)
                        {
                            logger.Log(LogLevel.Error, ex, "BUG: Failed to insert/update {0} => {1}",
                                       typeof(Models.BackupedFile).Name,
                                       CustomJsonSerializer.SerializeObject(backupedFile, 1));
                            throw;
                        }

                        //backupedFiles.Add(backupedFile);

                        batchProcessor.ProcessBatch(session);
                    }

                    batchProcessor.ProcessBatch(session, true);
                    stats.End();

                    // ------------------------------------------------------------------------------------

                    stats.Begin("STEP 4");

                    // 4 - Update all `BackupPlanFile`s that already exist for the backup plan associated with this backup operation.
                    {
                        var AllFilesFromPlanThatWerentUpdatedYet = AllFilesFromPlan.Values.Except(FilesToInsertOrUpdate);
                        foreach (Models.BackupPlanFile file in AllFilesFromPlanThatWerentUpdatedYet)
                        {
                            // Throw if the operation was canceled.
                            CancellationToken.ThrowIfCancellationRequested();

                            //Console.WriteLine("2: {0}", file.Path);
                            try
                            {
                                daoBackupPlanFile.Update(tx, file);
                            }
                            catch (Exception ex)
                            {
                                string message = string.Format("BUG: Failed to update {0} => {1} ",
                                                               typeof(Models.BackupPlanFile).Name,
                                                               CustomJsonSerializer.SerializeObject(file, 1));

                                Results.OnError(this, message);
                                logger.Log(LogLevel.Error, ex, message);

                                throw;
                            }

                            batchProcessor.ProcessBatch(session);
                        }
                    }

                    batchProcessor.ProcessBatch(session, true);
                    stats.End();

                    // ------------------------------------------------------------------------------------

                    stats.Begin("STEP 5");

                    // 5 - Insert/Update `Backup` and its `BackupedFile`s into the database, also saving
                    //     the `BackupPlanFile`s instances that may have been changed by step 2.
                    {
                        //foreach (var bf in backupedFiles)
                        //{
                        //	// Throw if the operation was canceled.
                        //	CancellationToken.ThrowIfCancellationRequested();
                        //
                        //	Backup.Files.Add(bf);
                        //
                        //	ProcessBatch(session);
                        //}

                        try
                        {
                            daoBackup.Update(tx, Backup);
                        }
                        catch (Exception ex)
                        {
                            string message = string.Format("BUG: Failed to update {0} => {1}",
                                                           typeof(Models.Backup).Name,
                                                           CustomJsonSerializer.SerializeObject(Backup, 1));

                            Results.OnError(this, message);
                            logger.Log(LogLevel.Error, ex, message);

                            throw;
                        }
                    }

                    batchProcessor.ProcessBatch(session, true);
                    stats.End();

                    // ------------------------------------------------------------------------------------

                    tx.Commit();
                }
                catch (OperationCanceledException)
                {
                    string message = "Operation cancelled";

                    Results.OnError(this, message);
                    logger.Warn(message);

                    tx.Rollback();                     // Rollback the transaction
                    throw;
                }
                catch (Exception ex)
                {
                    string message = string.Format("Caught Exception: {0}", ex.Message);

                    Results.OnError(this, message);
                    logger.Log(LogLevel.Error, ex, message);

                    tx.Rollback();                     // Rollback the transaction
                    throw;
                }
                finally
                {
                    // ...
                }
            }

            IsSaved = true;

            // 6 - Create versioned files and remove files that won't belong to this backup.
            TransferSet.Files = GetFilesToTransfer(Backup, SuppliedFiles);

            // Test to see if things are okay!
            {
                var transferCount = TransferSet.Files.Count();
                var filesCount    = ChangeSet.AddedFiles.Count() + ChangeSet.ModifiedFiles.Count();

                Assert.IsTrue(transferCount == filesCount, "TransferSet.Files must be equal (ChangeSet.AddedFiles + ChangeSet.ModifiedFiles)");
            }
        }
Ejemplo n.º 5
0
        //
        // Summary:
        // Update the `LastWrittenAt`,`LastSize`,`LastStatus`,`LastUpdatedAt`,`LastChecksum`
        // properties of each file in `files` according to the actual state of the file in the filesystem.
        //
        // NOTE: This function has a side effect - It updates properties of items from `files`.
        //
        private void DoUpdateBackupPlanFilesStatus(LinkedList <Models.BackupPlanFile> files, bool isNewVersion)
        {
            Assert.IsNotNull(files);

            ISession session = NHibernateHelper.GetSession();
            BackupedFileRepository daoBackupedFile = new BackupedFileRepository(session);

            BlockPerfStats stats = new BlockPerfStats();

            stats.Begin();

            // Check all files.
            LinkedListNode <Models.BackupPlanFile> node = files.First;

            while (node != null)
            {
                var next = node.Next;
                Models.BackupPlanFile entry = node.Value;
                // TODO(jweyrich): Measure whether `daoBackupedFile.GetLatestVersion(entry)` is faster or not,
                //                 and whether "entry.Versions.anything" would cause all related version to be fetched.
#if false
                Models.BackupedFile lastVersion = entry.Versions != null && entry.Versions.Count > 0
                                        ? entry.Versions.Last() : null;
#else
                // This may be a version that has not COMPLETED the transfer.
                Models.BackupedFile lastVersion = entry.Id.HasValue ? daoBackupedFile.GetLatestVersion(entry) : null;
#endif

                // Throw if the operation was canceled.
                CancellationToken.ThrowIfCancellationRequested();

                //
                // Check what happened to the file.
                //

                bool fileExistsOnFilesystem            = FileManager.FileExists(entry.Path);
                Models.BackupFileStatus?changeStatusTo = null;

                try
                {
                    //
                    // Update file properties
                    //
                    if (fileExistsOnFilesystem)
                    {
                        try
                        {
                            DateTime fileLastWrittenAt = FileManager.UnsafeGetFileLastWriteTimeUtc(entry.Path);
                            long     fileLength        = FileManager.UnsafeGetFileSize(entry.Path);

                            entry.LastWrittenAt = fileLastWrittenAt;
                            entry.LastSize      = fileLength;
                        }
                        catch (Exception ex)
                        {
                            string message = string.Format("Caught an exception while retrieving file properties: {0}", ex.Message);

                            Results.OnFileFailed(this, new FileVersionerEventArgs {
                                FilePath = entry.Path, FileSize = 0
                            }, message);
                            logger.Warn(message);

                            throw;
                        }

                        try
                        {
                            // Skip files larger than `MAX_FILESIZE_TO_HASH`.
                            int result = BigInteger.Compare(entry.LastSize, MAX_FILESIZE_TO_HASH);
                            if (result < 0)
                            {
                                entry.LastChecksum = CalculateHashForFile(entry.Path);
                            }
                        }
                        catch (Exception ex)
                        {
                            string message = string.Format("Caught an exception while calculating the file hash: {0}", ex.Message);

                            Results.OnFileFailed(this, new FileVersionerEventArgs {
                                FilePath = entry.Path, FileSize = 0
                            }, message);
                            logger.Warn(message);

                            throw;
                        }

                        Results.OnFileCompleted(this, new FileVersionerEventArgs {
                            FilePath = entry.Path, FileSize = entry.LastSize
                        });
                    }

                    //
                    // Update file status
                    //
                    if (lastVersion != null)                     // File was backed up at least once in the past?
                    {
                        switch (entry.LastStatus)
                        {
                        case Models.BackupFileStatus.DELETED:                               // File was marked as DELETED by a previous backup?
                            if (fileExistsOnFilesystem)                                     // Exists?
                            {
                                changeStatusTo = Models.BackupFileStatus.ADDED;
                            }
                            break;

                        case Models.BackupFileStatus.REMOVED:                               // File was marked as REMOVED by a previous backup?
                            if (fileExistsOnFilesystem)                                     // Exists?
                            {
                                changeStatusTo = Models.BackupFileStatus.ADDED;
                            }
                            else
                            {
                                // QUESTION: Do we really care to transition REMOVED to DELETED?
                                changeStatusTo = Models.BackupFileStatus.DELETED;
                            }
                            break;

                        default:                                 // ADDED, MODIFIED, UNCHANGED
                            if (fileExistsOnFilesystem)          // Exists?
                            {
                                // DO NOT verify whether the file changed for a `ResumeBackupOperation`,
                                // only for `NewBackupOperation`.
                                if (isNewVersion)
                                {
                                    if (IsFileModified(entry, lastVersion))                                             // Modified?
                                    {
                                        changeStatusTo = Models.BackupFileStatus.MODIFIED;
                                    }
                                    else if (NeedsToRetryFile(entry, lastVersion))                                             // Didn't complete last file transfer?
                                    {
                                        changeStatusTo = Models.BackupFileStatus.MODIFIED;
                                    }
                                    else                                             // Not modified?
                                    {
                                        changeStatusTo = Models.BackupFileStatus.UNCHANGED;
                                    }
                                }
                            }
                            else                                     // Deleted from filesystem?
                            {
                                changeStatusTo = Models.BackupFileStatus.DELETED;
                            }
                            break;
                        }
                    }
                    else                            // Adding to this backup?
                    {
                        if (fileExistsOnFilesystem) // Exists?
                        {
                            changeStatusTo = Models.BackupFileStatus.ADDED;
                        }
                        else
                        {
                            // Error? Can't add a non-existent file to the plan.
                        }
                    }

                    if (changeStatusTo.HasValue)
                    {
                        entry.LastStatus = changeStatusTo.Value;
                        entry.UpdatedAt  = DateTime.UtcNow;
                    }
                }
                catch (Exception ex)
                {
                    FailedFile <Models.BackupPlanFile> failedEntry = new FailedFile <Models.BackupPlanFile>(entry, ex.Message, ex);
                    ChangeSet.FailedFiles.AddLast(failedEntry);

                    // Remove this entry from `files` as it clearly failed.
                    files.Remove(node);                     // Complexity is O(1)
                }

                node = next;
            }

            stats.End();
        }
Ejemplo n.º 6
0
        // Summary:
        //    Saves all instances from RemoteObjects list to the database.
        //    Also removes them from RemoteObjects list to free memory.
        private void Save(CancellationToken CancellationToken)
        {
            ISession session = NHibernateHelper.GetSession();

            BatchProcessor               batchProcessor        = new BatchProcessor(250);
            StorageAccountRepository     daoStorageAccount     = new StorageAccountRepository(session);
            BackupPlanFileRepository     daoBackupPlanFile     = new BackupPlanFileRepository(session);
            BackupPlanPathNodeRepository daoBackupPlanPathNode = new BackupPlanPathNodeRepository(session);
            BackupedFileRepository       daoBackupedFile       = new BackupedFileRepository(session);

            BlockPerfStats stats = new BlockPerfStats();

            using (BatchTransaction tx = batchProcessor.BeginTransaction(session))
            {
                try
                {
                    // ------------------------------------------------------------------------------------

                    Models.StorageAccount account = daoStorageAccount.Get(Synchronization.StorageAccount.Id);

                    // ------------------------------------------------------------------------------------

                    stats.Begin("STEP 1");

                    BackupPlanPathNodeCreator pathNodeCreator = new BackupPlanPathNodeCreator(daoBackupPlanPathNode, tx);

                    // Report save progress
                    ReportSaveProgress(SyncAgent.Results.Stats.SavedFileCount, true);

                    // Saving loop
                    for (int i = RemoteObjects.Count - 1; i >= 0; i--)
                    {
                        ListingObject obj = RemoteObjects[i];              // Get instance of object.
                        //RemoteObjects[i] = null;
                        RemoteObjects.RemoveAt(i);                         // Remove to free memory. RemoveAt(int) is O(N).

                        // Throw if the operation was canceled.
                        CancellationToken.ThrowIfCancellationRequested();

                        Models.EntryType type;
                        string           path          = string.Empty;
                        string           versionString = string.Empty;

                        try
                        {
                            // Parse obj.Key into its relevant parts.
                            bool ok = ParseS3Key(obj.Key, out type, out path, out versionString);
                        }
                        catch (Exception ex)
                        {
                            if (ex is ArgumentException || ex is IndexOutOfRangeException)
                            {
                                // Report error.
                                logger.Warn("Failed to parse S3 key: {0} -- Skipping.", obj.Key);
                                //logger.Log(LogLevel.Warn, ex, "Failed to parse S3 key: {0}", obj.Key);

                                //SyncAgent.Results.Stats.FailedSavedFileCount += 1;

                                // Report save progress
                                //ReportSaveProgress(SyncAgent.Results.Stats.SavedFileCount);

                                continue;                                 // Skip this file.
                            }

                            throw;
                        }

                        path = StringUtils.NormalizeUsingPreferredForm(path);

                        DateTime lastWrittenAt = DateTime.ParseExact(versionString, Models.BackupedFile.VersionFormat, CultureInfo.InvariantCulture);

                        // Create/Update BackupPlanFile, but do not SAVE it.
                        Models.BackupPlanFile entry   = daoBackupPlanFile.GetByStorageAccountAndPath(account, path);
                        Models.BackupedFile   version = null;

                        if (entry == null)
                        {
                            // Create `BackupPlanFile`.
                            entry                    = new Models.BackupPlanFile();
                            entry.BackupPlan         = null;
                            entry.StorageAccountType = account.Type;
                            entry.StorageAccount     = account;
                            entry.Path               = path;
                            entry.LastSize           = obj.Size;
                            entry.LastWrittenAt      = lastWrittenAt;
                            //entry.LastChecksum = ;
                            entry.LastStatus = Models.BackupFileStatus.UNCHANGED;
                            entry.CreatedAt  = DateTime.UtcNow;

                            // Create `BackupedFile`.
                            version = new Models.BackupedFile(null, entry, Synchronization);
                            version.StorageAccountType = account.Type;
                            version.StorageAccount     = account;
                            version.FileLastWrittenAt  = lastWrittenAt;
                            version.FileLastChecksum   = entry.LastChecksum;
                            version.FileSize           = entry.LastSize;
                            version.FileStatus         = Models.BackupFileStatus.MODIFIED;
                            version.TransferStatus     = TransferStatus.COMPLETED;
                            version.UpdatedAt          = DateTime.UtcNow;

                            entry.Versions.Add(version);
                            //daoBackupedFile.Insert(tx, version);
                        }
                        else
                        {
                            // Update `BackupPlanFile`.
                            entry.LastSize      = obj.Size;
                            entry.LastWrittenAt = lastWrittenAt;
                            //entry.LastChecksum =
                            //entry.LastStatus = Models.BackupFileStatus.MODIFIED;
                            entry.UpdatedAt = DateTime.UtcNow;

                            IList <Models.BackupedFile> versions = null;
                            try
                            {
                                versions = daoBackupedFile.GetCompletedByStorageAccountAndPath(account, path, versionString);
                            }
                            catch (FormatException)
                            {
                                // Report error.
                                logger.Warn("Failed to parse versionString: {0} -- Skipping.", versionString);

                                //SyncAgent.Results.Stats.FailedSavedFileCount += 1;

                                continue;                                 // TODO(jweyrich): Should we abort?
                            }

                            // Check whether our database already contains this exact file + version.
                            if (versions == null || (versions != null && versions.Count == 0))
                            {
                                // Create `BackupedFile`.
                                version = new Models.BackupedFile(null, entry, Synchronization);
                                version.StorageAccountType = account.Type;
                                version.StorageAccount     = account;
                                version.FileLastWrittenAt  = entry.LastWrittenAt;
                                version.FileLastChecksum   = entry.LastChecksum;
                                version.FileSize           = entry.LastSize;
                                version.FileStatus         = Models.BackupFileStatus.MODIFIED;
                                version.TransferStatus     = TransferStatus.COMPLETED;
                                version.UpdatedAt          = DateTime.UtcNow;

                                entry.Versions.Add(version);
                                //daoBackupedFile.Insert(tx, version);
                            }
                            else
                            {
                                // Update `BackupedFile`.
                                version = versions.First();
                                version.FileLastWrittenAt = entry.LastWrittenAt;
                                version.FileLastChecksum  = entry.LastChecksum;
                                version.FileSize          = entry.LastSize;
                                version.UpdatedAt         = DateTime.UtcNow;
                                //daoBackupedFile.Update(tx, version);
                            }
                        }

                        try
                        {
                            // Create path nodes and INSERT them, if they don't exist yet.
                            entry.PathNode = pathNodeCreator.CreateOrUpdatePathNodes(account, entry);

                            // Create or update `BackupPlanFile`.
                            daoBackupPlanFile.InsertOrUpdate(tx, entry);
                        }
                        catch (Exception ex)
                        {
                            logger.Log(LogLevel.Error, ex, "BUG: Failed to insert/update {0} => {1}",
                                       typeof(Models.BackupPlanFile).Name,
                                       CustomJsonSerializer.SerializeObject(entry, 1));

                            logger.Error("Dump of failed object: {0}", entry.DumpMe());
                            throw;
                        }

                        bool didCommit = batchProcessor.ProcessBatch(tx);

                        SyncAgent.Results.Stats.SavedFileCount += 1;

                        // Report save progress
                        ReportSaveProgress(SyncAgent.Results.Stats.SavedFileCount);
                    }

                    batchProcessor.ProcessBatch(tx, true);

                    // Report save progress
                    ReportSaveProgress(SyncAgent.Results.Stats.SavedFileCount, true);

                    stats.End();

                    // ------------------------------------------------------------------------------------

                    tx.Commit();
                }
                catch (OperationCanceledException)
                {
                    tx.Rollback();                     // Rollback the transaction
                    throw;
                }
                catch (Exception ex)
                {
                    logger.Log(LogLevel.Error, ex, "Caught exception");
                    tx.Rollback();                     // Rollback the transaction
                    throw;
                }
                finally
                {
                    //session.Close();
                    if (session.IsConnected)
                    {
                        session.Disconnect();
                    }
                }
            }
        }
Ejemplo n.º 7
0
        protected void RegisterResultsEventHandlers(Models.Backup backup, TransferResults results)
        {
            BackupedFileRepository daoBackupedFile = new BackupedFileRepository();

            results.DeleteCompleted += (object sender, DeletionArgs e) =>
            {
                Int64?backupedFileId = (Int64?)e.UserData;
                // TODO(jweyrich): We could get rid of the SELECT and perform just the UPDATE.
                Models.BackupedFile backupedFile = daoBackupedFile.Get(backupedFileId.Value);
                backupedFile.TransferStatus = TransferStatus.PURGED;
                backupedFile.UpdatedAt      = DateTime.UtcNow;
                daoBackupedFile.Update(backupedFile);

                //var message = string.Format("Purged {0}", e.FilePath);
                //Info(message);
                //OnUpdate(new BackupOperationEvent { Status = BackupOperationStatus.Updated, Message = message });
            };
            results.Failed += (object sender, TransferFileProgressArgs args) =>
            {
                Models.BackupedFile backupedFile = daoBackupedFile.GetByBackupAndPath(backup, args.FilePath);
                backupedFile.TransferStatus = TransferStatus.FAILED;
                backupedFile.UpdatedAt      = DateTime.UtcNow;
                daoBackupedFile.Update(backupedFile);

                var message = string.Format("Failed {0} - {1}", args.FilePath, args.Exception != null ? args.Exception.Message : "Unknown reason");
                Warn(message);
                //StatusInfo.Update(BackupStatusLevel.ERROR, message);
                OnUpdate(new BackupOperationEvent {
                    Status = BackupOperationStatus.Updated, Message = message, TransferStatus = TransferStatus.FAILED
                });
            };
            results.Canceled += (object sender, TransferFileProgressArgs args) =>
            {
                Models.BackupedFile backupedFile = daoBackupedFile.GetByBackupAndPath(backup, args.FilePath);
                backupedFile.TransferStatus = TransferStatus.CANCELED;
                backupedFile.UpdatedAt      = DateTime.UtcNow;
                daoBackupedFile.Update(backupedFile);

                var message = string.Format("Canceled {0} - {1}", args.FilePath, args.Exception != null ? args.Exception.Message : "Unknown reason");
                Warn(message);
                //StatusInfo.Update(BackupStatusLevel.ERROR, message);
                OnUpdate(new BackupOperationEvent {
                    Status = BackupOperationStatus.Updated, Message = message, TransferStatus = TransferStatus.CANCELED
                });
            };
            results.Completed += (object sender, TransferFileProgressArgs args) =>
            {
                Models.BackupedFile backupedFile = daoBackupedFile.GetByBackupAndPath(backup, args.FilePath);
                backupedFile.TransferStatus = TransferStatus.COMPLETED;
                backupedFile.UpdatedAt      = DateTime.UtcNow;
                daoBackupedFile.Update(backupedFile);

                var message = string.Format("Completed {0}", args.FilePath);
                Info(message);
                OnUpdate(new BackupOperationEvent {
                    Status = BackupOperationStatus.Updated, Message = message, TransferStatus = TransferStatus.COMPLETED
                });

                Models.BackupPlan plan = Backup.BackupPlan;                 //backupedFile.Backup.BackupPlan;

                if (plan.PurgeOptions != null && plan.PurgeOptions.IsTypeCustom && plan.PurgeOptions.EnabledKeepNumberOfVersions)
                {
                    // Purge the oldest versioned files if the count of versions exceeds the maximum specified for the Backup Plan.
                    IList <Models.BackupedFile> previousVersions = daoBackupedFile.GetCompleteByPlanAndPath(plan, args.FilePath);
                    int found = previousVersions.Count;
                    int keep  = plan.PurgeOptions.NumberOfVersionsToKeep;
                    int diff  = found - keep;
                    if (diff > 0)
                    {
                        // Delete the oldest Count-N versions.
                        List <Models.BackupedFile> versionsToPurge = previousVersions.Skip(keep).ToList();
                        foreach (var vp in versionsToPurge)
                        {
                            DeleteVersionedFile(vp.File.Path, new FileVersion {
                                Version = vp.Version
                            }, vp.Id);
                        }
                    }
                }
            };
            results.Started += (object sender, TransferFileProgressArgs args) =>
            {
                Models.BackupedFile backupedFile = daoBackupedFile.GetByBackupAndPath(backup, args.FilePath);
                backupedFile.TransferStatus = TransferStatus.RUNNING;
                backupedFile.UpdatedAt      = DateTime.UtcNow;
                daoBackupedFile.Update(backupedFile);

                var message = string.Format("Started {0}", args.FilePath);
                Info(message);
                OnUpdate(new BackupOperationEvent {
                    Status = BackupOperationStatus.Updated, Message = message
                });
            };
            results.Progress += (object sender, TransferFileProgressArgs args) =>
            {
#if DEBUG
                var message = string.Format("Progress {0}% {1} ({2}/{3} bytes)",
                                            args.PercentDone, args.FilePath, args.TransferredBytes, args.TotalBytes);
                //Info(message);
#endif
                OnUpdate(new BackupOperationEvent {
                    Status = BackupOperationStatus.Updated, Message = null
                });
            };
        }