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 (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(myGrainReference, stream.StreamId); await pubSub.RegisterConsumer(subscriptionId, stream.StreamId, streamProviderName, myGrainReference, token, filterWrapper); return(myExtension.SetObserver(subscriptionId, stream, observer, filterWrapper)); }
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 (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; } }
public async Task <StreamSubscriptionHandle <T> > SubscribeAsync( IAsyncObserver <T> observer, StreamSequenceToken token, StreamFilterPredicate filterFunc, object filterData) { if (token != null && !IsRewindable) { throw new ArgumentNullException("token", "Passing a non-null token to a non-rewindable IAsyncObservable."); } 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 (!connectedToRendezvous) { if (logger.IsVerbose) { logger.Verbose("Subscribe - Connecting to Rendezvous {0} My GrainRef={1} Token={2}", pubSub, myGrainReference, token); } await pubSub.RegisterConsumer(stream.StreamId, streamProviderName, myGrainReference, token, filterWrapper); connectedToRendezvous = true; } else if (filterWrapper != null) { // Already connected and registered this grain, but also need to register this additional filter too. await pubSub.RegisterConsumer(stream.StreamId, streamProviderName, myGrainReference, token, filterWrapper); } return(myExtension.AddObserver(stream, observer, filterWrapper)); }