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); }
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); }
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); }
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); }
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); }
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); }
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)); } }
public static Link Previous(ReadStreamPage page) => new Link( Relations.Previous, LinkFormatter.FormatBackwardLink(page.StreamId, 20, page.Messages.Min(m => m.StreamVersion) - 1));
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)); }
public static Link Last(ReadStreamPage page) => new Link( Relations.Last, LinkFormatter.FormatBackwardLink(page.StreamId, 20, StreamVersion.End));
public static Link Feed(ReadStreamPage page) => new Link(Relations.Feed, Last(page).Href);