/ToMigrate/Raven.Tests.Issues/RavenDB_3573.cs

https://github.com/fitzchak/ravendb · C# · 437 lines · 324 code · 96 blank · 17 comment · 10 complexity · cdf32c70b8769ad98e287629a94459f4 MD5 · raw file

  1. using System;
  2. using System.Linq;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using Raven.Abstractions.Data;
  6. using Raven.Abstractions.Indexing;
  7. using Raven.Bundles.Replication.Tasks;
  8. using Raven.Client.Indexes;
  9. using Raven.Database;
  10. using Raven.Tests.Common;
  11. using Xunit;
  12. namespace Raven.Tests.Issues
  13. {
  14. public class RavenDB_3573 : ReplicationBase
  15. {
  16. public class Foo
  17. {
  18. public string Id { get; set; }
  19. public int Bar { get; set; }
  20. }
  21. public class User
  22. {
  23. public string Id { get; set; }
  24. public string Name { get; set; }
  25. }
  26. public class UserIndex : AbstractIndexCreationTask<User>
  27. {
  28. public UserIndex()
  29. {
  30. Map = users => from user in users
  31. select new
  32. {
  33. user.Name,
  34. Date = new DateTime(2015,1,1)
  35. };
  36. }
  37. }
  38. public class UserIndex2 : AbstractIndexCreationTask<User>
  39. {
  40. private string indexName;
  41. public override string IndexName
  42. {
  43. get { return indexName; }
  44. }
  45. public UserIndex2()
  46. {
  47. Map = users => from user in users
  48. select new
  49. {
  50. user.Name,
  51. Date1 = new DateTime(2015, 1, 1),
  52. Date2 = new DateTime(1998, 1, 1),
  53. };
  54. }
  55. public void SetName(string name)
  56. {
  57. indexName = name;
  58. }
  59. }
  60. [Fact]
  61. public async Task Side_by_side_index_should_be_replicated()
  62. {
  63. using(var source = CreateStore())
  64. using (var destination = CreateStore())
  65. {
  66. var testIndex = new UserIndex();
  67. var oldIndexDef = new IndexDefinition
  68. {
  69. Map = "from user in docs.Users\n select new {\n\tName = user.Name\n}"
  70. };
  71. source.DatabaseCommands.PutIndex(testIndex.IndexName, oldIndexDef);
  72. var sourceDatabase = await servers[0].Server.GetDatabaseInternal(source.DefaultDatabase);
  73. var destinationDatabase = await servers[1].Server.GetDatabaseInternal(destination.DefaultDatabase);
  74. sourceDatabase.StopBackgroundWorkers();
  75. destinationDatabase.StopBackgroundWorkers();
  76. var sourceReplicationTask = sourceDatabase.StartupTasks.OfType<ReplicationTask>().First();
  77. sourceReplicationTask.Pause();
  78. SetupReplication(source.DatabaseCommands, destination);
  79. sourceReplicationTask.IndexReplication.Execute(); //now the old index replicated to destination
  80. var sideBySideIndexReplicated = new ManualResetEventSlim();
  81. var replaceIndexName = Constants.SideBySideIndexNamePrefix + testIndex.IndexName;
  82. destinationDatabase.Notifications.OnIndexChange += (database, notification) =>
  83. {
  84. if (notification.Type == IndexChangeTypes.IndexAdded &&
  85. notification.Name.Equals(replaceIndexName))
  86. sideBySideIndexReplicated.Set();
  87. };
  88. testIndex.SideBySideExecute(source);
  89. sourceReplicationTask.IndexReplication.Execute();
  90. Assert.True(sideBySideIndexReplicated.Wait(2000));
  91. var definition = destination.DatabaseCommands.GetIndex(replaceIndexName);
  92. Assert.NotNull(definition);
  93. Assert.True(definition.Equals(testIndex.CreateIndexDefinition(),false));
  94. var replacementIndexName = Constants.SideBySideIndexNamePrefix + testIndex.IndexName;
  95. VerifyReplacementDocumentIsThere(replacementIndexName, destinationDatabase);
  96. }
  97. }
  98. [Fact]
  99. public async Task If_deleted_original_index_on_destination_should_simply_create_the_replacement_index()
  100. {
  101. using (var source = CreateStore())
  102. using (var destination = CreateStore())
  103. {
  104. var sourceDatabase = await servers[0].Server.GetDatabaseInternal(source.DefaultDatabase);
  105. var destinationDatabase = await servers[1].Server.GetDatabaseInternal(destination.DefaultDatabase);
  106. sourceDatabase.StopBackgroundWorkers();
  107. destinationDatabase.StopBackgroundWorkers();
  108. var testIndex = new UserIndex();
  109. var oldIndexDef = new IndexDefinition
  110. {
  111. Map = "from user in docs.Users\n select new {\n\tName = user.Name\n}"
  112. };
  113. source.DatabaseCommands.PutIndex(testIndex.IndexName, oldIndexDef);
  114. using (var session = source.OpenSession())
  115. {
  116. for (var i = 0; i < 10; i++)
  117. session.Store(new User
  118. {
  119. Name = "User - " + i
  120. });
  121. session.SaveChanges();
  122. }
  123. var sourceReplicationTask = sourceDatabase.StartupTasks.OfType<ReplicationTask>().First();
  124. sourceReplicationTask.Pause();
  125. SetupReplication(source.DatabaseCommands, destination);
  126. testIndex.SideBySideExecute(source);
  127. //the side by side will be automatically replicated and saved as a simple index
  128. Assert.Null(destinationDatabase.Indexes.GetIndexDefinition(Constants.SideBySideIndexNamePrefix + testIndex.IndexName));
  129. var definition = destination.DatabaseCommands.GetIndex(testIndex.IndexName);
  130. Assert.NotNull(definition);
  131. Assert.True(definition.Equals(testIndex.CreateIndexDefinition(), false));
  132. }
  133. }
  134. [Fact]
  135. public async Task If_original_index_exists_but_no_side_by_side_index_then_create_side_by_side_index()
  136. {
  137. using (var source = CreateStore())
  138. using (var destination = CreateStore())
  139. {
  140. var testIndex = new UserIndex();
  141. var oldIndexDef = new IndexDefinition
  142. {
  143. Map = "from user in docs.Users\n select new {\n\tName = user.Name\n}"
  144. };
  145. source.DatabaseCommands.PutIndex(testIndex.IndexName, oldIndexDef);
  146. var sourceDatabase = await servers[0].Server.GetDatabaseInternal(source.DefaultDatabase);
  147. var destinationDatabase = await servers[1].Server.GetDatabaseInternal(destination.DefaultDatabase);
  148. sourceDatabase.StopBackgroundWorkers();
  149. destinationDatabase.StopBackgroundWorkers();
  150. var sourceReplicationTask = sourceDatabase.StartupTasks.OfType<ReplicationTask>().First();
  151. sourceReplicationTask.Pause();
  152. SetupReplication(source.DatabaseCommands, destination);
  153. //replicate the original index
  154. sourceReplicationTask.IndexReplication.Execute();
  155. testIndex.SideBySideExecute(source);
  156. //do side-by-side index replication -> since in the destination there is original index,
  157. //simply create the side-by-side index as so it will replace the original when it catches up
  158. sourceReplicationTask.IndexReplication.Execute();
  159. var originalDefinition = destination.DatabaseCommands.GetIndex(testIndex.IndexName);
  160. Assert.NotNull(originalDefinition);
  161. Assert.True(originalDefinition.Equals(oldIndexDef, false));
  162. var sideBySideDefinition = destination.DatabaseCommands.GetIndex(Constants.SideBySideIndexNamePrefix + testIndex.IndexName);
  163. Assert.NotNull(sideBySideDefinition);
  164. Assert.True(sideBySideDefinition.Equals(testIndex.CreateIndexDefinition(), false));
  165. }
  166. }
  167. [Fact]
  168. public async Task Side_by_side_index_after_replication_should_have_appropriate_minimum_etag_for_the_destination_if_applicable()
  169. {
  170. using (var source = CreateStore())
  171. using (var destination = CreateStore())
  172. {
  173. var testIndex = new UserIndex();
  174. var oldIndexDef = new IndexDefinition
  175. {
  176. Map = "from user in docs.Users\n select new {\n\tName = user.Name\n}"
  177. };
  178. source.DatabaseCommands.PutIndex(testIndex.IndexName, oldIndexDef);
  179. var sourceDatabase = await servers[0].Server.GetDatabaseInternal(source.DefaultDatabase);
  180. var destinationDatabase = await servers[1].Server.GetDatabaseInternal(destination.DefaultDatabase);
  181. using (var session = source.OpenSession())
  182. {
  183. for(int i = 0; i < 10; i++)
  184. session.Store(new User
  185. {
  186. Name = "User - " + i
  187. });
  188. session.SaveChanges();
  189. }
  190. WaitForIndexing(source);
  191. sourceDatabase.StopBackgroundWorkers();
  192. destinationDatabase.StopBackgroundWorkers();
  193. var sourceReplicationTask = sourceDatabase.StartupTasks.OfType<ReplicationTask>().First();
  194. sourceReplicationTask.Pause();
  195. SetupReplication(source.DatabaseCommands, destination);
  196. //replicate the original index
  197. await sourceReplicationTask.ExecuteReplicationOnce(true);
  198. sourceReplicationTask.IndexReplication.Execute();
  199. destinationDatabase.SpinBackgroundWorkers();
  200. WaitForIndexing(destination);
  201. destinationDatabase.StopBackgroundWorkers();
  202. testIndex.SideBySideExecute(source,sourceDatabase.Statistics.LastDocEtag);
  203. var replacementIndexName = Constants.SideBySideIndexNamePrefix + testIndex.IndexName;
  204. //do side-by-side index replication -> since in the destination there is original index,
  205. //simply create the side-by-side index as so it will replace the original when it catches up
  206. sourceReplicationTask.IndexReplication.Execute();
  207. var originalDefinition = destination.DatabaseCommands.GetIndex(testIndex.IndexName);
  208. Assert.NotNull(originalDefinition);
  209. Assert.True(originalDefinition.Equals(oldIndexDef, false));
  210. var sideBySideDefinition = destination.DatabaseCommands.GetIndex(replacementIndexName);
  211. Assert.NotNull(sideBySideDefinition);
  212. Assert.True(sideBySideDefinition.Equals(testIndex.CreateIndexDefinition(), false));
  213. VerifyReplacementDocumentIsThere(replacementIndexName, destinationDatabase, true);
  214. }
  215. }
  216. [Fact]
  217. public async Task Out_of_date_side_by_side_index_will_get_updated_on_replication()
  218. {
  219. using (var source = CreateStore())
  220. using (var destination = CreateStore())
  221. {
  222. var testIndex = new UserIndex();
  223. var testIndex2 = new UserIndex2();
  224. testIndex2.SetName(testIndex.IndexName);
  225. var oldIndexDef = new IndexDefinition
  226. {
  227. Map = "from user in docs.Users\n select new {\n\tName = user.Name\n}"
  228. };
  229. source.DatabaseCommands.PutIndex(testIndex.IndexName, oldIndexDef);
  230. var sourceDatabase = await servers[0].Server.GetDatabaseInternal(source.DefaultDatabase);
  231. var destinationDatabase = await servers[1].Server.GetDatabaseInternal(destination.DefaultDatabase);
  232. using (var session = source.OpenSession())
  233. {
  234. session.Store(new User
  235. {
  236. Name = "John Doe"
  237. });
  238. session.SaveChanges();
  239. }
  240. WaitForIndexing(source);
  241. sourceDatabase.StopBackgroundWorkers();
  242. destinationDatabase.StopBackgroundWorkers();
  243. var sourceReplicationTask = sourceDatabase.StartupTasks.OfType<ReplicationTask>().First();
  244. sourceReplicationTask.Pause();
  245. SetupReplication(source.DatabaseCommands, destination);
  246. sourceReplicationTask.IndexReplication.Execute(); //replicate the usual index
  247. source.SideBySideExecuteIndex(testIndex);
  248. sourceReplicationTask.IndexReplication.Execute(); //replicate the side-by-side index
  249. //sanity check
  250. Assert.NotNull(destinationDatabase.Indexes.GetIndexDefinition(Constants.SideBySideIndexNamePrefix + testIndex.IndexName));
  251. sourceDatabase.Indexes.DeleteIndex(Constants.SideBySideIndexNamePrefix + testIndex.IndexName);
  252. source.SideBySideExecuteIndex(testIndex2); //replaces the testIndex side-by-side index on source
  253. sourceReplicationTask.IndexReplication.Execute(); //should replicate the replaced side-by-sude index to destination
  254. var sideBySideIndex = destinationDatabase.Indexes.GetIndexDefinition(Constants.SideBySideIndexNamePrefix + testIndex.IndexName);
  255. Assert.NotNull(sideBySideIndex);
  256. Assert.True(sideBySideIndex.Equals(testIndex2.CreateIndexDefinition(),false));
  257. }
  258. }
  259. /*
  260. * This tests the following scenario
  261. * 1) Index + Side-by-Side index replicated to destination
  262. * 2) Original index is deleted at destination, side-by-side index remains
  263. * 3) Index + Side-by-Side index replicated to destination again
  264. * 4) Result: The original index is recreated with side-by-side definition; The remaining side-by-side index on destination is deleted
  265. */
  266. [Fact]
  267. public async Task If_deleted_original_index_on_destination_but_not_side_by_side_index()
  268. {
  269. using (var source = CreateStore())
  270. using (var destination = CreateStore())
  271. {
  272. var sourceDatabase = await servers[0].Server.GetDatabaseInternal(source.DefaultDatabase);
  273. var destinationDatabase = await servers[1].Server.GetDatabaseInternal(destination.DefaultDatabase);
  274. sourceDatabase.StopBackgroundWorkers();
  275. destinationDatabase.StopBackgroundWorkers();
  276. var testIndex = new UserIndex();
  277. var oldIndexDef = new IndexDefinition
  278. {
  279. Map = "from user in docs.Users\n select new {\n\tName = user.Name\n}"
  280. };
  281. source.DatabaseCommands.PutIndex(testIndex.IndexName, oldIndexDef);
  282. using (var session = source.OpenSession())
  283. {
  284. session.Store(new User
  285. {
  286. Name = "John Doe"
  287. });
  288. session.SaveChanges();
  289. }
  290. var sourceReplicationTask = sourceDatabase.StartupTasks.OfType<ReplicationTask>().First();
  291. sourceReplicationTask.Pause();
  292. SetupReplication(source.DatabaseCommands, destination);
  293. sourceReplicationTask.IndexReplication.Execute(); //replicate the usual index
  294. source.SideBySideExecuteIndex(testIndex);
  295. //the side by side index will be automatically replicated
  296. SpinWait.SpinUntil(() =>
  297. {
  298. var index = destinationDatabase.Indexes.GetIndexDefinition(Constants.SideBySideIndexNamePrefix + testIndex.IndexName);
  299. return index != null;
  300. }, 5000);
  301. Assert.NotNull(destinationDatabase.Indexes.GetIndexDefinition(Constants.SideBySideIndexNamePrefix + testIndex.IndexName));
  302. destinationDatabase.Indexes.DeleteIndex(testIndex.IndexName); //delete the original index
  303. var sideBySideIndex = destinationDatabase.Indexes.GetIndexDefinition(Constants.SideBySideIndexNamePrefix + testIndex.IndexName);
  304. Assert.NotNull(sideBySideIndex);
  305. VerifyReplacementDocumentIsThere(Constants.SideBySideIndexNamePrefix + testIndex.IndexName, destinationDatabase);
  306. sourceReplicationTask.IndexReplication.Execute();
  307. var indexDefinition = destinationDatabase.Indexes.Definitions.First(x => x.Name == Constants.SideBySideIndexNamePrefix + testIndex.IndexName);
  308. destinationDatabase.IndexReplacer.ForceReplacement(indexDefinition);
  309. //wait until the index will be replaced
  310. SpinWait.SpinUntil(() =>
  311. {
  312. var index = destinationDatabase.Indexes.GetIndexDefinition(testIndex.IndexName);
  313. return index != null;
  314. }, 5000);
  315. var replacingIndex = destinationDatabase.Indexes.GetIndexDefinition(testIndex.IndexName);
  316. Assert.True(replacingIndex != null);
  317. var oldIndex = destinationDatabase.Indexes.GetIndexDefinition(testIndex.IndexName);
  318. Assert.True(oldIndex.Equals(testIndex.CreateIndexDefinition(), false));
  319. sideBySideIndex = destinationDatabase.Indexes.GetIndexDefinition(Constants.SideBySideIndexNamePrefix + testIndex.IndexName);
  320. Assert.Null(sideBySideIndex);
  321. SpinWait.SpinUntil(() =>
  322. {
  323. var doc = destinationDatabase.Documents.Get(Constants.IndexReplacePrefix + Constants.SideBySideIndexNamePrefix + testIndex.IndexName, null);
  324. return doc == null;
  325. }, 5000);
  326. Assert.Null(destinationDatabase.Documents.Get(Constants.IndexReplacePrefix + Constants.SideBySideIndexNamePrefix + testIndex.IndexName, null));
  327. }
  328. }
  329. private static void VerifyReplacementDocumentIsThere(string replacementIndexName, DocumentDatabase destinationDatabase,bool shouldVerifyEtag = false)
  330. {
  331. var id = Constants.IndexReplacePrefix + replacementIndexName;
  332. var documentInfo = destinationDatabase.Documents.Get(id, null);
  333. Assert.NotNull(documentInfo);
  334. if (shouldVerifyEtag)
  335. {
  336. var indexReplacementDoc = documentInfo.DataAsJson;
  337. Assert.Equal(destinationDatabase.Statistics.LastDocEtag, Etag.Parse(indexReplacementDoc.Value<string>("MinimumEtagBeforeReplace")));
  338. }
  339. }
  340. }
  341. }