示例#1
0
        protected override async Task <ReadStreamPage> ReadStreamForwardsInternal(
            string streamId,
            int start,
            int count,
            bool prefetch,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            using (var connection = _createConnection())
            {
                await connection.OpenAsync(cancellationToken).ConfigureAwait(false);

                var streamIdInfo = new StreamIdInfo(streamId);
                return(await ReadStreamInternal(
                           streamIdInfo.SqlStreamId,
                           start,
                           count,
                           ReadDirection.Forward,
                           prefetch,
                           readNext,
                           connection,
                           null,
                           cancellationToken));
            }
        }
        protected override async Task <ReadStreamPage> ReadStreamForwardsInternal(
            string streamId,
            int start,
            int count,
            bool prefetch,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            try
            {
                using (var connection = await OpenConnection(cancellationToken))
                    using (var transaction = await connection
                                             .BeginTransactionAsync(cancellationToken)
                                             .NotOnCapturedContext())
                    {
                        var streamIdInfo = new StreamIdInfo(streamId);

                        return(await ReadStreamInternal(
                                   streamIdInfo.MySqlStreamId,
                                   start,
                                   count,
                                   ReadDirection.Forward,
                                   prefetch,
                                   readNext,
                                   transaction,
                                   cancellationToken));
                    }
            }
            catch (MySqlException exception) when(exception.InnerException is ObjectDisposedException disposedException)
            {
                throw new ObjectDisposedException(disposedException.Message, exception);
            }
        }
示例#3
0
        protected override async Task <ReadStreamPage> ReadStreamBackwardsInternal(
            string streamId,
            int start,
            int count,
            bool prefetch,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            using (var connection = _createConnection())
            {
                await connection.OpenAsync(cancellationToken).NotOnCapturedContext();

                var streamIdInfo = new StreamIdInfo(streamId);
                var(page, _) = await ReadStreamInternal(
                    streamIdInfo.SqlStreamId,
                    start,
                    count,
                    ReadDirection.Backward,
                    prefetch,
                    readNext,
                    connection,
                    null,
                    cancellationToken);

                return(page);
            }
        }
        public async Task <ReadStreamPage> ReadStreamBackwards(
            string streamId,
            int fromVersionInclusive,
            int maxCount,
            bool prefetchJsonData,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            Ensure.That(streamId, nameof(streamId)).IsNotNullOrWhiteSpace();
            Ensure.That(fromVersionInclusive, nameof(fromVersionInclusive)).IsGte(-1);
            Ensure.That(maxCount, nameof(maxCount)).IsGte(1);

            GuardAgainstDisposed();
            cancellationToken.ThrowIfCancellationRequested();

            if (Logger.IsDebugEnabled())
            {
                Logger.DebugFormat("ReadStreamBackwards {streamId} from version {fromVersionInclusive} with max count " +
                                   "{maxCount}.", streamId, fromVersionInclusive, maxCount);
            }
            ReadNextStreamPage readNext =
                (nextVersion, ct) => ReadStreamBackwards(streamId, nextVersion, maxCount, prefetchJsonData, ct);
            var page = await ReadStreamBackwardsInternal(streamId, fromVersionInclusive, maxCount, prefetchJsonData, readNext,
                                                         cancellationToken);

            return(await FilterExpired(page, readNext, cancellationToken));
        }
示例#5
0
        public async Task <ReadStreamPage> ReadStreamBackwards(
            StreamId streamId,
            int fromVersionInclusive,
            int maxCount,
            bool prefetchJsonData = true,
            CancellationToken cancellationToken = default)
        {
            Ensure.That(fromVersionInclusive, nameof(fromVersionInclusive)).IsGte(-1);
            Ensure.That(maxCount, nameof(maxCount)).IsGte(1);

            GuardAgainstDisposed();
            cancellationToken.ThrowIfCancellationRequested();

            Logger.Debug(
                "ReadStreamBackwards {streamId} from version {fromVersionInclusive} with max count {maxCount}.",
                streamId,
                fromVersionInclusive,
                maxCount);

            ReadNextStreamPage readNext =
                (nextVersion, ct) => ReadStreamBackwards(streamId, nextVersion, maxCount, prefetchJsonData, ct);
            var page = await ReadStreamBackwardsInternal(streamId, fromVersionInclusive, maxCount, prefetchJsonData, readNext,
                                                         cancellationToken);

            return(await FilterExpired(page, readNext, cancellationToken));
        }
