예제 #1
0
        public async Task <StreamHandshakeToken> DeliverBatch(IBatchContainer batch, StreamHandshakeToken handshakeToken)
        {
            // we validate expectedToken only for ordered (rewindable) streams
            if (expectedToken != null)
            {
                if (!expectedToken.Equals(handshakeToken))
                {
                    return(expectedToken);
                }
            }

            if (batch is IBatchContainerBatch)
            {
                var batchContainerBatch = batch as IBatchContainerBatch;
                await NextBatch(batchContainerBatch);
            }
            else
            {
                foreach (var itemTuple in batch.GetEvents <T>())
                {
                    await NextItem(itemTuple.Item1, itemTuple.Item2);
                }
            }

            if (IsRewindable)
            {
                expectedToken = StreamHandshakeToken.CreateDeliveyToken(batch.SequenceToken);
            }
            return(null);
        }
예제 #2
0
        public async Task <StreamHandshakeToken> DeliverItem(object item, StreamSequenceToken currentToken, StreamHandshakeToken handshakeToken)
        {
            if (expectedToken != null)
            {
                if (!expectedToken.Equals(handshakeToken))
                {
                    return(expectedToken);
                }
            }

            await NextItem(item, currentToken);

            // check again, in case the expectedToken was changed indiretly via ResumeAsync()
            if (expectedToken != null)
            {
                if (!expectedToken.Equals(handshakeToken))
                {
                    return(expectedToken);
                }
            }

            expectedToken = StreamHandshakeToken.CreateDeliveyToken(currentToken);

            return(null);
        }
예제 #3
0
        private async Task DeliverBatchToConsumer(StreamConsumerData consumerData, IBatchContainer batch)
        {
            StreamHandshakeToken prevToken = consumerData.LastToken;
            Task<StreamHandshakeToken> batchDeliveryTask;

            bool isRequestContextSet = batch.ImportRequestContext();
            try
            {
                batchDeliveryTask = consumerData.StreamConsumer.DeliverBatch(consumerData.SubscriptionId, batch.AsImmutable(), prevToken);
            }
            finally
            {
                if (isRequestContextSet)
                {
                    // clear RequestContext before await!
                    RequestContext.Clear();
                }
            }
            StreamHandshakeToken newToken = await batchDeliveryTask;
            if (newToken != null)
            {
                consumerData.LastToken = newToken;
                consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId.Guid,
                    consumerData.StreamId.Namespace, newToken.Token);
            }
            else
            {
                consumerData.LastToken = StreamHandshakeToken.CreateDeliveyToken(batch.SequenceToken); // this is the currently delivered token
            }

        }
        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;
        }
 public StreamSubscriptionHandleImpl(GuidId subscriptionId, IAsyncObserver <T> observer, IAsyncBatchObserver <T> batchObserver, StreamImpl <T> streamImpl, StreamSequenceToken token)
 {
     this.subscriptionId = subscriptionId ?? throw new ArgumentNullException("subscriptionId");
     this.observer       = observer;
     this.batchObserver  = batchObserver;
     this.streamImpl     = streamImpl ?? throw new ArgumentNullException("streamImpl");
     this.isRewindable   = streamImpl.IsRewindable;
     if (IsRewindable)
     {
         expectedToken = StreamHandshakeToken.CreateStartToken(token);
     }
 }
        private async Task <StreamHandshakeToken> DeliverBatchToConsumer(StreamConsumerData consumerData, IBatchContainer batch)
        {
            try
            {
                StreamHandshakeToken newToken = await ContextualizedDeliverBatchToConsumer(consumerData, batch);

                consumerData.LastToken = StreamHandshakeToken.CreateDeliveyToken(batch.SequenceToken); // this is the currently delivered token
                return(newToken);
            }
            catch (Exception ex)
            {
                this.logger.LogWarning(ex, "Failed to deliver message to consumer on {SubscriptionId} for stream {StreamId}, may retry.", consumerData.SubscriptionId, consumerData.StreamId);
                throw;
            }
        }
예제 #7
0
        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       = StreamHandshakeToken.CreateStartToken(token);
        }
