示例#1
0
 private async Task Push(ReadStreamPage page)
 {
     foreach (var message in page.Messages)
     {
         if (_disposed.IsCancellationRequested)
         {
             NotifySubscriptionDropped(SubscriptionDroppedReason.Disposed);
             _disposed.Token.ThrowIfCancellationRequested();
         }
         _nextVersion = message.StreamVersion + 1;
         LastVersion  = message.StreamVersion;
         try
         {
             await _streamMessageReceived(this, message).NotOnCapturedContext();
         }
         catch (Exception ex)
         {
             s_logger.ErrorException(
                 $"Exception with subscriber receiving message {Name}/{StreamId}" +
                 $"Message: {message}.",
                 ex);
             NotifySubscriptionDropped(SubscriptionDroppedReason.SubscriberError, ex);
             throw;
         }
     }
 }
 public static Link Next(ReadStreamPage page, ReadStreamOperation operation)
 => new Link(
     Constants.Relations.Next,
     LinkFormatter.FormatForwardLink(
         page.StreamId,
         operation.MaxCount,
         page.Messages.Max(m => m.StreamVersion) + 1,
         operation.EmbedPayload));
 public static Link Last(ReadStreamPage page, ReadStreamOperation operation)
 => new Link(
     Constants.Relations.Last,
     LinkFormatter.FormatBackwardLink(
         page.StreamId,
         operation.MaxCount,
         StreamVersion.End,
         operation.EmbedPayload));
 public static Link Previous(ReadStreamPage page, ReadStreamOperation operation)
 => new Link(
     Constants.Relations.Previous,
     LinkFormatter.FormatBackwardLink(
         page.StreamId,
         operation.MaxCount,
         page.Messages.Min(m => m.StreamVersion) - 1,
         operation.EmbedPayload));
 public ReadStreamTheory(
     string streamId,
     int start,
     int pageSize,
     ReadStreamPage expectedReadStreamPage)
 {
     StreamId = streamId;
     Start    = start;
     PageSize = pageSize;
     ExpectedReadStreamPage = expectedReadStreamPage;
 }
        public static Links StreamsNavigation(this Links links, ReadStreamPage page, ReadStreamOperation operation)
        {
            var baseAddress = $"streams/{operation.StreamId}";

            var first = Links.FormatForwardLink(
                baseAddress,
                operation.MaxCount,
                StreamVersion.Start,
                operation.EmbedPayload);

            var last = Links.FormatBackwardLink(
                baseAddress,
                operation.MaxCount,
                StreamVersion.End,
                operation.EmbedPayload);

            links.Add(Constants.Relations.First, first);

            if (operation.Self != first && !page.IsEnd)
            {
                links.Add(
                    Constants.Relations.Previous,
                    Links.FormatBackwardLink(
                        baseAddress,
                        operation.MaxCount,
                        page.Messages.Min(m => m.StreamVersion) - 1,
                        operation.EmbedPayload));
            }

            links.Add(Constants.Relations.Feed, operation.Self, operation.StreamId).Self();

            if (operation.Self != last && !page.IsEnd)
            {
                links.Add(
                    Constants.Relations.Next,
                    Links.FormatForwardLink(
                        baseAddress,
                        operation.MaxCount,
                        page.Messages.Max(m => m.StreamVersion) + 1,
                        operation.EmbedPayload));
            }

            links.Add(Constants.Relations.Last, last)
            .Add(Constants.Relations.Metadata,
                 $"{baseAddress}/metadata");

            return(links);
        }
示例#7
0
        private static ReadStreamPage ReadStreamBackwardsInternal(
            IHalClient client,
            StreamId streamId,
            int fromVersionInclusive,
            bool prefetchJsonData)
        {
            var resource = client.Current.First();

            if (client.StatusCode == HttpStatusCode.NotFound)
            {
                return(new ReadStreamPage(
                           streamId,
                           PageReadStatus.StreamNotFound,
                           fromVersionInclusive,
                           -1,
                           -1,
                           -1,
                           ReadDirection.Backward,
                           true));
            }

            var pageInfo = resource.Data <HalReadPage>();

            var streamMessages = Convert(
                resource.Embedded
                .Where(r => r.Rel == Constants.Relations.Message)
                .ToArray(),
                client,
                prefetchJsonData);

            var readStreamPage = new ReadStreamPage(
                streamId,
                PageReadStatus.Success,
                pageInfo.FromStreamVersion,
                pageInfo.NextStreamVersion,
                pageInfo.LastStreamVersion,
                pageInfo.LastStreamPosition,
                ReadDirection.Backward,
                pageInfo.IsEnd,
                async(nextVersion, token) => ReadStreamBackwardsInternal(
                    await client.GetAsync(resource, Constants.Relations.Previous),
                    streamId,
                    pageInfo.LastStreamVersion,
                    prefetchJsonData),
                streamMessages);

            return(readStreamPage);
        }
        public static Links StreamsNavigation(this Links links, ReadStreamPage page, ReadStreamOperation operation)
        {
            var first = LinkFormatter.ReadStreamForwards(
                operation.StreamId,
                StreamVersion.Start,
                operation.MaxCount,
                operation.EmbedPayload);

            var last = LinkFormatter.ReadStreamBackwards(
                operation.StreamId,
                StreamVersion.End,
                operation.MaxCount,
                operation.EmbedPayload);

            links.Add(Constants.Relations.First, first);

            if (operation.Self != first && !page.IsEnd)
            {
                links.Add(
                    Constants.Relations.Previous,
                    LinkFormatter.ReadStreamBackwards(
                        operation.StreamId,
                        page.Messages.Min(m => m.StreamVersion) - 1,
                        operation.MaxCount,
                        operation.EmbedPayload));
            }

            links.Add(Constants.Relations.Feed, operation.Self, operation.StreamId).Self();

            if (operation.Self != last && !page.IsEnd)
            {
                links.Add(
                    Constants.Relations.Next,
                    LinkFormatter.ReadStreamForwards(
                        operation.StreamId,
                        page.Messages.Max(m => m.StreamVersion) + 1,
                        operation.MaxCount,
                        operation.EmbedPayload));
            }

            links.Add(Constants.Relations.Last, last)
            .Add(Constants.Relations.Metadata, LinkFormatter.StreamMetadata(operation.StreamId));

            return(links);
        }
