public void Subscribe(IEventConsumer eventConsumer) { Guard.NotNull(eventConsumer, nameof(eventConsumer)); ThrowIfDisposed(); if (timer != null) { return; } var consumerName = eventConsumer.Name; var consumerStarted = false; timer = new CompletionTimer(5000, async ct => { if (!consumerStarted) { await eventConsumerInfoRepository.CreateAsync(consumerName); consumerStarted = true; } try { var status = await eventConsumerInfoRepository.FindAsync(consumerName); var position = status.Position; if (status.IsResetting) { currentSubscription?.Dispose(); currentSubscription = null; position = null; await ResetAsync(eventConsumer); } else if (status.IsStopped) { currentSubscription?.Dispose(); currentSubscription = null; return; } if (currentSubscription == null) { await SubscribeAsync(eventConsumer, position); } } catch (Exception ex) { log.LogFatal(ex, w => w.WriteProperty("action", "EventHandlingFailed")); } }); }
public void Subscribe(IEventConsumer eventConsumer, int delay = 5000) { Guard.NotNull(eventConsumer, nameof(eventConsumer)); ThrowIfDisposed(); if (timer != null) { return; } var consumerName = eventConsumer.Name; var consumerStarted = false; timer = new CompletionTimer(delay, async ct => { if (!consumerStarted) { await eventConsumerInfoRepository.CreateAsync(consumerName); consumerStarted = true; } try { var status = await eventConsumerInfoRepository.FindAsync(consumerName); var position = status.Position; if (status.IsResetting) { position = null; await ResetAsync(eventConsumer, consumerName, position); } else if (status.IsStopped) { return; } await eventStore.GetEventsAsync(se => { return(HandleEventAsync(eventConsumer, se, consumerName)); }, ct, eventConsumer.EventsFilter, position); } catch (Exception ex) { log.LogFatal(ex, w => w.WriteProperty("action", "EventHandlingFailed")); await eventConsumerInfoRepository.StopAsync(consumerName, ex.ToString()); } }); eventNotifier.Subscribe(timer.Trigger); }
public EventConsumerActorTests() { consumerInfo.Position = Guid.NewGuid().ToString(); consumerName = eventConsumer.GetType().Name; A.CallTo(() => eventStore.CreateSubscription(A <IEventSubscriber> .Ignored, A <string> .Ignored, A <string> .Ignored)).Returns(eventSubscription); A.CallTo(() => eventConsumer.Name).Returns(consumerName); A.CallTo(() => eventConsumerInfoRepository.FindAsync(consumerName)).Returns(consumerInfo); A.CallTo(() => formatter.Parse(eventData, true)).Returns(envelope); sut = new EventConsumerActor(formatter, eventStore, eventConsumerInfoRepository, log) { ReconnectWaitMs = 0 }; sutActor = sut; sutSubscriber = sut; }
private async Task HandleAsync(object input) { try { if (!isStarted) { await eventConsumerInfoRepository.CreateAsync(eventConsumer.Name); isStarted = true; } var status = await eventConsumerInfoRepository.FindAsync(eventConsumer.Name); var lastReceivedEventNumber = status.LastHandledEventNumber; if (status.IsResetting) { await ResetAsync(); Reset(); } if (!status.IsStopped || !isRunning) { var ct = new CancellationTokenSource(); await eventStore.GetEventsAsync(async storedEvent => { if (!isRunning) { ct.Cancel(); } var onEvent = OnEvent; if (onEvent != null) { await onEvent(storedEvent); } }, ct.Token, null, lastReceivedEventNumber); } } catch (OperationCanceledException) { } catch (Exception ex) { OnError?.Invoke(ex); } }
private async Task HandleSetupAsync(IEventConsumer consumer) { eventConsumer = consumer; var status = await eventConsumerInfoRepository.FindAsync(eventConsumer.Name); if (status != null) { statusError = status.Error; statusPosition = status.Position; statusIsRunning = !status.IsStopped; } if (statusIsRunning) { Subscribe(statusPosition); } }
public EventReceiverTests() { var events = new[] { new StoredEvent("3", 3, eventData1), new StoredEvent("4", 4, eventData2), new StoredEvent("5", 5, eventData3) }; consumerName = eventConsumer.GetType().Name; var eventStore = new MyEventStore(events); A.CallTo(() => eventConsumer.Name).Returns(consumerName); A.CallTo(() => eventConsumerInfoRepository.FindAsync(consumerName)).Returns(Task.FromResult <IEventConsumerInfo>(consumerInfo)); A.CallTo(() => formatter.Parse(eventData1)).Returns(envelope1); A.CallTo(() => formatter.Parse(eventData2)).Returns(envelope2); A.CallTo(() => formatter.Parse(eventData3)).Returns(envelope3); sut = new EventReceiver(formatter, eventStore, eventConsumerInfoRepository, log); }
private async Task OnMessage(object message) { if (isStopped) { return; } try { var oldStateId = stateId; var newStateId = stateId = Guid.NewGuid(); switch (message) { case Teardown teardown: { isStopped = true; return; } case Setup setup: { eventConsumer = setup.EventConsumer; var status = await eventConsumerInfoRepository.FindAsync(eventConsumer.Name); if (status != null) { statusError = status.Error; statusPosition = status.Position; statusIsRunning = !status.IsStopped; } if (statusIsRunning) { await SubscribeThisAsync(statusPosition); } break; } case StartConsumerMessage startConsumer: { if (statusIsRunning) { return; } await SubscribeThisAsync(statusPosition); statusError = null; statusIsRunning = true; break; } case StopConsumerMessage stopConsumer: { if (!statusIsRunning) { return; } await UnsubscribeThisAsync(); statusIsRunning = false; break; } case ResetConsumerMessage resetConsumer: { await UnsubscribeThisAsync(); await ClearAsync(); await SubscribeThisAsync(null); statusError = null; statusPosition = null; statusIsRunning = true; break; } case Reconnect reconnect: { if (!statusIsRunning || reconnect.StateId != oldStateId) { return; } await SubscribeThisAsync(statusPosition); break; } case SubscriptionFailed subscriptionFailed: { if (subscriptionFailed.Subscription != eventSubscription) { return; } await UnsubscribeThisAsync(); if (retryWindow.CanRetryAfterFailure()) { Task.Delay(ReconnectWaitMs).ContinueWith(t => dispatcher.SendAsync(new Reconnect { StateId = newStateId })).Forget(); } else { throw subscriptionFailed.Exception; } break; } case SubscriptionEventReceived eventReceived: { if (eventReceived.Subscription != eventSubscription) { return; } var @event = ParseEvent(eventReceived.Event); await DispatchConsumerAsync(@event); statusError = null; statusPosition = @eventReceived.Event.EventPosition; break; } } await eventConsumerInfoRepository.SetAsync(eventConsumer.Name, statusPosition, !statusIsRunning, statusError); } catch (Exception ex) { try { await UnsubscribeThisAsync(); } catch (Exception unsubscribeException) { ex = new AggregateException(ex, unsubscribeException); } log.LogFatal(ex, w => w .WriteProperty("action", "HandleEvent") .WriteProperty("state", "Failed") .WriteProperty("eventConsumer", eventConsumer.Name)); statusError = ex.ToString(); statusIsRunning = false; await eventConsumerInfoRepository.SetAsync(eventConsumer.Name, statusPosition, !statusIsRunning, statusError); } }