Example #1
0
        bool ResetSyncStateIfNecessary(IGroup group, IMultiFileSystemDiff diff)
        {
            var syncPointService = group.GetSyncPointService();
            var syncActionService = group.GetSyncActionService();
            var conflictService = group.GetSyncConflictService();

            var syncFolders = group.GetConfigurationService().Items.ToArray();
                
            var latestSyncPoint = syncPointService.LatestSyncPoint;

            if (ContainsNewFolders(diff) || WasFilterModified(syncFolders, latestSyncPoint))
            {
                // insert "Reset" sync point
                var resetSyncPoint = new MutableSyncPoint()
                {
                    Id = GetNextSyncPointId(latestSyncPoint),                    
                    MultiFileSystemSnapshotId = null,
                    FilterConfigurations = syncFolders.ToDictionary(f => f.Name, f => f.Filter)
                };

                syncPointService.AddItem(resetSyncPoint);

                // cancel all pending sync actions
                var cancelledSyncActions = syncActionService.PendingItems
                    .Select(a => a.WithState(SyncActionState.Cancelled));

                syncActionService.UpdateItems(cancelledSyncActions);

                // remove all conflicts                
                conflictService.RemoveItems(conflictService.Items.ToArray());


                return true;
            }

            return false;

        }
Example #2
0
        public void Synchronize(IGroup group)
        {
            var syncFolders = group.GetConfigurationService().Items.ToArray();

            // there need to be at least 2 sync folders, otherwise, syncing makes no sense
            if (syncFolders.Length < 2)
            {
                return;
            }

            var historyService = group.GetService<IMultiFileSystemHistoryService>();
            historyService.CreateSnapshot();

            // we cannot sync if there isn't at least one snapshot
            if(!historyService.Snapshots.Any())
            {
                return;                
            }
            
            // we cannot sync if there is not at least one snapshot per history
            if (historyService.LatestSnapshot.HistoryNames.Any(name => historyService.LatestSnapshot.GetSnapshot(name) == null))
            {
                return;
            }

            // get required services
            var syncPointService = group.GetSyncPointService();
            var conflictService = group.GetSyncConflictService();
            var syncActionService = group.GetSyncActionService();

            var filter = syncFolders.ToMultiFileSystemChangeFilter(m_FilterFactory);

            var latestSyncPoint = syncPointService.LatestSyncPoint;
            var diff = GetDiff(historyService, latestSyncPoint, filter);

            var wasReset = ResetSyncStateIfNecessary(group, diff);
            if (wasReset)
            {
                diff = GetDiff(historyService, latestSyncPoint, filter);
                latestSyncPoint = syncPointService.LatestSyncPoint;
            }
            
            // for all folders, get the changes since the last sync
                
            var newSyncPoint = new MutableSyncPoint()
            {
                Id = GetNextSyncPointId(syncPointService.LatestSyncPoint),                                
                MultiFileSystemSnapshotId = diff.ToSnapshot.Id,
                FilterConfigurations = syncFolders.ToDictionary(f => f.Name, f => f.Filter)
            };

            
                                                   
            
            var syncStateUpdater = new SyncActionUpdateBuilder();
            var changeGraphBuilder = new ChangeGraphBuilder(m_FileReferenceComparer);

            foreach (var graph in changeGraphBuilder.GetChangeGraphs(diff))
            {
                var path = graph.ValueNodes.First(node => node.Value != null).Value.Path;

                // skip if there is a conflict for the current file
                if (conflictService.ItemExists(path))
                {
                    continue;                    
                }                

                // check if all pending sync actions can be applied to the change graph
                var unapplicaleSyncActions = GetUnapplicableSyncActions(graph, syncActionService[path].Where(IsPendingSyncAction));

                // pending sync actions could not be applied => skip file
                if (unapplicaleSyncActions.Any())
                {
                    // cancel unapplicable actions
                    syncStateUpdater.UpdateSyncActions(unapplicaleSyncActions.Select(a => a.WithState(SyncActionState.Cancelled)));
                   
                    // add a conflict for the file (the snapshot id of the conflict can be determined from the oldest unapplicable sync action)
                    var oldestSyncPointId = unapplicaleSyncActions.Min(a => a.SyncPointId);                    
                    var snapshotId = oldestSyncPointId > 1 
                        ? syncPointService[oldestSyncPointId - 1].MultiFileSystemSnapshotId
                        : null;                    
                    syncStateUpdater.AddConflict(new ConflictInfo(unapplicaleSyncActions.First().Path, snapshotId));
                                        
                    continue;
                }
                
                //in the change graph, detect conflicts
                // if there is only one sink, no conflicts exist
                var acylicGraph = graph.ToAcyclicGraph();
                var sinks = acylicGraph.GetSinks().ToArray();
                if (!sinks.Any())
                {
                    // not possible (in this case the graph would be empty, which cannot happen)
                    throw new InvalidOperationException();
                }

                if (sinks.Length == 1)
                {                 
                    // no conflict => generate sync actions, to replace the outdated file versions or add the file to a target

                    var sink = sinks.Single();

                    //TODO: Use C# 7 tuples after it was released
                    IEnumerable<Tuple<string, IFile>> currentFiles = diff.ToSnapshot.GetFiles(path);                
                    foreach (var historyFileTuple in currentFiles)
                    {
                        var targetSyncFolderName = historyFileTuple.Item1;
                        var currentVersion = historyFileTuple.Item2;

                        var syncAction = m_SyncActionFactory.GetSyncAction(targetSyncFolderName, newSyncPoint.Id, currentVersion?.ToReference(), sink);
                        if (syncAction != null && filter.GetFilter(targetSyncFolderName).IncludeInResult(syncAction))
                        {
                            syncStateUpdater.AddSyncAction(syncAction);
                        }
                    }
                
                }
                else
                {
                    // multiple sinks in the change graph => conflict

                    // if there are pending actions for this file, they need to be cancelled
                    var pendingSyncActions = syncActionService[path]
                        .Where(IsPendingSyncAction)
                        .ToArray();

                    if (pendingSyncActions.Any())
                    {
                        //cancel actions          
                        syncStateUpdater.UpdateSyncActions(pendingSyncActions.Select(a => a.WithState(SyncActionState.Cancelled)));                        

                        //determine the oldest sync action to determine the snapshot ids for the conflict
                        var syncPointId = pendingSyncActions.Min(x => x.SyncPointId);
                        var snapshotId = syncPointId > 1
                            ? syncPointService[syncPointId - 1].MultiFileSystemSnapshotId
                            : null;

                        // generate conflict;
                        syncStateUpdater.AddConflict(new ConflictInfo(path, snapshotId));
                    }
                    else
                    {
                        // no pending action => the snapshot ids for the conflict are the start snapshots of the current sync

                        // generate conflict                        
                        var conflictInfo = new ConflictInfo(path, diff.FromSnapshot?.Id);
                        syncStateUpdater.AddConflict(conflictInfo);
                    }
                }
            }

            // save actions, conflicts and sync point          
            syncPointService.AddItem(newSyncPoint);            
            syncStateUpdater.Apply(syncActionService, conflictService);
                        
        }