示例#6
0
 protected abstract Task <ReadStreamPage> ReadStreamForwardsInternal(
     string streamId,
     int start,
     int count,
     bool prefetch,
     ReadNextStreamPage readNext,
     CancellationToken cancellationToken);
示例#7
0
        protected override async Task <ReadStreamPage> ReadStreamBackwardsInternal(
            string streamId,
            int fromVersionInclusive,
            int count,
            bool prefetch,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            using (var connection = await OpenConnection(cancellationToken))
                using (var transaction = await connection
                                         .BeginTransactionAsync(cancellationToken)
                                         .NotOnCapturedContext())
                {
                    var streamIdInfo = new StreamIdInfo(streamId);

                    return(await ReadStreamInternal(
                               streamIdInfo.MySqlStreamId,
                               fromVersionInclusive,
                               count,
                               ReadDirection.Backward,
                               prefetch,
                               readNext,
                               transaction,
                               cancellationToken));
                }
        }
示例#8
0
        protected override async Task <ReadStreamPage> ReadStreamBackwardsInternal(
            string streamId,
            int fromVersionInclusive,
            int count,
            bool prefetch,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            try
            {
                using (var connection = await OpenConnection(cancellationToken))
                    using (var transaction = await connection
                                             .BeginTransactionAsync(cancellationToken)
                                             .ConfigureAwait(false))
                    {
                        var streamIdInfo = new StreamIdInfo(streamId);

                        return(await ReadStreamInternal(
                                   streamIdInfo.MySqlStreamId,
                                   fromVersionInclusive,
                                   count,
                                   ReadDirection.Backward,
                                   prefetch,
                                   readNext,
                                   transaction,
                                   cancellationToken));
                    }
            }
            catch (MySqlException exception) when(exception.InnerException is ObjectDisposedException disposedException)
            {
                throw new ObjectDisposedException(disposedException.Message, exception);
            }
        }
示例#9
0
        private async Task <ReadStreamPage> FilterExpired(
            ReadStreamPage page,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            if (page.StreamId.StartsWith("$"))
            {
                return(page);
            }

            int?maxAge = _metadataMaxAgeCache == null
                ? null
                : await _metadataMaxAgeCache.GetMaxAge(page.StreamId, cancellationToken);

            if (!maxAge.HasValue)
            {
                return(page);
            }
            var currentUtc = GetUtcNow();
            var valid      = new List <StreamMessage>();

            foreach (var message in page.Messages)
            {
                if (message.CreatedUtc.AddSeconds(maxAge.Value) > currentUtc)
                {
                    valid.Add(message);
                }
                else
                {
                    PurgeExpiredMessage(message);
                }
            }
            return(new ReadStreamPage(
                       page.StreamId,
                       page.Status,
                       page.FromStreamVersion,
                       page.NextStreamVersion,
                       page.LastStreamVersion,
                       page.LastStreamPosition,
                       page.ReadDirection,
                       page.IsEnd,
                       readNext,
                       valid.ToArray()));
        }
        protected override async Task <ReadStreamPage> ReadStreamForwardsInternal(
            string streamId,
            int fromVersion,
            int count,
            bool prefetch,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            GuardAgainstDisposed();
            cancellationToken.ThrowIfCancellationRequested();

            using (var connection = OpenConnection())
            {
                var streamProperties = await connection.Streams(streamId)
                                       .Properties(initializeIfNotFound: false, cancellationToken);

                if (streamProperties == null)
                {
                    // not found.
                    return(new ReadStreamPage(
                               streamId,
                               PageReadStatus.StreamNotFound,
                               fromVersion,
                               StreamVersion.End,
                               StreamVersion.End,
                               StreamVersion.End,
                               ReadDirection.Forward,
                               true,
                               readNext));
                }

                return(await PrepareStreamResponse(connection,
                                                   streamId,
                                                   ReadDirection.Forward,
                                                   fromVersion,
                                                   prefetch,
                                                   readNext,
                                                   count,
                                                   streamProperties.MaxAge));
            }
        }
