public async Task <AppendResult> SaveAsync <T>(T domainObject) where T : class, IDomainObject { AppendResult result = await SaveAsync(domainObject, false); AssertSave(domainObject, result); return(result); }
private async Task <AppendResult> CreateEmptyStream( StreamIdInfo streamId, int expectedVersion, CancellationToken cancellationToken) { var appendResult = new AppendResult(StreamVersion.End, Position.End); using (var connection = await OpenConnection(cancellationToken)) using (var transaction = await connection .BeginTransactionAsync(cancellationToken) .ConfigureAwait(false)) using (var command = BuildStoredProcedureCall( _schema.CreateEmptyStream, transaction, Parameters.StreamId(streamId.MySqlStreamId), Parameters.StreamIdOriginal(streamId.MySqlStreamId), Parameters.MetadataStreamId(streamId.MetadataMySqlStreamId), Parameters.ExpectedVersion(expectedVersion))) using (var reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false)) { await reader.ReadAsync(cancellationToken).ConfigureAwait(false); appendResult = new AppendResult(reader.GetInt32(0), reader.GetInt64(1)); reader.Close(); await transaction.CommitAsync(cancellationToken).ConfigureAwait(false); } return(appendResult); }
public async Task InvokeAndSaveAsync(Type domainObjectType, ICommand cmd, IEnumerable <CommandMethodMapping> commandMapping) { Precondition.For(domainObjectType, nameof(domainObjectType)).NotNull("Type has to be set!"); Precondition.For(cmd, nameof(cmd)).IsValidCommand(); Precondition.For(commandMapping, nameof(commandMapping)).NotNull(); var type = cmd.GetType(); logger.LogTrace("Invoking Command \"{type}\" for \"{domainObjectType}\"", domainObjectType); var currentTry = 0; while (currentTry < retryCount) { if (currentTry > 0) { logger.LogTrace("Retrying Command \"{type}\" for \"{domainObjectType}\"..."); } AppendResult result = await InvokeAndSaveInternalAsync(domainObjectType, cmd, commandMapping); if (result.HadWrongVersion) { currentTry++; } else { break; } } }
private async Task <AppendResult> InsertEvent(EventCommit commit, bool versionCheck) { IClientSessionHandle session = null; if (UseTransactions) { session = await Database.Client.StartSessionAsync(new ClientSessionOptions()); session.StartTransaction(new TransactionOptions()); } var watch = Stopwatch.StartNew(); commit.Ordinal = await identifier.Next("commit"); AppendResult result = AppendResult.NoUpdate; try { var currentVersion = await GetVersion(commit.AggregateType, commit.AggregateId); if (versionCheck && currentVersion != commit.ExpectedPreviousVersion) { logger.LogWarning("Event Version check requested and version was wrong . was: {0} expected: {1}", currentVersion, commit.ExpectedPreviousVersion); //TODO If version check throw exception! result = AppendResult.WrongVersion(commit.VersionCommit); } commit.VersionCommit = currentVersion + commit.Events.Count; await Collection.InsertOneAsync(commit); result = new AppendResult(commit.Id.ToString(), false, commit.VersionCommit, "SUCCESS"); } catch (MongoWriteException e) { if (e.Message.Contains("E11000 duplicate key")) { result = AppendResult.WrongVersion(commit.VersionCommit); } else { logger.LogError(e, "Error when saving a commit for {type} {id}", commit.AggregateType, commit.AggregateId); throw; } } finally { await CommitOrAbortTx(session, result); watch.Stop(); logger.LogDebug("{Count} events for {Type} - {Id} handled. Result: {Result}.", commit.Events.Count, commit.AggregateType, commit.AggregateId, result.CommitId); } return(result); }
public void AcceptedWhenNoError() { var result = new AppendResult("1", false, 14, "SUCCESS"); HttpStatusCode code = StatusCodeConverter.From(result); Assert.Equal(HttpStatusCode.Accepted, code); }
public void ConflictWhenVersionError() { var result = new AppendResult("", true, 14, "WRONGVERSION"); HttpStatusCode code = StatusCodeConverter.From(result); Assert.Equal(HttpStatusCode.Conflict, code); }
public async Task <AppendResult> SaveAsync <T>(T domainObject) where T : class, IDomainObject { AppendResult result = await SaveAsync(domainObject, false); if (result.HadWrongVersion) { throw new VersionConflictException(domainObject.GetType().Name, domainObject.Id, result.CurrentVersion); } return(result); }
protected override async Task <AppendResult> SaveUncomittedEventsAsync <T>(T domainObject, bool versionCheck) { string streamName = context.StreamNamer.Resolve(domainObject); long expectedVersion = versionCheck ? ExpectedVersionFromDo(domainObject) : ExpectedVersion.Any; AppendResult result = await context.Writer.AppendAsync(streamName, domainObject.GetUncommittedEvents(), expectedVersion); return(result); }
public void ItThrowsAtEndOfRetries() { var repo = A.Fake <IDomainObjectRepository>(); A.CallTo(() => repo.SaveAsync(A <IDomainObject> .Ignored, A <bool> .Ignored)) .Returns(AppendResult.WrongVersion(2)); ConventionCommandInvoker sut = GetSut(repo); var cmd = new SampleCommand(); }
private async Task <AppendResult> AppendMessagesToStream( StreamIdInfo streamId, int expectedVersion, NewStreamMessage[] messages, CancellationToken cancellationToken) { var appendResult = new AppendResult(StreamVersion.End, Position.End); var nextExpectedVersion = expectedVersion; using (var connection = await OpenConnection(cancellationToken)) using (var transaction = await connection .BeginTransactionAsync(cancellationToken) .ConfigureAwait(false)) { var throwIfAdditionalMessages = false; for (var i = 0; i < messages.Length; i++) { bool messageExists; (nextExpectedVersion, appendResult, messageExists) = await AppendMessageToStream( streamId, nextExpectedVersion, messages[i], transaction, cancellationToken); if (i == 0) { throwIfAdditionalMessages = messageExists; } else { if (throwIfAdditionalMessages && !messageExists) { throw new WrongExpectedVersionException( ErrorMessages.AppendFailedWrongExpectedVersion( streamId.MySqlStreamId.IdOriginal, expectedVersion), streamId.MySqlStreamId.IdOriginal, expectedVersion); } } } await transaction.CommitAsync(cancellationToken).NotOnCapturedContext(); } await TryScavenge(streamId, cancellationToken).NotOnCapturedContext(); return(appendResult); }
public static PostMessagesResponse FromAppendResult(AppendResult r) { var array = new long[r.Ids.Count]; for (int i = 0; i < array.Length; i++) { array[i] = r.Ids[i].GetOffset(); } return(new PostMessagesResponse() { Position = r.Position, Offsets = array }); }
private void AssertSave <T>(T domainObject, AppendResult result) where T : class, IDomainObject { if (result.HadWrongVersion) { Type type = domainObject.GetType(); string id = domainObject.Id; logger.LogError("Version conflict when saving a {type} with id {id}. Version {CurrentVersion}", type, id, result.CurrentVersion); throw new VersionConflictException(domainObject.GetType().Name, domainObject.Id, result.CurrentVersion); } }
private static async Task CommitOrAbortTx(IClientSessionHandle session, AppendResult result) { if (session != null) { if (result.HadWrongVersion || string.IsNullOrWhiteSpace(result.CommitId) || result.CurrentVersion <= 0) { await session.AbortTransactionAsync(); } else { await session.CommitTransactionAsync(); } session.Dispose(); } }
public async Task <AppendResult> SaveAsync <T>(T domainObject, bool preventVersionCheck) where T : class, IDomainObject { Precondition.For(domainObject, nameof(domainObject)) .NotNull("The domainObject to be saved must not be null!"); string type = typeof(T).FullName; if (!domainObject.HasUncommittedEvents) { string id = domainObject.Id; logger.LogDebug("\"{type}\"-{id} had no events to save", type, id); return(AppendResult.NoUpdate); } int count = domainObject.GetUncommittedEvents().Count; logger.LogTrace("Saving \"{type}\" with {count} events...", type, count); Stopwatch watch = Stopwatch.StartNew(); bool check = domainObject.CheckVersionOnSave && !preventVersionCheck; AppendResult result = await SaveUncomittedEventsAsync(domainObject, check); if (!result.HadWrongVersion) { foreach (IEvent @event in domainObject.GetUncommittedEvents()) { if (configuration.PostSavePipeline != null) { configuration.PostSavePipeline(@event); } if (configuration.DirectDenormalizers != null) { await configuration.DirectDenormalizers.HandleAsync(@event); } } } domainObject.CommitChanges(result.CurrentVersion); watch.Stop(); logger.LogTrace("Saved {count} events for \"{type}\" in {watch.ElapsedMilliseconds}ms", count, type, watch.ElapsedMilliseconds); return(result); }
public AppendResult Append(IMessageView sm) { AppendResult result = new AppendResult(false); bool bottom = GetBottommostIndex() == Collection.Count - 2; // -2 because we already incremented Count Collection.Add(sm, sm.Model.Author == null); if (bottom) { UpdateLayout(); ScrollToBottom(); if (ActivationState != CoreWindowActivationState.Deactivated) { result = new AppendResult(true); } } return(result); }
public AppendResult Append(SignalMessageContainer sm) { AppendResult result = null; bool bottom = GetBottommostIndex() == Collection.Count - 2; // -2 because we already incremented Count Collection.Add(sm, sm.Message.Author == null); if (bottom) { UpdateLayout(); ScrollToBottom(); if (ActivationState != CoreWindowActivationState.Deactivated) { result = new AppendResult(sm.Index); } } return(result); }
private async Task <AppendResult> InsertEvent(EventCommit commit, bool versionCheck) { commit.Ordinal = await identifier.Next("commit"); try { await Collection.InsertOneAsync(commit); } catch (MongoWriteException e) { if (e.Message.Contains("E11000 duplicate key ")) { return(AppendResult.WrongVersion(commit.VersionCommit)); } throw; } return(new AppendResult(false, commit.VersionCommit)); }
public async Task <long> PersistFacts(Fact[] facts) { var factsByAggregate = facts.GroupBy(x => x.Identifier); AppendResult result = null; foreach (var aggregateWithEvents in factsByAggregate) { result = await _streamStore.AppendToStream( aggregateWithEvents.Key, ExpectedVersion.Any, aggregateWithEvents .Select(e => new NewStreamMessage( Guid.NewGuid(), _eventMapping.GetEventName(e.Event.GetType()), _eventSerializer.SerializeObject(e.Event))).ToArray()); } return(result?.CurrentPosition ?? await _streamStore.ReadHeadPosition()); }
public async Task <AppendResult> SaveAsync <T>(T domainObject, bool preventVersionCheck) where T : class, IDomainObject { string type = typeof(T).FullName; if (!domainObject.HasUncommittedEvents) { Trace.WriteLine("No events to save"); return(AppendResult.NoUpdate); } Trace.WriteLine($"Saving \"{type}\"...", TraceCategory); Stopwatch watch = Stopwatch.StartNew(); bool check = domainObject.CheckVersionOnSave && !preventVersionCheck; AppendResult result = await SaveUncomittedEventsAsync(domainObject, check); domainObject.CommitChanges(result.CurrentVersion); watch.Stop(); Trace.WriteLine($"Saved \"{type}\" in {watch.ElapsedMilliseconds}ms", TraceCategory); return(result); }
public async Task InvokeAndSaveAsync(Type domainObjectType, ICommand cmd, IEnumerable <CommandMethodMapping> commandMapping) { Precondition.For(domainObjectType, nameof(domainObjectType)).NotNull("Type has to be set!"); Precondition.For(cmd, nameof(cmd)).IsValidCommand(); Precondition.For(commandMapping, nameof(commandMapping)).NotNull(); logger.LogTrace("Invoking Command \"{type}\" for \"{domainObjectType}\"", domainObjectType); var currentTry = 0; AppendResult result = AppendResult.NoUpdate; while (currentTry < retryCount) { if (currentTry > 0) { logger.LogTrace("Retrying Command \"{type}\" for \"{domainObjectType}\"..."); } result = await InvokeAndSaveInternalAsync(domainObjectType, cmd, commandMapping); if (result.HadWrongVersion) { currentTry++; } else { break; } } if (currentTry >= retryCount) { var msg = $"Saving \"{domainObjectType.Name}\" with id \"{cmd.DomainObjectId}\" failed due to version conflicts"; logger.LogWarning(msg); throw new VersionConflictException(domainObjectType.Name, cmd.DomainObjectId, result.CurrentVersion); } }
public async Task InvokeAndSaveAsync(Type domainObjectType, ICommand cmd, IEnumerable <CommandMethodMapping> commands) { Precondition.For(cmd, nameof(cmd)).IsValidCommand(); Precondition.For(domainObjectType, nameof(domainObjectType)).NotNull("Type has to be set!"); var currentTry = 0; while (currentTry < retryCount) { AppendResult result = await InvokeAndSaveInternalAsync(domainObjectType, cmd, commands); if (result.HadWrongVersion) { currentTry++; } else { break; } } }
public Producer(int id, ProducerAppendBehavior behavior, IStreamStore store, IScheduler scheduler) { Id = id; Behavior = behavior; Store = store ?? throw new ArgumentNullException(nameof(store)); Scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); MessagePumpCancellation = new CancellationTokenSource(); Mailbox = new BufferBlock <object>(new DataflowBlockOptions { BoundedCapacity = int.MaxValue, MaxMessagesPerTask = 1, CancellationToken = MessagePumpCancellation.Token }); MessagePump = Task.Run(async() => { try { var text = new Lorem(); var random = new Random(); while (!MessagePumpCancellation.Token.IsCancellationRequested) { var message = await Mailbox.ReceiveAsync(MessagePumpCancellation.Token).ConfigureAwait(false); switch (message) { case AppendToStream append: var messages = Enumerable .Range(0, random.Next(1, 100)) // produce between 1 and 99 messages per append .Select(index => new NewStreamMessage(Guid.NewGuid(), append.Stream, text.Sentences(random.Next(5, 10)))) //randomize the data a bit .ToArray(); if (behavior == ProducerAppendBehavior.Batched) { try { var result = await Store .AppendToStream( append.Stream, append.ExpectedVersion, messages, MessagePumpCancellation.Token) .ConfigureAwait(false); await scheduler .ScheduleTellOnceAsync( () => Mailbox.Post(new AppendToStream { Stream = append.Stream, ExpectedVersion = result.CurrentVersion }), TimeSpan.FromMilliseconds(random.Next(100, 5000)), // produce another append on the same stream in about 100 to 5000ms MessagePumpCancellation.Token) .ConfigureAwait(false); } catch (Exception exception) { if (!MessagePumpCancellation.IsCancellationRequested) { Console.WriteLine("[{0}]AppendToStream failed because {1}", Id, exception); await scheduler .ScheduleTellOnceAsync( () => Mailbox.Post(new AppendToStream { Stream = append.Stream, ExpectedVersion = append.ExpectedVersion }), TimeSpan.FromMilliseconds(random.Next(100, 5000)), // produce another append on the same stream in about 100 to 5000ms MessagePumpCancellation.Token) .ConfigureAwait(false); } } } else { AppendResult result = new AppendResult(append.ExpectedVersion, -1); try { foreach (var msg in messages) { result = await Store .AppendToStream( append.Stream, append.ExpectedVersion, messages, MessagePumpCancellation.Token) .ConfigureAwait(false); } await scheduler .ScheduleTellOnceAsync( () => Mailbox.Post(new AppendToStream { Stream = append.Stream, ExpectedVersion = result.CurrentVersion }), TimeSpan.FromMilliseconds(random.Next(100, 5000)), // produce another append on the same stream in about 100 to 5000ms MessagePumpCancellation.Token) .ConfigureAwait(false); } catch (Exception exception) { if (!MessagePumpCancellation.IsCancellationRequested) { Console.WriteLine("[{0}]AppendToStream failed because {1}", Id, exception); await scheduler .ScheduleTellOnceAsync( () => Mailbox.Post(new AppendToStream { Stream = append.Stream, ExpectedVersion = result.CurrentVersion }), TimeSpan.FromMilliseconds(random.Next(100, 5000)), // produce another append on the same stream in about 100 to 5000ms MessagePumpCancellation.Token) .ConfigureAwait(false); } } } break; } } } catch (OperationCanceledException) { } }, MessagePumpCancellation.Token); }
protected override Task <List <IEvent> > ByAppendResult(AppendResult result) { throw new NotImplementedException(); }
public static LogRef ExpectOK(this AppendResult result) => result.Success ? (LogRef)result : throw new Exception("Append failed!");
protected abstract Task <List <IEvent> > ByAppendResult(AppendResult result);
protected override async Task <AppendResult> AppendToStreamInternal( string streamId, int expectedVersion, NewStreamMessage[] messages, CancellationToken cancellationToken) { int maxRetries = 2; //TODO too much? too little? configurable? Exception exception; int retryCount = 0; do { try { AppendResult result; var streamIdInfo = new StreamIdInfo(streamId); using (var connection = await OpenConnection(cancellationToken)) using (var transaction = connection.BeginTransaction()) using (var command = BuildFunctionCommand( _schema.AppendToStream, transaction, Parameters.StreamId(streamIdInfo.PostgresqlStreamId), Parameters.StreamIdOriginal(streamIdInfo.PostgresqlStreamId), Parameters.MetadataStreamId(streamIdInfo.MetadataPosgresqlStreamId), Parameters.ExpectedVersion(expectedVersion), Parameters.CreatedUtc(_settings.GetUtcNow?.Invoke()), Parameters.NewStreamMessages(messages))) { try { using (var reader = await command.ExecuteReaderAsync(cancellationToken).NotOnCapturedContext()) { await reader.ReadAsync(cancellationToken).NotOnCapturedContext(); result = new AppendResult(reader.GetInt32(0), reader.GetInt64(1)); } await transaction.CommitAsync(cancellationToken).NotOnCapturedContext(); } catch (PostgresException ex) when(ex.IsWrongExpectedVersion()) { await transaction.RollbackAsync(cancellationToken).NotOnCapturedContext(); throw new WrongExpectedVersionException( ErrorMessages.AppendFailedWrongExpectedVersion( streamIdInfo.PostgresqlStreamId.IdOriginal, expectedVersion), ex); } } if (_settings.ScavengeAsynchronously) { #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed Task.Run(() => TryScavenge(streamIdInfo, cancellationToken), cancellationToken); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } else { await TryScavenge(streamIdInfo, cancellationToken).NotOnCapturedContext(); } return(result); } catch (PostgresException ex) when(ex.IsDeadlock()) { exception = ex; retryCount++; } } while(retryCount < maxRetries); ExceptionDispatchInfo.Capture(exception).Throw(); return(default); // never actually run
protected void initSuite2() { // some tests require time-stamps, other don't. The ones that do use this method, others don't. createTS = createNewTS(); createTime = Suite.ADSclient.GetCurrentServerTime();// DateTime.Now; // changesSince = full TS //createTime = createTime.AddHours(Suite.locUTCOff_hrs - Suite.servUTCOff_hrs); // Sets createTime to the location's timezone appendResult = appendData2(ptArray1); Thread.Sleep(2000); halfTime = Suite.ADSclient.GetCurrentServerTime(); //DateTime.Now; // asAt = 1st half TS; changesSince = 2nd half TS //halfTime = halfTime.AddHours(Suite.locUTCOff_hrs - Suite.servUTCOff_hrs); // Sets halfTime to the location's timezone }
public void ItAppliesTheVersion() { AppendResult result = GetSut(15); Assert.Equal(15, result.CurrentVersion); }
public static HttpStatusCode From(AppendResult result) { return(result.HadWrongVersion ? HttpStatusCode.Conflict : HttpStatusCode.Accepted); }