public StreamConsumerData AddConsumer(GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { var consumerData = new StreamConsumerData(subscriptionId, streamId, streamConsumer, filter); queueData.Add(subscriptionId, consumerData); lastActivityTime = DateTime.UtcNow; return consumerData; }
public StreamConsumerData(GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { SubscriptionId = subscriptionId; StreamId = streamId; StreamConsumer = streamConsumer; Filter = filter; }
public Task RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { if (!IsImplicitSubscriber(streamConsumer, streamId)) { throw new ArgumentOutOfRangeException(streamId.ToString(), "Only implicit subscriptions are supported."); } return TaskDone.Done; }
public Task AddSubscriber(GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { return(Task.CompletedTask); }
internal void AddRemoteSubscriber(GuidId subscriptionId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { consumers.TryAdd(subscriptionId, Tuple.Create(streamConsumer, filter)); }
// Called by rendezvous when new remote subscriber subscribes to this stream or when registering a new stream with the pubsub system. private void AddSubscriberToSubscriptionCache( GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IQueueCacheCursor newCursor, StreamSequenceToken requestedToken, IStreamFilterPredicateWrapper filter) { StreamConsumerCollection streamDataCollection; if (!pubSubCache.TryGetValue(streamId, out streamDataCollection)) { streamDataCollection = new StreamConsumerCollection(DateTime.UtcNow); pubSubCache.Add(streamId, streamDataCollection); } StreamConsumerData data; if (!streamDataCollection.TryGetConsumer(subscriptionId, out data)) data = streamDataCollection.AddConsumer(subscriptionId, streamId, streamConsumer, filter); data.LastToken = requestedToken; // if we have a new cursor, use it if (newCursor != null) { data.Cursor = newCursor; } // else if we don't yet have a cursor, get a cursor at the end of the cash (null sequence token). else if (data.Cursor == null && queueCache != null) { data.Cursor = queueCache.GetCacheCursor(streamId.Guid, streamId.Namespace, null); } if (data.State == StreamConsumerDataState.Inactive) RunConsumerCursor(data, filter).Ignore(); // Start delivering events if not actively doing so }
public OrFilter(IStreamFilterPredicateWrapper filter1, IStreamFilterPredicateWrapper filter2) { filters = new List<IStreamFilterPredicateWrapper> { filter1, filter2 }; }
public StreamSubscriptionHandleImpl(GuidId subscriptionId, IAsyncObserver <T> observer, StreamImpl <T> streamImpl, IStreamFilterPredicateWrapper filterWrapper, StreamSequenceToken token) { if (subscriptionId == null) { throw new ArgumentNullException("subscriptionId"); } if (streamImpl == null) { throw new ArgumentNullException("streamImpl"); } this.subscriptionId = subscriptionId; this.observer = observer; this.streamImpl = streamImpl; this.filterWrapper = filterWrapper; expectedToken = token; }
public Task RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { return IsImplicitSubscriber(streamConsumer, streamId) ? TaskDone.Done : explicitPubSub.RegisterConsumer(subscriptionId, streamId, streamProvider, streamConsumer, token, filter); }
Task IStreamPubSub.RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { return(registry.RegisterConsumer(subscriptionId, streamId, streamProvider, streamConsumer, filter)); }
// Called by rendezvous when new remote subscriber subscribes to this stream. private async Task AddSubscriber_Impl( GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, StreamSequenceToken cacheToken, IStreamFilterPredicateWrapper filter) { if (IsShutdown) return; StreamConsumerCollection streamDataCollection; if (!pubSubCache.TryGetValue(streamId, out streamDataCollection)) { streamDataCollection = new StreamConsumerCollection(DateTime.UtcNow); pubSubCache.Add(streamId, streamDataCollection); } StreamConsumerData data; if (!streamDataCollection.TryGetConsumer(subscriptionId, out data)) data = streamDataCollection.AddConsumer(subscriptionId, streamId, streamConsumer, filter ?? DefaultStreamFilter); if (await DoHandshakeWithConsumer(data, cacheToken)) { if (data.State == StreamConsumerDataState.Inactive) RunConsumerCursor(data, data.Filter).Ignore(); // Start delivering events if not actively doing so } }
public StreamConsumerData(StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { StreamId = streamId; StreamConsumer = streamConsumer; Filter = filter; }
public StreamConsumerData AddConsumer(GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { var consumerData = new StreamConsumerData(subscriptionId, streamId, streamConsumer, filter); queueData.Add(subscriptionId, consumerData); lastActivityTime = DateTime.UtcNow; return(consumerData); }
public Task RegisterConsumer(StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { var streamRendezvous = GetRendezvousGrain(streamId); return(streamRendezvous.RegisterConsumer(streamId, streamConsumer, token, filter)); }
public Task RegisterConsumer(StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { return(IsImplicitSubscriber(streamConsumer, streamId) ? TaskDone.Done : explicitPubSub.RegisterConsumer(streamId, streamProvider, streamConsumer, token, filter)); }
public async Task RegisterConsumer( StreamId streamId, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { // This Where clause will return either zero or one PubSubSubscriptionState var found = State.Consumers.Where(s => s.Equals(streamId, streamConsumer)).ToArray(); PubSubSubscriptionState pubSubState; if (found.Length == 0) { pubSubState = new PubSubSubscriptionState(streamId, streamConsumer, token, filter); State.Consumers.Add(pubSubState); } else { pubSubState = found[0]; if (filter != null) { pubSubState.AddFilter(filter); } } counterConsumersAdded.Increment(); counterConsumersTotal.Increment(); LogPubSubCounts("RegisterConsumer {0}", streamConsumer); await State.WriteStateAsync(); int numProducers = State.Producers.Count; if (numProducers > 0) { if (logger.IsVerbose) { logger.Info("Notifying {0} existing producer(s) about new consumer {1}. Producers={2}", numProducers, streamConsumer, Utils.EnumerableToString(State.Producers)); } // Notify producers about a new streamConsumer. var tasks = new List <Task>(); var producers = State.Producers.ToList(); bool someProducersRemoved = false; foreach (var producerState in producers) { PubSubPublisherState producer = producerState; // Capture loop variable if (!IsActiveProducer(producer.Producer)) { // Producer is not active (could be stopping / shutting down) so skip if (logger.IsVerbose) { logger.Verbose("Producer {0} on stream {1} is not active - skipping.", producer, streamId); } continue; } Task addSubscriberPromise = producer.Producer.AddSubscriber(streamId, streamConsumer, token, filter) .ContinueWith(t => { if (t.IsFaulted) { var exc = t.Exception.GetBaseException(); if (exc is GrainExtensionNotInstalledException) { logger.Warn((int)ErrorCode.Stream_ProducerIsDead, "Producer {0} on stream {1} is no longer active - discarding.", producer, streamId); // This publisher has gone away, so we should cleanup pub-sub state. bool removed = State.Producers.Remove(producer); someProducersRemoved = true; // Re-save state changes at end counterProducersRemoved.Increment(); counterProducersTotal.DecrementBy(removed ? 1 : 0); // And ignore this error } else { throw exc; } } }, TaskContinuationOptions.OnlyOnFaulted); tasks.Add(addSubscriberPromise); } Exception exception = null; try { await Task.WhenAll(tasks); } catch (Exception exc) { exception = exc; } if (someProducersRemoved) { await State.WriteStateAsync(); } if (exception != null) { throw exception; } } }
public Task RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { return implicitPubSub.IsImplicitSubscriber(streamConsumer, streamId) ? implicitPubSub.RegisterConsumer(subscriptionId, streamId, streamProvider, streamConsumer, filter) : explicitPubSub.RegisterConsumer(subscriptionId, streamId, streamProvider, streamConsumer, filter); }
public Task RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { var streamRendezvous = GetRendezvousGrain(streamId); return(streamRendezvous.RegisterConsumer(subscriptionId, streamId, streamConsumer, filter)); }
public StreamConsumerData AddConsumer(GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { var consumerData = new StreamConsumerData(subscriptionId, streamId, streamConsumer, filter); queueData.Add(subscriptionId, consumerData); return consumerData; }
public Task AddSubscriber( GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { if (logger.IsVerbose) logger.Verbose((int)ErrorCode.PersistentStreamPullingAgent_09, "AddSubscriber: Stream={0} Subscriber={1} Token={2}.", streamId, streamConsumer, token); AddSubscriber_Impl(subscriptionId, streamId, streamConsumer, token, filter); return TaskDone.Done; }
public async Task <StreamSubscriptionHandle <T> > SubscribeAsync( IAsyncObserver <T> observer, StreamSequenceToken token, StreamFilterPredicate filterFunc = null, object filterData = null) { if (token != null && !IsRewindable) { throw new ArgumentNullException("token", "Passing a non-null token to a non-rewindable IAsyncObservable."); } if (observer is GrainReference) { throw new ArgumentException("On-behalf subscription via grain references is not supported. Only passing of object references is allowed.", "observer"); } if (logger.IsVerbose) { logger.Verbose("Subscribe Observer={0} Token={1}", observer, token); } await BindExtensionLazy(); IStreamFilterPredicateWrapper filterWrapper = null; if (filterFunc != null) { filterWrapper = new FilterPredicateWrapperData(filterData, filterFunc); } if (logger.IsVerbose) { logger.Verbose("Subscribe - Connecting to Rendezvous {0} My GrainRef={1} Token={2}", pubSub, myGrainReference, token); } GuidId subscriptionId = pubSub.CreateSubscriptionId(stream.StreamId, myGrainReference); // Optimistic Concurrency: // In general, we should first register the subsription with the pubsub (pubSub.RegisterConsumer) // and only if it succeeds store it locally (myExtension.SetObserver). // Basicaly, those 2 operations should be done as one atomic transaction - either both or none and isolated from concurrent reads. // BUT: there is a distributed race here: the first msg may arrive before the call is awaited // (since the pubsub notifies the producer that may immideately produce) // and will thus not find the subriptionHandle in the extension, basically violating "isolation". // Therefore, we employ Optimistic Concurrency Control here to guarantee isolation: // we optimisticaly store subscriptionId in the handle first before calling pubSub.RegisterConsumer // and undo it in the case of failure. // There is no problem with that we call myExtension.SetObserver too early before the handle is registered in pub sub, // since this subscriptionId is unique (random Guid) and no one knows it anyway, unless successfully subscribed in the pubsub. var subriptionHandle = myExtension.SetObserver(subscriptionId, stream, observer, token, filterWrapper); try { await pubSub.RegisterConsumer(subscriptionId, stream.StreamId, streamProviderName, myGrainReference, filterWrapper); return(subriptionHandle); } catch (Exception) { // Undo the previous call myExtension.SetObserver. myExtension.RemoveObserver(subscriptionId); throw; } }
private async Task RunConsumerCursor(StreamConsumerData consumerData, IStreamFilterPredicateWrapper filterWrapper) { try { // double check in case of interleaving if (consumerData.State == StreamConsumerDataState.Active || consumerData.Cursor == null) { return; } consumerData.State = StreamConsumerDataState.Active; while (consumerData.Cursor != null && consumerData.Cursor.MoveNext()) { IBatchContainer batch = null; Exception ex; Task deliveryTask; try { batch = consumerData.Cursor.GetCurrent(out ex); } catch (DataNotAvailableException dataNotAvailable) { ex = dataNotAvailable; } // Apply filtering to this batch, if applicable if (filterWrapper != null && batch != null) { try { // Apply batch filter to this input batch, to see whether we should deliver it to this consumer. if (!batch.ShouldDeliver( consumerData.StreamId, filterWrapper.FilterData, filterWrapper.ShouldReceive)) { continue; // Skip this batch -- nothing to do } } catch (Exception exc) { var message = string.Format("Ignoring exception while trying to evaluate subscription filter function {0} on stream {1} in PersistentStreamPullingAgentGrain.RunConsumerCursor", filterWrapper, consumerData.StreamId); logger.Warn((int)ErrorCode.PersistentStreamPullingAgent_13, message, exc); } } if (batch != null) { deliveryTask = consumerData.StreamConsumer .DeliverBatch(consumerData.StreamId, batch.AsImmutable()); } else if (ex == null) { deliveryTask = consumerData.StreamConsumer.CompleteStream(consumerData.StreamId); } else { deliveryTask = consumerData.StreamConsumer.ErrorInStream(consumerData.StreamId, ex); } try { numSentMessagesCounter.Increment(); await deliveryTask; } catch (Exception exc) { var message = string.Format("Exception while trying to deliver msgs to stream {0} in PersistentStreamPullingAgentGrain.RunConsumerCursor", consumerData.StreamId); logger.Error((int)ErrorCode.PersistentStreamPullingAgent_14, message, exc); } } consumerData.State = StreamConsumerDataState.Inactive; } catch (Exception exc) { // RunConsumerCursor is fired with .Ignore so we should log if anything goes wrong, because there is no one to catch the exception logger.Error((int)ErrorCode.PersistentStreamPullingAgent_15, "Ignored RunConsumerCursor Error", exc); throw; } }
// 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); }
internal StreamSubscriptionHandleImpl <T> SetObserver <T>(GuidId subscriptionId, StreamImpl <T> stream, IAsyncObserver <T> observer, StreamSequenceToken token, IStreamFilterPredicateWrapper filter) { if (null == stream) { throw new ArgumentNullException("stream"); } if (null == observer) { throw new ArgumentNullException("observer"); } try { if (logger.IsEnabled(LogLevel.Debug)) { logger.Debug("{0} AddObserver for stream {1}", providerRuntime.ExecutingEntityIdentity(), stream.StreamId); } // Note: The caller [StreamConsumer] already handles locking for Add/Remove operations, so we don't need to repeat here. var handle = new StreamSubscriptionHandleImpl <T>(subscriptionId, observer, stream, filter, token); return(allStreamObservers.AddOrUpdate(subscriptionId, handle, (key, old) => handle) as StreamSubscriptionHandleImpl <T>); } catch (Exception exc) { logger.Error(ErrorCode.StreamProvider_AddObserverException, $"{providerRuntime.ExecutingEntityIdentity()} StreamConsumerExtension.AddObserver({stream.StreamId}) caugth exception.", exc); throw; } }
// 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 }
public OrFilter(IStreamFilterPredicateWrapper filter1, IStreamFilterPredicateWrapper filter2) { filters = new List <IStreamFilterPredicateWrapper> { filter1, filter2 }; }
// 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); }
public void AddFilter(IStreamFilterPredicateWrapper filter) { filters.Add(filter); }
private async Task RunConsumerCursor(StreamConsumerData consumerData, IStreamFilterPredicateWrapper filterWrapper) { try { // double check in case of interleaving if (consumerData.State == StreamConsumerDataState.Active || consumerData.Cursor == null) return; consumerData.State = StreamConsumerDataState.Active; while (consumerData.Cursor != null && consumerData.Cursor.MoveNext()) { IBatchContainer batch = null; Exception ex; Task deliveryTask; bool deliveryFailed = false; try { batch = consumerData.Cursor.GetCurrent(out ex); } catch (DataNotAvailableException dataNotAvailable) { ex = dataNotAvailable; } // Apply filtering to this batch, if applicable if (filterWrapper != null && batch != null) { try { // Apply batch filter to this input batch, to see whether we should deliver it to this consumer. if (!batch.ShouldDeliver( consumerData.StreamId, filterWrapper.FilterData, filterWrapper.ShouldReceive)) continue; // Skip this batch -- nothing to do } catch (Exception exc) { var message = string.Format("Ignoring exception while trying to evaluate subscription filter function {0} on stream {1} in PersistentStreamPullingAgentGrain.RunConsumerCursor", filterWrapper, consumerData.StreamId); logger.Warn((int) ErrorCode.PersistentStreamPullingAgent_13, message, exc); } } if (batch != null) { deliveryTask = AsyncExecutorWithRetries.ExecuteWithRetries(i => DeliverBatchToConsumer(consumerData, batch), AsyncExecutorWithRetries.INFINITE_RETRIES, (exception, i) => !(exception is DataNotAvailableException), config.MaxEventDeliveryTime, DefaultBackoffProvider); } else if (ex == null) { deliveryTask = consumerData.StreamConsumer.CompleteStream(consumerData.SubscriptionId); } else { // If data is not avialable, bring cursor current if (ex is DataNotAvailableException) { consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId.Guid, consumerData.StreamId.Namespace, null); } // Notify client of error. deliveryTask = DeliverErrorToConsumer(consumerData, ex, null); } try { numSentMessagesCounter.Increment(); await deliveryTask; } catch (Exception exc) { var message = string.Format("Exception while trying to deliver msgs to stream {0} in PersistentStreamPullingAgentGrain.RunConsumerCursor", consumerData.StreamId); logger.Error((int)ErrorCode.PersistentStreamPullingAgent_14, message, exc); deliveryFailed = true; } // if we failed to deliver a batch if (deliveryFailed && batch != null) { // notify consumer of delivery error, if we can. await OrleansTaskExtentions.ExecuteAndIgnoreException(() => DeliverErrorToConsumer(consumerData, new StreamEventDeliveryFailureException(consumerData.StreamId), batch)); // record that there was a delivery failure await streamFailureHandler.OnDeliveryFailure(consumerData.SubscriptionId, streamProviderName, consumerData.StreamId, batch.SequenceToken); // if configured to fault on delivery failure and this is not an implicit subscription, fault and remove the subscription if (streamFailureHandler.ShouldFaultSubsriptionOnError && !SubscriptionMarker.IsImplicitSubscription(consumerData.SubscriptionId.Guid)) { try { // notify consumer of faulted subscription, if we can. DeliverErrorToConsumer(consumerData, new FaultedSubscriptionException(consumerData.SubscriptionId, consumerData.StreamId), batch) .Ignore(); // mark subscription as faulted. await pubSub.FaultSubscription(consumerData.StreamId, consumerData.SubscriptionId); } finally { // remove subscription RemoveSubscriber_Impl(consumerData.SubscriptionId, consumerData.StreamId); } return; } } } consumerData.State = StreamConsumerDataState.Inactive; } catch (Exception exc) { // RunConsumerCursor is fired with .Ignore so we should log if anything goes wrong, because there is no one to catch the exception logger.Error((int)ErrorCode.PersistentStreamPullingAgent_15, "Ignored RunConsumerCursor Error", exc); throw; } }
private async Task NotifyProducerOfNewSubscriber(PubSubPublisherState producer, GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { try { await producer.Producer.AddSubscriber(subscriptionId, streamId, streamConsumer, filter); } catch (GrainExtensionNotInstalledException) { RemoveProducer(producer); } catch (ClientNotAvailableException) { RemoveProducer(producer); } }
// Called by rendezvous when new remote subsriber subscribes to this stream. public Task AddSubscriber(GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { if (logger.IsEnabled(LogLevel.Debug)) { logger.Debug("{0} AddSubscriber {1} for stream {2}", providerRuntime.ExecutingEntityIdentity(), streamConsumer, streamId); } StreamConsumerExtensionCollection consumers; if (remoteConsumers.TryGetValue(streamId, out consumers)) { consumers.AddRemoteSubscriber(subscriptionId, streamConsumer, filter); } else { // We got an item when we don't think we're the subscriber. This is a normal race condition. // We can drop the item on the floor, or pass it to the rendezvous, or log a warning. } return(Task.CompletedTask); }
// Called by rendezvous when new remote subsriber subscribes to this stream. public Task AddSubscriber(GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { if (logger.IsVerbose) { logger.Verbose("{0} AddSubscriber {1} for stream {2}", providerRuntime.ExecutingEntityIdentity(), streamConsumer, streamId); } StreamConsumerExtensionCollection consumers; if (remoteConsumers.TryGetValue(streamId, out consumers)) { consumers.AddRemoteSubscriber(subscriptionId, streamConsumer, filter); } else { // We got an item when we don't think we're the subscriber. This is a normal race condition. // We can drop the item on the floor, or pass it to the rendezvous, or log a warning. } return TaskDone.Done; }
Task IStreamPubSub.RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { return registry.RegisterConsumer(subscriptionId, streamId, streamProvider, streamConsumer, filter); }
public StreamSubscriptionHandleImpl(GuidId subscriptionId, IAsyncObserver <T> observer, IAsyncBatchObserver <T> batchObserver, StreamImpl <T> streamImpl, IStreamFilterPredicateWrapper filterWrapper, StreamSequenceToken token) { this.subscriptionId = subscriptionId ?? throw new ArgumentNullException("subscriptionId"); this.observer = observer; this.batchObserver = batchObserver; this.streamImpl = streamImpl ?? throw new ArgumentNullException("streamImpl"); this.filterWrapper = filterWrapper; this.isRewindable = streamImpl.IsRewindable; if (IsRewindable) { expectedToken = StreamHandshakeToken.CreateStartToken(token); } }
public Task RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { var streamRendezvous = GetRendezvousGrain(streamId); return streamRendezvous.RegisterConsumer(subscriptionId, streamId, streamConsumer, filter); }
private async Task RunConsumerCursor(StreamConsumerData consumerData, IStreamFilterPredicateWrapper filterWrapper) { try { // double check in case of interleaving if (consumerData.State == StreamConsumerDataState.Active || consumerData.Cursor == null) { return; } consumerData.State = StreamConsumerDataState.Active; while (consumerData.Cursor != null) { IBatchContainer batch = null; Exception exceptionOccured = null; try { batch = GetBatchForConsumer(consumerData.Cursor, filterWrapper, consumerData.StreamId); if (batch == null) { break; } } catch (Exception exc) { exceptionOccured = exc; consumerData.SafeDisposeCursor(logger); consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId, null); } // Apply filtering to this batch, if applicable if (filterWrapper != null && batch != null) { try { // Apply batch filter to this input batch, to see whether we should deliver it to this consumer. if (!batch.ShouldDeliver( consumerData.StreamId, filterWrapper.FilterData, filterWrapper.ShouldReceive)) { continue; // Skip this batch -- nothing to do } } catch (Exception exc) { var message = $"Ignoring exception while trying to evaluate subscription filter function {filterWrapper} on stream {consumerData.StreamId} in PersistentStreamPullingAgentGrain.RunConsumerCursor"; logger.Warn((int)ErrorCode.PersistentStreamPullingAgent_13, message, exc); } } try { numSentMessagesCounter.Increment(); if (batch != null) { StreamHandshakeToken newToken = await AsyncExecutorWithRetries.ExecuteWithRetries( i => DeliverBatchToConsumer(consumerData, batch), AsyncExecutorWithRetries.INFINITE_RETRIES, (exception, i) => !(exception is ClientNotAvailableException), this.options.MaxEventDeliveryTime, DeliveryBackoffProvider); if (newToken != null) { consumerData.LastToken = newToken; IQueueCacheCursor newCursor = queueCache.GetCacheCursor(consumerData.StreamId, newToken.Token); consumerData.SafeDisposeCursor(logger); consumerData.Cursor = newCursor; } } } catch (Exception exc) { consumerData.Cursor?.RecordDeliveryFailure(); var message = $"Exception while trying to deliver msgs to stream {consumerData.StreamId} in PersistentStreamPullingAgentGrain.RunConsumerCursor"; logger.Error(ErrorCode.PersistentStreamPullingAgent_14, message, exc); exceptionOccured = exc is ClientNotAvailableException ? exc : new StreamEventDeliveryFailureException(consumerData.StreamId); } // if we failed to deliver a batch if (exceptionOccured != null) { bool faultedSubscription = await ErrorProtocol(consumerData, exceptionOccured, true, batch, batch?.SequenceToken); if (faultedSubscription) { return; } } } consumerData.State = StreamConsumerDataState.Inactive; } catch (Exception exc) { // RunConsumerCursor is fired with .Ignore so we should log if anything goes wrong, because there is no one to catch the exception logger.Error(ErrorCode.PersistentStreamPullingAgent_15, "Ignored RunConsumerCursor Error", exc); consumerData.State = StreamConsumerDataState.Inactive; throw; } }
public Task AddSubscriber( GuidId subscriptionId, StreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { if (logger.IsVerbose) logger.Verbose((int)ErrorCode.PersistentStreamPullingAgent_09, "AddSubscriber: Stream={0} Subscriber={1}.", streamId, streamConsumer); // cannot await here because explicit consumers trigger this call, so it could cause a deadlock. AddSubscriber_Impl(subscriptionId, streamId, streamConsumer, null, filter) .LogException(logger, ErrorCode.PersistentStreamPullingAgent_26, String.Format("Failed to add subscription for stream {0}." , streamId)) .Ignore(); return TaskDone.Done; }
public Task RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { return(implicitPubSub.IsImplicitSubscriber(streamConsumer, streamId) ? implicitPubSub.RegisterConsumer(subscriptionId, streamId, streamProvider, streamConsumer, filter) : explicitPubSub.RegisterConsumer(subscriptionId, streamId, streamProvider, streamConsumer, filter)); }
private async Task RunConsumerCursor(StreamConsumerData consumerData, IStreamFilterPredicateWrapper filterWrapper) { try { // double check in case of interleaving if (consumerData.State == StreamConsumerDataState.Active || consumerData.Cursor == null) return; consumerData.State = StreamConsumerDataState.Active; while (consumerData.Cursor != null) { IBatchContainer batch = null; Exception exceptionOccured = null; try { Exception ignore; if (!consumerData.Cursor.MoveNext()) { break; } batch = consumerData.Cursor.GetCurrent(out ignore); } catch (Exception exc) { exceptionOccured = exc; consumerData.SafeDisposeCursor(logger); consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId.Guid, consumerData.StreamId.Namespace, null); } // Apply filtering to this batch, if applicable if (filterWrapper != null && batch != null) { try { // Apply batch filter to this input batch, to see whether we should deliver it to this consumer. if (!batch.ShouldDeliver( consumerData.StreamId, filterWrapper.FilterData, filterWrapper.ShouldReceive)) continue; // Skip this batch -- nothing to do } catch (Exception exc) { var message = string.Format("Ignoring exception while trying to evaluate subscription filter function {0} on stream {1} in PersistentStreamPullingAgentGrain.RunConsumerCursor", filterWrapper, consumerData.StreamId); logger.Warn((int) ErrorCode.PersistentStreamPullingAgent_13, message, exc); } } try { numSentMessagesCounter.Increment(); if (batch != null) { StreamHandshakeToken newToken = await AsyncExecutorWithRetries.ExecuteWithRetries( i => DeliverBatchToConsumer(consumerData, batch), AsyncExecutorWithRetries.INFINITE_RETRIES, (exception, i) => true, config.MaxEventDeliveryTime, DefaultBackoffProvider); if (newToken != null) { consumerData.LastToken = newToken; IQueueCacheCursor newCursor = queueCache.GetCacheCursor(consumerData.StreamId.Guid, consumerData.StreamId.Namespace, newToken.Token); consumerData.SafeDisposeCursor(logger); consumerData.Cursor = newCursor; } } } catch (Exception exc) { var message = string.Format("Exception while trying to deliver msgs to stream {0} in PersistentStreamPullingAgentGrain.RunConsumerCursor", consumerData.StreamId); logger.Error((int)ErrorCode.PersistentStreamPullingAgent_14, message, exc); exceptionOccured = new StreamEventDeliveryFailureException(consumerData.StreamId); } // if we failed to deliver a batch if (exceptionOccured != null) { bool faultedSubscription = await ErrorProtocol(consumerData, exceptionOccured, true, batch, batch != null ? batch.SequenceToken : null); if (faultedSubscription) return; } } consumerData.State = StreamConsumerDataState.Inactive; } catch (Exception exc) { // RunConsumerCursor is fired with .Ignore so we should log if anything goes wrong, because there is no one to catch the exception logger.Error((int)ErrorCode.PersistentStreamPullingAgent_15, "Ignored RunConsumerCursor Error", exc); consumerData.State = StreamConsumerDataState.Inactive; throw; } }
public Task RegisterConsumer(GuidId subscriptionId, StreamId streamId, string streamProvider, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { if (!IsImplicitSubscriber(streamConsumer, streamId)) { throw new ArgumentOutOfRangeException(streamId.ToString(), "Only implicit subscriptions are supported."); } return(TaskDone.Done); }
public async Task RegisterConsumer( GuidId subscriptionId, InternalStreamId streamId, IStreamConsumerExtension streamConsumer, IStreamFilterPredicateWrapper filter) { counterConsumersAdded.Increment(); PubSubSubscriptionState pubSubState = State.Consumers.FirstOrDefault(s => s.Equals(subscriptionId)); if (pubSubState != null && pubSubState.IsFaulted) { throw new FaultedSubscriptionException(subscriptionId, streamId); } try { if (pubSubState == null) { pubSubState = new PubSubSubscriptionState(subscriptionId, streamId, streamConsumer); State.Consumers.Add(pubSubState); } if (filter != null) { pubSubState.AddFilter(filter); } LogPubSubCounts("RegisterConsumer {0}", streamConsumer); await WriteStateAsync(); counterConsumersTotal.Increment(); } catch (Exception exc) { logger.Error(ErrorCode.Stream_RegisterConsumerFailed, $"Failed to register a stream consumer. Stream: {streamId}, SubscriptionId {subscriptionId}, Consumer: {streamConsumer}", exc); // Corrupted state, deactivate grain. DeactivateOnIdle(); throw; } int numProducers = State.Producers.Count; if (numProducers <= 0) { return; } if (logger.IsEnabled(LogLevel.Debug)) { logger.Debug("Notifying {0} existing producer(s) about new consumer {1}. Producers={2}", numProducers, streamConsumer, Utils.EnumerableToString(State.Producers)); } // Notify producers about a new streamConsumer. var tasks = new List <Task>(); var producers = State.Producers.ToList(); int initialProducerCount = producers.Count; try { foreach (PubSubPublisherState producerState in producers) { tasks.Add(ExecuteProducerTask(producerState, producerState.Producer.AddSubscriber(subscriptionId, streamId, streamConsumer, filter))); } Exception exception = null; try { await Task.WhenAll(tasks); } catch (Exception exc) { exception = exc; } // if the number of producers has been changed, resave state. if (State.Producers.Count != initialProducerCount) { await WriteStateAsync(); counterConsumersTotal.DecrementBy(initialProducerCount - State.Producers.Count); } if (exception != null) { throw exception; } } catch (Exception exc) { logger.Error(ErrorCode.Stream_RegisterConsumerFailed, $"Failed to update producers while register a stream consumer. Stream: {streamId}, SubscriptionId {subscriptionId}, Consumer: {streamConsumer}", exc); // Corrupted state, deactivate grain. DeactivateOnIdle(); throw; } }