/// <summary> /// Handles an acknowlegement in the case there is no associated ProduceResponse. /// Messages are reenqueued if they were never sent in the first place or if we /// are in Retry mode. They're discarded otherwise, in which case the MessageDiscarded /// event is raised. /// </summary> /// <param name="acknowledgement"></param> private void HandleProduceAcknowledgementNone(ProduceAcknowledgement acknowledgement) { foreach (var message in acknowledgement.OriginalBatch.SelectMany(gt => gt.SelectMany(gp => gp))) { if (_configuration.ErrorStrategy == ErrorStrategy.Retry || acknowledgement.ReceiveDate == default(DateTime)) { // Repost ReEnqueue(message); } else { // Discard OnMessageDiscarded(message); } } }
/// <summary> /// Handle an acknowledgement with a ProduceResponse. Essentially search for errors and discard/retry /// accordingly. The logic is rather painful and boring. We try to be as efficient as possible /// in the standard case (i.e. no error). /// </summary> /// <param name="acknowledgement"></param> /// <returns></returns> private async Task HandleProduceAcknowledgement(ProduceAcknowledgement acknowledgement) { // The whole point of scanning the response is to search for errors var originalBatch = acknowledgement.OriginalBatch; var produceResponse = acknowledgement.ProduceResponse.ProducePartitionResponse; var throttled = acknowledgement.ProduceResponse.ThrottleTime; if (throttled > 0) { Throttled(throttled); } // Fill partitions in error caches _tmpPartitionsInError.Clear(); _tmpPartitionsInRecoverableError.Clear(); foreach (var tr in produceResponse.TopicsResponse) { bool errors = false; foreach (var p in tr.PartitionsData.Where(p => !IsPartitionOkForClients(p.ErrorCode))) { if (!errors) { errors = true; _tmpPartitionsInError[tr.TopicName] = new HashSet <int>(); _tmpPartitionsInRecoverableError[tr.TopicName] = new HashSet <int>(); } if (Error.IsPartitionErrorRecoverableForProducer(p.ErrorCode)) { _cluster.Logger.LogWarning( string.Format("Recoverable error detected: [topic: {0} - partition: {1} - error: {2}]", tr.TopicName, p.Partition, p.ErrorCode)); _tmpPartitionsInRecoverableError[tr.TopicName].Add(p.Partition); if (p.ErrorCode != ErrorCode.InvalidMessageSize && p.ErrorCode != ErrorCode.InvalidMessage) { _cluster.Logger.LogWarning( string.Format("Will ignore [topic: {0} / partition: {1}] for a time", tr.TopicName, p.Partition)); BlacklistPartition(tr.TopicName, p.Partition); if (p.ErrorCode == ErrorCode.RequestTimedOut) { BrokerTimeoutError(tr.TopicName); } } } else { _cluster.Logger.LogError( string.Format("Irrecoverable error detected: [topic: {0} - partition: {1} - error: {2}]", tr.TopicName, p.Partition, p.ErrorCode)); _tmpPartitionsInError[tr.TopicName].Add(p.Partition); } } } // In case of recoverable errors, refresh metadata if they are too old if (_tmpPartitionsInRecoverableError.Count > 0 && acknowledgement.ReceiveDate > _routingTable.LastRefreshed) { await EnsureHasRoutingTable(); } // Scan messages for errors and release memory if needed. // Messages associated to recoverable errors are reenqueued for rerouting, // messages with irrecoverable errors are discarded and messages with // no error are marked sent. foreach (var grouping in originalBatch) { int sent = 0; HashSet <int> errPartitions; if (!_tmpPartitionsInError.TryGetValue(grouping.Key, out errPartitions)) { errPartitions = NullHash; } HashSet <int> recPartitions; if (!_tmpPartitionsInRecoverableError.TryGetValue(grouping.Key, out recPartitions)) { recPartitions = NullHash; } foreach (var pm in grouping.SelectMany(g => g)) { if (recPartitions.Contains(pm.Partition)) { ReEnqueue(pm); } else { if (errPartitions.Contains(pm.Partition)) { _cluster.Logger.LogError( string.Format( "[Producer] Irrecoverable error, discarding message for [topic: {0} / partition: {1}]", pm.Topic, pm.Partition)); OnMessageDiscarded(pm); } else { ++sent; ClearMessage(pm.Message); } } } if (sent > 0) { MessagesAcknowledged(grouping.Key, sent); } } }
/// <summary> /// Accept an acknowledgement request. Post a ProduceEvent message /// to the inner actor. /// </summary> /// <param name="acknowledgement"></param> public void Acknowledge(ProduceAcknowledgement acknowledgement) { _produceResponses.Enqueue(acknowledgement); _messages.Post(RouterMessageType.ProduceEvent); }