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