public object Lock(IAggregateRootId aggregateRootId, TimeSpan ttl)
        {
            if (ReferenceEquals(null, aggregateRootId)) throw new ArgumentNullException(nameof(aggregateRootId));

            var lockresult = lockManager.Lock(aggregateRootId, ttl);

            return lockresult.LockAcquired ? lockresult.Mutex : null;
        }
Exemple #2
0
 public AggregateCommit(IAggregateRootId aggregateId, int revision, List<IEvent> events)
 {
     AggregateRootId = aggregateId.RawId;
     BoundedContext = aggregateId.GetType().GetBoundedContext().BoundedContextName;
     Revision = revision;
     InternalEvents = events.Cast<object>().ToList();
     Timestamp = DateTime.UtcNow.ToFileTimeUtc();
 }
 internal protected List<AggregateCommit> Seek(IAggregateRootId aggregateId)
 {
     var idHash = Convert.ToBase64String(aggregateId.RawId);
     ConcurrentQueue<AggregateCommit> commits;
     if (eventsStreams.TryGetValue(idHash, out commits))
         return commits.ToList();
     else
         return new List<AggregateCommit>();
 }
        public Result<bool> Execute(IAggregateRootId aggregateRootId, int aggregateRootRevision, Action action)
        {
            var result = new Result<bool>(false);
            var acquired = new AtomicBoolean(false);

            try
            {
                if (aggregateLock.TryGetValue(aggregateRootId, out acquired) == false)
                {
                    acquired = acquired ?? new AtomicBoolean(false);
                    if (aggregateLock.TryAdd(aggregateRootId, acquired) == false)
                        aggregateLock.TryGetValue(aggregateRootId, out acquired);
                }

                if (acquired.CompareAndSet(false, true))
                {
                    try
                    {
                        AtomicInteger revision = null;
                        if (aggregateRevisions.TryGetValue(aggregateRootId, out revision) == false)
                        {
                            revision = new AtomicInteger(aggregateRootRevision - 1);
                            if (aggregateRevisions.TryAdd(aggregateRootId, revision) == false)
                                return result;
                        }
                        var currentRevision = revision.Value;
                        if (revision.CompareAndSet(aggregateRootRevision - 1, aggregateRootRevision))
                        {
                            try
                            {
                                action();
                                return Result.Success;
                            }
                            catch (Exception)
                            {
                                revision.GetAndSet(currentRevision);
                                throw;
                            }
                        }
                    }
                    finally
                    {
                        acquired.GetAndSet(false);
                    }
                }

                return result;
            }
            catch (Exception ex)
            {
                return result.WithError(ex);
            }
        }
        private Result<object> Lock(IAggregateRootId arId, TimeSpan ttl)
        {
            object mutex;

            try
            {
                mutex = aggregateRootLock.Lock(arId, ttl);

                if (ReferenceEquals(null, mutex))
                    return new Result<object>().WithError("failed lock");

                return new Result<object>(mutex);
            }
            catch (Exception ex)
            {
                return new Result<object>().WithError(ex);
            }
        }
        public Result<bool> HasRevision(IAggregateRootId aggregateRootId)
        {
            if (ReferenceEquals(null, aggregateRootId)) throw new ArgumentNullException(nameof(aggregateRootId));

            if (connection.IsConnected == false)
                return Result.Error($"Unreachable endpoint '{connection.ClientName}'.");

            var revisionKey = CreateRedisRevisionKey(aggregateRootId);

            try
            {
                var result = connection.GetDatabase().KeyExists(revisionKey);

                return new Result<bool>(result);
            }
            catch (Exception ex)
            {
                return Result.Error(ex);
            }
        }
        public Result<bool> SaveRevision(IAggregateRootId aggregateRootId, int revision, TimeSpan? expiry)
        {
            if (ReferenceEquals(null, aggregateRootId)) throw new ArgumentNullException(nameof(aggregateRootId));

            if (connection.IsConnected == false)
                return Result.Error($"Unreachable endpoint '{connection.ClientName}'.");

            var revisionKey = CreateRedisRevisionKey(aggregateRootId);

            try
            {
                var result = connection.GetDatabase().StringSet(revisionKey, string.Join(",", revision, DateTime.UtcNow), expiry);

                return new Result<bool>(result);
            }
            catch (Exception ex)
            {
                return Result.Error(ex);
            }
        }
        public Result<int> GetRevision(IAggregateRootId aggregateRootId)
        {
            if (ReferenceEquals(null, aggregateRootId)) throw new ArgumentNullException(nameof(aggregateRootId));

            if (connection.IsConnected == false)
                return new Result<int>().WithError($"Unreachable endpoint '{connection.ClientName}'.");

            var revisionKey = CreateRedisRevisionKey(aggregateRootId);

            try
            {
                var value = connection.GetDatabase().StringGet(revisionKey);
                if (value.HasValue == false)
                    return new Result<int>().WithError($"Missing value for {revisionKey} '{connection.ClientName}'.");
                var revisionValue = ((string)value).Split(',').First();

                return new Result<int>(int.Parse(revisionValue));
            }
            catch (Exception ex)
            {
                return new Result<int>().WithError(ex);
            }
        }
 public EventStream Load(IAggregateRootId aggregateId)
 {
     using (SqlConnection connection = new SqlConnection(connectionString))
     {
         connection.Open();
         string boundedContext = aggregateId.GetType().GetBoundedContext().BoundedContextName;
         string query = String.Format(LoadAggregateStateQueryTemplate, tableNameStrategy.GetEventsTableName(boundedContext, aggregateId));
         SqlCommand command = new SqlCommand(query, connection);
         command.Parameters.AddWithValue("@aggregateId", Convert.ToBase64String(aggregateId.RawId));
         List<AggregateCommit> aggregateCommits = new List<AggregateCommit>();
         using (var reader = command.ExecuteReader())
         {
             while (reader.Read())
             {
                 var buffer = reader[0] as byte[];
                 using (var stream = new MemoryStream(buffer))
                 {
                     aggregateCommits.Add((AggregateCommit)serializer.Deserialize(stream));
                 }
             }
         }
         return new EventStream(aggregateCommits);
     }
 }
        public Result<bool> Execute(IAggregateRootId arId, int aggregateRootRevision, Action action)
        {
            var lockResult = Lock(arId, options.LockTtl);
            if (lockResult.IsNotSuccessful)
                return Result.Error("lock failed");

            try
            {
                if (CanExecuteAction(arId, aggregateRootRevision))
                {
                    var actionResult = ExecuteAction(action);

                    if (actionResult.IsNotSuccessful)
                    {
                        Rollback(arId, aggregateRootRevision - 1);

                        return Result.Error("action failed");
                    }

                    PersistRevision(arId, aggregateRootRevision);

                    return actionResult;
                }

                return Result.Error("unable to execute action");
            }
            catch (Exception ex)
            {
                // TODO log
                return Result.Error(ex);
            }
            finally
            {
                Unlock(lockResult.Value);
            }
        }
 public Result<bool> SaveRevision(IAggregateRootId aggregateRootId, int revision)
 {
     return SaveRevision(aggregateRootId, revision, null);
 }
 public virtual bool Equals(IAggregateRootId other)
 {
     if (ReferenceEquals(null, other)) return false;
     if (ReferenceEquals(this, other)) return true;
     return ByteArrayHelper.Compare(RawId, other.RawId);
 }
 public bool IsLocked(IAggregateRootId aggregateRootId)
 {
     return lockManager.IsLocked(aggregateRootId);
 }
 private void Rollback(IAggregateRootId arId, int revision)
 {
     revisionStore.SaveRevision(arId, revision, options.LongTtl);
 }
        private bool CanExecuteAction(IAggregateRootId arId, int aggregateRootRevision)
        {
            try
            {
                var existingRevisionResult = CheckForExistingRevision(arId);

                if (existingRevisionResult.IsNotSuccessful)
                {
                    return false; // TODO: log
                }

                if (existingRevisionResult.Value == false)
                {
                    var prevRevResult = SavePreviouseRevison(arId, aggregateRootRevision);

                    if (prevRevResult.IsNotSuccessful)
                        return false;
                }

                var isConsecutiveRevision = IsConsecutiveRevision(arId, aggregateRootRevision);

                if (isConsecutiveRevision)
                {
                    return IncrementRevision(arId, aggregateRootRevision).IsSuccessful;
                }

                return false;
            }
            catch (Exception)
            {
                return false;
            }
        }
 private Result<bool> IncrementRevision(IAggregateRootId arId, int newRevision)
 {
     return revisionStore.SaveRevision(arId, newRevision, options.ShorTtl);
 }
 private bool IsConsecutiveRevision(IAggregateRootId arId, int revision)
 {
     var storedRevisionResult = revisionStore.GetRevision(arId);
     return storedRevisionResult.IsSuccessful && storedRevisionResult.Value == revision - 1;
 }
 private Result<bool> PersistRevision(IAggregateRootId arId, int revision)
 {
     return revisionStore.SaveRevision(arId, revision, options.LongTtl);
 }
