예제 #1
0
        public async Task Should_propagate_exception_to_subscriber()
        {
            var ex = new InvalidOperationException();

            A.CallTo(() => eventStore.GetEventsAsync(A <Func <StoredEvent, Task> > .Ignored, A <CancellationToken> .Ignored, "^my-stream", position))
            .Throws(ex);

            var sut = new PollingSubscription(eventStore, eventNotifier, eventSubscriber, "^my-stream", position);

            await WaitAndStopAsync(sut);

            A.CallTo(() => eventSubscriber.OnErrorAsync(sut, ex))
            .MustHaveHappened();
        }
예제 #2
0
        public async Task Should_propagate_exception_to_subscriber()
        {
            var ex = new InvalidOperationException();

            A.CallTo(() => eventStore.QueryAllAsync("^my-stream", position, A <long> ._, A <CancellationToken> ._))
            .Throws(ex);

            var sut = new PollingSubscription(eventStore, eventSubscriber, "^my-stream", position);

            await WaitAndStopAsync(sut);

            A.CallTo(() => eventSubscriber.OnErrorAsync(sut, ex))
            .MustHaveHappened();
        }
예제 #3
0
        public PollingSubscription(
            IEventStore eventStore,
            IEventSubscriber eventSubscriber,
            string?streamFilter,
            string?position)
        {
            Guard.NotNull(eventStore, nameof(eventStore));
            Guard.NotNull(eventSubscriber, nameof(eventSubscriber));

            timer = new CompletionTimer(5000, async ct =>
            {
                try
                {
                    await foreach (var storedEvent in eventStore.QueryAllAsync(streamFilter, position, ct: ct))
                    {
                        await eventSubscriber.OnEventAsync(this, storedEvent);

                        position = storedEvent.EventPosition;
                    }
                }
                catch (Exception ex)
                {
                    await eventSubscriber.OnErrorAsync(this, ex);
                }
            });
        }
예제 #4
0
        public PollingSubscription(
            IEventStore eventStore,
            IEventSubscriber eventSubscriber,
            string?streamFilter,
            string?position)
        {
            Guard.NotNull(eventStore);
            Guard.NotNull(eventSubscriber);

            timer = new CompletionTimer(5000, async ct =>
            {
                try
                {
                    await eventStore.QueryAsync(async storedEvent =>
                    {
                        await eventSubscriber.OnEventAsync(this, storedEvent);

                        position = storedEvent.EventPosition;
                    }, streamFilter, position, ct);
                }
                catch (Exception ex)
                {
                    if (!ex.Is <OperationCanceledException>())
                    {
                        await eventSubscriber.OnErrorAsync(this, ex);
                    }
                }
            });
        }
예제 #5
0
 public async Task CloseAsync(IChangeFeedObserverContext context, ChangeFeedObserverCloseReason reason)
 {
     if (reason == ChangeFeedObserverCloseReason.ObserverError)
     {
         await subscriber.OnErrorAsync(this, new InvalidOperationException("Change feed observer failed."));
     }
 }
예제 #6
0
        public CosmosDbSubscription(CosmosDbEventStore store, IEventSubscriber subscriber, string streamFilter, string position = null)
        {
            this.store = store;

            var fromBeginning = string.IsNullOrWhiteSpace(position);

            if (fromBeginning)
            {
                hostName = $"squidex.{DateTime.UtcNow.Ticks.ToString()}";
            }
            else
            {
                hostName = position;
            }

            if (!StreamFilter.IsAll(streamFilter))
            {
                regex = new Regex(streamFilter);
            }

            this.subscriber = subscriber;

            processorTask = Task.Run(async() =>
            {
                try
                {
                    Collection CreateCollection(string name)
                    {
                        var collection = new Collection();

                        collection.CollectionName = name;
                        collection.DatabaseName   = store.DatabaseId;
                        collection.MasterKey      = store.MasterKey;
                        collection.Uri            = store.ServiceUri;

                        return(collection);
                    }

                    var processor =
                        await new Builder()
                        .WithFeedCollection(CreateCollection(Constants.Collection))
                        .WithLeaseCollection(CreateCollection(Constants.LeaseCollection))
                        .WithHostName(hostName)
                        .WithProcessorOptions(new Options {
                        StartFromBeginning = fromBeginning, LeasePrefix = hostName
                    })
                        .WithObserverFactory(this)
                        .BuildAsync();

                    await processor.StartAsync();
                    await processorStopRequested.Task;
                    await processor.StopAsync();
                }
                catch (Exception ex)
                {
                    await subscriber.OnErrorAsync(this, ex);
                }
            });
        }
