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; }
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; } }
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; } }
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; } }
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.Guid, consumerData.StreamId.Namespace, 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.Guid, consumerData.StreamId.Namespace, 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.Guid, consumerData.StreamId.Namespace, cacheToken); } catch (Exception) { consumerData.Cursor = queueCache.GetCacheCursor(consumerData.StreamId.Guid, consumerData.StreamId.Namespace, null); // just in case last GetCacheCursor failed. } } return true; }