예제 #1
0
        public async Task WriteCheckpoint(Flow flow)
        {
            var result = await _context.Connection.AppendToStreamAsync(
                flow.Context.Key.GetCheckpointStream(),
                ExpectedVersion.Any,
                _context.GetCheckpointEventData(flow));

            if (flow is Query query)
            {
                try
                {
                    var checkpoint = new TimelinePosition(result.NextExpectedVersion);
                    var etag       = QueryETag.From(query.Context.Key, checkpoint).ToString();

                    await _context.Connection.AppendToStreamAsync(
                        TimelineStreams.ChangedQueries,
                        ExpectedVersion.Any,
                        _context.GetQueryChangedEventData(new QueryChanged(etag)));
                }
                catch (Exception error)
                {
                    Log.Error(error, "Failed to write QueryChanged for {Query} - subscribers will not be aware of the change until the query observes another event.", query);
                }
            }
        }
예제 #2
0
        public async Task <QueryState> ReadState(QueryETag etag)
        {
            var stream = etag.Key.GetCheckpointStream();

            var result = await _context.Connection.ReadEventAsync(stream, StreamPosition.End, resolveLinkTos : false);

            switch (result.Status)
            {
            case EventReadStatus.NoStream:
            case EventReadStatus.NotFound:
                return(new QueryState(etag.WithoutCheckpoint(), GetDefaultData(etag)));

            case EventReadStatus.Success:
                var number = result.Event?.Event.EventNumber;
                var data   = result.Event?.Event.Data;

                var checkpoint = new TimelinePosition(number);

                return(checkpoint == etag.Checkpoint
            ? new QueryState(etag)
            : new QueryState(etag.WithCheckpoint(checkpoint), new MemoryStream(data)));

            default:
                throw new Exception($"Unexpected result when reading {stream}: {result.Status}");
            }
        }
예제 #3
0
        internal ReadQueryContentCommand(EventStoreContext context, QueryETag etag)
        {
            _context = context;
            _etag    = etag;

            _stream = etag.Key.GetCheckpointStream();
        }
예제 #4
0
        internal async Task <TQuery> Get <TQuery>(Id id) where TQuery : Query
        {
            var key = GetKey <TQuery>(id);

            var state = await _queryDb.ReadState(QueryETag.From(key));

            return((TQuery)Deserialize(key.Type, state.ReadContent()));
        }
예제 #5
0
        internal void OnChanged(QueryETag etag)
        {
            if (_instancesByKey.TryGetValue(etag.Key, out var instance))
            {
                _notifier.NotifyChanged(etag, instance.ConnectionIds);
            }

            _changedWindow.OnChanged(etag);
        }
예제 #6
0
        QueryETag ReadETag(Type type, Id id)
        {
            if (TryGetIfNoneMatch(out var ifNoneMatch))
            {
                return(QueryETag.From(ifNoneMatch, _area));
            }

            var key = FlowKey.From(_area.Queries.Get(type), id);

            return(QueryETag.From(key, TimelinePosition.None));
        }
예제 #7
0
        internal void OnChanged(QueryETag etag)
        {
            var now = Clock.Now;

            _queriesByKey[etag.Key] = new QueryWindow(etag, now);

            if (now - _whenTrimmed > _trimDelay)
            {
                TrimQueries(now);
            }
        }
예제 #8
0
파일: TimelineDb.cs 프로젝트: jtark/Totem
        async Task TryWriteQueryChanged(Flow query)
        {
            try
            {
                var etag = QueryETag.From(query.Context.Key, query.Context.CheckpointPosition).ToString();

                await _context.AppendToClient(new QueryChanged(etag));
            }
            catch (Exception error)
            {
                Log.Error(error, "Failed to write update of query {Query} to the client stream. Subscribers will not be aware of the change until the query observes another event.", query);
            }
        }
예제 #9
0
파일: TimelineDb.cs 프로젝트: jtark/Totem
        async Task TryWriteQueryStopped(Flow flow)
        {
            try
            {
                var etag = QueryETag.From(flow.Context.Key, flow.Context.CheckpointPosition).ToString();

                await _context.AppendToClient(new QueryStopped(etag, flow.Context.ErrorMessage));
            }
            catch (Exception error)
            {
                Log.Error(error, "Failed to write stoppage of query {Query} to the client stream. Subscribers will not be aware of the failure", flow);
            }
        }
예제 #10
0
        TaskSource RemoveOrStartTask(QueryETag etag)
        {
            lock (_tasksByETag)
            {
                if (!_tasksByETag.Remove(etag, out var task))
                {
                    task = new TaskSource();

                    _tasksByETag[etag] = task;
                }

                return(task);
            }
        }
예제 #11
0
파일: ClientDb.cs 프로젝트: jtark/Totem
        QueryContent GetCheckpointContent(QueryETag etag, ResolvedEvent e)
        {
            var metadata = _context.ReadCheckpointMetadata(e);

            if (metadata.ErrorPosition.IsSome)
            {
                throw new Exception($"Query is stopped at {metadata.ErrorPosition} with the following error: {metadata.ErrorMessage}");
            }

            var checkpoint = new TimelinePosition(e.Event.EventNumber);

            return(checkpoint == etag.Checkpoint
        ? new QueryContent(etag)
        : new QueryContent(etag.WithCheckpoint(checkpoint), new MemoryStream(e.Event.Data)));
        }
