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(); }
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(); }
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); } }); }
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); } } }); }
public async Task CloseAsync(IChangeFeedObserverContext context, ChangeFeedObserverCloseReason reason) { if (reason == ChangeFeedObserverCloseReason.ObserverError) { await subscriber.OnErrorAsync(this, new InvalidOperationException("Change feed observer failed.")); } }
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); } }); }
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(); }
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(); }
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); }
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); } }
private async Task HandleErrorAsync(IEventSubscription subscription, Exception exception) { if (subscription == currentSubscription) { Unsubscribe(); if (retryWindow.CanRetryAfterFailure()) { RetryAsync().Forget(); } else { await eventSubscriber.OnErrorAsync(this, exception); } } }
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(); } }); }
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); } }); }
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); } } }
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); } })); }
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); } }
public Task OnErrorAsync(IEventSubscription subscription, Exception exception) { return(currentSubscriber.OnErrorAsync(subscription, exception)); }
private Task OnErrorAsync(IEventSubscription subscriber, Exception ex) { return(sutSubscriber.OnErrorAsync(subscriber, ex)); }