Exemple #19
0
 private string GetRevisionKey(IAggregateRootId aggregateRootId) => $"cronus/{aggregateRootId.NID}/{Convert.ToBase64String(aggregateRootId.RawId)}";
Exemple #20
0
        protected override async Task <JobExecutionStatus> RunJobAsync(IClusterOperations cluster, CancellationToken cancellationToken = default)
        {
            // mynkow. this one fails
            IndexStatus indexStatus = await GetIndexStatusAsync <EventToAggregateRootId>().ConfigureAwait(false);

            if (indexStatus.IsNotPresent())
            {
                return(JobExecutionStatus.Running);
            }

            //projectionStoreInitializer.Initialize(version);

            foreach (Type eventType in eventTypes.Items)
            {
                string eventTypeId = eventType.GetContractId();

                bool hasMoreRecords = true;

                while (hasMoreRecords && Data.IsCompleted == false)
                {
                    RebuildEventCounterIndex_JobData.EventTypeRebuildPaging paging = Data.EventTypePaging.Where(et => et.Type.Equals(eventTypeId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

                    string paginationToken = paging?.PaginationToken;
                    if (string.IsNullOrEmpty(paginationToken))
                    {
                        logger.Info(() => $"Message counter for {eventTypeId} has been reset");
                        // Maybe we should move this to a BeforeRun method.
                        await messageCounter.ResetAsync(eventType).ConfigureAwait(false);
                    }
                    LoadIndexRecordsResult indexRecordsResult = await eventToAggregateIndex.EnumerateRecordsAsync(eventTypeId, paginationToken).ConfigureAwait(false);

                    IEnumerable <IndexRecord> indexRecords = indexRecordsResult.Records;
                    long currentSessionProcessedCount      = 0;
                    foreach (IndexRecord indexRecord in indexRecords)
                    {
                        currentSessionProcessedCount++;

                        string           mess   = Encoding.UTF8.GetString(indexRecord.AggregateRootId);
                        IAggregateRootId arId   = GetAggregateRootId(mess);
                        EventStream      stream = await eventStore.LoadAsync(arId).ConfigureAwait(false);

                        List <Task> incrementTasks = new List <Task>();

                        foreach (AggregateCommit arCommit in stream.Commits)
                        {
                            foreach (var @event in arCommit.Events)
                            {
                                if (cancellationToken.IsCancellationRequested)
                                {
                                    logger.Info(() => $"Job has been cancelled.");
                                    return(JobExecutionStatus.Running);
                                }

                                if (eventTypeId.Equals(@event.GetType().GetContractId(), StringComparison.OrdinalIgnoreCase))
                                {
                                    incrementTasks.Add(messageCounter.IncrementAsync(eventType));
                                }
                            }
                        }

                        await Task.WhenAll(incrementTasks).ConfigureAwait(false);
                    }

                    Data.MarkPaginationTokenAsProcessed(eventTypeId, indexRecordsResult.PaginationToken);
                    Data = await cluster.PingAsync(Data, cancellationToken).ConfigureAwait(false);

                    hasMoreRecords = indexRecordsResult.Records.Any();
                }
            }

            Data.IsCompleted = true;
            Data             = await cluster.PingAsync(Data).ConfigureAwait(false);

            logger.Info(() => $"The job has been completed.");

            return(JobExecutionStatus.Completed);
        }
 /// <summary>
 /// Loads all the commits of an aggregate with the specified aggregate identifier.
 /// </summary>
 /// <param name="aggregateId">The aggregate identifier.</param>
 /// <returns></returns>
 public EventStream Load(IAggregateRootId aggregateId)
 {
     return new EventStream(eventStoreStorage.Seek(aggregateId));
 }
 private Result<bool> SavePreviouseRevison(IAggregateRootId arId, int revision)
 {
     return revisionStore.SaveRevision(arId, revision - 1, options.ShorTtl);
 }
Exemple #23
0
        public ReplayResult Rebuild(ProjectionVersion version, DateTime rebuildUntil)
        {
            if (ReferenceEquals(null, version))
            {
                throw new ArgumentNullException(nameof(version));
            }

            Type projectionType = version.ProjectionName.GetTypeByContract();

            try
            {
                IndexStatus indexStatus = GetIndexStatus <EventToAggregateRootId>();
                if (indexStatus.IsNotPresent() && IsNotSystemProjection(projectionType))
                {
                    return(ReplayResult.RetryLater($"The index is not present"));
                }

                if (IsVersionTrackerMissing() && IsNotSystemProjection(projectionType))
                {
                    return(ReplayResult.RetryLater($"Projection `{version}` still don't have present index."));                                                                    //WHEN TO RETRY AGAIN
                }
                if (HasReplayTimeout(rebuildUntil))
                {
                    return(ReplayResult.Timeout($"Rebuild of projection `{version}` has expired. Version:{version} Deadline:{rebuildUntil}."));
                }

                var allVersions = GetAllVersions(version);
                if (allVersions.IsOutdatad(version))
                {
                    return(new ReplayResult($"Version `{version}` is outdated. There is a newer one which is already live."));
                }
                if (allVersions.IsCanceled(version))
                {
                    return(new ReplayResult($"Version `{version}` was canceled."));
                }

                DateTime startRebuildTimestamp = DateTime.UtcNow;
                int      progressCounter       = 0;
                logger.Info(() => $"Start rebuilding projection `{version.ProjectionName}` for version {version}. Deadline is {rebuildUntil}");
                Dictionary <int, string> processedAggregates = new Dictionary <int, string>();

                projectionStoreInitializer.Initialize(version);

                var projectionHandledEventTypes = GetInvolvedEvents(projectionType);
                foreach (var eventType in projectionHandledEventTypes)
                {
                    logger.Info(() => $"Rebuilding projection `{version.ProjectionName}` for version {version} using eventType `{eventType}`. Deadline is {rebuildUntil}");

                    IEnumerable <IndexRecord> indexRecords = index.EnumerateRecords(eventType);
                    foreach (IndexRecord indexRecord in indexRecords)
                    {
                        // TODO: (5) Decorator pattern which will give us the tracking
                        progressCounter++;
                        if (progressCounter % 1000 == 0)
                        {
                            logger.Info(() => $"Rebuilding projection {version.ProjectionName} => PROGRESS:{progressCounter} Version:{version} EventType:{eventType} Deadline:{rebuildUntil} Total minutes working:{(DateTime.UtcNow - startRebuildTimestamp).TotalMinutes}. logId:{Guid.NewGuid().ToString()} ProcessedAggregatesSize:{processedAggregates.Count}");
                        }

                        int aggreagteRootIdHash = indexRecord.AggregateRootId.GetHashCode();
                        if (processedAggregates.ContainsKey(aggreagteRootIdHash))
                        {
                            continue;
                        }
                        processedAggregates.Add(aggreagteRootIdHash, null);

                        string           mess   = Encoding.UTF8.GetString(indexRecord.AggregateRootId);
                        IAggregateRootId arId   = GetAggregateRootId(mess);
                        EventStream      stream = eventStore.Load(arId);

                        foreach (AggregateCommit arCommit in stream.Commits)
                        {
                            for (int i = 0; i < arCommit.Events.Count; i++)
                            {
                                IEvent theEvent = arCommit.Events[i].Unwrap();

                                if (projectionHandledEventTypes.Contains(theEvent.GetType().GetContractId())) // filter out the events which are not part of the projection
                                {
                                    var origin = new EventOrigin(mess, arCommit.Revision, i, arCommit.Timestamp);
                                    projectionWriter.Save(projectionType, theEvent, origin, version);
                                }
                            }
                        }
                    }
                }

                logger.Info(() => $"Finish rebuilding projection `{projectionType.Name}` for version {version}. Deadline was {rebuildUntil}");
                return(new ReplayResult());
            }
            catch (Exception ex)
            {
                string message = $"Unable to replay projection. Version:{version} ProjectionType:{projectionType.FullName}";
                logger.ErrorException(ex, () => message);
                return(new ReplayResult(message + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace));
            }
        }
        private string CreateRedisRevisionKey(IAggregateRootId aggregateRootId)
        {
            var stringRawId = Convert.ToBase64String(aggregateRootId.RawId);

            return string.Concat("revision-", stringRawId);
        }
 private Result<bool> CheckForExistingRevision(IAggregateRootId arId)
 {
     return revisionStore.HasRevision(arId);
 }
 public Task <ReadResult <AR> > LoadAsync <AR>(IAggregateRootId id) where AR : IAggregateRoot
 {
     return(aggregateRepository.LoadAsync <AR>(id));
 }