Пример #1
0
        public async Task <IEventStream> GetStream <T>(string bucket, string streamId, ISnapshot snapshot = null) where T : class, IEventSource
        {
            var streamName = _streamGen(typeof(T), StreamTypes.Domain, bucket, streamId);

            Logger.Write(LogLevel.Debug, () => $"Retreiving stream [{streamId}] in bucket [{bucket}] for type {typeof(T).FullName}");

            if (_shouldCache)
            {
                var cached = _cache.Retreive(streamName) as EventStream <T>;
                if (cached != null)
                {
                    HitMeter.Mark();
                    Logger.Write(LogLevel.Debug, () => $"Found stream [{streamName}] in cache");
                    return(new EventStream <T>(cached, Builder, this, snapshot));
                }
                MissMeter.Mark();
            }

            while (await CheckFrozen <T>(bucket, streamId).ConfigureAwait(false))
            {
                Logger.Write(LogLevel.Info, () => $"Stream [{streamName}] is frozen - waiting");
                Thread.Sleep(100);
            }
            Logger.Write(LogLevel.Debug, () => $"Stream [{streamName}] not in cache - reading from store");

            var events = await _store.GetEvents(streamName, start : snapshot?.Version + 1L).ConfigureAwait(false);

            var eventstream = new EventStream <T>(Builder, this, StreamTypes.Domain, bucket, streamId, events, snapshot);

            await Cache <T>(eventstream).ConfigureAwait(false);

            Logger.Write(LogLevel.Debug, () => $"Stream [{streamName}] read - version is {eventstream.CommitVersion}");
            return(eventstream);
        }
Пример #2
0
        public async Task <IEventStream> GetStream <T>(string bucket, Id streamId, IEnumerable <Id> parents = null) where T : class, IEventSource
        {
            parents = parents ?? new Id[] { };

            var streamName = _streamGen(typeof(T), StreamTypes.Domain, bucket, streamId, parents);

            Logger.Write(LogLevel.Debug, () => $"Retreiving stream [{streamId}] in bucket [{bucket}] for type {typeof(T).FullName}");

            var cached = _cache.Retreive(streamName) as IImmutableEventStream;

            if (cached != null)
            {
                HitMeter.Mark();
                Logger.Write(LogLevel.Debug, () => $"Found stream [{streamName}] in cache");
                return(new EventStream <T>(cached));
            }
            MissMeter.Mark();


            while (await CheckFrozen <T>(bucket, streamId, parents).ConfigureAwait(false))
            {
                Logger.Write(LogLevel.Info, () => $"Stream [{streamId}] in bucket [{bucket}] is frozen - waiting");
                await Task.Delay(100).ConfigureAwait(false);
            }
            Logger.Write(LogLevel.Debug, () => $"Stream [{streamId}] in bucket [{bucket}] not in cache - reading from store");

            ISnapshot snapshot = null;

            if (typeof(ISnapshotting).IsAssignableFrom(typeof(T)))
            {
                snapshot = await _snapstore.GetSnapshot <T>(bucket, streamId, parents).ConfigureAwait(false);

                Logger.Write(LogLevel.Debug, () =>
                {
                    if (snapshot != null)
                    {
                        return($"Retreived snapshot for entity id [{streamId}] bucket [{bucket}] version {snapshot.Version}");
                    }
                    return($"No snapshot found for entity id [{streamId}] bucket [{bucket}]");
                });
            }

            var events = await _store.GetEvents(streamName, start : snapshot?.Version + 1).ConfigureAwait(false);

            var oobMetadata = await _store.GetMetadata(streamName, OobMetadataKey).ConfigureAwait(false);

            IEnumerable <OobDefinition> oobs = null;

            if (!string.IsNullOrEmpty(oobMetadata))
            {
                oobs = JsonConvert.DeserializeObject <IEnumerable <OobDefinition> >(oobMetadata);
            }

            var eventstream = new EventStream <T>(bucket, streamId, parents, oobs, events, snapshot);

            _cache.Cache(streamName, eventstream.Clone());

            Logger.Write(LogLevel.Debug, () => $"Stream [{streamId}] in bucket [{bucket}] read - version is {eventstream.CommitVersion}");
            return(eventstream);
        }