示例#11
0
 /// <summary>
 ///     Initialized a new instance of <see cref="ReadStreamPage"/>/
 /// </summary>
 /// <param name="streamId">The id of the stream that was read.</param>
 /// <param name="status">The <see cref="PageReadStatus"/> of the read operation.</param>
 /// <param name="fromStreamVersion">The version of the stream that read from.</param>
 /// <param name="nextStreamVersion">The next message version that can be read.</param>
 /// <param name="lastStreamVersion">The version of the last message in the stream.</param>
 /// <param name="lastStreamPosition">The position of the last message in the stream.</param>
 /// <param name="direction">The direction of the read operation.</param>
 /// <param name="isEnd">Whether or not this is the end of the stream.</param>
 /// <param name="readNext">An operation to read the next page.</param>
 /// <param name="messages">The messages read.</param>
 public ReadStreamPage(
     string streamId,
     PageReadStatus status,
     int fromStreamVersion,
     int nextStreamVersion,
     int lastStreamVersion,
     long lastStreamPosition,
     ReadDirection direction,
     bool isEnd,
     ReadNextStreamPage readNext = null,
     StreamMessage[] messages    = null)
 {
     StreamId           = streamId;
     Status             = status;
     FromStreamVersion  = fromStreamVersion;
     LastStreamVersion  = lastStreamVersion;
     LastStreamPosition = lastStreamPosition;
     NextStreamVersion  = nextStreamVersion;
     ReadDirection      = direction;
     IsEnd     = isEnd;
     Messages  = messages ?? new StreamMessage[0];
     _readNext = readNext ?? ((_, __) => throw new NotSupportedException());
 }
示例#12
0
 protected override Task <ReadStreamPage> ReadStreamForwardsInternal(string streamId, int start, int count, bool prefetch, ReadNextStreamPage readNext, CancellationToken cancellationToken)
 {
     throw new NotImplementedException();
 }
