// Called by rendezvous when new remote subscriber subscribes to this stream. private void AddSubscriber_Impl( GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { StreamConsumerCollection streamDataCollection; if (!pubSubCache.TryGetValue(streamId, out streamDataCollection)) { streamDataCollection = new StreamConsumerCollection(); pubSubCache.Add(streamId, streamDataCollection); } StreamConsumerData data; if (!streamDataCollection.TryGetConsumer(subscriptionId, out data)) { data = streamDataCollection.AddConsumer(subscriptionId, streamId, streamConsumer, token, filter); } // Set cursor if not cursor is set, or if subscription provides new token if ((data.Cursor == null || token != null) && queueCache != null) { data.Cursor = queueCache.GetCacheCursor(streamId.Guid, streamId.Namespace, token); } if (data.State == StreamConsumerDataState.Inactive) { RunConsumerCursor(data, filter).Ignore(); // Start delivering events if not actively doing so } }
// Called by rendezvous when new remote subscriber subscribes to this stream. private async Task AddSubscriber_Impl( GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { IQueueCacheCursor cursor = null; // if not cache, then we can't get cursor and there is no reason to ask consumer for token. if (queueCache != null) { try { StreamSequenceToken consumerToken = await streamConsumer.GetSequenceToken(subscriptionId); // Set cursor if not cursor is set, or if subscription provides new token consumerToken = consumerToken ?? token; if (token != null) { cursor = queueCache.GetCacheCursor(streamId.Guid, streamId.Namespace, consumerToken); } } catch (DataNotAvailableException dataNotAvailableException) { // notify consumer that the data is not available, if we can. streamConsumer.ErrorInStream(subscriptionId, dataNotAvailableException).Ignore(); } } AddSubscriberToSubscriptionCache(subscriptionId, streamId, streamConsumer, cursor, filter); }
private async Task<bool> DoHandshakeWithConsumer( StreamConsumerData consumerData, StreamSequenceToken cacheToken) { StreamHandshakeToken requestedHandshakeToken = null; // if not cache, then we can't get cursor and there is no reason to ask consumer for token. if (queueCache != null) { Exception exceptionOccured = null; try { requestedHandshakeToken = await AsyncExecutorWithRetries.ExecuteWithRetries( i => consumerData.StreamConsumer.GetSequenceToken(consumerData.SubscriptionId), AsyncExecutorWithRetries.INFINITE_RETRIES, (exception, i) => true, config.MaxEventDeliveryTime, DefaultBackoffProvider); if (requestedHandshakeToken != null) { consumerData.SafeDisposeCursor(logger); consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId, requestedHandshakeToken.Token); } else { if (consumerData.Cursor == null) // if the consumer did not ask for a specific token and we already have a cursor, jsut keep using it. consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId, cacheToken); } } catch (Exception exception) { exceptionOccured = exception; } if (exceptionOccured != null) { bool faultedSubscription = await ErrorProtocol(consumerData, exceptionOccured, false, null, requestedHandshakeToken != null ? requestedHandshakeToken.Token : null); if (faultedSubscription) return false; } } consumerData.LastToken = requestedHandshakeToken; // use what ever the consumer asked for as LastToken for next handshake (even if he asked for null). // if we don't yet have a cursor (had errors in the handshake or data not available exc), get a cursor at the event that triggered that consumer subscription. if (consumerData.Cursor == null && queueCache != null) { try { consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId, cacheToken); } catch (Exception) { consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId, null); // just in case last GetCacheCursor failed. } } return true; }
// Called by rendezvous when new remote subscriber subscribes to this stream. private async Task AddSubscriber_Impl( GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { IQueueCacheCursor cursor = null; StreamSequenceToken requestedToken = null; // if not cache, then we can't get cursor and there is no reason to ask consumer for token. if (queueCache != null) { DataNotAvailableException errorOccured = null; try { requestedToken = await streamConsumer.GetSequenceToken(subscriptionId); // Set cursor if not cursor is set, or if subscription provides new token requestedToken = requestedToken ?? token; if (requestedToken != null) { cursor = queueCache.GetCacheCursor(streamId.Guid, streamId.Namespace, requestedToken); } } catch (DataNotAvailableException dataNotAvailableException) { errorOccured = dataNotAvailableException; } if (errorOccured != null) { // notify consumer that the data is not available, if we can. await OrleansTaskExtentions.ExecuteAndIgnoreException(() => streamConsumer.ErrorInStream(subscriptionId, errorOccured)); } } AddSubscriberToSubscriptionCache(subscriptionId, streamId, streamConsumer, cursor, requestedToken, filter); }
private async Task RegisterStream(StreamId streamId, StreamSequenceToken firstToken, DateTime now) { var streamData = new StreamConsumerCollection(now); pubSubCache.Add(streamId, streamData); // Create a fake cursor to point into a cache. // That way we will not purge the event from the cache, until we talk to pub sub. // This will help ensure the "casual consistency" between pre-existing subscripton (of a potentially new already subscribed consumer) // and later production. var pinCursor = queueCache?.GetCacheCursor(streamId, firstToken); try { await RegisterAsStreamProducer(streamId, firstToken); }finally { // Cleanup the fake pinning cursor. pinCursor?.Dispose(); } }
public IQueueCacheCursor GetCacheCursor(Guid streamGuid, string streamNamespace, StreamSequenceToken token) { return(cache.GetCacheCursor(streamGuid, streamNamespace, token)); }