public static IFullEvent Event(IVersionRegistrar versionRegistry, Aggregates.UnitOfWork.IDomain uow, IEntity entity, IEvent @event)
        {
            var eventId = UnitOfWork.NextEventId(uow.CommitId);

            var newEvent = new FullEvent
            {
                Descriptor = new EventDescriptor
                {
                    EntityType = versionRegistry.GetVersionedName(entity.GetType()),
                    StreamType = StreamTypes.Domain,
                    Bucket     = entity.Bucket,
                    StreamId   = entity.Id,
                    Parents    = getParents(versionRegistry, entity),
                    Timestamp  = DateTime.UtcNow,
                    Version    = entity.StateVersion,
                    Headers    = new Dictionary <string, string>()
                    {
                        [$"{Defaults.PrefixHeader}.{Defaults.MessageIdHeader}"]     = eventId.ToString(),
                        [$"{Defaults.PrefixHeader}.{Defaults.CorrelationIdHeader}"] = uow.CommitId.ToString(),
                    }
                },
                EventId = eventId,
                Event   = @event
            };

            return(newEvent);
        }
Esempio n. 2
0
        private IWritableEvent MakeWritableEvent(IEvent @event, IDictionary <string, string> headers, bool version = true)
        {
            var writable = new WritableEvent
            {
                Descriptor = new EventDescriptor
                {
                    EntityType = typeof(T).AssemblyQualifiedName,
                    StreamType = StreamType,
                    Bucket     = Bucket,
                    StreamId   = StreamId,
                    Timestamp  = DateTime.UtcNow,
                    Version    = version ? StreamVersion + 1 : StreamVersion,
                    Headers    = headers ?? new Dictionary <string, string>()
                },
                EventId = UnitOfWork.NextEventId(_commitId),
                Event   = @event
            };

            var mutators = _builder.BuildAll <IEventMutator>();

            if (!mutators.Any())
            {
                return(writable);
            }

            IMutating mutated = new Mutating(writable.Event, writable.Descriptor.Headers);

            foreach (var mutate in mutators)
            {
                Logger.Write(LogLevel.Debug, () => $"Mutating outgoing event {@event.GetType().FullName} with mutator {mutate.GetType().FullName}");
                mutated = mutate.MutateOutgoing(mutated);
            }

            foreach (var header in mutated.Headers)
            {
                writable.Descriptor.Headers[header.Key] = header.Value;
            }
            writable.Event = mutated.Message;

            return(writable);
        }
Esempio n. 3
0
        public async Task WriteStream <T>(Guid commitId, IEventStream stream, IDictionary <string, string> commitHeaders) where T : class, IEventSource
        {
            var streamName = _streamGen(typeof(T), StreamTypes.Domain, stream.Bucket, stream.StreamId, stream.Parents);

            Logger.Write(LogLevel.Debug,
                         () =>
                         $"Writing {stream.Uncommitted.Count()} events to stream {stream.StreamId} bucket {stream.Bucket} with commit id {commitId}");

            if (await CheckFrozen <T>(stream.Bucket, stream.StreamId, stream.Parents).ConfigureAwait(false))
            {
                throw new FrozenException();
            }

            Saved.Mark();

            var events = stream.Uncommitted.Select(writable =>
            {
                IMutating mutated = new Mutating(writable.Event, writable.Descriptor.Headers);
                foreach (var mutate in _mutators)
                {
                    Logger.Write(LogLevel.Debug,
                                 () => $"Mutating outgoing event {writable.Event.GetType()} with mutator {mutate.GetType().FullName}");
                    mutated = mutate.MutateOutgoing(mutated);
                }

                // Todo: have some bool that is set true if they modified headers
                if (_mutators.Any())
                {
                    foreach (var header in mutated.Headers)
                    {
                        writable.Descriptor.Headers[header.Key] = header.Value;
                    }
                }
                return((IFullEvent) new WritableEvent
                {
                    Descriptor = writable.Descriptor,
                    Event = mutated.Message,
                    EventId = UnitOfWork.NextEventId(commitId)
                });
            }).ToList();

            var oobs = stream.Oobs.ToDictionary(x => x.Id, x => x);

            foreach (var oob in stream.PendingOobs)
            {
                oobs[oob.Id] = oob;
            }

            var domainEvents = events.Where(x => x.Descriptor.StreamType == StreamTypes.Domain);
            var oobEvents    = events.Where(x => x.Descriptor.StreamType == StreamTypes.OOB);

            if (domainEvents.Any())
            {
                _cache.Evict(streamName);

                Logger.Write(LogLevel.Debug,
                             () => $"Event stream [{stream.StreamId}] in bucket [{stream.Bucket}] committing {domainEvents.Count()} events");
                await _store.WriteEvents(streamName, domainEvents, commitHeaders, expectedVersion : stream.CommitVersion)
                .ConfigureAwait(false);
            }
            if (stream.PendingOobs.Any())
            {
                await _store.WriteMetadata(streamName, custom : new Dictionary <string, string>
                {
                    [OobMetadataKey] = JsonConvert.SerializeObject(oobs.Values)
                }).ConfigureAwait(false);
            }

            if (stream.PendingSnapshot != null)
            {
                Logger.Write(LogLevel.Debug,
                             () => $"Event stream [{stream.StreamId}] in bucket [{stream.Bucket}] committing snapshot");
                await _snapstore.WriteSnapshots <T>(stream.Bucket, stream.StreamId, stream.Parents, stream.StreamVersion, stream.PendingSnapshot, commitHeaders).ConfigureAwait(false);
            }
            if (oobEvents.Any())
            {
                Logger.Write(LogLevel.Debug,
                             () => $"Event stream [{stream.StreamId}] in bucket [{stream.Bucket}] publishing {oobEvents.Count()} out of band events");

                foreach (var group in oobEvents.GroupBy(x => x.Descriptor.Headers[Defaults.OobHeaderKey]))
                {
                    // OOB events of the same stream name don't need to all be written to the same stream
                    // if we parallelize the events into 10 known streams we can take advantage of internal
                    // ES optimizations and ES sharding
                    var vary      = _random.Next(10) + 1;
                    var oobstream = $"{streamName}-{group.Key}.{vary}";


                    var definition = oobs[group.Key];
                    if (definition.Transient ?? false)
                    {
                        await _publisher.Publish <T>(oobstream, group, commitHeaders).ConfigureAwait(false);
                    }
                    else if (definition.DaysToLive.HasValue)
                    {
                        var version = await _store.WriteEvents(oobstream, group, commitHeaders).ConfigureAwait(false);

                        // if new stream, write metadata
                        if (version == (group.Count() - 1))
                        {
                            await _store.WriteMetadata(oobstream, maxAge : TimeSpan.FromDays(definition.DaysToLive.Value)).ConfigureAwait(false);
                        }
                    }
                    else
                    {
                        await _store.WriteEvents(oobstream, group, commitHeaders).ConfigureAwait(false);
                    }
                }
            }
        }