예제 #8
0
        public async Task <StreamHandshakeToken> DeliverBatch(IBatchContainer batch, StreamHandshakeToken handshakeToken)
        {
            if (expectedToken != null)
            {
                if (!expectedToken.Equals(handshakeToken))
                {
                    return(expectedToken);
                }
            }

            foreach (var itemTuple in batch.GetEvents <T>())
            {
                await NextItem(itemTuple.Item1, itemTuple.Item2);
            }

            expectedToken = StreamHandshakeToken.CreateDeliveyToken(batch.SequenceToken);

            return(null);
        }
        public async Task <StreamHandshakeToken> DeliverItem(object item, StreamSequenceToken currentToken, StreamHandshakeToken handshakeToken)
        {
            if (this.expectedToken != null)
            {
                if (!this.expectedToken.Equals(handshakeToken))
                {
                    return(this.expectedToken);
                }
            }

            T typedItem;

            try
            {
                typedItem = (T)item;
            }
            catch (InvalidCastException)
            {
                // We got an illegal item on the stream -- close it with a Cast exception
                throw new InvalidCastException("Received an item of type " + item.GetType().Name + ", expected " + typeof(T).FullName);
            }

            await((this.observer != null)
                ? NextItem(typedItem, currentToken)
                : NextItems(new[] { Tuple.Create(typedItem, currentToken) }));

            // check again, in case the expectedToken was changed indiretly via ResumeAsync()
            if (this.expectedToken != null)
            {
                if (!this.expectedToken.Equals(handshakeToken))
                {
                    return(this.expectedToken);
                }
            }
            if (IsRewindable)
            {
                this.expectedToken = StreamHandshakeToken.CreateDeliveyToken(currentToken);
            }
            return(null);
        }
        private async Task<StreamHandshakeToken> DeliverBatchToConsumer(StreamConsumerData consumerData, IBatchContainer batch)
        {
            StreamHandshakeToken prevToken = consumerData.LastToken;
            Task<StreamHandshakeToken> batchDeliveryTask;

            bool isRequestContextSet = batch.ImportRequestContext();
            try
            {
                batchDeliveryTask = consumerData.StreamConsumer.DeliverBatch(consumerData.SubscriptionId, batch.AsImmutable(), prevToken);
            }
            finally
            {
                if (isRequestContextSet)
                {
                    // clear RequestContext before await!
                    RequestContext.Clear();
                }
            }
            StreamHandshakeToken newToken = await batchDeliveryTask;
            consumerData.LastToken = StreamHandshakeToken.CreateDeliveyToken(batch.SequenceToken); // this is the currently delivered token
            return newToken;
        }
        private async Task RunConsumerCursor(StreamConsumerData consumerData)
        {
            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, consumerData.StreamId, consumerData.FilterData);
                        if (batch == null)
                        {
                            break;
                        }
                    }
                    catch (Exception exc)
                    {
                        exceptionOccured = exc;
                        consumerData.SafeDisposeCursor(logger);
                        consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId, null);
                    }

                    if (batch != null)
                    {
                        if (!ShouldDeliverBatch(consumerData.StreamId, batch, consumerData.FilterData))
                        {
                            continue;
                        }
                    }

                    try
                    {
                        numSentMessagesCounter.Increment();
                        if (batch != null)
                        {
                            StreamHandshakeToken newToken = await AsyncExecutorWithRetries.ExecuteWithRetries(
                                i => DeliverBatchToConsumer(consumerData, batch),
                                AsyncExecutorWithRetries.INFINITE_RETRIES,
                                (exception, i) => !(exception is ClientNotAvailableException) || IsShutdown,
                                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 async Task <StreamHandshakeToken> DeliverMutable(GuidId subscriptionId, StreamId streamId, object item, StreamSequenceToken currentToken, StreamHandshakeToken handshakeToken)
        {
            if (logger.IsEnabled(LogLevel.Trace))
            {
                var itemString = item.ToString();
                itemString = (itemString.Length > MAXIMUM_ITEM_STRING_LOG_LENGTH) ? itemString.Substring(0, MAXIMUM_ITEM_STRING_LOG_LENGTH) + "..." : itemString;
                logger.Trace("DeliverItem {0} for subscription {1}", itemString, subscriptionId);
            }
            IStreamSubscriptionHandle observer;

            if (allStreamObservers.TryGetValue(subscriptionId, out observer))
            {
                return(await observer.DeliverItem(item, currentToken, handshakeToken));
            }
            else if (this.streamSubscriptionObserver != null)
            {
                var streamProvider = this.providerRuntime.ServiceProvider.GetServiceByName <IStreamProvider>(streamId.ProviderName);
                if (streamProvider != null)
                {
                    var subscriptionHandlerFactory = new StreamSubscriptionHandlerFactory(streamProvider, streamId, streamId.ProviderName, subscriptionId);
                    await this.streamSubscriptionObserver.OnSubscribed(subscriptionHandlerFactory);

                    //check if an observer were attached after handling the new subscription, deliver on it if attached
                    if (allStreamObservers.TryGetValue(subscriptionId, out observer))
                    {
                        return(await observer.DeliverItem(item, currentToken, handshakeToken));
                    }
                }
            }

            logger.Warn((int)(ErrorCode.StreamProvider_NoStreamForItem), "{0} got an item for subscription {1}, but I don't have any subscriber for that stream. Dropping on the floor.",
                        providerRuntime.ExecutingEntityIdentity(), subscriptionId);
            // 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 ...
            return(default(StreamHandshakeToken));
        }
 public Task <StreamHandshakeToken> DeliverImmutable(GuidId subscriptionId, StreamId streamId, Immutable <object> item, StreamSequenceToken currentToken, StreamHandshakeToken handshakeToken)
 {
     return(DeliverMutable(subscriptionId, streamId, item.Value, currentToken, handshakeToken));
 }
        public async Task <StreamHandshakeToken> DeliverBatch(GuidId subscriptionId, StreamId streamId, Immutable <IBatchContainer> batch, StreamHandshakeToken handshakeToken)
        {
            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace("DeliverBatch {0} for subscription {1}", batch.Value, subscriptionId);
            }

            IStreamSubscriptionHandle observer;

            if (allStreamObservers.TryGetValue(subscriptionId, out observer))
            {
                return(await observer.DeliverBatch(batch.Value, handshakeToken));
            }
            else if (this.streamSubscriptionObserver != null)
            {
                var streamProvider = this.providerRuntime.ServiceProvider.GetServiceByName <IStreamProvider>(streamId.ProviderName);
                if (streamProvider != null)
                {
                    var subscriptionHandlerFactory = new StreamSubscriptionHandlerFactory(streamProvider, streamId, streamId.ProviderName, subscriptionId);
                    await this.streamSubscriptionObserver.OnSubscribed(subscriptionHandlerFactory);

                    // check if an observer were attached after handling the new subscription, deliver on it if attached
                    if (allStreamObservers.TryGetValue(subscriptionId, out observer))
                    {
                        return(await observer.DeliverBatch(batch.Value, handshakeToken));
                    }
                }
            }

            logger.Warn((int)(ErrorCode.StreamProvider_NoStreamForBatch), "{0} got an item for subscription {1}, but I don't have any subscriber for that stream. Dropping on the floor.",
                        providerRuntime.ExecutingEntityIdentity(), subscriptionId);
            // 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 ...
            return(default(StreamHandshakeToken));
        }
예제 #15
0
        public async Task <StreamHandshakeToken> DeliverBatch(GuidId subscriptionId, StreamId streamId, Immutable <IBatchContainer> batch, StreamHandshakeToken handshakeToken)
        {
            if (logger.IsVerbose3)
            {
                logger.Verbose3("DeliverBatch {0} for subscription {1}", batch.Value, subscriptionId);
            }

            IStreamSubscriptionHandle observer;

            if (allStreamObservers.TryGetValue(subscriptionId, out observer))
            {
                return(await observer.DeliverBatch(batch.Value, handshakeToken));
            }
            else
            {
                // if no observer attached to the subscription, check if there's onSubscriptinChange actions defined
                ISubscriptionChangeHandler handler;
                if (this.onSubscriptionChangeActionMap.TryGetValue(batch.Value.GetType(), out handler))
                {
                    //if the onAddAction attached an observer to the subscription
                    var streamProvider = this.providerRuntime.ServiceProvider
                                         .GetService <IStreamProviderManager>()
                                         .GetStreamProvider(streamId.ProviderName);
                    await handler.InvokeOnAdd(streamId, subscriptionId, isRewindable, streamProvider);

                    //if the onAddAction attached an observer to the subscription
                    if (allStreamObservers.TryGetValue(subscriptionId, out observer))
                    {
                        return(await observer.DeliverBatch(batch.Value, handshakeToken));
                    }
                }
            }

            logger.Warn((int)(ErrorCode.StreamProvider_NoStreamForBatch), "{0} got an item for subscription {1}, but I don't have any subscriber for that stream. Dropping on the floor.",
                        providerRuntime.ExecutingEntityIdentity(), subscriptionId);
            // 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 ...
            return(default(StreamHandshakeToken));
        }
예제 #16
0
        public Task <StreamHandshakeToken> DeliverItem(GuidId subscriptionId, Immutable <object> item, StreamSequenceToken currentToken, StreamHandshakeToken handshakeToken)
        {
            if (logger.IsVerbose3)
            {
                logger.Verbose3("DeliverItem {0} for subscription {1}", item.Value, subscriptionId);
            }

            IStreamSubscriptionHandle observer;

            if (allStreamObservers.TryGetValue(subscriptionId, out observer))
            {
                return(observer.DeliverItem(item.Value, currentToken, handshakeToken));
            }

            logger.Warn((int)(ErrorCode.StreamProvider_NoStreamForItem), "{0} got an item for subscription {1}, but I don't have any subscriber for that stream. Dropping on the floor.",
                        providerRuntime.ExecutingEntityIdentity(), subscriptionId);
            // 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 ...
            return(Task.FromResult(default(StreamHandshakeToken)));
        }
        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, 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),
                                config.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;
            }
        }
