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);
 }
Example #3
0
        // ------------------------------------------------------------------------------------------------------------
        // ------------------------------ 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));
            }
        }
Example #4
0
        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>));
        }
Example #5
0
        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);
            }
        }
Example #6
0
        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);
        }
Example #7
0
        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;
                    }
                }
            }
        }
Example #9
0
        // 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);
        }