public async Task CanDropAndConnectInDifferentMode(SubscriptionOpeningStrategy strategy1, SubscriptionOpeningStrategy strategy2)
        {
            using (var store = GetDocumentStore())
            {
                var id = store.Subscriptions.Create <User>();
                using var subscription1 = store.Subscriptions.GetSubscriptionWorker(new SubscriptionWorkerOptions(id)
                {
                    Strategy = strategy1,
                });
                using var subscription2 = store.Subscriptions.GetSubscriptionWorker(new SubscriptionWorkerOptions(id)
                {
                    Strategy = strategy2,
                });

                var mre1 = new ManualResetEventSlim();
                var mre2 = new ManualResetEventSlim();

                subscription1.OnEstablishedSubscriptionConnection += mre1.Set;
                subscription2.OnEstablishedSubscriptionConnection += mre2.Set;

                var t = subscription1.Run(x =>
                {
                });

                Assert.True(mre1.Wait(TimeSpan.FromSeconds(15)));
                mre1.Reset();

                await store.Subscriptions.DropSubscriptionWorkerAsync(subscription1);

                await Assert.ThrowsAsync <SubscriptionClosedException>(() => t);

                t = subscription2.Run((_) => { });
                Assert.True(mre2.Wait(TimeSpan.FromSeconds(15)));
            }
        }
        private static async Task CreateWorker(DocumentStore store, string subscriptionName, SubscriptionOpeningStrategy strategy = SubscriptionOpeningStrategy.WaitForFree, CancellationToken token = default)
        {
            while (token.IsCancellationRequested == false)
            {
                try
                {
                    using (var subscription = store.Subscriptions.GetSubscriptionWorker <User>(new SubscriptionWorkerOptions(subscriptionName)
                    {
                        TimeToWaitBeforeConnectionRetry = TimeSpan.FromMilliseconds(500),
                        MaxDocsPerBatch = 5,
                        MaxErroneousPeriod = TimeSpan.FromSeconds(5),
                        Strategy = strategy,
                    }))
                    {
                        await subscription.Run((async batch =>
                        {
                            if (Interlocked.Increment(ref _batchCount) % 11 == 0)
                            {
                                throw new InvalidOperationException("Random sub failure before");
                            }

                            using (var session = batch.OpenAsyncSession())
                            {
                                foreach (var item in batch.Items)
                                {
                                    item.Result.Count--;
                                }

                                await session.SaveChangesAsync(token);
                            }

                            if (Interlocked.Increment(ref _batchCount) % 7 == 0)
                            {
                                throw new InvalidOperationException("Random sub failure after");
                            }
                        }));
                    }
                }
                catch
                {
                    await Task.Delay(500, token);
                }
            }
        }
示例#3
0
        public void ShouldWaitForClientToProcessCurrentBatchBeforeTakingOver(SubscriptionOpeningStrategy openingStrategy)
        {
            using (var store = NewDocumentStore())
            {
                // fill in database to make sure first subscription has something to process
                using (var s = store.OpenSession())
                {
                    s.Store(new User());
                    s.Store(new User());
                    s.SaveChanges();
                }

                var id           = store.Subscriptions.Create(new SubscriptionCriteria <User>());
                var subscription = store.Subscriptions.Open <User>(id, new SubscriptionConnectionOptions()
                {
                    Strategy = SubscriptionOpeningStrategy.OpenIfFree
                });
                store.Changes().WaitForAllPendingSubscriptions();
                var items = new BlockingCollection <User>();

                var firstSubscriptionStartedProcessingMre = new ManualResetEvent(false);

                subscription.Subscribe(item =>
                {
                    firstSubscriptionStartedProcessingMre.Set();
                    Thread.Sleep(500);
                    items.Add(item);
                });

                // we want to make sure that we open second subscription when first one in processed
                Assert.True(firstSubscriptionStartedProcessingMre.WaitOne(TimeSpan.FromSeconds(10)));

                var forcedSubscription = store.Subscriptions.Open <User>(id, new SubscriptionConnectionOptions()
                {
                    Strategy = openingStrategy
                });

                store.Changes().WaitForAllPendingSubscriptions();

                var forcedItems = new BlockingCollection <User>();

                forcedSubscription.Subscribe(forcedItems.Add);

                User user;
                // first subscription should contain 2 elements
                Assert.True(items.TryTake(out user, waitForDocTimeout));
                Assert.True(items.TryTake(out user, waitForDocTimeout));
                Assert.False(items.TryTake(out user, TimeSpan.FromSeconds(1)));

                // forcedItems should be empty at this point as server should wait for first subscription
                // to complete, thus leaving no records to process for second subscription

                Assert.False(forcedItems.TryTake(out user, waitForDocTimeout));
                Assert.True(SpinWait.SpinUntil(() => subscription.IsConnectionClosed, TimeSpan.FromSeconds(5)));
                Assert.True(subscription.SubscriptionConnectionException is SubscriptionInUseException);

                // now fill database with extra records
                using (var s = store.OpenSession())
                {
                    s.Store(new User());
                    s.Store(new User());
                    s.SaveChanges();
                }

                // only second subscription should get results
                Assert.True(forcedItems.TryTake(out user, waitForDocTimeout));
                Assert.True(forcedItems.TryTake(out user, waitForDocTimeout));
                Assert.False(forcedItems.TryTake(out user, TimeSpan.FromSeconds(1)));

                Assert.False(items.TryTake(out user, TimeSpan.FromSeconds(1)));
            }
        }