Esempio n. 4
0
        public IMutating MutateIncoming(IMutating command)
        {
            CurrentMessage = command.Message;

            // There are certain headers that we can make note of
            // These will be committed to the event stream and included in all .Reply or .Publish done via this Unit Of Work
            // Meaning all receivers of events from the command will get information about the command's message, if they care
            foreach (var header in NSBDefaults.CarryOverHeaders)
            {
                var defaultHeader = "";
                command.Headers.TryGetValue(header, out defaultHeader);

                if (string.IsNullOrEmpty(defaultHeader))
                {
                    defaultHeader = NotFound;
                }

                var workHeader = $"{Defaults.OriginatingHeader}.{header}";
                CurrentHeaders[workHeader] = defaultHeader;
            }
            CurrentHeaders[$"{Defaults.PrefixHeader}.OriginatingType"] = CurrentMessage.GetType().FullName;

            // Copy any application headers the user might have included
            var userHeaders = command.Headers.Keys.Where(h =>
                                                         !h.Equals("CorrId", StringComparison.InvariantCultureIgnoreCase) &&
                                                         !h.Equals("WinIdName", StringComparison.InvariantCultureIgnoreCase) &&
                                                         !h.StartsWith("NServiceBus", StringComparison.InvariantCultureIgnoreCase) &&
                                                         !h.StartsWith("$", StringComparison.InvariantCultureIgnoreCase) &&
                                                         !h.Equals(CommitHeader) &&
                                                         !h.Equals(Defaults.CommitIdHeader, StringComparison.InvariantCultureIgnoreCase) &&
                                                         !h.Equals(Defaults.RequestResponse, StringComparison.InvariantCultureIgnoreCase) &&
                                                         !h.Equals(Defaults.Retries, StringComparison.InvariantCultureIgnoreCase) &&
                                                         !h.Equals(Defaults.LocalHeader, StringComparison.InvariantCultureIgnoreCase) &&
                                                         !h.Equals(Defaults.BulkHeader, StringComparison.InvariantCultureIgnoreCase));

            foreach (var header in userHeaders)
            {
                CurrentHeaders[header] = command.Headers[header];
            }

            if (command.Headers.ContainsKey(Headers.CorrelationId))
            {
                CurrentHeaders[$"{Defaults.PrefixHeader}.{Defaults.CorrelationIdHeader}"] = command.Headers[Headers.CorrelationId];
            }

            string messageId;
            Guid   commitId = Guid.NewGuid();

            // Attempt to get MessageId from NServicebus headers
            // If we maintain a good CommitId convention it should solve the message idempotentcy issue (assuming the storage they choose supports it)
            if (CurrentHeaders.TryGetValue(NSBDefaults.MessageIdHeader, out messageId))
            {
                Guid.TryParse(messageId, out commitId);
            }
            if (CurrentHeaders.TryGetValue($"{Defaults.PrefixHeader}.{Defaults.MessageIdHeader}", out messageId))
            {
                Guid.TryParse(messageId, out commitId);
            }

            // Allow the user to send a CommitId along with his message if he wants
            if (CurrentHeaders.TryGetValue(Defaults.CommitIdHeader, out messageId))
            {
                Guid.TryParse(messageId, out commitId);
            }


            CommitId = commitId;

            // Helpful log and gets CommitId into the dictionary
            var firstEventId = UnitOfWork.NextEventId(CommitId);

            return(command);
        }