示例#13
0
 protected abstract Task <ReadStreamPage> ReadStreamBackwardsInternal(
     string streamId,
     int fromVersionInclusive,
     int count,
     bool prefetch,
     ReadNextStreamPage readNext, CancellationToken cancellationToken);
        protected override Task <ReadStreamPage> ReadStreamBackwardsInternal(
            string streamId,
            int fromVersionInclusive,
            int count,
            bool prefetch,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            GuardAgainstDisposed();
            cancellationToken.ThrowIfCancellationRequested();

            using (_lock.UseReadLock())
            {
                InMemoryStream stream;
                if (!_streams.TryGetValue(streamId, out stream))
                {
                    var notFound = new ReadStreamPage(streamId,
                                                      PageReadStatus.StreamNotFound,
                                                      fromVersionInclusive,
                                                      -1,
                                                      -1,
                                                      ReadDirection.Backward,
                                                      true,
                                                      StreamMessage.EmptyArray,
                                                      s_readNextNotFound);
                    return(Task.FromResult(notFound));
                }

                var messages = new List <StreamMessage>();
                var i        = fromVersionInclusive == StreamVersion.End ? stream.Messages.Count - 1 : fromVersionInclusive;
                while (i >= 0 && count > 0)
                {
                    var           inMemorymessage = stream.Messages[i];
                    StreamMessage message;
                    if (prefetch)
                    {
                        message = new StreamMessage(
                            streamId,
                            inMemorymessage.MessageId,
                            inMemorymessage.StreamVersion,
                            inMemorymessage.Position,
                            inMemorymessage.Created,
                            inMemorymessage.Type,
                            inMemorymessage.JsonMetadata,
                            inMemorymessage.JsonData);
                    }
                    else
                    {
                        message = new StreamMessage(
                            streamId,
                            inMemorymessage.MessageId,
                            inMemorymessage.StreamVersion,
                            inMemorymessage.Position,
                            inMemorymessage.Created,
                            inMemorymessage.Type,
                            inMemorymessage.JsonMetadata,
                            ct => Task.Run(() => ReadMessageData(streamId, inMemorymessage.MessageId), ct));
                        messages.Add(message);
                    }
                    messages.Add(message);

                    i--;
                    count--;
                }

                var lastStreamVersion = stream.Messages.Last().StreamVersion;
                var nextStreamVersion = messages.Last().StreamVersion - 1;
                var endOfStream       = nextStreamVersion < 0;

                var page = new ReadStreamPage(
                    streamId,
                    PageReadStatus.Success,
                    fromVersionInclusive,
                    nextStreamVersion,
                    lastStreamVersion,
                    ReadDirection.Backward,
                    endOfStream,
                    messages.ToArray(),
                    readNext);

                return(Task.FromResult(page));
            }
        }
        private async Task <ReadStreamPage> ReadStreamInternal(
            SqlStreamId sqlStreamId,
            int start,
            int count,
            ReadDirection direction,
            bool prefetch,
            ReadNextStreamPage readNext,
            SqlConnection connection, CancellationToken cancellationToken)
        {
            // If the count is int.MaxValue, TSql will see it as a negative number.
            // Users shouldn't be using int.MaxValue in the first place anyway.
            count = count == int.MaxValue ? count - 1 : count;

            // To read backwards from end, need to use int MaxValue
            var    streamVersion = start == StreamVersion.End ? int.MaxValue : start;
            string commandText;
            Func <List <StreamMessage>, int, int> getNextVersion;

            if (direction == ReadDirection.Forward)
            {
                commandText    = prefetch ? _scripts.ReadStreamForwardWithData : _scripts.ReadStreamForward;
                getNextVersion = (events, lastVersion) =>
                {
                    if (events.Any())
                    {
                        return(events.Last().StreamVersion + 1);
                    }
                    return(lastVersion + 1);
                };
            }
            else
            {
                commandText    = prefetch ? _scripts.ReadStreamBackwardWithData : _scripts.ReadStreamBackward;
                getNextVersion = (events, lastVersion) =>
                {
                    if (events.Any())
                    {
                        return(events.Last().StreamVersion - 1);
                    }
                    return(-1);
                };
            }

            using (var command = new SqlCommand(commandText, connection))
            {
                command.Parameters.AddWithValue("streamId", sqlStreamId.Id);
                command.Parameters.AddWithValue("count", count + 1); //Read extra row to see if at end or not
                command.Parameters.AddWithValue("streamVersion", streamVersion);

                using (var reader = await command.ExecuteReaderAsync(cancellationToken).NotOnCapturedContext())
                {
                    await reader.ReadAsync(cancellationToken).NotOnCapturedContext();

                    if (reader.IsDBNull(0))
                    {
                        return(new ReadStreamPage(
                                   sqlStreamId.IdOriginal,
                                   PageReadStatus.StreamNotFound,
                                   start,
                                   -1,
                                   -1,
                                   direction,
                                   true,
                                   StreamMessage.EmptyArray,
                                   readNext));
                    }
                    var lastStreamVersion = reader.GetInt32(0);

                    await reader.NextResultAsync(cancellationToken).NotOnCapturedContext();

                    var messages = new List <StreamMessage>();
                    while (await reader.ReadAsync(cancellationToken).NotOnCapturedContext())
                    {
                        var streamVersion1 = reader.GetInt32(0);
                        var ordinal        = reader.GetInt64(1);
                        var eventId        = reader.GetGuid(2);
                        var created        = reader.GetDateTime(3);
                        var type           = reader.GetString(4);
                        var jsonMetadata   = reader.GetString(5);

                        Func <CancellationToken, Task <string> > getJsonData;
                        if (prefetch)
                        {
                            var jsonData = reader.GetString(6);
                            getJsonData = _ => Task.FromResult(jsonData);
                        }
                        else
                        {
                            getJsonData = ct => GetJsonData(sqlStreamId.Id, streamVersion1, ct);
                        }

                        var message = new StreamMessage(
                            sqlStreamId.IdOriginal,
                            eventId,
                            streamVersion1,
                            ordinal,
                            created,
                            type,
                            jsonMetadata, getJsonData);

                        messages.Add(message);
                    }

                    var isEnd = true;
                    if (messages.Count == count + 1)
                    {
                        isEnd = false;
                        messages.RemoveAt(count);
                    }

                    return(new ReadStreamPage(
                               sqlStreamId.IdOriginal,
                               PageReadStatus.Success,
                               start,
                               getNextVersion(messages, lastStreamVersion),
                               lastStreamVersion,
                               direction,
                               isEnd,
                               messages.ToArray(),
                               readNext));
                }
            }
        }