示例#4
0
        public void CanTakeOverEvenAfterPreviousSubscriptionTimedOut(SubscriptionOpeningStrategy openingStrategy)
        {
            using (var store = NewDocumentStore())
            {
                // fill in database to make sure first subscription has something to process
                using (var s = store.OpenSession())
                {
                    s.Store(new User());
                    s.Store(new User());
                    s.SaveChanges();
                }

                var id           = store.Subscriptions.Create(new SubscriptionCriteria <User>());
                var subscription = store.Subscriptions.Open <User>(id, new SubscriptionConnectionOptions
                {
                    Strategy     = SubscriptionOpeningStrategy.OpenIfFree,
                    BatchOptions = new SubscriptionBatchOptions
                    {
                        AcknowledgmentTimeout = TimeSpan.FromSeconds(5)
                    }
                });
                store.Changes().WaitForAllPendingSubscriptions();
                var items = new BlockingCollection <User>();

                var firstSubscriptionStartedProcessingMre = new ManualResetEvent(false);

                subscription.Subscribe(item =>
                {
                    firstSubscriptionStartedProcessingMre.Set();
                    Thread.Sleep(3000); // 2 objects * 3000 gives to 6 second to process, so we can't ack batch
                    items.Add(item);
                });

                // we want to make sure that we open second subscription when first one in processed
                Assert.True(firstSubscriptionStartedProcessingMre.WaitOne(TimeSpan.FromSeconds(10)));

                var forcedSubscription = store.Subscriptions.Open <User>(id, new SubscriptionConnectionOptions()
                {
                    Strategy = openingStrategy
                });

                store.Changes().WaitForAllPendingSubscriptions();

                var forcedItems = new BlockingCollection <User>();

                forcedSubscription.Subscribe(forcedItems.Add);

                User user;
                // first subscription should contain 2 elements - how ever this batch won't be acknowledged
                Assert.True(items.TryTake(out user, waitForDocTimeout));
                Assert.True(items.TryTake(out user, waitForDocTimeout));
                Assert.False(items.TryTake(out user, TimeSpan.FromSeconds(1)));

                // forcedItems should contain also 2 elements because batch in first subscription was not acknowledged on time
                Assert.True(forcedItems.TryTake(out user, waitForDocTimeout));
                Assert.True(forcedItems.TryTake(out user, waitForDocTimeout));
                Assert.False(forcedItems.TryTake(out user, TimeSpan.FromSeconds(1)));

                Assert.True(SpinWait.SpinUntil(() => subscription.IsConnectionClosed, TimeSpan.FromSeconds(5)));
                Assert.True(subscription.SubscriptionConnectionException is SubscriptionInUseException);
            }
        }
示例#5
0
 public void RecordConnectionInfo(SubscriptionState subscriptionState, string clientUri, SubscriptionOpeningStrategy strategy, string workerId)
 {
     _stats.TaskId    = subscriptionState.SubscriptionId;
     _stats.TaskName  = subscriptionState.SubscriptionName;
     _stats.ClientUri = clientUri;
     _stats.Strategy  = strategy;
     _stats.WorkerId  = workerId;
 }