public async Task GetAtVersionAsync_returns_correct_value() { // Arrange var stream = await _fixture.GetStreamADAsync(); var firstValue = "firstValue"; var middleValue = "middleValue"; // <= Expected value var lastValue = "lastValue"; await stream.AppendAsync(new StoredValue(firstValue)); // version 0 await stream.AppendAsync(new StoredValue(middleValue)); // version 1 <= Pick this one await stream.AppendAsync(new StoredValue(lastValue)); // version 2 var middleVersion = ExpectedVersion.Specific(1); // Act var result = await stream.GetAtVersionAsync((ulong)middleVersion.Value); // Assert 2 Assert.IsNotNull(result); Assert.IsTrue(result.HasValue); Assert.IsInstanceOfType(result, typeof(Result <StoredValue>)); Assert.AreEqual(middleValue, result.Value.Parse <string>()); }
public TestEventStore(IKnownSerializers serializers) { _eventSerializer = serializers.Events.Serializer; _eventDeserializer = serializers.Events.Deserializer; _streams = new Dictionary <string, IReadOnlyList <SerializedEvent> >(); _streamPositions = new Dictionary <string, int>(); ExpectedVersion = new ExpectedVersion(-1, -2, -3); }
// ------------------------------------------------------------------------------------------------------------ // ------------------------------ PRIVATE ------------------------------ // ------------------------------------------------------------------------------------------------------------ Result <ExpectedVersion> ValidateVersion(ExpectedVersion expectedVersion) { switch (expectedVersion) { case AnyVersion _: case NoVersion noversion when noversion == this.Version: case SpecificVersion specific when specific == this.Version: return(Result.OK(expectedVersion)); case NoVersion noversion when noversion != this.Version: case SpecificVersion specific when specific != this.Version: return(new VersionMismatch <ExpectedVersion>()); case null: default: throw new ArgumentOutOfRangeException(nameof(expectedVersion)); } }
public async Task TrySetValueAsync_with_correct_version_succeeds() { // Arrange var valueAD = await _fixture.GetValueADAsync(); var firstValue = "firstValue"; await valueAD.SetAsync(new StoredValue(firstValue)); var expectedVersion = ExpectedVersion.Specific(0); // Act var lastValue = "lastValue"; var addResult = await valueAD.TrySetAsync(new StoredValue(lastValue), expectedVersion); // Assert Assert.IsNotNull(addResult); Assert.IsTrue(addResult.HasValue); Assert.IsNotInstanceOfType(addResult, typeof(VersionMismatch <Pointer>)); }
public override int GetHashCode() { unchecked { int result = LogPosition.GetHashCode(); result = (result * 397) ^ Flags.GetHashCode(); result = (result * 397) ^ TransactionPosition.GetHashCode(); result = (result * 397) ^ TransactionOffset; result = (result * 397) ^ ExpectedVersion.GetHashCode(); result = (result * 397) ^ EventStreamId.GetHashCode(); result = (result * 397) ^ EventId.GetHashCode(); result = (result * 397) ^ CorrelationId.GetHashCode(); result = (result * 397) ^ TimeStamp.GetHashCode(); result = (result * 397) ^ EventType.GetHashCode(); result = (result * 397) ^ Data.GetHashCode(); result = (result * 397) ^ Metadata.GetHashCode(); return(result); } }
async Task <Result <Pointer> > ExpandLevelAsync(StoredValue value, ExpectedVersion expectedVersion, IMdNode previous) { if (Level == 0) { return(new ArgumentOutOfRange <Pointer>(nameof(Level))); } byte[] snapshot = default; if (_snapshotter != null && previous != null) { var snapshotResult = await _snapshotter.StoreSnapshot(previous); if (!snapshotResult.HasValue) { return(snapshotResult.CastError <byte[], Pointer>()); } snapshot = snapshotResult.Value; } var meta = new MdMetadata { Level = this.Level - 1, Snapshot = snapshot, Previous = previous?.MdLocator, StartIndex = previous?.EndIndex + 1 ?? 0 }; var md = await _dataOps.NodeFactory.CreateNewMdNodeAsync(meta).ConfigureAwait(false); var leafPointer = await md.TryAppendAsync(value, expectedVersion).ConfigureAwait(false); if (!leafPointer.HasValue) { return(leafPointer); } switch (md.Type) { case MdType.Pointers: // i.e. we have still not reached the end of the tree await AddAsync(new Pointer { MdLocator = md.MdLocator, ValueType = typeof(Pointer).Name }).ConfigureAwait(false); break; case MdType.Values: // i.e. we are now right above leaf level await AddAsync(new Pointer { MdLocator = leafPointer.Value.MdLocator, ValueType = typeof(Pointer).Name }).ConfigureAwait(false); break; default: return(new ArgumentOutOfRange <Pointer>(nameof(md.Type))); } return(leafPointer); }
public async Task <Result <Pointer> > TryAppendAsync(StoredValue value, ExpectedVersion expectedVersion) { if (IsFull) { return(new MdOutOfEntriesError <Pointer>($"Filled: {Count}/{Constants.MdCapacity}")); } switch (Type) { case MdType.Values: var versionRes = ValidateVersion(expectedVersion); if (!versionRes.HasValue) // optimistic concurrency { return(new VersionMismatch <Pointer>(versionRes.ErrorMsg)); } return(await AddObjectAsync($"{NextVersion}", value).ConfigureAwait(false)); case MdType.Pointers: if (Count > 0) { var pointer = await GetLastPointer().ConfigureAwait(false); if (!pointer.HasValue) { return(pointer); } var targetResult = await _dataOps.NodeFactory.LocateAsync(pointer.Value.MdLocator).ConfigureAwait(false); if (!targetResult.HasValue) { return(targetResult.CastError <IMdNode, Pointer>()); } var target = targetResult.Value; if (target.IsFull) { return(await ExpandLevelAsync(value, expectedVersion, previous : target).ConfigureAwait(false)); } return(await target.TryAppendAsync(value, expectedVersion).ConfigureAwait(false)); } // Count == 0, i.e. we must get last MdNode held by Previous for this node. // (i.e. last node, one level down, of previous node on this level) if (Previous == null) // (if null Previous, this would be the very first, still empty, MdNode in the tree) { return(await ExpandLevelAsync(value, expectedVersion, previous : default).ConfigureAwait(false)); } var prevNode = await _dataOps.NodeFactory.LocateAsync(Previous).ConfigureAwait(false); if (!prevNode.HasValue) { return(prevNode.CastError <IMdNode, Pointer>()); } var lastPointerOfPrevNode = await(prevNode.Value as MdNode).GetLastPointer(); if (!lastPointerOfPrevNode.HasValue) { return(lastPointerOfPrevNode); } var prevNodeForNewNode = await _dataOps.NodeFactory.LocateAsync(lastPointerOfPrevNode.Value.MdLocator).ConfigureAwait(false); if (!prevNodeForNewNode.HasValue) { return(prevNodeForNewNode.CastError <IMdNode, Pointer>()); } return(await ExpandLevelAsync(value, expectedVersion, previous : prevNodeForNewNode.Value).ConfigureAwait(false)); default: return(new ArgumentOutOfRange <Pointer>(nameof(Type))); } }
private async Task AppendToStreamWithExpectedVersion(string stream, int expectedVersion, UncommittedMessages data, CancellationToken token) { using (var connection = new SqlConnection(_settings.ConnectionString)) { await connection.OpenAsync(token).NotOnCapturedContext(); using (var tran = connection.BeginTransaction(IsolationLevel.ReadCommitted)) { try { var messageVersion = expectedVersion; foreach (var msg in data.Messages) { messageVersion += 1; using (var cmd = expectedVersion == 0 && messageVersion == 1 ? connection.CreateCommandToAppendingWithNoStream(stream, data, msg) : connection.CreateCommandToAppendingWithExpectedVersion(stream, data, msg, messageVersion)) { cmd.Transaction = tran; await cmd.ExecuteNonQueryAsync(token).NotOnCapturedContext(); } } tran.Commit(); } catch (SqlException e) { tran.Rollback(); if (e.IsUniqueConstraintViolation() || e.IsWrongExpectedVersionRised()) { throw new WrongExpectedVersionException($"Appending {data.Messages.Length} messages to stream '{stream}' with expected version {ExpectedVersion.Parse(expectedVersion)} failed.", e); } throw; } } } }
// Pass in data that was encrypted at rest // for upload to SAFENetwork. public async Task <(NetworkEvent, Result <Pointer>)> Upload(WALContent walContent) { var data = ZipEncryptedEvent.From(walContent.EncryptedContent); var localEvt = data.GetEvent(_pwd); var networkEvt = await GetNetworkEvent(localEvt); var expectedVersion = walContent.SequenceNr == 0 ? ExpectedVersion.None : ExpectedVersion.Specific(walContent.SequenceNr - 1); var result = await _stream.TryAppendAsync(new StoredValue(networkEvt), expectedVersion); return(networkEvt, result); }