/ToMigrate/Raven.Database/FileSystem/Synchronization/ContentUpdateWorkItem.cs

https://github.com/fitzchak/ravendb · C# · 216 lines · 177 code · 37 blank · 2 comment · 21 complexity · 5f6c268a57f4295c691e260f7d3c60bb MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using Raven.Abstractions.Logging;
  7. using Raven.Database.Config;
  8. using Raven.Database.FileSystem.Storage;
  9. using Raven.Database.FileSystem.Synchronization.Multipart;
  10. using Raven.Database.FileSystem.Synchronization.Rdc;
  11. using Raven.Database.FileSystem.Synchronization.Rdc.Wrapper;
  12. using Raven.Database.FileSystem.Util;
  13. using Raven.Client.FileSystem;
  14. using Raven.Abstractions.FileSystem;
  15. using Raven.Abstractions.Data;
  16. namespace Raven.Database.FileSystem.Synchronization
  17. {
  18. public class ContentUpdateWorkItem : SynchronizationWorkItem
  19. {
  20. private readonly ILog log = LogManager.GetCurrentClassLogger();
  21. private readonly SigGenerator sigGenerator;
  22. private readonly RavenConfiguration configuration;
  23. private DataInfo fileDataInfo;
  24. private SynchronizationMultipartRequest multipartRequest;
  25. public ContentUpdateWorkItem(string file, string sourceServerUrl, ITransactionalStorage storage, SigGenerator sigGenerator, RavenConfiguration configuration) : base(file, sourceServerUrl, storage)
  26. {
  27. this.sigGenerator = sigGenerator;
  28. this.configuration = configuration;
  29. }
  30. public override SynchronizationType SynchronizationType
  31. {
  32. get { return SynchronizationType.ContentUpdate; }
  33. }
  34. private DataInfo FileDataInfo
  35. {
  36. get { return fileDataInfo ?? (fileDataInfo = GetLocalFileDataInfo(FileName)); }
  37. }
  38. public override void Cancel()
  39. {
  40. Cts.Cancel();
  41. }
  42. public override async Task<SynchronizationReport> PerformAsync(ISynchronizationServerClient synchronizationServerClient)
  43. {
  44. AssertLocalFileExistsAndIsNotConflicted(FileMetadata);
  45. var destinationMetadata = await synchronizationServerClient.GetMetadataForAsync(FileName).ConfigureAwait(false);
  46. if (destinationMetadata == null)
  47. {
  48. // if file doesn't exist on destination server - upload it there
  49. return await UploadToAsync(synchronizationServerClient).ConfigureAwait(false);
  50. }
  51. var destinationServerRdcStats = await synchronizationServerClient.GetRdcStatsAsync().ConfigureAwait(false);
  52. if (!IsRemoteRdcCompatible(destinationServerRdcStats))
  53. throw new SynchronizationException("Incompatible RDC version detected on destination server");
  54. var conflict = CheckConflictWithDestination(FileMetadata, destinationMetadata, FileSystemInfo.Url);
  55. if (conflict != null)
  56. {
  57. var report = await HandleConflict(synchronizationServerClient, conflict, log).ConfigureAwait(false);
  58. if (report != null)
  59. return report;
  60. }
  61. using (var localSignatureRepository = new StorageSignatureRepository(Storage, FileName, configuration))
  62. using (var remoteSignatureCache = new VolatileSignatureRepository(FileName, configuration))
  63. {
  64. var localRdcManager = new LocalRdcManager(localSignatureRepository, Storage, sigGenerator);
  65. var destinationRdcManager = new RemoteRdcManager(synchronizationServerClient, localSignatureRepository, remoteSignatureCache);
  66. if (log.IsDebugEnabled)
  67. log.Debug("Starting to retrieve signatures of a local file '{0}'.", FileName);
  68. Cts.Token.ThrowIfCancellationRequested();
  69. // first we need to create a local file signatures before we synchronize with remote ones
  70. var localSignatureManifest = await localRdcManager.GetSignatureManifestAsync(FileDataInfo).ConfigureAwait(false);
  71. if (log.IsDebugEnabled)
  72. log.Debug("Number of a local file '{0}' signatures was {1}.", FileName, localSignatureManifest.Signatures.Count);
  73. if (localSignatureManifest.Signatures.Any())
  74. {
  75. var destinationSignatureManifest = await destinationRdcManager.SynchronizeSignaturesAsync(FileDataInfo, Cts.Token).ConfigureAwait(false);
  76. if (destinationSignatureManifest.Signatures.Any())
  77. {
  78. return await SynchronizeTo(synchronizationServerClient, localSignatureRepository, remoteSignatureCache, localSignatureManifest, destinationSignatureManifest).ConfigureAwait(false);
  79. }
  80. }
  81. return await UploadToAsync(synchronizationServerClient).ConfigureAwait(false);
  82. }
  83. }
  84. private bool IsRemoteRdcCompatible(RdcStats destinationServerRdcStats)
  85. {
  86. using (var versionChecker = new RdcVersionChecker())
  87. {
  88. var localRdcVersion = versionChecker.GetRdcVersion();
  89. return destinationServerRdcStats.CurrentVersion >= localRdcVersion.MinimumCompatibleAppVersion;
  90. }
  91. }
  92. private async Task<SynchronizationReport> SynchronizeTo(ISynchronizationServerClient synchronizationServerClient,
  93. ISignatureRepository localSignatureRepository,
  94. ISignatureRepository remoteSignatureRepository,
  95. SignatureManifest sourceSignatureManifest,
  96. SignatureManifest destinationSignatureManifest)
  97. {
  98. var seedSignatureInfo = SignatureInfo.Parse(destinationSignatureManifest.Signatures.Last().Name);
  99. var sourceSignatureInfo = SignatureInfo.Parse(sourceSignatureManifest.Signatures.Last().Name);
  100. using (var localFile = StorageStream.Reading(Storage, FileName))
  101. {
  102. IList<RdcNeed> needList;
  103. using (var needListGenerator = new NeedListGenerator(remoteSignatureRepository, localSignatureRepository))
  104. {
  105. needList = needListGenerator.CreateNeedsList(seedSignatureInfo, sourceSignatureInfo, Cts.Token);
  106. }
  107. return await PushByUsingMultipartRequest(synchronizationServerClient, localFile, needList).ConfigureAwait(false);
  108. }
  109. }
  110. public async Task<SynchronizationReport> UploadToAsync(ISynchronizationServerClient synchronizationServerClient)
  111. {
  112. using (var sourceFileStream = StorageStream.Reading(Storage, FileName))
  113. {
  114. var fileSize = sourceFileStream.Length;
  115. var onlySourceNeed = new List<RdcNeed>
  116. {
  117. new RdcNeed
  118. {
  119. BlockType = RdcNeedType.Source,
  120. BlockLength = (ulong) fileSize,
  121. FileOffset = 0
  122. }
  123. };
  124. return await PushByUsingMultipartRequest(synchronizationServerClient, sourceFileStream, onlySourceNeed).ConfigureAwait(false);
  125. }
  126. }
  127. private Task<SynchronizationReport> PushByUsingMultipartRequest(ISynchronizationServerClient synchronizationServerClient, Stream sourceFileStream,
  128. IList<RdcNeed> needList)
  129. {
  130. Cts.Token.ThrowIfCancellationRequested();
  131. multipartRequest = new SynchronizationMultipartRequest(synchronizationServerClient, FileSystemInfo, FileName, FileMetadata, sourceFileStream, needList);
  132. var bytesToTransferCount = needList.Where(x => x.BlockType == RdcNeedType.Source).Sum(x => (double)x.BlockLength);
  133. if (log.IsDebugEnabled)
  134. log.Debug(
  135. "Synchronizing a file '{0}' (ETag {1}) to {2} by using multipart request. Need list length is {3}. Number of bytes that needs to be transfered is {4}",
  136. FileName, FileETag, synchronizationServerClient, needList.Count, bytesToTransferCount);
  137. return multipartRequest.PushChangesAsync(Cts.Token);
  138. }
  139. private DataInfo GetLocalFileDataInfo(string fileName)
  140. {
  141. FileAndPagesInformation fileAndPages = null;
  142. try
  143. {
  144. Storage.Batch(accessor => fileAndPages = accessor.GetFile(fileName, 0, 0));
  145. }
  146. catch (FileNotFoundException)
  147. {
  148. return null;
  149. }
  150. return new DataInfo
  151. {
  152. LastModified = fileAndPages.Metadata.Value<DateTime>(Constants.LastModified).ToUniversalTime(),
  153. Length = fileAndPages.TotalSize ?? 0,
  154. Name = fileAndPages.Name
  155. };
  156. }
  157. public override bool Equals(object obj)
  158. {
  159. if (ReferenceEquals(null, obj)) return false;
  160. if (ReferenceEquals(this, obj)) return true;
  161. if (obj.GetType() != typeof(ContentUpdateWorkItem)) return false;
  162. return Equals((ContentUpdateWorkItem)obj);
  163. }
  164. public bool Equals(ContentUpdateWorkItem other)
  165. {
  166. if (ReferenceEquals(null, other)) return false;
  167. if (ReferenceEquals(this, other)) return true;
  168. return Equals(other.FileName, FileName) && Equals(other.FileETag, FileETag);
  169. }
  170. public override int GetHashCode()
  171. {
  172. return (FileName != null ? GetType().Name.GetHashCode() ^ FileName.GetHashCode() ^ FileETag.GetHashCode() : 0);
  173. }
  174. public override string ToString()
  175. {
  176. return string.Format("Synchronization of a file content '{0}'", FileName);
  177. }
  178. }
  179. }