public void BatchContentIsTransformedToSpecificEnvelope() { string expectedEnvelopeContent; var envelopeStream = new StringStream(EnvelopeFactory.Create <Envelope>().OuterXml); var batchContentStream = MessageBodyFactory.Create <Batch.Content>(MessageBody.Samples.LoadString("Message.BatchContent.xml")).AsStream(); var transformedStream = new[] { envelopeStream, batchContentStream }.Transform().Apply(typeof(BatchContentToAnyEnvelope), new UTF8Encoding(false)); using (var expectedReader = XmlReader.Create(transformedStream, new XmlReaderSettings { CloseInput = true })) { expectedReader.Read(); expectedEnvelopeContent = expectedReader.ReadOuterXml(); } using (var stream = MessageBody.Samples.Load("Message.BatchContent.xml")) { MessageMock.Object.BodyPart.Data = stream; PipelineContextMock .Setup(pc => pc.GetDocumentSpecByType(SchemaMetadata.For <Envelope>().MessageType)) .Returns(SchemaMetadata.For <Envelope>().DocumentSpec); var sut = new EnvelopeBuilder(); sut.Execute(PipelineContextMock.Object, MessageMock.Object); using (var actualReader = XmlReader.Create(MessageMock.Object.BodyPart.Data, new XmlReaderSettings { CloseInput = true, IgnoreWhitespace = true })) { actualReader.Read(); var actualEnvelopeContent = actualReader.ReadOuterXml(); actualEnvelopeContent.Should().Be(expectedEnvelopeContent); } } }
public void Enrich_SpecificBaseMessageType_HeaderAddedToMessagesOfMatchingType() { var envelopeEventOne = EnvelopeFactory.Create( new TestEventOne(), null, TestProducerEndpoint.GetDefault()); var envelopeEventTwo = EnvelopeFactory.Create( new TestEventTwo(), null, TestProducerEndpoint.GetDefault()); var envelopeBinaryMessage = EnvelopeFactory.Create( new BinaryMessage(), null, TestProducerEndpoint.GetDefault()); var enricher = new GenericOutboundHeadersEnricher <IIntegrationEvent>("x-test", "value"); enricher.Enrich(envelopeEventOne); enricher.Enrich(envelopeEventTwo); enricher.Enrich(envelopeBinaryMessage); envelopeEventOne.Headers.Should().HaveCount(1); envelopeEventTwo.Headers.Should().HaveCount(1); envelopeBinaryMessage.Headers.Should().BeEmpty(); }
public override IBaseMessage Execute(IPipelineContext pipelineContext, IBaseMessage message) { var markableForwardOnlyEventingReadStream = message.BodyPart.WrapOriginalDataStream( originalStream => originalStream.AsMarkable(), pipelineContext.ResourceTracker); var batchDescriptor = markableForwardOnlyEventingReadStream.ProbeBatchContent().BatchDescriptor; if (batchDescriptor == null || batchDescriptor.EnvelopeSpecName.IsNullOrEmpty()) throw new InvalidOperationException($"No EnvelopeSpecName has been found in {nameof(Batch.Content)} message and no envelope can be applied."); var envelopeSchema = Type.GetType(batchDescriptor.EnvelopeSpecName, true); message.Promote(BatchProperties.EnvelopePartition, batchDescriptor.Partition); markableForwardOnlyEventingReadStream.StopMarking(); if (_logger.IsInfoEnabled) _logger.DebugFormat("Applying '{0}' envelope to message.", envelopeSchema.AssemblyQualifiedName); var envelope = EnvelopeFactory.Create(envelopeSchema); // can't use .AsStream() that relies on StringStream, which is Unicode/UTF-16, over envelope.OuterXml as CompositeXmlStream assumes UTF-8 var envelopeStream = new MemoryStream(Encoding.UTF8.GetBytes(envelope.OuterXml)); message.BodyPart.WrapOriginalDataStream( originalStream => new CompositeXmlStream(new[] { envelopeStream, originalStream }), pipelineContext.ResourceTracker); SchemaMetadata.For(envelopeSchema).Annotations.Find<EnvelopeMapAnnotation>().EnvelopeMapType.IfNotNull(m => MapType = m); return base.Execute(pipelineContext, message); }
public ConnectedProjectionContext( TContext context, EnvelopeFactory envelopeFactory) { _context = context ?? throw new ArgumentNullException(nameof(context)); _envelopeFactory = envelopeFactory ?? throw new ArgumentNullException(nameof(envelopeFactory)); }
public void TransformBatchContentWithEnvironmentTagAndPartition() { var setup = Given( input => input .Message(EnvelopeFactory.Create <Envelope>().AsStream()) .Message(MessageBodyFactory.Create <Batch.Content>(MessageBody.Samples.LoadString("Message.BatchContentWithEnvironmentTagAndPartition.xml")).AsStream())) .Transform .OutputsXml( output => output .ConformingTo <Envelope>() .ConformingTo <Batch.Release>() .WithStrictConformanceLevel()); var result = setup.Validate(); result.NamespaceManager.AddNamespace("env", SchemaMetadata.For <Envelope>().TargetNamespace); result.NamespaceManager.AddNamespace("tns", SchemaMetadata.For <Batch.Release>().TargetNamespace); result.SelectSingleNode("/*") !.LocalName.Should().Be("Envelope"); result.Select("/env:Envelope/tns:ReleaseBatch").Cast <object>().Should().HaveCount(3); result.Select("/env:Envelope/*").Cast <object>().Should().HaveCount(3); var part = result.SelectSingleNode("/env:Envelope/tns:ReleaseBatch[3]"); part !.SelectSingleNode("tns:EnvelopeSpecName") !.Value.Should().Be(SchemaMetadata.For <Batch.Release>().DocumentSpec.DocSpecName); part !.SelectSingleNode("tns:EnvironmentTag") !.Value.Should().Be("graffiti"); part !.SelectSingleNode("tns:Partition") !.Value.Should().Be("A"); }
public void Create_ShouldReturnEnvelope_OnBeingPassedSingleRouteNodesAndSingleRouteSegments() { var routeSegment = new RouteSegment { Coord = Convert.FromBase64String("AQIAACDoZAAABgAAALx5ruNWRSFBsc8ScAykV0HZ6xJ8lEUhQYU+y98RpFdBILoYecJFIUEVfnDVB6RXQZH1zbVhRSFBTFhvegSkV0G/QerRbkUhQYWC7LEKpFdB/e8AFj1FIUG8d8O9BqRXQQ=="), }; var routeNode = new RouteNode { Coord = Convert.FromBase64String("AQEAAAC8ea7jVkUhQbHPEnAMpFdB") }; var routeNodes = new List <RouteNode> { routeNode }; var routeSegments = new List <RouteSegment> { routeSegment }; var envelopeFactory = new EnvelopeFactory(); var result = envelopeFactory.Create(routeNodes, routeSegments); using (var scope = new AssertionScope()) { result.MinX.Should().Be(565918.5429759022); result.MaxX.Should().Be(565985.2365167774); result.MinY.Should().Be(6197265.913045954); result.MaxY.Should().Be(6197319.496780043); } }
protected Runner( string runnerName, EnvelopeFactory envelopeFactory, ILogger logger, params IEnumerable <ConnectedProjectionHandler <TContext> >[] handlers) : this(runnerName, envelopeFactory, logger, handlers.SelectMany(x => x)) { }
protected Runner( string runnerName, EnvelopeFactory envelopeFactory, ILogger logger, params ConnectedProjection <TContext>[] projections) : this(runnerName, envelopeFactory, logger, projections.SelectMany(x => x.Handlers)) { }
public void HasMetadataFromFactory() { var target = new EnvelopeFactory("A", new LocalIncrementIdGenerator()).Create("test", 5, new Dictionary <string, string> { { "value1", "result1" } }); target.Should().BeOfType <Envelope <int> >(); target.GetMetadata("value1").Should().Be("result1"); }
public LastChangedListRunner( string name, EnvelopeFactory envelopeFactory, ILogger logger, params ConnectedProjection <LastChangedListContext>[] projections) : base(name, envelopeFactory, logger, projections) { }
public LastChangedListRunner( string name, ConnectedProjection <LastChangedListContext> projections, EnvelopeFactory envelopeFactory, ILogger logger) : base(name, envelopeFactory, logger, projections.Handlers) { }
public TestRunner( EnvelopeFactory envelopeFactory, ILogger <TestRunner> logger, ConnectedProjection <TestDbContext> projection) : base( Name, envelopeFactory, logger, projection) { }
public void Send <T>(IDipNode recipient, IMessage <T> message) { if (this.m_peersByGuid.ContainsKey(recipient.Guid)) { var envelope = EnvelopeFactory.NewDirectEnvelopeFromMessage(this, recipient, message); Send(envelope); } else { RerouteEnvelope(EnvelopeFactory.NewUnroutedEnvelopeToRecipient(this, recipient, message)); } }
public void NoDuplicateIds_NetworkedIncrement() { const int numEnvelopes = 1000; var target = new EnvelopeFactory("test", new NetworkedIncrementIdGenerator(5)); var seenIds = new HashSet <long>(); for (int i = 0; i < numEnvelopes; i++) { var envelope = target.Create("", i); seenIds.Contains(envelope.Id).Should().BeFalse(); seenIds.Add(envelope.Id); } }
private Envelope WrapOutputInEnvelope(Type innerType, object output, IDeserializerContext context) { Envelope envelope = EnvelopeFactory.Create(output, innerType); IKeyValueCollection metadata = context.GetEnvelopeMetadata(); foreach (string key in metadata.Keys) { envelope.Metadata.Add(key, metadata.Get <object>(key)); } return(envelope); }
public PublicServiceBackofficeRunner( EnvelopeFactory envelopeFactory, ILogger <PublicServiceBackofficeRunner> logger) : base( Name, envelopeFactory, logger, new PublicServiceListProjections(new ClockProvider()), new PublicServiceProjections(), new PublicServiceLabelListProjections(), new PublicServiceLifeCycleListProjections()) { }
protected Runner( string runnerName, EnvelopeFactory envelopeFactory, ILogger logger, IEnumerable <ConnectedProjectionHandler <TContext> > handlers) { RunnerName = runnerName; _envelopeFactory = envelopeFactory; _logger = logger; _tasks = new ConcurrentBag <Task>(); _subscriptions = new ConcurrentBag <IAllStreamSubscription>(); _projector = new ConnectedProjector <TContext>(Resolve.WhenEqualToHandlerMessageType(handlers.ToArray())); }
public ConnectedProjectionMessageHandler( ConnectedProjectionName runnerName, ConnectedProjectionHandler <TContext>[] handlers, Func <Owned <TContext> > contextFactory, EnvelopeFactory envelopeFactory, ILoggerFactory loggerFactory) { _runnerName = runnerName; _contextFactory = contextFactory ?? throw new ArgumentNullException(nameof(contextFactory)); _projector = new ConnectedProjector <TContext>(Resolve.WhenEqualToHandlerMessageType(handlers)); _envelopeFactory = envelopeFactory ?? throw new ArgumentNullException(nameof(envelopeFactory)); _logger = loggerFactory?.CreateLogger <ConnectedProjectionMessageHandler <TContext> >() ?? throw new ArgumentNullException(nameof(loggerFactory)); }
public void Enrich_StaticValues_HeaderAdded() { var envelope = EnvelopeFactory.Create( new TestEventOne(), null, TestProducerEndpoint.GetDefault()); var enricher = new GenericOutboundHeadersEnricher("x-test", "value"); enricher.Enrich(envelope); envelope.Headers.Should().HaveCount(1); envelope.Headers.Should().BeEquivalentTo(new[] { new MessageHeader("x-test", "value") }); }
public MessageBus(MessageBusCreateParameters parameters = null) { parameters = parameters ?? MessageBusCreateParameters.Default; Id = parameters.Id ?? Guid.NewGuid().ToString(); Logger = parameters.GetLogger() ?? new SilentLogger(); WorkerPool = new WorkerPool(Logger, parameters.NumberOfWorkers, parameters.MaximumQueuedMessages); Modules = new ModuleManager(Logger); EnvelopeFactory = new EnvelopeFactory(Id, parameters.IdGenerator ?? new LocalIncrementIdGenerator()); _subscriptionDispatcher = new SubscriptionDispatcher(Logger, parameters.AllowWildcards); _requestDispatcher = new RequestDispatcher(Logger, parameters.AllowWildcards); _participantDispatcher = new ParticipantDispatcher(Logger, parameters.AllowWildcards); _router = new TopicRouter(); }
public ConnectedProjection( Func <Owned <TContext> > contextFactory, TConnectedProjection connectedProjection, EnvelopeFactory envelopeFactory, ILoggerFactory loggerFactory) { ContextFactory = contextFactory ?? throw new ArgumentNullException(nameof(contextFactory)); ConnectedProjectionMessageHandler = new ConnectedProjectionMessageHandler <TContext>( Name, connectedProjection?.Handlers ?? throw new ArgumentNullException(nameof(connectedProjection)), ContextFactory, envelopeFactory ?? throw new ArgumentNullException(nameof(envelopeFactory)), loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory))); }
/// <summary> /// Re-publishes commands from unpublished queue. /// Uses <paramref name="formatter"/> to deserialize commands from store. /// </summary> /// <param name="formatter">The command deserializer.</param> /// <returns>The continuation task.</returns> public async Task RecoverAsync(IDeserializer formatter) { Ensure.NotNull(store, "store"); Ensure.NotNull(formatter, "formatter"); IEnumerable <CommandModel> models = await store.GetAsync(); foreach (CommandModel model in models) { Type envelopeType = EnvelopeFactory.GetType(Type.GetType(model.CommandKey.Type)); Envelope envelope = (Envelope)await formatter.DeserializeAsync(envelopeType, model.Payload); await HandleAsync(envelope, false); } await store.ClearAsync(); }
public void Create_OutboundEnvelope_Created() { var envelope = EnvelopeFactory.Create( new TestEventOne(), new MessageHeaderCollection { { "one", "1" }, { "two", "2" } }, TestProducerEndpoint.GetDefault()); envelope.Should().NotBeNull(); envelope.Should().BeOfType <OutboundEnvelope <TestEventOne> >(); envelope.Message.Should().BeOfType <TestEventOne>(); envelope.Headers.Should().HaveCount(2); envelope.Endpoint.Should().BeOfType <TestProducerEndpoint>(); }
public void Create_RawInboundEnvelopeFromStream_Created() { var envelope = EnvelopeFactory.Create( new MemoryStream(), new MessageHeaderCollection { { "one", "1" }, { "two", "2" } }, TestConsumerEndpoint.GetDefault(), new TestOffset()); envelope.Should().NotBeNull(); envelope.Should().BeOfType <RawInboundEnvelope>(); envelope.RawMessage.Should().NotBeNull(); envelope.Headers.Should().HaveCount(2); envelope.Endpoint.Should().BeOfType <TestConsumerEndpoint>(); }
public void Enrich_ValueProvider_HeaderAdded() { var envelope = EnvelopeFactory.Create( new TestEventOne { Content = "content" }, null, TestProducerEndpoint.GetDefault()); var enricher = new GenericOutboundHeadersEnricher <TestEventOne>( "x-test", envelopeToEnrich => envelopeToEnrich.Message?.Content); enricher.Enrich(envelope); envelope.Headers.Should().HaveCount(1); envelope.Headers.Should().BeEquivalentTo(new[] { new MessageHeader("x-test", "content") }); }
public void Create_ShouldReturnEnvelope_OnBeingPassedMultipleRouteNodesAndMultipleRouteSegments() { var routeSegmentOne = new RouteSegment { Coord = Convert.FromBase64String("AQIAACDoZAAABgAAALx5ruNWRSFBsc8ScAykV0HZ6xJ8lEUhQYU+y98RpFdBILoYecJFIUEVfnDVB6RXQZH1zbVhRSFBTFhvegSkV0G/QerRbkUhQYWC7LEKpFdB/e8AFj1FIUG8d8O9BqRXQQ=="), }; var routeSegmentTwo = new RouteSegment { Coord = Convert.FromBase64String("AQIAACDoZAAABAAAADW/1D1HRSFB6AnTgBWkV0FmW1lvUEUhQahJhp4UpFdBQeY1iklFIUGI6V8tFKRXQQ5MF2tHRSFBFZAntRSkV0E=") }; var routeNodeOne = new RouteNode { Coord = Convert.FromBase64String("AQEAAAC8ea7jVkUhQbHPEnAMpFdB") }; var routeNodeTwo = new RouteNode { Coord = Convert.FromBase64String("AQEAAAC8ea7jVkUhQbHPEnAMpFdB") }; var routeNodes = new List <RouteNode> { routeNodeOne }; var routeSegments = new List <RouteSegment> { routeSegmentOne, routeSegmentTwo }; var envelopeFactory = new EnvelopeFactory(); var result = envelopeFactory.Create(routeNodes, routeSegments); using (var scope = new AssertionScope()) { result.MinX.Should().Be(565918.5429759022); result.MaxX.Should().Be(565985.2365167774); result.MinY.Should().Be(6197265.913045954); result.MaxY.Should().Be(6197334.01288078); } }
private void WrapAndSend <T>( T message, Address?receiver, Address?sender, ZonedDateTime timestamp) where T : Message { var envelope = EnvelopeFactory.Create(message, receiver, sender, timestamp); switch (message) { case Command _: this.cmdBus.Endpoint.Send(envelope); break; case Event _: this.evtBus.Endpoint.Send(envelope); break; default: this.msgBus.Endpoint.Send(envelope); break; } }
private async Task HandleInternalAsync(HandlerDescriptor handler, ArgumentDescriptor argument, object commandPayload, bool isPersistenceUsed, bool isEnvelopeDelayUsed) { try { bool hasContextHandler = handler.IsContext; bool hasEnvelopeHandler = hasContextHandler || handler.IsEnvelope; object payload = commandPayload; object context = null; Envelope envelope = null; ICommand commandWithKey = null; if (argument.IsContext) { // If passed argument is context, throw. log.Fatal("Passed in a context object."); throw Ensure.Exception.NotSupported("PersistentCommandDispatcher doesn't support passing in a command handler context."); } else { // If passed argument is not context, try to create it if needed. if (argument.IsEnvelope) { // If passed argument is envelope, extract payload. envelope = (Envelope)payload; payload = envelope.Body; } else { commandWithKey = payload as ICommand; hasEnvelopeHandler = hasEnvelopeHandler || commandWithKey != null; // If passed argument is not envelope, try to create it if needed. if (hasEnvelopeHandler) { envelope = EnvelopeFactory.Create(payload, argument.ArgumentType); } } if (hasContextHandler) { log.Fatal("Context handler not supported."); throw Ensure.Exception.NotSupported("PersistentCommandDispatcher doesn't support command handler context."); } } if (commandWithKey == null) { commandWithKey = payload as ICommand; } log.Info(commandWithKey, "Got a command."); log.Info(commandWithKey, "Execution on the handler '{0}'.", handler); // If we have command with the key, serialize it for persisten delivery. if (store != null && isPersistenceUsed && commandWithKey != null) { string serializedEnvelope = await formatter.SerializeAsync(envelope); store.Save(new CommandModel(commandWithKey.Key, serializedEnvelope)); log.Debug(commandWithKey, "Saved to the store."); } // If isEnvelopeDelayUsed, try to schedule the execution. // If succeeded, return. if (isEnvelopeDelayUsed && TrySchedule(envelope, handler, argument)) { return; } // Distribute the execution. DistributeExecution(payload, context, envelope, commandWithKey, handler); } catch (Exception e) { DispatcherExceptionHandlers.Handle(e); } }
public EventProcessor( IStreamStore streamStore, AcceptStreamMessageFilter filter, EnvelopeFactory envelopeFactory, ConnectedProjectionHandlerResolver <EditorContext> resolver, Func <EditorContext> dbContextFactory, Scheduler scheduler, ILogger <EventProcessor> logger) { if (streamStore == null) { throw new ArgumentNullException(nameof(streamStore)); } if (filter == null) { throw new ArgumentNullException(nameof(filter)); } if (envelopeFactory == null) { throw new ArgumentNullException(nameof(envelopeFactory)); } if (resolver == null) { throw new ArgumentNullException(nameof(resolver)); } if (dbContextFactory == null) { throw new ArgumentNullException(nameof(dbContextFactory)); } _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _messagePumpCancellation = new CancellationTokenSource(); _messageChannel = Channel.CreateUnbounded <object>(new UnboundedChannelOptions { SingleReader = true, SingleWriter = false, AllowSynchronousContinuations = false }); _messagePump = Task.Factory.StartNew(async() => { IAllStreamSubscription subscription = null; try { logger.LogInformation("EventProcessor message pump entered ..."); while (await _messageChannel.Reader.WaitToReadAsync(_messagePumpCancellation.Token).ConfigureAwait(false)) { while (_messageChannel.Reader.TryRead(out var message)) { switch (message) { case Resume _: logger.LogInformation("Resuming ..."); await using (var resumeContext = dbContextFactory()) { var projection = await resumeContext.ProjectionStates .SingleOrDefaultAsync( item => item.Name == RoadRegistryEditorProjectionHost, _messagePumpCancellation.Token) .ConfigureAwait(false); var after = projection?.Position; var head = await streamStore.ReadHeadPosition(); if (head == Position.Start || (after.HasValue ? head - after.Value <= CatchUpThreshold : head - CatchUpThreshold <= 0)) { await _messageChannel.Writer .WriteAsync(new Subscribe(after), _messagePumpCancellation.Token) .ConfigureAwait(false); } else { await _messageChannel.Writer .WriteAsync(new CatchUp(after, CatchUpBatchSize), _messagePumpCancellation.Token) .ConfigureAwait(false); } } break; case CatchUp catchUp: logger.LogInformation("Catching up as of {Position}", catchUp.AfterPosition ?? -1L); var observedMessageCount = 0; var catchUpPosition = catchUp.AfterPosition ?? Position.Start; var context = dbContextFactory(); var page = await streamStore .ReadAllForwards( catchUpPosition, catchUp.BatchSize, true, _messagePumpCancellation.Token) .ConfigureAwait(false); while (!page.IsEnd) { foreach (var streamMessage in page.Messages) { if (catchUp.AfterPosition.HasValue && streamMessage.Position == catchUp.AfterPosition.Value) { continue; // skip already processed message } if (filter(streamMessage)) { logger.LogInformation("Catching up on {MessageType} at {Position}", streamMessage.Type, streamMessage.Position); var envelope = envelopeFactory.Create(streamMessage); var handlers = resolver(envelope); foreach (var handler in handlers) { await handler .Handler(context, envelope, _messagePumpCancellation.Token) .ConfigureAwait(false); } } observedMessageCount++; catchUpPosition = streamMessage.Position; if (observedMessageCount % CatchUpBatchSize == 0) { logger.LogInformation( "Flushing catch up position of {0} and persisting changes ...", catchUpPosition); await context .UpdateProjectionState( RoadRegistryEditorProjectionHost, catchUpPosition, _messagePumpCancellation.Token) .ConfigureAwait(false); await context.SaveChangesAsync(_messagePumpCancellation.Token).ConfigureAwait(false); await context.DisposeAsync().ConfigureAwait(false); context = dbContextFactory(); observedMessageCount = 0; } } page = await page.ReadNext(_messagePumpCancellation.Token).ConfigureAwait(false); } if (observedMessageCount > 0) // case where we just read the last page and pending work in memory needs to be flushed { logger.LogInformation( "Flushing catch up position of {Position} and persisting changes ...", catchUpPosition); await context .UpdateProjectionState( RoadRegistryEditorProjectionHost, catchUpPosition, _messagePumpCancellation.Token) .ConfigureAwait(false); await context.SaveChangesAsync(_messagePumpCancellation.Token).ConfigureAwait(false); } await context.DisposeAsync().ConfigureAwait(false); //switch to subscription as of the last page await _messageChannel.Writer .WriteAsync( new Subscribe(catchUpPosition), _messagePumpCancellation.Token) .ConfigureAwait(false); break; case Subscribe subscribe: logger.LogInformation("Subscribing as of {0}", subscribe.AfterPosition ?? -1L); subscription?.Dispose(); subscription = streamStore.SubscribeToAll( subscribe.AfterPosition, async(_, streamMessage, token) => { if (filter(streamMessage)) { logger.LogInformation("Observing {0} at {1}", streamMessage.Type, streamMessage.Position); var command = new ProcessStreamMessage(streamMessage); await _messageChannel.Writer.WriteAsync(command, token).ConfigureAwait(false); await command.Completion.ConfigureAwait(false); } else if (streamMessage.Position % RecordPositionThreshold == 0 && !_messagePumpCancellation.IsCancellationRequested) { await _messageChannel.Writer .WriteAsync(new RecordPosition(streamMessage), token) .ConfigureAwait(false); } }, async(_, reason, exception) => { if (!_messagePumpCancellation.IsCancellationRequested) { await _messageChannel.Writer .WriteAsync( new SubscriptionDropped(reason, exception), _messagePumpCancellation.Token) .ConfigureAwait(false); } }, prefetchJsonData: false, name: "RoadRegistry.Editor.ProjectionHost.EventProcessor"); break; case RecordPosition record: try { logger.LogInformation("Recording position of {MessageType} at {Position}.", record.Message.Type, record.Message.Position); await using (var recordContext = dbContextFactory()) { await recordContext .UpdateProjectionState( RoadRegistryEditorProjectionHost, record.Message.Position, _messagePumpCancellation.Token) .ConfigureAwait(false); await recordContext.SaveChangesAsync(_messagePumpCancellation.Token).ConfigureAwait(false); } } catch (Exception exception) { logger.LogError(exception, exception.Message); } break; case ProcessStreamMessage process: try { logger.LogInformation("Processing {MessageType} at {Position}", process.Message.Type, process.Message.Position); var envelope = envelopeFactory.Create(process.Message); var handlers = resolver(envelope); await using (var processContext = dbContextFactory()) { foreach (var handler in handlers) { await handler .Handler(processContext, envelope, _messagePumpCancellation.Token) .ConfigureAwait(false); } await processContext.UpdateProjectionState( RoadRegistryEditorProjectionHost, process.Message.Position, _messagePumpCancellation.Token).ConfigureAwait(false); await processContext.SaveChangesAsync(_messagePumpCancellation.Token).ConfigureAwait(false); } process.Complete(); } catch (Exception exception) { logger.LogError(exception, exception.Message); // how are we going to recover from this? do we even need to recover from this? // prediction: it's going to be a serialization error, a data quality error, or a bug process.Fault(exception); } break; case SubscriptionDropped dropped: if (dropped.Reason == SubscriptionDroppedReason.StreamStoreError) { logger.LogError(dropped.Exception, "Subscription was dropped because of a stream store error"); await scheduler.Schedule(async token => { if (!_messagePumpCancellation.IsCancellationRequested) { await _messageChannel.Writer.WriteAsync(new Resume(), token).ConfigureAwait(false); } }, ResubscribeAfter).ConfigureAwait(false); } else if (dropped.Reason == SubscriptionDroppedReason.SubscriberError) { logger.LogError(dropped.Exception, "Subscription was dropped because of a subscriber error"); if (dropped.Exception != null && dropped.Exception is SqlException sqlException && sqlException.Number == -2 /* timeout */) { await scheduler.Schedule(async token => { if (!_messagePumpCancellation.IsCancellationRequested) { await _messageChannel.Writer.WriteAsync(new Resume(), token).ConfigureAwait(false); } }, ResubscribeAfter).ConfigureAwait(false); } } break; } } } } catch (TaskCanceledException) { logger.LogInformation("EventProcessor message pump is exiting due to cancellation"); } catch (OperationCanceledException) { logger.LogInformation("EventProcessor message pump is exiting due to cancellation"); } catch (Exception exception) { logger.LogError(exception, "EventProcessor message pump is exiting due to a bug"); } finally { subscription?.Dispose(); } }, _messagePumpCancellation.Token, TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); }
private Task PublishAsync(IEnumerable <HandlerDescriptor> handlers, ArgumentDescriptor argument, object eventPayload, bool isEnvelopeDelayUsed) { try { bool hasContextHandler = handlers.Any(d => d.IsContext); bool hasEnvelopeHandler = hasContextHandler || handlers.Any(d => d.IsEnvelope); object payload = eventPayload; object context = null; Envelope envelope = null; IEvent eventWithKey = null; if (argument.IsContext) { // If passed argument is context, throw. throw Ensure.Exception.NotSupported("PersistentEventDispatcher doesn't support passing in event handler context."); } else { // If passed argument is not context, try to create it if needed. if (argument.IsEnvelope) { // If passed argument is envelope, extract payload. envelope = (Envelope)payload; payload = envelope.Body; } else { eventWithKey = payload as IEvent; hasEnvelopeHandler = hasEnvelopeHandler || eventWithKey != null; // If passed argument is not envelope, try to create it if needed. if (hasEnvelopeHandler) { envelope = EnvelopeFactory.Create(payload, argument.ArgumentType); } } if (hasContextHandler) { Type contextType = typeof(DefaultEventHandlerContext <>).MakeGenericType(argument.ArgumentType); context = Activator.CreateInstance(contextType, envelope, this, this); } } if (eventWithKey == null) { eventWithKey = payload as IEvent; } log.Info(eventWithKey, "Got an event."); // If isEnvelopeDelayUsed, try to schedule the execution. // If succeeded, return. if (isEnvelopeDelayUsed && TrySchedule(envelope, context, handlers, argument)) { return(Async.CompletedTask); } // Distribute the execution. return(DistributeExecution(payload, context, envelope, eventWithKey, handlers)); } catch (Exception e) { DispatcherExceptionHandlers.Handle(e); return(Async.CompletedTask); } }