Пример #3
0
        public async Task <TEntity> Get <TEntity, TState>(string bucket, Id id, IEntity parent) where TEntity : IEntity <TState> where TState : class, IState, new()
        {
            var uow = (Configuration.Settings.LocalContainer.Value ?? Configuration.Settings.Container).Resolve <Aggregates.UnitOfWork.IDomain>();

            var factory = EntityFactory.For <TEntity>();

            var parents = getParents(parent);
            // Todo: pass parent instead of Id[]?
            var snapshot = await _snapstore.GetSnapshot <TEntity>(bucket, id, parents?.Select(x => x.StreamId).ToArray()).ConfigureAwait(false);

            var events = await _eventstore.GetEvents <TEntity>(bucket, id, parents?.Select(x => x.StreamId).ToArray(), start : snapshot?.Version).ConfigureAwait(false);

            var entity = factory.Create(bucket, id, parents, events.Select(x => x.Event as IEvent).ToArray(), snapshot?.Payload);


            (entity as INeedDomainUow).Uow             = uow;
            (entity as INeedEventFactory).EventFactory = _factory;
            (entity as INeedStore).Store                = _eventstore;
            (entity as INeedStore).OobWriter            = _oobstore;
            (entity as INeedVersionRegistrar).Registrar = _registrar;
            (entity as INeedChildTracking).Tracker      = _childTracker;

            Logger.DebugEvent("Get", "[{EntityId:l}] bucket [{Bucket:l}] entity [{EntityType:l}] version {Version}", id, bucket, typeof(TEntity).FullName, entity.Version);

            return(entity);
        }