예제 #12
0
        //
        // Subscriptions
        //

        public async Task SubscribeToChanged(Id connectionId, QueryETag etag)
        {
            var connection = _connectionsById.GetOrAdd(connectionId, _ => new QueryConnection(connectionId, this));
            var instance   = _instancesByKey.GetOrAdd(etag.Key, _ => new QueryInstance(etag.Key, this));

            connection.Subscribe(instance);

            await _changedSubscription.EnsureSubscribed();

            var latestETag = _changedWindow.OnSubscribed(etag);

            if (latestETag != etag)
            {
                await _notifier.NotifyChanged(latestETag, Many.Of(connectionId));
            }
        }
예제 #13
0
        async Task <Stream> AppendAndWaitUntilChanged(FlowKey key, Event e, TestTimeline timeline)
        {
            var connectionId = Id.FromGuid();

            await _queryDb.SubscribeToChanged(connectionId, QueryETag.From(key));

            var position = await timeline.Append(e);

            var newETag = QueryETag.From(key, position);

            await _notifier.WaitUntilChanged(connectionId, newETag);

            _queryDb.UnsubscribeFromChanged(connectionId);

            return(await _queryDb.ReadContent(newETag));
        }
예제 #14
0
 QueryETag ETagFrom(string etag) =>
 QueryETag.From(etag, _context.Area);
예제 #15
0
 void OnChanged(ResolvedEvent e) =>
 _db.OnChanged(QueryETag.From(
                   _context.Json.FromJsonUtf8 <QueryChanged>(e.Event.Data).ETag,
                   _context.Area));
예제 #16
0
파일: ClientDb.cs 프로젝트: jtark/Totem
 public Task <QueryContent> ReadQueryContent(QueryETag etag) =>
 ReadQueryCheckpoint(etag.Key, () => GetDefaultContent(etag), e => GetCheckpointContent(etag, e));
예제 #17
0
파일: ClientDb.cs 프로젝트: jtark/Totem
        QueryContent GetDefaultContent(QueryETag etag)
        {
            var defaultJson = _context.Json.ToJsonUtf8(etag.Key.Type.New());

            return(new QueryContent(etag.WithoutCheckpoint(), new MemoryStream(defaultJson)));
        }
예제 #18
0
        Task IClientObserver.OnQueryStopped(QueryETag query, string error)
        {
            GetQueryInstance(query.Key).OnError(new Exception($"Query {query} stopped with the following error: {error}"));

            return(Task.CompletedTask);
        }
예제 #19
0
 public Task NotifyStopped(QueryETag etag, string error, IEnumerable <Id> subscriberIds) =>
 GetClients(subscriberIds).SendAsync("onStopped", etag.ToString(), error);
예제 #20
0
        public Task NotifyChanged(QueryETag etag, IEnumerable <Id> connectionIds)
        {
            RemoveOrStartTask(etag).TrySetResult();

            return(Task.CompletedTask);
        }
예제 #21
0
 internal QueryETag OnSubscribed(QueryETag etag) =>
 _queriesByKey
 .GetOrAdd(etag.Key, _ => new QueryWindow(etag, Clock.Now))
 .GetLatest(etag);
예제 #22
0
 internal Task WaitUntilChanged(Id connectionId, QueryETag etag) =>
 RemoveOrStartTask(etag).Task;
예제 #23
0
 public QueryNotModifiedResult(QueryETag etag)
 {
     ETag = etag;
 }
예제 #24
0
 public async Task <Stream> ReadContent(QueryETag etag) =>
 etag.Checkpoint.IsNone
 ? GetDefaultData(etag)
 : await new ReadQueryContentCommand(_context, etag).Execute();
예제 #25
0
 internal QueryWindow(QueryETag etag, DateTimeOffset whenChanged)
 {
     _etag        = etag;
     _whenChanged = whenChanged;
 }
예제 #26
0
 public Task NotifyChanged(QueryETag etag, IEnumerable <Id> subscriberIds) =>
 GetClients(subscriberIds).SendAsync("onChanged", etag.ToString());
예제 #27
0
 Stream GetDefaultData(QueryETag etag) =>
 new MemoryStream(_context.Json.ToJsonUtf8(etag.Key.Type.New()));
예제 #28
0
 internal QueryETag GetLatest(QueryETag etag) =>
 _etag.GetLatest(etag);
예제 #29
0
 public Task SubscribeToChanged(string etag) =>
 _db.SubscribeToChanged(ConnectionId, QueryETag.From(etag, _area));
예제 #30
0
 public Task NotifyChanged(QueryETag etag, IEnumerable <Id> subscriberIds) =>
 _hubContext
 .Clients
 .Clients(subscriberIds.ToMany(id => id.ToString()))
 .SendAsync("onChanged", etag.ToString());