Esempio n. 1
0
        Task IRepository.Prepare(Guid commitId)
        {
            Logger.Write(LogLevel.Debug, () => $"Repository {typeof(T).FullName} starting prepare {commitId}");

            return
                (Tracked.Values
                 .ToArray()
                 .SelectAsync(async(x) =>
            {
                try
                {
                    await x.Stream.VerifyVersion(commitId).ConfigureAwait(false);
                }
                catch (VersionException)
                {
                    await _store.Evict <T>(x.Stream.Bucket, x.Stream.StreamId).ConfigureAwait(false);
                    throw;
                }
            }));
        }
Esempio n. 2
0
        async Task <Guid> IRepository.Commit(Guid commitId, Guid startingEventId, IDictionary <string, string> commitHeaders)
        {
            Logger.Write(LogLevel.Debug, () => $"Repository {typeof(T).FullName} starting commit {commitId}");
            var written = 0;

            using (CommitTime.NewContext())
            {
                foreach (var tracked in Tracked.Values)
                {
                    if (tracked.Stream.TotalUncommitted == 0)
                    {
                        return(startingEventId);
                    }

                    var headers = new Dictionary <string, string>(commitHeaders);

                    var stream = tracked.Stream;

                    Interlocked.Add(ref written, stream.TotalUncommitted);

                    if (stream.StreamVersion != stream.CommitVersion && tracked is ISnapshotting &&
                        (tracked as ISnapshotting).ShouldTakeSnapshot())
                    {
                        Logger.Write(LogLevel.Debug,
                                     () =>
                                     $"Taking snapshot of [{tracked.GetType().FullName}] id [{tracked.StreamId}] version {tracked.Version}");
                        var memento = (tracked as ISnapshotting).TakeSnapshot();
                        stream.AddSnapshot(memento, headers);
                    }

                    var evict = true;
                    try
                    {
                        startingEventId = await stream.Commit(commitId, startingEventId, headers).ConfigureAwait(false);

                        await _store.Cache <T>(stream).ConfigureAwait(false);

                        evict = false;
                    }
                    catch (VersionException e)
                    {
                        // If we expected no stream, no reason to try to resolve the conflict
                        if (stream.CommitVersion == -1)
                        {
                            throw new ConflictResolutionFailedException(
                                      $"New stream [{tracked.StreamId}] entity {tracked.GetType().FullName} already exists in store");
                        }

                        Conflicts.Mark();
                        try
                        {
                            Logger.Write(LogLevel.Debug,
                                         () =>
                                         $"Stream [{tracked.StreamId}] entity {tracked.GetType().FullName} version {stream.StreamVersion} has version conflicts with store - Message: {e.Message}");
                            // make new clean entity

                            Logger.Write(LogLevel.Debug,
                                         () => $"Attempting to resolve conflict with strategy {_conflictResolution.Conflict}");

                            using (ConflictResolutionTime.NewContext())
                            {
                                var uncommitted = stream.Uncommitted.ToList();
                                stream.Flush(false);
                                var clean = await GetUntracked(stream).ConfigureAwait(false);

                                try
                                {
                                    var strategy = _conflictResolution.Conflict.Build(_builder,
                                                                                      _conflictResolution.Resolver);
                                    startingEventId =
                                        await
                                        strategy.Resolve(clean, uncommitted, commitId, startingEventId,
                                                         commitHeaders).ConfigureAwait(false);
                                }
                                catch (ConflictingCommandException)
                                {
                                    throw new ConflictResolutionFailedException("Failed to resolve stream conflict");
                                }
                                await _store.Cache <T>(clean.Stream).ConfigureAwait(false);

                                evict = false;
                            }

                            Logger.WriteFormat(LogLevel.Debug,
                                               "Stream [{0}] entity {1} version {2} had version conflicts with store - successfully resolved",
                                               tracked.StreamId, tracked.GetType().FullName, stream.StreamVersion);
                            ConflictsResolved.Mark();
                        }
                        catch (AbandonConflictException abandon)
                        {
                            ConflictsUnresolved.Mark();
                            Logger.WriteFormat(LogLevel.Error,
                                               "Stream [{0}] entity {1} has version conflicts with store - abandoning resolution",
                                               tracked.StreamId, tracked.GetType().FullName);
                            throw new ConflictResolutionFailedException(
                                      $"Aborted conflict resolution for stream [{tracked.StreamId}] entity {tracked.GetType().FullName}",
                                      abandon);
                        }
                        catch (Exception ex)
                        {
                            ConflictsUnresolved.Mark();
                            Logger.WriteFormat(LogLevel.Error,
                                               "Stream [{0}] entity {1} has version conflicts with store - FAILED to resolve due to: {3}: {2}",
                                               tracked.StreamId, tracked.GetType().FullName, ex.Message, ex.GetType().Name);
                            throw new ConflictResolutionFailedException(
                                      $"Failed to resolve conflict for stream [{tracked.StreamId}] entity {tracked.GetType().FullName} due to exception",
                                      ex);
                        }
                    }
                    catch (PersistenceException e)
                    {
                        Logger.WriteFormat(LogLevel.Warn,
                                           "Failed to commit events to store for stream: [{0}] bucket [{1}] Exception: {3}: {2}",
                                           stream.StreamId, stream.Bucket, e.Message, e.GetType().Name);
                        throw;
                    }
                    catch (DuplicateCommitException)
                    {
                        Logger.WriteFormat(LogLevel.Warn,
                                           "Detected a double commit for stream: [{0}] bucket [{1}] - discarding changes for this stream",
                                           stream.StreamId, stream.Bucket);
                        // I was throwing this, but if this happens it means the events for this message have already been committed.  Possibly as a partial message failure earlier.
                        // Im changing to just discard the changes, perhaps can take a deeper look later if this ever bites me on the ass
                        //throw;
                    }
                    finally
                    {
                        if (evict)
                        {
                            await _store.Evict <T>(stream.Bucket, stream.StreamId).ConfigureAwait(false);

                            await _snapstore.Evict <T>(stream.Bucket, stream.StreamId).ConfigureAwait(false);

                            WriteErrors.Mark();
                        }
                    }
                }
            }
            WrittenEvents.Update(written);
            Logger.Write(LogLevel.Debug, () => $"Repository {typeof(T).FullName} finished commit {commitId}");
            return(startingEventId);
        }