/ToMigrate/Raven.Tests.Issues/RavenDB_3484.cs

https://github.com/fitzchak/ravendb · C# · 385 lines · 298 code · 82 blank · 5 comment · 8 complexity · caeb8e872e0f4af035d042ef9b6d40e7 MD5 · raw file

  1. // -----------------------------------------------------------------------
  2. // <copyright file="RavenDB_3484.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. // -----------------------------------------------------------------------
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Threading;
  9. using Raven.Abstractions.Data;
  10. using Raven.Abstractions.Exceptions.Subscriptions;
  11. using Raven.Client.Document;
  12. using Raven.Tests.Common;
  13. using Raven.Tests.Common.Dto;
  14. using Xunit;
  15. using Xunit.Extensions;
  16. namespace Raven.Tests.Issues
  17. {
  18. public class RavenDB_3484 : RavenTest
  19. {
  20. private readonly TimeSpan waitForDocTimeout = TimeSpan.FromSeconds(20);
  21. [Fact]
  22. public void OpenIfFree_ShouldBeDefaultStrategy()
  23. {
  24. Assert.Equal(SubscriptionOpeningStrategy.OpenIfFree, new SubscriptionConnectionOptions().Strategy);
  25. }
  26. [Theory]
  27. [PropertyData("Storages")]
  28. public void ShouldRejectWhen_OpenIfFree_StrategyIsUsed(string storage)
  29. {
  30. using (var store = NewDocumentStore(requestedStorage: storage))
  31. {
  32. var id = store.Subscriptions.Create(new SubscriptionCriteria());
  33. store.Subscriptions.Open(id, new SubscriptionConnectionOptions());
  34. store.Changes().WaitForAllPendingSubscriptions();
  35. Assert.Throws<SubscriptionInUseException>(() => store.Subscriptions.Open(id, new SubscriptionConnectionOptions()
  36. {
  37. Strategy = SubscriptionOpeningStrategy.OpenIfFree
  38. }));
  39. }
  40. }
  41. [Theory]
  42. [PropertyData("Storages")]
  43. public void ShouldReplaceActiveClientWhen_TakeOver_StrategyIsUsed(string storage)
  44. {
  45. using (var store = NewDocumentStore(requestedStorage: storage))
  46. {
  47. var id = store.Subscriptions.Create(new SubscriptionCriteria<User>());
  48. const int numberOfClients = 4;
  49. var subscriptions = new Subscription<User>[numberOfClients];
  50. var items = new BlockingCollection<User>[numberOfClients];
  51. for (int i = 0; i < numberOfClients; i++)
  52. {
  53. subscriptions[i] = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  54. {
  55. Strategy = i > 0 ? SubscriptionOpeningStrategy.TakeOver : SubscriptionOpeningStrategy.OpenIfFree
  56. });
  57. store.Changes().WaitForAllPendingSubscriptions();
  58. items[i] = new BlockingCollection<User>();
  59. subscriptions[i].Subscribe(items[i].Add);
  60. bool batchAcknowledged = false;
  61. subscriptions[i].AfterAcknowledgment += x => batchAcknowledged = true;
  62. using (var s = store.OpenSession())
  63. {
  64. s.Store(new User());
  65. s.Store(new User());
  66. s.SaveChanges();
  67. }
  68. User user;
  69. Assert.True(items[i].TryTake(out user, waitForDocTimeout));
  70. Assert.True(items[i].TryTake(out user, waitForDocTimeout));
  71. SpinWait.SpinUntil(() => batchAcknowledged, TimeSpan.FromSeconds(5)); // let it acknowledge the processed batch before we open another subscription
  72. if (i > 0)
  73. {
  74. Assert.False(items[i - 1].TryTake(out user, TimeSpan.FromSeconds(2)));
  75. Assert.True(SpinWait.SpinUntil(() => subscriptions[i - 1].IsConnectionClosed, TimeSpan.FromSeconds(5)));
  76. Assert.True(subscriptions[i - 1].SubscriptionConnectionException is SubscriptionInUseException);
  77. }
  78. }
  79. }
  80. }
  81. [Theory]
  82. [PropertyData("Storages")]
  83. public void ShouldReplaceActiveClientWhen_ForceAndKeep_StrategyIsUsed(string storage)
  84. {
  85. using (var store = NewDocumentStore(requestedStorage: storage))
  86. {
  87. foreach (var strategyToReplace in new[] { SubscriptionOpeningStrategy.OpenIfFree, SubscriptionOpeningStrategy.TakeOver, SubscriptionOpeningStrategy.WaitForFree })
  88. {
  89. var id = store.Subscriptions.Create(new SubscriptionCriteria<User>());
  90. var subscription = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  91. {
  92. Strategy = strategyToReplace
  93. });
  94. store.Changes().WaitForAllPendingSubscriptions();
  95. var items = new BlockingCollection<User>();
  96. subscription.Subscribe(items.Add);
  97. var forcedSubscription = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  98. {
  99. Strategy = SubscriptionOpeningStrategy.ForceAndKeep
  100. });
  101. store.Changes().WaitForAllPendingSubscriptions();
  102. var forcedItems = new BlockingCollection<User>();
  103. forcedSubscription.Subscribe(forcedItems.Add);
  104. using (var s = store.OpenSession())
  105. {
  106. s.Store(new User());
  107. s.Store(new User());
  108. s.SaveChanges();
  109. }
  110. User user;
  111. Assert.True(forcedItems.TryTake(out user, waitForDocTimeout));
  112. Assert.True(forcedItems.TryTake(out user, waitForDocTimeout));
  113. Assert.True(SpinWait.SpinUntil(() => subscription.IsConnectionClosed, TimeSpan.FromSeconds(5)));
  114. Assert.True(subscription.SubscriptionConnectionException is SubscriptionInUseException);
  115. }
  116. }
  117. }
  118. [Theory]
  119. [PropertyData("Storages")]
  120. public void OpenIfFree_And_TakeOver_StrategiesCannotDropClientWith_ForceAndKeep_Strategy(string storage)
  121. {
  122. using (var store = NewDocumentStore(requestedStorage: storage))
  123. {
  124. var id = store.Subscriptions.Create(new SubscriptionCriteria<User>());
  125. var forcedSubscription = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  126. {
  127. Strategy = SubscriptionOpeningStrategy.ForceAndKeep
  128. });
  129. foreach (var strategy in new[] { SubscriptionOpeningStrategy.OpenIfFree, SubscriptionOpeningStrategy.TakeOver })
  130. {
  131. Assert.Throws<SubscriptionInUseException>(() => store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  132. {
  133. Strategy = strategy
  134. }));
  135. }
  136. }
  137. }
  138. [Theory]
  139. [PropertyData("Storages")]
  140. public void ForceAndKeep_StrategyUsageCanTakeOverAnotherClientWith_ForceAndKeep_Strategy(string storage)
  141. {
  142. using (var store = NewDocumentStore(requestedStorage: storage))
  143. {
  144. var id = store.Subscriptions.Create(new SubscriptionCriteria<User>());
  145. const int numberOfClients = 4;
  146. var subscriptions = new Subscription<User>[numberOfClients];
  147. try
  148. {
  149. var items = new BlockingCollection<User>[numberOfClients];
  150. for (int i = 0; i < numberOfClients; i++)
  151. {
  152. subscriptions[i] = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  153. {
  154. Strategy = SubscriptionOpeningStrategy.ForceAndKeep
  155. });
  156. store.Changes().WaitForAllPendingSubscriptions();
  157. items[i] = new BlockingCollection<User>();
  158. subscriptions[i].Subscribe(items[i].Add);
  159. bool batchAcknowledged = false;
  160. subscriptions[i].AfterAcknowledgment += x => batchAcknowledged = true;
  161. using (var s = store.OpenSession())
  162. {
  163. s.Store(new User());
  164. s.Store(new User());
  165. s.SaveChanges();
  166. }
  167. User user;
  168. Assert.True(items[i].TryTake(out user, waitForDocTimeout), $"Waited for {waitForDocTimeout.TotalSeconds} seconds to get notified about a user, giving up... ");
  169. Assert.True(items[i].TryTake(out user, waitForDocTimeout), $"Waited for {waitForDocTimeout.TotalSeconds} seconds to get notified about a user, giving up... ");
  170. SpinWait.SpinUntil(() => batchAcknowledged, TimeSpan.FromSeconds(5)); // let it acknowledge the processed batch before we open another subscription
  171. Assert.True(batchAcknowledged, "Wait for 5 seconds for batch to be acknoeledged, giving up...");
  172. if (i > 0)
  173. {
  174. Assert.False(items[i - 1].TryTake(out user, TimeSpan.FromSeconds(2)),
  175. "Was able to take a connection to subscription even though a new connection was opened with ForceAndKeep strategy.");
  176. Assert.True(SpinWait.SpinUntil(() => subscriptions[i - 1].IsConnectionClosed, TimeSpan.FromSeconds(5)),
  177. "Previous connection to subscription was not closed even though a new connection was opened with ForceAndKeep strategy.");
  178. Assert.True(subscriptions[i - 1].SubscriptionConnectionException is SubscriptionInUseException,
  179. "SubscriptionConnectionException is not set to expected type, SubscriptionInUseException.");
  180. }
  181. }
  182. }
  183. finally
  184. {
  185. foreach (var subscription in subscriptions)
  186. {
  187. subscription?.Dispose();
  188. }
  189. }
  190. }
  191. }
  192. [Theory]
  193. [PropertyData("Storages")]
  194. public void ShouldOpenSubscriptionWith_WaitForFree_StrategyWhenItIsNotInUseByAnotherClient(string storage)
  195. {
  196. using (var store = NewDocumentStore(requestedStorage: storage))
  197. {
  198. var id = store.Subscriptions.Create(new SubscriptionCriteria<User>());
  199. var subscription = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  200. {
  201. Strategy = SubscriptionOpeningStrategy.WaitForFree
  202. });
  203. var items = new BlockingCollection<User>();
  204. subscription.Subscribe(items.Add);
  205. using (var s = store.OpenSession())
  206. {
  207. s.Store(new User());
  208. s.Store(new User());
  209. s.SaveChanges();
  210. }
  211. User user;
  212. Assert.True(items.TryTake(out user, waitForDocTimeout));
  213. Assert.True(items.TryTake(out user, waitForDocTimeout));
  214. }
  215. }
  216. [Theory]
  217. [PropertyData("Storages")]
  218. public void ShouldProcessSubscriptionAfterItGetsReleasedWhen_WaitForFree_StrategyIsSet(string storage)
  219. {
  220. using (var store = NewDocumentStore(requestedStorage: storage))
  221. {
  222. var id = store.Subscriptions.Create(new SubscriptionCriteria<User>());
  223. var userId = 0;
  224. foreach (var activeClientStrategy in new []{SubscriptionOpeningStrategy.OpenIfFree, SubscriptionOpeningStrategy.TakeOver, SubscriptionOpeningStrategy.ForceAndKeep})
  225. {
  226. var active = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  227. {
  228. Strategy = activeClientStrategy
  229. });
  230. var pending = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  231. {
  232. Strategy = SubscriptionOpeningStrategy.WaitForFree
  233. });
  234. Assert.Null(pending.SubscriptionConnectionException);
  235. Assert.False(pending.IsConnectionClosed);
  236. bool batchAcknowledged = false;
  237. pending.AfterAcknowledgment += x => batchAcknowledged = true;
  238. var items = new BlockingCollection<User>();
  239. pending.Subscribe(items.Add);
  240. active.Dispose(); // disconnect the active client, the pending one should be notified the the subscription is free and retry to open it
  241. using (var s = store.OpenSession())
  242. {
  243. s.Store(new User(), "users/" + userId++);
  244. s.Store(new User(), "users/" + userId++);
  245. s.SaveChanges();
  246. }
  247. User user;
  248. Assert.True(items.TryTake(out user, waitForDocTimeout));
  249. Assert.Equal("users/" + (userId - 2), user.Id);
  250. Assert.True(items.TryTake(out user, waitForDocTimeout));
  251. Assert.Equal("users/" + (userId - 1), user.Id);
  252. Assert.True(SpinWait.SpinUntil(() => batchAcknowledged, TimeSpan.FromSeconds(5))); // let it acknowledge the processed batch before we open another subscription
  253. pending.Dispose();
  254. }
  255. }
  256. }
  257. [Theory]
  258. [PropertyData("Storages")]
  259. public void AllClientsWith_WaitForFree_StrategyShouldGetAccessToSubscription(string storage)
  260. {
  261. using (var store = NewDocumentStore(requestedStorage: storage))
  262. {
  263. var id = store.Subscriptions.Create(new SubscriptionCriteria<User>());
  264. const int numberOfClients = 4;
  265. var subscriptions = new Subscription<User>[numberOfClients];
  266. var processed = new bool[numberOfClients];
  267. int? processedClient = null;
  268. for (int i = 0; i < numberOfClients; i++)
  269. {
  270. var clientNumber = i;
  271. subscriptions[clientNumber] = store.Subscriptions.Open<User>(id, new SubscriptionConnectionOptions()
  272. {
  273. Strategy = SubscriptionOpeningStrategy.WaitForFree
  274. });
  275. subscriptions[clientNumber].AfterBatch += x =>
  276. {
  277. processed[clientNumber] = true;
  278. };
  279. subscriptions[clientNumber].Subscribe(x =>
  280. {
  281. processedClient = clientNumber;
  282. });
  283. }
  284. for (int i = 0; i < numberOfClients; i++)
  285. {
  286. using (var s = store.OpenSession())
  287. {
  288. s.Store(new User());
  289. s.SaveChanges();
  290. }
  291. Assert.True(SpinWait.SpinUntil(() => processedClient != null, waitForDocTimeout));
  292. Assert.True(SpinWait.SpinUntil(() => processed[processedClient.Value], waitForDocTimeout));
  293. subscriptions[processedClient.Value].Dispose();
  294. processedClient = null;
  295. }
  296. for (int i = 0; i < numberOfClients; i++)
  297. {
  298. Assert.True(processed[i]);
  299. }
  300. }
  301. }
  302. }
  303. }