PageRenderTime 223ms CodeModel.GetById 211ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/Raven.Database/Server/RavenFS/Synchronization/SynchronizationWorkItem.cs

https://github.com/nwendel/ravendb
C# | 131 lines | 106 code | 24 blank | 1 comment | 8 complexity | f490116ac489644c3f4943a3cfcfe35d MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Collections.Specialized;
  4using System.Linq;
  5using System.Threading;
  6using System.Threading.Tasks;
  7using Raven.Abstractions.Logging;
  8using Raven.Client.Connection.Profiling;
  9using Raven.Database.Server.RavenFS.Extensions;
 10using Raven.Database.Server.RavenFS.Storage;
 11using Raven.Database.Server.RavenFS.Storage.Esent;
 12using Raven.Database.Server.RavenFS.Synchronization.Conflictuality;
 13using Raven.Json.Linq;
 14using Raven.Client.FileSystem;
 15using Raven.Abstractions.FileSystem;
 16using Raven.Client.FileSystem.Connection;
 17using Raven.Abstractions.Data;
 18
 19namespace Raven.Database.Server.RavenFS.Synchronization
 20{
 21	public abstract class SynchronizationWorkItem : IHoldProfilingInformation
 22	{
 23		private readonly ConflictDetector conflictDetector;
 24		private readonly ConflictResolver conflictResolver;
 25		protected readonly CancellationTokenSource Cts = new CancellationTokenSource();
 26        protected FilesConvention Convention = new FilesConvention();
 27		protected SynchronizationWorkItem(string fileName, string sourceServerUrl, ITransactionalStorage storage)
 28		{
 29			Storage = storage;
 30			FileName = fileName;
 31
 32			FileAndPagesInformation fileAndPages = null;
 33			Storage.Batch(accessor => fileAndPages = accessor.GetFile(fileName, 0, 0));
 34			FileMetadata = fileAndPages.Metadata;
 35			ServerInfo = new ServerInfo
 36			{
 37				Id = Storage.Id,
 38				FileSystemUrl = sourceServerUrl
 39			};
 40
 41			conflictDetector = new ConflictDetector();
 42			conflictResolver = new ConflictResolver();
 43		}
 44
 45		protected ITransactionalStorage Storage { get; private set; }
 46
 47		public string FileName { get; private set; }
 48
 49		public Guid FileETag
 50		{
 51            get { return FileMetadata.Value<Guid>(Constants.MetadataEtagField); }
 52		}
 53
 54		public bool IsCancelled
 55		{
 56			get { return Cts.Token.IsCancellationRequested; }
 57		}
 58
 59        protected RavenJObject FileMetadata { get; set; }
 60
 61		protected ServerInfo ServerInfo { get; private set; }
 62
 63		public abstract SynchronizationType SynchronizationType { get; }
 64
 65        public abstract Task<SynchronizationReport> PerformAsync(IAsyncFilesSynchronizationCommands destination);
 66
 67		public virtual void Cancel()
 68		{
 69		}
 70
 71        protected void AssertLocalFileExistsAndIsNotConflicted(RavenJObject sourceMetadata)
 72		{
 73			if (sourceMetadata == null)
 74				throw new SynchronizationException(string.Format("File {0} does not exist", FileName));
 75
 76            if (sourceMetadata.ContainsKey(SynchronizationConstants.RavenSynchronizationConflict))
 77                throw new SynchronizationException(string.Format("File {0} is conflicted", FileName));
 78		}
 79
 80        protected ConflictItem CheckConflictWithDestination(RavenJObject sourceMetadata,
 81                                                            RavenJObject destinationMetadata, string localServerUrl)
 82		{
 83            var conflict = conflictDetector.CheckOnSource(FileName, sourceMetadata, destinationMetadata, localServerUrl);
 84            var isConflictResolved = conflictResolver.IsResolved(destinationMetadata, conflict);
 85
 86            // optimization - conflict checking on source side before any changes pushed
 87            if (conflict != null && !isConflictResolved)
 88                return conflict;
 89
 90            return null;
 91		}
 92
 93        protected async Task<SynchronizationReport> ApplyConflictOnDestinationAsync(ConflictItem conflict, RavenJObject remoteMetadata, IAsyncFilesSynchronizationCommands destination, string localServerUrl, ILog log)
 94		{
 95            var commands = (IAsyncFilesCommandsImpl)destination.Commands;
 96
 97            log.Debug("File '{0}' is in conflict with destination version from {1}. Applying conflict on destination", FileName, commands.UrlFor());
 98
 99			try
100			{
101				var version = conflict.RemoteHistory.Last().Version;
102				var serverId = conflict.RemoteHistory.Last().ServerId;
103				var history = new List<HistoryItem>(conflict.RemoteHistory);
104				history.RemoveAt(conflict.RemoteHistory.Count - 1);
105
106                await destination.ApplyConflictAsync(FileName, version, serverId, remoteMetadata, localServerUrl);
107			}
108			catch (Exception ex)
109			{
110				log.WarnException(string.Format("Failed to apply conflict on {0} for file '{1}'", destination, FileName), ex);
111			}
112
113			return new SynchronizationReport(FileName, FileETag, SynchronizationType)
114			{
115				Exception = new SynchronizationException(string.Format("File {0} is conflicted", FileName)),
116			};
117		}
118
119		public void RefreshMetadata()
120		{
121			if (Storage != null)
122			{
123				FileAndPagesInformation fileAndPages = null;
124				Storage.Batch(accessor => fileAndPages = accessor.GetFile(FileName, 0, 0));
125				FileMetadata = fileAndPages.Metadata;
126			}
127		}
128
129		public ProfilingInformation ProfilingInformation { get; private set; }
130	}
131}