示例#16
0
        private async Task <(ReadStreamPage, StreamMeta)> ReadStreamInternal(
            SqlStreamId sqlStreamId,
            int start,
            int count,
            ReadDirection direction,
            bool prefetch,
            ReadNextStreamPage readNext,
            SqlConnection connection,
            SqlTransaction transaction,
            CancellationToken cancellationToken)
        {
            // If the count is int.MaxValue, TSql will see it as a negative number.
            // Users shouldn't be using int.MaxValue in the first place anyway.
            count = count == int.MaxValue ? count - 1 : count;

            // To read backwards from end, need to use int MaxValue
            var    streamVersion = start == StreamVersion.End ? int.MaxValue : start;
            string commandText;
            Func <List <(StreamMessage, int?)>, int, int> getNextVersion;

            if (direction == ReadDirection.Forward)
            {
                commandText    = prefetch ? _scripts.ReadStreamForwardWithData : _scripts.ReadStreamForward;
                getNextVersion = (events, lastVersion) =>
                                 events.Any()
                        ? events.Last().Item1.StreamVersion + 1
                        : lastVersion + 1;
            }
            else
            {
                commandText    = prefetch ? _scripts.ReadStreamBackwardWithData : _scripts.ReadStreamBackward;
                getNextVersion = (events, lastVersion) =>
                                 events.Any()
                        ? events.Last().Item1.StreamVersion - 1
                        : -1;
            }

            using (var command = new SqlCommand(commandText, connection, transaction))
            {
                command.Parameters.Add(new SqlParameter("streamId", SqlDbType.Char, 42)
                {
                    Value = sqlStreamId.Id
                });
                command.Parameters.AddWithValue("count", count + 1); //Read extra row to see if at end or not
                command.Parameters.AddWithValue("streamVersion", streamVersion);

                using (var reader = await command.ExecuteReaderAsync(cancellationToken).NotOnCapturedContext())
                {
                    await reader.ReadAsync(cancellationToken).NotOnCapturedContext();

                    if (reader.IsDBNull(0))
                    {
                        return(new ReadStreamPage(
                                   sqlStreamId.IdOriginal,
                                   PageReadStatus.StreamNotFound,
                                   start,
                                   -1,
                                   -1,
                                   -1,
                                   direction,
                                   true,
                                   readNext),
                               StreamMeta.None);
                    }
                    var lastStreamVersion  = reader.GetInt32(0);
                    var lastStreamPosition = reader.GetInt64(1);
                    var maxAge             = reader.GetNullableInt32(2);
                    var maxCount           = reader.GetNullableInt32(3);

                    await reader.NextResultAsync(cancellationToken).NotOnCapturedContext();

                    var messages = new List <(StreamMessage, int?)>();
                    while (await reader.ReadAsync(cancellationToken).NotOnCapturedContext())
                    {
                        if (messages.Count == count)
                        {
                            messages.Add(default);
        protected override async Task <ReadStreamPage> ReadStreamBackwardsInternal(
            string streamId,
            int fromStreamVersion,
            int count,
            bool prefetch,
            ReadNextStreamPage readNext,
            CancellationToken cancellationToken)
        {
            GuardAgainstDisposed();
            cancellationToken.ThrowIfCancellationRequested();

            var streamVersion = fromStreamVersion == StreamVersion.End ? int.MaxValue - 1 : fromStreamVersion;

            using (var connection = OpenConnection())
            {
                var streamProperties = await connection.Streams(streamId)
                                       .Properties(initializeIfNotFound: false, cancellationToken);

                if (streamProperties == null)
                {
                    // not found.
                    return(new ReadStreamPage(
                               streamId,
                               PageReadStatus.StreamNotFound,
                               fromStreamVersion,
                               StreamVersion.End,
                               StreamVersion.End,
                               StreamVersion.End,
                               ReadDirection.Forward,
                               true,
                               readNext));
                }

                var position = connection.Streams(streamId)
                               .AllStreamPosition(ReadDirection.Backward, streamVersion);

                // if no position, then need to return success with end of stream.
                if (position == null)
                {
                    // not found.
                    return(new ReadStreamPage(
                               streamId,
                               PageReadStatus.Success,
                               fromStreamVersion,
                               StreamVersion.End,
                               StreamVersion.End,
                               StreamVersion.End,
                               ReadDirection.Backward,
                               true,
                               readNext));
                }

                return(await PrepareStreamResponse(connection,
                                                   streamId,
                                                   ReadDirection.Backward,
                                                   fromStreamVersion,
                                                   prefetch,
                                                   readNext,
                                                   count,
                                                   streamProperties.MaxAge));
            }
        }
        private Task <ReadStreamPage> PrepareStreamResponse(
            SqliteConnection connection,
            string streamId,
            ReadDirection direction,
            int fromVersion,
            bool prefetch,
            ReadNextStreamPage readNext,
            int maxRecords,
            int?maxAge)
        {
            // If the count is int.MaxValue, TSql will see it as a negative number.
            // Users shouldn't be using int.MaxValue in the first place anyway.
            maxRecords = maxRecords == int.MaxValue ? maxRecords - 1 : maxRecords;
            var streamVersion = fromVersion == StreamVersion.End ? int.MaxValue - 1 : fromVersion;
            int nextVersion   = 0;
            var stream        = connection.Streams(streamId);

            var header = stream
                         .Properties()
                         .GetAwaiter().GetResult();

            var position = stream
                           .AllStreamPosition(direction, streamVersion)
                           .GetAwaiter().GetResult();

            var remaining = stream
                            .Length(direction, position, CancellationToken.None)
                            .GetAwaiter().GetResult();

            var messages = stream
                           .Read(direction, position, prefetch, maxRecords)
                           .GetAwaiter().GetResult()
                           .Select(Message => (Message, maxAge))
                           .ToList();

            var filtered = FilterExpired(messages);

            var isEnd = remaining - messages.Count <= 0;

            if (direction == ReadDirection.Forward)
            {
                if (messages.Any())
                {
                    nextVersion = messages.Last().Message.StreamVersion + 1;
                }
                else
                {
                    nextVersion = header.Version + 1;
                }
            }
            else if (direction == ReadDirection.Backward)
            {
                if (streamVersion == int.MaxValue - 1 && !messages.Any())
                {
                    nextVersion = StreamVersion.End;
                }

                if (messages.Any())
                {
                    nextVersion = messages.Last().Message.StreamVersion - 1;
                }
            }

            var page = new ReadStreamPage(
                streamId,
                status: PageReadStatus.Success,
                fromStreamVersion: fromVersion,
                nextStreamVersion: nextVersion,
                lastStreamVersion: header.Version,
                lastStreamPosition: header.Position,
                direction: direction,
                isEnd: isEnd,
                readNext: readNext,
                messages: filtered.ToArray());

            return(Task.FromResult(page));
        }
示例#19
0
        private async Task <ReadStreamPage> ReadStreamInternal(
            MySqlStreamId streamId,
            int start,
            int count,
            ReadDirection direction,
            bool prefetch,
            ReadNextStreamPage readNext,
            MySqlTransaction transaction,
            CancellationToken cancellationToken)
        {
            // If the count is int.MaxValue, TSql will see it as a negative number.
            // Users shouldn't be using int.MaxValue in the first place anyway.
            count = count == int.MaxValue ? count - 1 : count;

            // To read backwards from end, need to use int MaxValue
            var streamVersion = start == StreamVersion.End ? int.MaxValue : start;

            var    messages = new List <(StreamMessage message, int?maxAge)>();
            string procedure;
            Func <List <StreamMessage>, int, int> getNextVersion;

            if (direction == ReadDirection.Forward)
            {
                procedure      = prefetch ? _schema.ReadStreamForwardsWithData : _schema.ReadStreamForwards;
                getNextVersion = (events, lastVersion) => events.Any()
                    ? events.Last().StreamVersion + 1
                    : lastVersion + 1;
            }
            else
            {
                procedure      = prefetch ? _schema.ReadStreamBackwardsWithData : _schema.ReadStreamBackwards;
                getNextVersion = (events, lastVersion) => events.Any()
                    ? events.Last().StreamVersion - 1
                    : -1;
            }

            using (var command = BuildStoredProcedureCall(
                       procedure,
                       transaction,
                       Parameters.StreamId(streamId),
                       Parameters.Count(count + 1),
                       Parameters.Version(streamVersion)))
                using (var reader = await command
                                    .ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken)
                                    .NotOnCapturedContext())
                {
                    if (!reader.HasRows)
                    {
                        return(new ReadStreamPage(
                                   streamId.IdOriginal,
                                   PageReadStatus.StreamNotFound,
                                   start,
                                   -1,
                                   -1,
                                   -1,
                                   direction,
                                   true,
                                   readNext));
                    }

                    if (messages.Count == count)
                    {
                        messages.Add(default);
示例#20
0
 protected override Task <ReadStreamPage> ReadStreamBackwardsInternal(string streamId, int fromVersionInclusive, int count, bool prefetch, ReadNextStreamPage readNext, CancellationToken cancellationToken)
 {
     throw new NotImplementedException();
 }
        protected override Task <ReadStreamPage> ReadStreamForwardsInternal(string streamId, int start, int count, bool prefetch, ReadNextStreamPage readNext, CancellationToken cancellationToken)
        {
            GuardAgainstDisposed();
            cancellationToken.ThrowIfCancellationRequested();

            using (_lock.UseReadLock())
            {
                InMemoryStream stream;
                if (!_streams.TryGetValue(streamId, out stream))
                {
                    var notFound = new ReadStreamPage(
                        streamId,
                        PageReadStatus.StreamNotFound,
                        start,
                        -1,
                        -1,
                        ReadDirection.Forward,
                        true,
                        StreamMessage.EmptyArray,
                        s_readNextNotFound);
                    return(Task.FromResult(notFound));
                }

                var messages = new List <StreamMessage>();
                var i        = start;

                while (i < stream.Messages.Count && count > 0)
                {
                    var           inMemorymessage = stream.Messages[i];
                    StreamMessage message;
                    if (prefetch)
                    {
                        message = new StreamMessage(
                            streamId,
                            inMemorymessage.MessageId,
                            inMemorymessage.StreamVersion,
                            inMemorymessage.Position,
                            inMemorymessage.Created,
                            inMemorymessage.Type,
                            inMemorymessage.JsonMetadata,
                            inMemorymessage.JsonData);
                    }
                    else
                    {
                        message = new StreamMessage(
                            streamId,
                            inMemorymessage.MessageId,
                            inMemorymessage.StreamVersion,
                            inMemorymessage.Position,
                            inMemorymessage.Created,
                            inMemorymessage.Type,
                            inMemorymessage.JsonMetadata,
                            ct => Task.Run(() => ReadMessageData(streamId, inMemorymessage.MessageId), ct));
                    }
                    messages.Add(message);

                    i++;
                    count--;
                }

                var lastStreamVersion = stream.CurrentVersion;
                int nextStreamVersion;
                if (lastStreamVersion == -1)
                {
                    nextStreamVersion = 0;
                }
                else if (messages.Count == 0)
                {
                    nextStreamVersion = lastStreamVersion + 1;
                }
                else
                {
                    nextStreamVersion = messages.Last().StreamVersion + 1;
                }
                var endOfStream = i == stream.Messages.Count;

                var page = new ReadStreamPage(
                    streamId,
                    PageReadStatus.Success,
                    start,
                    nextStreamVersion,
                    lastStreamVersion,
                    ReadDirection.Forward,
                    endOfStream,
                    messages.ToArray(),
                    readNext);

                return(Task.FromResult(page));
            }
        }
示例#22
0
        private async Task <ReadStreamPage> ReadStreamInternal(
            PostgresqlStreamId streamId,
            int start,
            int count,
            ReadDirection direction,
            bool prefetch,
            ReadNextStreamPage readNext,
            NpgsqlTransaction transaction,
            CancellationToken cancellationToken)
        {
            // If the count is int.MaxValue, TSql will see it as a negative number.
            // Users shouldn't be using int.MaxValue in the first place anyway.
            count = count == int.MaxValue ? count - 1 : count;

            // To read backwards from end, need to use int MaxValue
            var streamVersion = start == StreamVersion.End ? int.MaxValue : start;

            var messages = new List <(StreamMessage message, int?maxAge)>();

            Func <List <StreamMessage>, int, int> getNextVersion;

            if (direction == ReadDirection.Forward)
            {
                getNextVersion = (events, lastVersion) =>
                {
                    if (events.Any())
                    {
                        return(events.Last().StreamVersion + 1);
                    }

                    return(lastVersion + 1);
                };
            }
            else
            {
                getNextVersion = (events, lastVersion) =>
                {
                    if (events.Any())
                    {
                        return(events.Last().StreamVersion - 1);
                    }

                    return(-1);
                };
            }

            var refcursorSql = new StringBuilder();

            using (var command = BuildFunctionCommand(
                       _schema.Read,
                       transaction,
                       Parameters.StreamId(streamId),
                       Parameters.Count(count + 1),
                       Parameters.Version(streamVersion),
                       Parameters.ReadDirection(direction),
                       Parameters.Prefetch(prefetch)))
                using (var reader = await command
                                    .ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken)
                                    .ConfigureAwait(false))
                {
                    while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
                    {
                        refcursorSql.AppendLine(Schema.FetchAll(reader.GetString(0)));
                    }
                }

            using (var command = new NpgsqlCommand(refcursorSql.ToString(), transaction.Connection, transaction))
                using (var reader = await command
                                    .ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken)
                                    .ConfigureAwait(false))
                {
                    if (!reader.HasRows)
                    {
                        return(new ReadStreamPage(
                                   streamId.IdOriginal,
                                   PageReadStatus.StreamNotFound,
                                   start,
                                   -1,
                                   -1,
                                   -1,
                                   direction,
                                   true,
                                   readNext));
                    }

                    if (messages.Count == count)
                    {
                        messages.Add(default);