Example #3
0
        public void Running_Sychronize_without_any_changes_since_the_last_sync_point_produces_no_actions_or_conflicts()
        {
            //ARRANGE
            var state1 = new Directory(null, "root") { d => new EmptyFile(d, "file1") };
            var state2 = new Directory(null, "root") { d => new EmptyFile(d, "file2")};

            var configService = m_Group.GetConfigurationService();
            configService.AddItem(new SyncFolder("folder1") { Path = "Irrelevant"});
            configService.AddItem(new SyncFolder("folder2") { Path = "Irrelevant"});

            var historyService = m_Group.GetHistoryService();
            historyService.CreateHistory("folder1");
            historyService.CreateHistory("folder2");
            var snapshot1 = historyService["folder1"].CreateSnapshot(state1);
            var snapshot2 = historyService["folder2"].CreateSnapshot(state2);
            
            // save a sync point
            var syncPoint = new MutableSyncPoint()
            {
                Id = 1,                
                MultiFileSystemSnapshotId = m_MultiFileSystemHistory.CreateSnapshot().Id,
                FilterConfigurations = new Dictionary<string, FilterConfiguration>()
                {
                    {"folder1", FilterConfiguration.Empty },
                    {"folder2", FilterConfiguration.Empty }
                }
            };

            m_Group.GetSyncPointService().AddItem(syncPoint);

            //ACT
            m_Instance.Synchronize(m_Group);

            //ASSERT
            Assert.Empty(m_Group.GetSyncConflictService().Items);
            Assert.Empty(m_Group.GetSyncActionService().AllItems);
            Assert.Equal(2, m_Group.GetSyncPointService().Items.Count());
        }