Esempio n. 1
0
        public void Save(T eventSourced, IEvent incomingEvent)
        {
            string key = null;

            try
            {
                // Check if incoming event is duplicate
                if (this.inbox.ContainsKey(incomingEvent.EventId))
                {
                    // Incoming event is duplicate
                    return;
                }

                var now      = this.time.Now;
                var localNow = this.time.Now.ToLocalTime();

                // No new events to persist
                if (eventSourced == null)
                {
                    // Log the incoming message in the inbox
                    this.inbox[incomingEvent.EventId] = this.inboxEntityFactory.Invoke(incomingEvent);
                    return;
                }

                if (eventSourced.Id == default(Guid))
                {
                    throw new ArgumentOutOfRangeException("StreamId", $"The eventsourced of type {typeof(T).FullName} has a default GUID value for its stream id, which is not valid");
                }

                // Log the incoming message in the inbox
                this.inbox[incomingEvent.EventId] = this.inboxEntityFactory.Invoke(incomingEvent);

                var pendingEvents = eventSourced.PendingEvents;
                if (pendingEvents.Count > 0)
                {
                    var currentVersion = this.events.Any(e => e.Value.StreamId == eventSourced.Id)
                          ? this.events
                                         .Where(e => e.Value.StreamId == eventSourced.Id)
                                         .Max(e => e.Value.Version)
                          : 0;


                    if (currentVersion + 1 != pendingEvents.First().Version)
                    {
                        throw new EventStoreConcurrencyException();
                    }

                    // Cache Memento And Publish Stream
                    var snapshot = ((ISnapshotOriginator)eventSourced).SaveToSnapshot();

                    key = eventSourced.Id.ToString();

                    // Cache in Sql Server
                    var serializedMemento = this.serializer.Serialize(snapshot);

                    var streamEntity = this.snapshots.TryGetValue(eventSourced.Id);
                    if (streamEntity != null)
                    {
                        streamEntity.Version         = eventSourced.Version;
                        streamEntity.Payload         = serializedMemento;
                        streamEntity.UpdateLocalTime = localNow;
                    }
                    else
                    {
                        streamEntity = new SnapshotEntity
                        {
                            StreamType        = this.streamType,
                            StreamId          = eventSourced.Id,
                            Version           = eventSourced.Version,
                            Payload           = serializedMemento,
                            CreationLocalTime = localNow,
                            UpdateLocalTime   = localNow
                        };
                        this.snapshots[eventSourced.Id] = streamEntity;
                    }

                    // Cache in memory
                    this.cache.Set(
                        key: key,
                        value: new Tuple <ISnapshot, DateTime?>(snapshot, now),
                        policy: new CacheItemPolicy {
                        AbsoluteExpiration = this.time.OffSetNow.AddMinutes(30)
                    });

                    List <EventEntity> eventEntities = new List <EventEntity>();
                    foreach (var @event in pendingEvents)
                    {
                        var e = (Message)@event;
                        e.TransactionId = incomingEvent.TransactionId;
                        e.EventId       = this.guid.NewGuid();
                        e.StreamType    = this.streamType;
                        e.LocalTime     = now;
                        e.UtcTime       = localNow;

                        eventEntities.Add(
                            new EventEntity
                        {
                            StreamType    = this.streamType,
                            StreamId      = @event.StreamId,
                            Version       = @event.Version,
                            EventId       = @event.EventId,
                            TransactionId = @event.TransactionId,
                            EventType     = @event.GetType().Name,
                            CorrelationId = incomingEvent.EventId,
                            LocalTime     = localNow,
                            UtcTime       = now
                        });
                    }

                    lock (this.dbLock)
                    {
                        var eventCollectionBeforeCrash = this.eventCollectionVersion;
                        try
                        {
                            for (int i = 0; i < pendingEvents.Count; i++)
                            {
                                this.eventCollectionVersion += 1;
                                var @event = pendingEvents[i];
                                ((Message)@event).EventCollectionVersion = this.eventCollectionVersion;
                                var entity = eventEntities[i];
                                entity.EventCollectionVersion = this.eventCollectionVersion;
                                entity.Payload = this.serializer.Serialize(@event);

                                this.events[this.eventCollectionVersion] = entity;
                            }

                            //var random = new Random();
                            //Thread.Sleep(random.Next(0, 1000));
                            this.CurrentEventCollectionVersion = this.eventCollectionVersion;
                        }
                        catch (Exception ex)
                        {
                            this.eventCollectionVersion = eventCollectionBeforeCrash;
                            throw ex;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.log.Error(ex, $"An error ocurred while storing events while processing incoming event of type '{incomingEvent.GetType().Name}'");

                // Mark cache as stale
                if (key != null)
                {
                    var item = (Tuple <ISnapshot, DateTime?>) this.cache.Get(key);
                    if (item != null && item.Item2.HasValue)
                    {
                        item = new Tuple <ISnapshot, DateTime?>(item.Item1, null);
                        this.cache.Set(
                            key,
                            item,
                            new CacheItemPolicy {
                            AbsoluteExpiration = this.time.OffSetNow.AddMinutes(30)
                        });
                    }
                }

                throw;
            }
        }
Esempio n. 2
0
        public long Save(T eventSourced, IEvent incomingEvent)
        {
            var pendingEvents = eventSourced.PendingEvents;

            if (pendingEvents.Count == 0)
            {
                return(this.eventCollectionVersion);
            }

            if (eventSourced.Id == default(Guid))
            {
                throw new ArgumentOutOfRangeException("StreamId", $"The eventsourced of type {typeof(T).FullName} has a default GUID value for its stream id, which is not valid");
            }

            var key = eventSourced.Id.ToString();

            try
            {
                using (var context = this.contextFactory.Invoke(false))
                {
                    // Check if incoming event is duplicate
                    if (this.IsDuplicate(incomingEvent.EventId, context))
                    {
                        // Incoming event is duplicate
                        return(this.eventCollectionVersion);
                    }

                    var currentVersion = context.Events.Any(e => e.StreamId == eventSourced.Id && e.StreamType == this.streamType)
                                          ? context.Events
                                         .Where(e => e.StreamId == eventSourced.Id && e.StreamType == this.streamType)
                                         .Max(e => e.Version)
                                          : 0;

                    if (currentVersion + 1 != pendingEvents.First().Version)
                    {
                        throw new EventStoreConcurrencyException();
                    }

                    var now      = this.time.Now;
                    var localNow = this.time.Now.ToLocalTime();

                    // Log the incoming message in the inbox
                    var message = new InboxEntity
                    {
                        InboxStreamType        = this.streamType,
                        EventId                = incomingEvent.EventId,
                        TransactionId          = incomingEvent.TransactionId,
                        StreamType             = incomingEvent.StreamType,
                        StreamId               = incomingEvent.StreamId,
                        Version                = incomingEvent.Version,
                        EventType              = incomingEvent.GetType().Name,
                        EventCollectionVersion = incomingEvent.EventCollectionVersion,
                        CreationLocalTime      = localNow,
                        Ignored                = false,
                        Payload                = this.serializer.Serialize(incomingEvent)
                    };
                    context.Inbox.Add(message);

                    // Update subscription
                    try
                    {
                        var subscription = context.Subscriptions.Where(s => s.StreamType == incomingEvent.StreamType && s.SubscriberStreamType == this.streamType).Single();
                        subscription.ProcessorBufferVersion = incomingEvent.ProcessorBufferVersion;
                        subscription.UpdateLocalTime        = localNow;
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidOperationException($"The incoming event belongs to a an stream type of {incomingEvent.StreamType}, but the event store could not found a subscription for that stream type.", ex);
                    }


                    // Cache Memento And Publish Stream
                    var snapshot = ((ISnapshotOriginator)eventSourced).SaveToSnapshot();

                    // Cache in Sql Server
                    var serializedMemento = this.serializer.Serialize(snapshot);

                    var streamEntity = context.Snapshots.Where(s => s.StreamId == eventSourced.Id && s.StreamType == this.streamType).SingleOrDefault();
                    if (streamEntity != null)
                    {
                        streamEntity.Version         = eventSourced.Version;
                        streamEntity.Payload         = serializedMemento;
                        streamEntity.UpdateLocalTime = localNow;
                    }
                    else
                    {
                        streamEntity = new SnapshotEntity
                        {
                            StreamType        = this.streamType,
                            StreamId          = eventSourced.Id,
                            Version           = eventSourced.Version,
                            Payload           = serializedMemento,
                            CreationLocalTime = localNow,
                            UpdateLocalTime   = localNow
                        };
                        context.Snapshots.Add(streamEntity);
                    }

                    // Cache in memory
                    this.cache.Set(
                        key: key,
                        value: new Tuple <ISnapshot, DateTime?>(snapshot, now),
                        policy: new CacheItemPolicy {
                        AbsoluteExpiration = this.time.OffSetNow.AddMinutes(30)
                    });

                    // Denormalize if applicable.
                    this.denormalizeIfApplicable(eventSourced, context);


                    List <EventEntity> eventEntities = new List <EventEntity>();
                    foreach (var @event in pendingEvents)
                    {
                        var e = (Message)@event;
                        e.TransactionId = incomingEvent.TransactionId;
                        e.EventId       = this.guid.NewGuid();
                        e.StreamType    = this.streamType;

                        eventEntities.Add(
                            new EventEntity
                        {
                            StreamType    = this.streamType,
                            StreamId      = @event.StreamId,
                            Version       = @event.Version,
                            EventId       = @event.EventId,
                            TransactionId = @event.TransactionId,
                            EventType     = @event.GetType().Name,
                            CorrelationId = incomingEvent.EventId,
                            LocalTime     = localNow,
                            UtcTime       = now
                        });
                    }

                    long eventCollectionVersionToPublish;
                    lock (this)
                    {
                        var eventCollectionBeforeCrash = this.eventCollectionVersion;
                        try
                        {
                            for (int i = 0; i < pendingEvents.Count; i++)
                            {
                                var ecv    = Interlocked.Increment(ref this.eventCollectionVersion);
                                var @event = pendingEvents[i];
                                ((Message)@event).EventCollectionVersion = ecv;
                                var entity = eventEntities[i];
                                entity.EventCollectionVersion = ecv;
                                entity.Payload = this.serializer.Serialize(@event);
                                context.Events.Add(entity);
                            }
                            context.SaveChanges();

                            eventCollectionVersionToPublish = pendingEvents.Last().EventCollectionVersion;
                            //return context.Events.Where(e => e.StreamType == this.streamType).Max(e => e.EventCollectionVersion);
                        }
                        catch (Exception ex)
                        {
                            this.eventCollectionVersion = eventCollectionBeforeCrash;
                            throw ex;
                        }
                    }

                    return(eventCollectionVersionToPublish);
                }
            }
            catch (Exception ex)
            {
                this.log.Error(ex, $"An error ocurred while storing events while processing incoming event of type '{incomingEvent.GetType().Name}'");

                // Mark cache as stale
                var item = (Tuple <ISnapshot, DateTime?>) this.cache.Get(key);
                if (item != null && item.Item2.HasValue)
                {
                    item = new Tuple <ISnapshot, DateTime?>(item.Item1, null);
                    this.cache.Set(
                        key,
                        item,
                        new CacheItemPolicy {
                        AbsoluteExpiration = this.time.OffSetNow.AddMinutes(30)
                    });
                }

                throw;
            }
        }