예제 #1
0
        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);
                }
            }
        }
예제 #2
0
        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();
        }
예제 #3
0
		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);
		}
예제 #4
0
 public ConnectedProjectionContext(
     TContext context,
     EnvelopeFactory envelopeFactory)
 {
     _context         = context ?? throw new ArgumentNullException(nameof(context));
     _envelopeFactory = envelopeFactory ?? throw new ArgumentNullException(nameof(envelopeFactory));
 }
예제 #5
0
        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");
        }
예제 #6
0
        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);
            }
        }
예제 #7
0
 protected Runner(
     string runnerName,
     EnvelopeFactory envelopeFactory,
     ILogger logger,
     params IEnumerable <ConnectedProjectionHandler <TContext> >[] handlers) :
     this(runnerName, envelopeFactory, logger, handlers.SelectMany(x => x))
 {
 }
예제 #8
0
 protected Runner(
     string runnerName,
     EnvelopeFactory envelopeFactory,
     ILogger logger,
     params ConnectedProjection <TContext>[] projections) :
     this(runnerName, envelopeFactory, logger, projections.SelectMany(x => x.Handlers))
 {
 }
예제 #9
0
        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)
 {
 }
예제 #12
0
 public TestRunner(
     EnvelopeFactory envelopeFactory,
     ILogger <TestRunner> logger,
     ConnectedProjection <TestDbContext> projection) :
     base(
         Name,
         envelopeFactory,
         logger,
         projection)
 {
 }
예제 #13
0
 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));
     }
 }
예제 #14
0
        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);
        }
예제 #16
0
 public PublicServiceBackofficeRunner(
     EnvelopeFactory envelopeFactory,
     ILogger <PublicServiceBackofficeRunner> logger) :
     base(
         Name,
         envelopeFactory,
         logger,
         new PublicServiceListProjections(new ClockProvider()),
         new PublicServiceProjections(),
         new PublicServiceLabelListProjections(),
         new PublicServiceLifeCycleListProjections())
 {
 }
예제 #17
0
 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()));
 }
예제 #18
0
 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));
 }
예제 #19
0
        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") });
        }
예제 #20
0
        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();
        }
예제 #21
0
        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();
        }
예제 #23
0
        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>();
        }
예제 #24
0
        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>();
        }
예제 #25
0
        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") });
        }
예제 #26
0
        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);
            }
        }
예제 #27
0
        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;
            }
        }
예제 #28
0
        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);
            }
        }
예제 #29
0
        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);
        }
예제 #30
0
        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);
            }
        }