示例#9
0
        public static bool TryGetETag(this ReadStreamPage page, out ETag eTag)
        {
            if (page.IsEnd)
            {
                eTag = ETag.FromStreamVersion(page.LastStreamVersion);
                return(true);
            }

            if (page.ReadDirection == ReadDirection.Backward && page.FromStreamVersion == StreamVersion.End)
            {
                eTag = ETag.FromStreamVersion(page.LastStreamVersion);
                return(true);
            }

            eTag = ETag.None;

            return(false);
        }
示例#10
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()));
        }
            public static IEnumerable <Link> Navigation(ReadStreamPage page, ReadStreamOperation operation)
            {
                var first = First(page, operation);

                var last = Last(page, operation);

                yield return(first);

                if (operation.Self != first.Href && !page.IsEnd)
                {
                    yield return(Previous(page, operation));
                }

                if (operation.Self != last.Href && !page.IsEnd)
                {
                    yield return(Next(page, operation));
                }

                yield return(last);
            }
示例#12
0
            public static IEnumerable <Link> Navigation(ReadStreamPage page, string self)
            {
                var first = First(page);

                var last = Last(page);

                yield return(first);

                if (self != first.Href && !page.IsEnd)
                {
                    yield return(Previous(page));
                }

                if (self != last.Href && !page.IsEnd)
                {
                    yield return(Next(page));
                }

                yield return(last);
            }
示例#13
0
        private static ReadStreamPage ReadStreamForwardsInternal(
            IHalClient client,
            StreamId streamId,
            int fromVersionInclusive,
            bool prefetchJsonData)
        {
            var resource = client.Current.First();

            if (client.StatusCode == HttpStatusCode.NotFound)
            {
                return(new ReadStreamPage(
                           streamId,
                           PageReadStatus.StreamNotFound,
                           fromVersionInclusive,
                           -1,
                           -1,
                           -1,
                           ReadDirection.Forward,
                           true));
            }

            var pageInfo = resource.Data <HalReadPage>();

            var streamMessages = Convert(
                resource.Embedded
                .Where(r => r.Rel == Constants.Relations.Message)
                .Reverse()
                .ToArray(),
                client,
                prefetchJsonData);

            var readStreamPage = new ReadStreamPage(
                streamId,
                PageReadStatus.Success,
                pageInfo.FromStreamVersion,
                pageInfo.NextStreamVersion,
                pageInfo.LastStreamVersion,
                pageInfo.LastStreamPosition,
                ReadDirection.Forward,
                pageInfo.IsEnd,
                ReadNextStreamPage,
                streamMessages);

            return(readStreamPage);

            async Task <ReadStreamPage> ReadNextStreamPage(int nextVersion, CancellationToken ct)
            => resource.Links.Any(link => link.Rel == Constants.Relations.Next)
                    ? ReadStreamForwardsInternal(
                await client.GetAsync(resource, Constants.Relations.Next),
                streamId,
                pageInfo.LastStreamVersion,
                prefetchJsonData)
                    : new ReadStreamPage(
                streamId,
                PageReadStatus.Success,
                pageInfo.LastStreamVersion,
                nextVersion,
                pageInfo.LastStreamVersion,
                pageInfo.LastStreamPosition,
                ReadDirection.Forward,
                true,
                ReadNextStreamPage);
        }
示例#14
0
 public static Link First(ReadStreamPage page) => new Link(
     Relations.First,
     LinkFormatter.FormatForwardLink(page.StreamId, 20, StreamVersion.Start));
        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));
            }
        }
        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));
            }
        }
示例#17
0
 public static Link Previous(ReadStreamPage page) => new Link(
     Relations.Previous,
     LinkFormatter.FormatBackwardLink(page.StreamId, 20, page.Messages.Min(m => m.StreamVersion) - 1));
示例#18
0
 public static Link Next(ReadStreamPage page) => new Link(
     Relations.Next,
     LinkFormatter.FormatForwardLink(page.StreamId, 20, page.Messages.Max(m => m.StreamVersion) + 1));
        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));
        }
示例#20
0
 public static Link Last(ReadStreamPage page) => new Link(
     Relations.Last,
     LinkFormatter.FormatBackwardLink(page.StreamId, 20, StreamVersion.End));
示例#21
0
 public static Link Feed(ReadStreamPage page) => new Link(Relations.Feed, Last(page).Href);