예제 #7
0
        public async Task Should_reopen_subscription_once_when_exception_is_retrieved()
        {
            await OnErrorAsync(eventSubscription, new InvalidOperationException());

            await Task.Delay(400);

            await sut.StopAsync();

            A.CallTo(() => eventSubscription.StopAsync())
            .MustHaveHappened(Repeated.Exactly.Twice);

            A.CallTo(() => eventStore.CreateSubscription(A <IEventSubscriber> .Ignored, A <string> .Ignored, A <string> .Ignored))
            .MustHaveHappened(Repeated.Exactly.Twice);

            A.CallTo(() => eventSubscriber.OnErrorAsync(A <IEventSubscription> .Ignored, A <Exception> .Ignored))
            .MustNotHaveHappened();
        }
예제 #8
0
        public async Task Should_reopen_subscription_once_if_exception_is_retrieved()
        {
            await OnErrorAsync(eventSubscription, new InvalidOperationException());

            await Task.Delay(1000);

            sut.Unsubscribe();

            A.CallTo(() => eventSubscription.Unsubscribe())
            .MustHaveHappened(2, Times.Exactly);

            A.CallTo(() => eventStore.CreateSubscription(A <IEventSubscriber> ._, A <string> ._, A <string> ._))
            .MustHaveHappened(2, Times.Exactly);

            A.CallTo(() => eventSubscriber.OnErrorAsync(eventSubscription, A <Exception> ._))
            .MustNotHaveHappened();
        }
예제 #9
0
        public GetEventStoreSubscription(
            IEventSubscriber subscriber,
            EventStoreClient client,
            EventStoreProjectionClient projectionClient,
            IJsonSerializer serializer,
            string?position,
            string?prefix,
            string?streamFilter)
        {
            Task.Run(async() =>
            {
                var ct = cts.Token;

                var streamName = await projectionClient.CreateProjectionAsync(streamFilter);

                async Task OnEvent(StreamSubscription subscription, ResolvedEvent @event,
                                   CancellationToken ct)
                {
                    var storedEvent = Formatter.Read(@event, prefix, serializer);

                    await subscriber.OnEventAsync(this, storedEvent);
                }

                void OnError(StreamSubscription subscription, SubscriptionDroppedReason reason, Exception? ex)
                {
                    if (reason != SubscriptionDroppedReason.Disposed &&
                        reason != SubscriptionDroppedReason.SubscriberError)
                    {
                        ex ??= new InvalidOperationException($"Subscription closed with reason {reason}.");

                        subscriber.OnErrorAsync(this, ex);
                    }
                }

                if (!string.IsNullOrWhiteSpace(position))
                {
                    var streamPosition = position.ToPosition(true);

                    subscription = await client.SubscribeToStreamAsync(streamName, streamPosition,
                                                                       OnEvent, true,
                                                                       OnError,
                                                                       cancellationToken: ct);
                }
                else
                {
                    subscription = await client.SubscribeToStreamAsync(streamName,
                                                                       OnEvent, true,
                                                                       OnError,
                                                                       cancellationToken: ct);
                }
            }, cts.Token);
        }
예제 #10
0
        public async Task OnErrorAsync(IEventSubscription subscription, Exception exception)
        {
            Unsubscribe();

            if (retryWindow.CanRetryAfterFailure())
            {
                await Task.Delay(ReconnectWaitMs, timerCancellation.Token);

                Subscribe();
            }
            else
            {
                await eventSubscriber.OnErrorAsync(subscription, exception);
            }
        }
예제 #11
0
        private async Task HandleErrorAsync(IEventSubscription subscription, Exception exception)
        {
            if (subscription == currentSubscription)
            {
                Unsubscribe();

                if (retryWindow.CanRetryAfterFailure())
                {
                    RetryAsync().Forget();
                }
                else
                {
                    await eventSubscriber.OnErrorAsync(this, exception);
                }
            }
        }