Пример #4
0
        public async Task Resolve <TEntity, TState>(TEntity entity, IFullEvent[] uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where TEntity : IEntity <TState> where TState : IState, new()
        {
            var state = entity.State;

            Logger.Write(LogLevel.Info, () => $"Resolving {uncommitted.Count()} uncommitted events to stream [{entity.Id}] type [{typeof(TEntity).FullName}] bucket [{entity.Bucket}]");

            var latestEvents =
                await _eventstore.GetEvents <TEntity>(entity.Bucket, entity.Id, entity.Parents, entity.Version).ConfigureAwait(false);

            Logger.Write(LogLevel.Info, () => $"Stream is {latestEvents.Count()} events behind store");

            for (var i = 0; i < latestEvents.Length; i++)
            {
                state.Apply(latestEvents[i].Event as IEvent);
            }

            Logger.Write(LogLevel.Debug, () => "Merging conflicted events");
            try
            {
                foreach (var u in uncommitted)
                {
                    if (u.Descriptor.StreamType == StreamTypes.Domain)
                    {
                        entity.Conflict(u.Event as IEvent);
                    }
                    else if (u.Descriptor.StreamType == StreamTypes.OOB)
                    {
                        // Todo: small hack
                        string id         = "";
                        bool   transient  = true;
                        int    daysToLive = -1;

                        id = u.Descriptor.Headers[Defaults.OobHeaderKey];

                        if (u.Descriptor.Headers.ContainsKey(Defaults.OobTransientKey))
                        {
                            bool.TryParse(u.Descriptor.Headers[Defaults.OobTransientKey], out transient);
                        }
                        if (u.Descriptor.Headers.ContainsKey(Defaults.OobDaysToLiveKey))
                        {
                            int.TryParse(u.Descriptor.Headers[Defaults.OobDaysToLiveKey], out daysToLive);
                        }

                        entity.Raise(u.Event as IEvent, id, transient, daysToLive);
                    }
                }
            }
            catch (NoRouteException e)
            {
                Logger.Write(LogLevel.Info, () => $"Failed to resolve conflict: {e.Message}");
                throw new ConflictResolutionFailedException("Failed to resolve conflict", e);
            }

            Logger.Write(LogLevel.Debug, () => "Successfully merged conflicted events");


            await _eventstore.WriteEvents <TEntity>(entity.Bucket, entity.Id, entity.Parents, entity.Uncommitted, commitHeaders).ConfigureAwait(false);
        }
Пример #5
0
        public async Task Resolve <TEntity, TState>(TEntity entity, IFullEvent[] uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where TEntity : IEntity <TState> where TState : IState, new()
        {
            var state = entity.State;

            Logger.DebugEvent("Resolver", "Resolving {Events} conflicting events to stream [{Stream:l}] type [{EntityType:l}] bucket [{Bucket:l}]", uncommitted.Count(), entity.Id, typeof(TEntity).FullName, entity.Bucket);

            var latestEvents =
                await _eventstore.GetEvents <TEntity>(entity.Bucket, entity.Id, entity.Parents, entity.Version).ConfigureAwait(false);

            Logger.DebugEvent("Behind", "Stream is {Count} events behind store", latestEvents.Count());

            for (var i = 0; i < latestEvents.Length; i++)
            {
                state.Apply(latestEvents[i].Event as IEvent);
            }

            try
            {
                foreach (var u in uncommitted)
                {
                    if (u.Descriptor.StreamType == StreamTypes.Domain)
                    {
                        entity.Conflict(u.Event as IEvent);
                    }
                    else if (u.Descriptor.StreamType == StreamTypes.OOB)
                    {
                        // Todo: small hack
                        string id         = "";
                        bool   transient  = true;
                        int    daysToLive = -1;

                        id = u.Descriptor.Headers[Defaults.OobHeaderKey];

                        if (u.Descriptor.Headers.ContainsKey(Defaults.OobTransientKey))
                        {
                            bool.TryParse(u.Descriptor.Headers[Defaults.OobTransientKey], out transient);
                        }
                        if (u.Descriptor.Headers.ContainsKey(Defaults.OobDaysToLiveKey))
                        {
                            int.TryParse(u.Descriptor.Headers[Defaults.OobDaysToLiveKey], out daysToLive);
                        }

                        entity.Raise(u.Event as IEvent, id, transient, daysToLive);
                    }
                }
            }
            catch (NoRouteException e)
            {
                Logger.WarnEvent("ResolveFailure", e, "Failed to resolve conflict: {ExceptionType} - {ExceptionMessage}", e.GetType().Name, e.Message);
                throw new ConflictResolutionFailedException("Failed to resolve conflict", e);
            }

            await _eventstore.WriteEvents <TEntity>(entity.Bucket, entity.Id, entity.Parents, entity.Uncommitted, commitHeaders).ConfigureAwait(false);
        }
Пример #6
0
        public async Task Resolve <T>(T entity, IEnumerable <IFullEvent> uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where T : class, IEventSource
        {
            var sourced = (IEventSourced)entity;

            var stream     = sourced.Stream;
            var streamName = _streamGen(typeof(T), StreamTypes.Domain, stream.Bucket, stream.StreamId, stream.Parents);

            Logger.Write(LogLevel.Info, () => $"Resolving {uncommitted.Count()} uncommitted events to stream [{stream.StreamId}] type [{typeof(T).FullName}] bucket [{stream.Bucket}]");

            try
            {
                await _store.Freeze <T>(stream).ConfigureAwait(false);

                var latestEvents =
                    await _eventstore.GetEvents(streamName, stream.CommitVersion + 1).ConfigureAwait(false);

                Logger.Write(LogLevel.Info, () => $"Stream is {latestEvents.Count()} events behind store");

                sourced.Hydrate(latestEvents.Select(x => x.Event as IEvent));

                Logger.Write(LogLevel.Debug, () => "Merging conflicted events");
                try
                {
                    foreach (var u in uncommitted)
                    {
                        sourced.Conflict(u.Event as IEvent, metadata: new Dictionary <string, string> {
                            { "ConflictResolution", ConcurrencyConflict.ResolveStrongly.ToString() }
                        });
                    }
                }
                catch (NoRouteException e)
                {
                    Logger.Write(LogLevel.Info, () => $"Failed to resolve conflict: {e.Message}");
                    throw new ConflictResolutionFailedException("Failed to resolve conflict", e);
                }

                Logger.Write(LogLevel.Debug, () => "Successfully merged conflicted events");

                if (stream.StreamVersion != stream.CommitVersion && entity is ISnapshotting &&
                    ((ISnapshotting)entity).ShouldTakeSnapshot())
                {
                    Logger.Write(LogLevel.Debug,
                                 () => $"Taking snapshot of {typeof(T).FullName} id [{entity.Id}] version {stream.StreamVersion}");
                    var memento = ((ISnapshotting)entity).TakeSnapshot();
                    stream.AddSnapshot(memento);
                }

                await _store.WriteStream <T>(commitId, stream, commitHeaders).ConfigureAwait(false);
            }
            finally
            {
                await _store.Unfreeze <T>(stream).ConfigureAwait(false);
            }
        }
Пример #7
0
        public async Task migrate_from_sqlite_to_sqlserver()
        {
            await SetupSrc();

loop:
            var i = new Random().Next(0, MaxEntities);

            _src.Advanced.MigrateEventsTo(_dest, "bubu", c => c.BatchSize(50).AddConverters(new RewriteEvent()));
            var evs = await _dest.GetEvents(_entities[i]);

            evs.Value.Count.Should().Be(1);
            var orig = _events[i].CastAs <Event1>();

            if (orig == null)
            {
                goto loop;
            }

            var ev = evs.Value.First() as Event1;

            ev.Nr.Should().Be(60 + orig.Nr);
        }
Пример #8
0
        public Task <IFullEvent[]> GetEvents <TEntity>(string bucket, Id streamId, Id[] parents, string oobId, long?start = null, int?count = null) where TEntity : IEntity
        {
            var stream = _generator(typeof(TEntity), StreamTypes.OOB, $"{oobId}.{bucket}", streamId, parents);

            return(_store.GetEvents(stream, start, count));
        }
Пример #9
0
        public Task <IEnumerable <IWritableEvent> > Retrieve <T>(string bucket, string streamId, int?skip = null, int?take = null, bool ascending = true) where T : class, IEventSource
        {
            var streamName = _streamGen(typeof(T), bucket + ".OOB", streamId);

            return(!ascending?_store.GetEventsBackwards(streamName) : _store.GetEvents(streamName));
        }
Пример #10
0
        public async Task Resolve <T>(T entity, IEnumerable <IFullEvent> uncommitted, Guid commitId, IDictionary <string, string> commitHeaders) where T : class, IEventSource
        {
            var sourced = (IEventSourced)entity;
            // Store conflicting events in memory
            // After 100 or so pile up pull the latest stream and attempt to write them again

            var streamName = _streamGen(typeof(T), StreamTypes.Domain, sourced.Stream.Bucket, sourced.Stream.StreamId, sourced.Stream.Parents);

            var message = new ConflictingEvents
            {
                Bucket     = sourced.Stream.Bucket,
                StreamId   = sourced.Stream.StreamId,
                EntityType = typeof(T).AssemblyQualifiedName,
                Parents    = BuildParentList(entity),
                Events     = uncommitted
            };

            var package = new DelayedMessage
            {
                MessageId  = Guid.NewGuid().ToString(),
                Headers    = new Dictionary <string, string>(),
                Message    = message,
                Received   = DateTime.UtcNow,
                ChannelKey = streamName
            };

            foreach (var header in commitHeaders)
            {
                package.Headers[$"Conflict.{header.Key}"] = header.Value;
            }

            await _delay.AddToQueue(ConcurrencyConflict.ResolveWeakly.ToString(), package, streamName)
            .ConfigureAwait(false);

            // Todo: make 30 seconds configurable
            var age = await _delay.Age(ConcurrencyConflict.ResolveWeakly.ToString(), streamName).ConfigureAwait(false);

            if (!age.HasValue || age < TimeSpan.FromSeconds(30))
            {
                return;
            }

            var stream = sourced.Stream;

            Logger.Write(LogLevel.Info,
                         () => $"Starting weak conflict resolve for stream [{stream.StreamId}] type [{typeof(T).FullName}] bucket [{stream.Bucket}]");
            try
            {
                await _store.Freeze <T>(stream).ConfigureAwait(false);

                var delayed = await _delay.Pull(streamName, max : 200).ConfigureAwait(false);

                // If someone else pulled while we were waiting
                if (!delayed.Any())
                {
                    return;
                }

                Logger.Write(LogLevel.Info,
                             () => $"Resolving {delayed.Count()} uncommitted events to stream [{stream.StreamId}] type [{typeof(T).FullName}] bucket [{stream.Bucket}]");

                var latestEvents =
                    await _eventstore.GetEvents(streamName, stream.CommitVersion + 1L)
                    .ConfigureAwait(false);

                Logger.Write(LogLevel.Info,
                             () => $"Stream [{stream.StreamId}] bucket [{stream.Bucket}] is {latestEvents.Count()} events behind store");

                sourced.Hydrate(latestEvents.Select(x => x.Event as IEvent));


                Logger.Write(LogLevel.Debug, () => $"Merging {delayed.Count()} conflicted events");
                try
                {
                    foreach (var u in delayed.Select(x => x.Message as ConflictingEvents).SelectMany(x => x.Events))
                    {
                        sourced.Conflict(u.Event as IEvent,
                                         metadata:
                                         new Dictionary <string, string>
                        {
                            { "ConflictResolution", ConcurrencyConflict.ResolveWeakly.ToString() }
                        });
                    }
                }
                catch (NoRouteException e)
                {
                    Logger.Write(LogLevel.Info, () => $"Failed to resolve conflict: {e.Message}");
                    throw new ConflictResolutionFailedException("Failed to resolve conflict", e);
                }

                Logger.Write(LogLevel.Info, () => "Successfully merged conflicted events");

                if (stream.StreamVersion != stream.CommitVersion && entity is ISnapshotting &&
                    ((ISnapshotting)entity).ShouldTakeSnapshot())
                {
                    Logger.Write(LogLevel.Debug,
                                 () =>
                                 $"Taking snapshot of [{typeof(T).FullName}] id [{entity.Id}] version {stream.StreamVersion}");
                    var memento = ((ISnapshotting)entity).TakeSnapshot();
                    stream.AddSnapshot(memento);
                }

                await _store.WriteStream <T>(commitId, stream, commitHeaders).ConfigureAwait(false);
            }
            finally
            {
                await _store.Unfreeze <T>(stream).ConfigureAwait(false);
            }
        }