예제 #18
0
        public async Task <StreamHandshakeToken> DeliverMutable(GuidId subscriptionId, StreamId streamId, object item, StreamSequenceToken currentToken, StreamHandshakeToken handshakeToken)
        {
            if (logger.IsVerbose3)
            {
                var itemString = item.ToString();
                itemString = (itemString.Length > MAXIMUM_ITEM_STRING_LOG_LENGTH) ? itemString.Substring(0, MAXIMUM_ITEM_STRING_LOG_LENGTH) + "..." : itemString;
                logger.Verbose3("DeliverItem {0} for subscription {1}", itemString, subscriptionId);
            }
            IStreamSubscriptionHandle observer;

            if (allStreamObservers.TryGetValue(subscriptionId, out observer))
            {
                return(await observer.DeliverItem(item, currentToken, handshakeToken));
            }
            else
            {
                // if no observer attached to the subscription, check if there's onSubscriptinChange actions defined
                ISubscriptionChangeHandler handler;
                if (this.onSubscriptionChangeActionMap.TryGetValue(item.GetType(), out handler))
                {
                    //if the onAddAction attached an observer to the subscription
                    var streamProvider = this.providerRuntime.ServiceProvider
                                         .GetService <IStreamProviderManager>()
                                         .GetStreamProvider(streamId.ProviderName);
                    await handler.InvokeOnAdd(streamId, subscriptionId, isRewindable, streamProvider);

                    if (allStreamObservers.TryGetValue(subscriptionId, out observer))
                    {
                        return(await observer.DeliverItem(item, currentToken, handshakeToken));
                    }
                }
            }

            logger.Warn((int)(ErrorCode.StreamProvider_NoStreamForItem), "{0} got an item for subscription {1}, but I don't have any subscriber for that stream. Dropping on the floor.",
                        providerRuntime.ExecutingEntityIdentity(), subscriptionId);
            // 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 ...
            return(default(StreamHandshakeToken));
        }