예제 #12
0
        public PollingSubscription(
            IEventStore eventStore,
            IEventNotifier eventNotifier,
            IEventSubscriber eventSubscriber,
            string streamFilter,
            string position)
        {
            Guard.NotNull(eventStore, nameof(eventStore));
            Guard.NotNull(eventNotifier, nameof(eventNotifier));
            Guard.NotNull(eventSubscriber, nameof(eventSubscriber));

            this.position        = position;
            this.eventNotifier   = eventNotifier;
            this.eventStore      = eventStore;
            this.eventSubscriber = eventSubscriber;
            this.streamFilter    = streamFilter;

            streamRegex = new Regex(streamFilter);

            timer = new CompletionTimer(5000, async ct =>
            {
                try
                {
                    await eventStore.GetEventsAsync(async storedEvent =>
                    {
                        await eventSubscriber.OnEventAsync(this, storedEvent);

                        position = storedEvent.EventPosition;
                    }, ct, streamFilter, position);
                }
                catch (Exception ex)
                {
                    if (!ex.Is <OperationCanceledException>())
                    {
                        await eventSubscriber.OnErrorAsync(this, ex);
                    }
                }
            });

            notification = eventNotifier.Subscribe(streamName =>
            {
                if (streamRegex.IsMatch(streamName))
                {
                    timer.SkipCurrentDelay();
                }
            });
        }
예제 #13
0
        public CosmosDbSubscription(CosmosDbEventStore store, IEventSubscriber subscriber, string?streamFilter, string?position = null)
        {
            this.store = store;

            var fromBeginning = string.IsNullOrWhiteSpace(position);

            if (fromBeginning)
            {
                hostName = $"squidex.{DateTime.UtcNow.Ticks}";
            }
            else
            {
                hostName = position ?? "none";
            }

            if (!StreamFilter.IsAll(streamFilter))
            {
                regex = new Regex(streamFilter);
            }

            this.subscriber = subscriber;

            Task.Run(async() =>
            {
                try
                {
                    var processor =
                        await new Builder()
                        .WithFeedCollection(CreateCollection(store, Constants.Collection))
                        .WithLeaseCollection(CreateCollection(store, Constants.LeaseCollection))
                        .WithHostName(hostName)
                        .WithProcessorOptions(new Options {
                        StartFromBeginning = fromBeginning, LeasePrefix = hostName
                    })
                        .WithObserverFactory(this)
                        .BuildAsync();

                    await processor.StartAsync();
                    await processorStopRequested.Task;
                    await processor.StopAsync();
                }
                catch (Exception ex)
                {
                    await subscriber.OnErrorAsync(this, ex);
                }
            });
        }
예제 #14
0
        private async Task HandleErrorAsync(IEventSubscription subscription, Exception exception)
        {
            if (subscription == currentSubscription)
            {
                Unsubscribe();

                if (retryWindow.CanRetryAfterFailure())
                {
                    Task.Delay(ReconnectWaitMs, timerCts.Token).ContinueWith(t =>
                    {
                        dispatcher.DispatchAsync(() => Subscribe());
                    }).Forget();
                }
                else
                {
                    await eventSubscriber.OnErrorAsync(this, exception);
                }
            }
        }
예제 #15
0
        private EventStoreCatchUpSubscription SubscribeToStream(string streamName)
        {
            var settings = CatchUpSubscriptionSettings.Default;

            return(connection.SubscribeToStreamFrom(streamName, position, settings,
                                                    (s, e) =>
            {
                var storedEvent = Formatter.Read(e);

                subscriber.OnEventAsync(this, storedEvent).Wait();
            }, null,
                                                    (s, reason, ex) =>
            {
                if (reason != SubscriptionDropReason.ConnectionClosed &&
                    reason != SubscriptionDropReason.UserInitiated)
                {
                    ex = ex ?? new ConnectionClosedException($"Subscription closed with reason {reason}.");

                    subscriber.OnErrorAsync(this, ex);
                }
            }));
        }
예제 #16
0
        private async Task QueryAsync(string?streamFilter, string?position)
        {
            try
            {
                string?lastRawPosition = null;

                try
                {
                    lastRawPosition = await QueryOldAsync(streamFilter, position);
                }
                catch (OperationCanceledException)
                {
                }

                if (!stopToken.IsCancellationRequested)
                {
                    await QueryCurrentAsync(streamFilter, lastRawPosition);
                }
            }
            catch (Exception ex)
            {
                await eventSubscriber.OnErrorAsync(this, ex);
            }
        }
예제 #17
0
 public Task OnErrorAsync(IEventSubscription subscription, Exception exception)
 {
     return(currentSubscriber.OnErrorAsync(subscription, exception));
 }
예제 #18
0
 private Task OnErrorAsync(IEventSubscription subscriber, Exception ex)
 {
     return(sutSubscriber.OnErrorAsync(subscriber, ex));
 }