You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Even during retries, offsets will always be committed only after successful processing, and in order.
646
646
647
+
== Retries
648
+
649
+
If processing of a record fails, the record will be placed back into it's queue and retried after a time which can be configured (see the `ParallelConsumerOptions` class).
650
+
Ordering guarantees will always be adhered to, regardless of failure.
651
+
652
+
A failure is denoted by *any* exception being thrown from the user's processing function.
653
+
The system catches these exceptions, logs them and replaces the record in the queue for processing later.
654
+
All types of Exceptions thrown are considered retriable.
655
+
To not retry a record, do not throw an exception from your processing fuction.
656
+
657
+
If for some reason you want to proactively fail a record, without relying on some other system throwing an exception which you don't catch - simply throw an exception of your own design, which the system will treat the same way.
658
+
659
+
To configure the retry delay, see `ParallelConsumerOptions#defaultRetryDelay`.
660
+
661
+
At the moment there is no terminal error support, so messages will continue to be retried forever as long as an exception continues to be thrown from the user function (see <<skipping-records>>).
662
+
But still this will not hold up the queues in `KEY` or `UNORDERED` modes, however `PARTITION` mode it *will* block progress.
663
+
Offsets will also continue to be committed (see <<commit-mode>> and <<Offset Map>>).
664
+
665
+
=== Retry Delay Function
666
+
667
+
As part of the https://github.com/confluentinc/parallel-consumer/issues/65[enhanced retry epic], the ability to https://github.com/confluentinc/parallel-consumer/issues/82[dynamically determine the retry delay] was added.
668
+
This can be used to customise retry delay for a record, such as exponential back off or have different delays for different types of records, or have the delay determined by the status of a system etc.
669
+
670
+
You can access the retry count of a record through it's wrapped `WorkContainer` class, which is the input variable to the retry delay function.
671
+
672
+
.Example retry delay function implementing exponential backoff
673
+
[source,java,indent=0]
674
+
----
675
+
final double multiplier = 0.5;
676
+
final int baseDelaySecond = 1;
677
+
678
+
ParallelConsumerOptions.<String, String>builder()
679
+
.retryDelayProvider(workContainer -> {
680
+
int numberOfFailedAttempts = workContainer.getNumberOfFailedAttempts();
681
+
long delayMillis = (long) (baseDelaySecond * Math.pow(multiplier, numberOfFailedAttempts) * 1000);
682
+
return Duration.ofMillis(delayMillis);
683
+
});
684
+
----
685
+
686
+
[[skipping-records]]
687
+
=== Skipping Records
688
+
689
+
If for whatever reason you want to skip a record, simply do not throw an exception, or catch any exception being thrown, log and swallow it and return from the user function normally.
690
+
The system will treat this as a record processing success, mark the record as completed and move on as though it were normal operation.
691
+
692
+
A user may choose to skip a record for example, if it has been retried too many times or if the record is invalid or doesn't need processing.
693
+
694
+
Implementing a https://github.com/confluentinc/parallel-consumer/issues/196[max retries feature] as a part of the system is planned.
695
+
696
+
.Example of skipping a record after a maximum number of retries is reached
697
+
[source,java,indent=0]
698
+
----
699
+
final int maxRetries = 10;
700
+
final Map<ConsumerRecord<String, String>, Long> retriesCount = new ConcurrentHashMap<>();
701
+
702
+
pc.poll(consumerRecord -> {
703
+
Long retryCount = retriesCount.computeIfAbsent(consumerRecord, ignore -> 0L);
704
+
if (retryCount < maxRetries) {
705
+
processRecord(consumerRecord);
706
+
// no exception, so completed - remove from map
707
+
retriesCount.remove(consumerRecord);
708
+
} else {
709
+
log.warn("Retry count {} exceed manx of {} for record {}", retryCount, maxRetries, consumerRecord);
710
+
// giving up, remove from map
711
+
retriesCount.remove(consumerRecord);
712
+
}
713
+
});
714
+
----
715
+
716
+
=== Circuit Breaker Pattern
717
+
718
+
Althought the system doesn't have an https://github.com/confluentinc/parallel-consumer/issues/110[explicit circuit breaker pattern feature], one can be created by conbining the custom retry delay function and proactive failure.
719
+
For example, the retry delay can be calculated based upon the status of an external system - i.e. if the external system is currently out of action, use a higher retry.
720
+
Then in the processing function, again check the status of the external system first, and if it's still offline, throw an exception proactively without attempting to process the message.
721
+
This will put the message back in the queue.
722
+
723
+
.Example of circuit break implementation
724
+
[source,java,indent=0]
725
+
----
726
+
final Map<String, Boolean> upMap = new ConcurrentHashMap<>();
boolean up = upMap.computeIfAbsent(serverId, ignore -> true);
731
+
732
+
if (!up) {
733
+
updateStatusOfSever(serverId);
734
+
}
735
+
736
+
if (up) {
737
+
try {
738
+
processRecord(consumerRecord);
739
+
} catch (CircuitBreakingException e) {
740
+
log.warn("Server {} is circuitBroken, will retry message when server is up. Record: {}", serverId, consumerRecord);
741
+
upMap.put(serverId, false);
742
+
}
743
+
// no exception, so set server status UP
744
+
upMap.put(serverId, true);
745
+
} else {
746
+
log.warn("Server {} currently down, will retry record latter {}", up, consumerRecord);
747
+
}
748
+
});
749
+
----
750
+
751
+
=== Head of Line Blocking
752
+
753
+
In order to have a failing record not block progress of a partition, one of the ordering modes other than `PARTITION` must be used, so that the system is allowed to process other messages that are perhaps in `KEY` order or in the case of `UNORDERED` processing - any message.
754
+
This is because in `PARTITION` ordering mode, records are always processed in order of partition, and so the Head of Line blocking feature is effectively disabled.
755
+
756
+
=== Future Work
757
+
758
+
Improvements to this system are planned, see the following issues:
* https://github.com/confluentinc/parallel-consumer/issues/196[Provide option for max retires, and a call back when reached (potential DLQ) #196]
763
+
* https://github.com/confluentinc/parallel-consumer/issues/34[Monitor for progress and optionally shutdown (leave consumer group), skip message or send to DLQ #34]
764
+
647
765
== Result Models
648
766
649
767
* Void
@@ -659,6 +777,7 @@ When your function is actually run, a result object will be streamed back to you
659
777
After your operation completes, you can also choose to publish a result message back to Kafka.
660
778
The message publishing metadata can be streamed back to your client code.
661
779
780
+
[[commit-mode]]
662
781
== Commit Mode
663
782
664
783
The system gives you three choices for how to do offset commits.
@@ -668,7 +787,7 @@ The `transactional` mode is explained in the next section.
668
787
669
788
`Asynchronous` mode is faster, as it doesn't block the control loop.
670
789
671
-
`Synchronous` will block the processing loop until a successful commit response is received, however, `Asynchronous` will still be capped by the max processing settings in the `Options` class.
790
+
`Synchronous` will block the processing loop until a successful commit response is received, however, `Asynchronous` will still be capped by the max processing settings in the `ParallelConsumerOptions` class.
672
791
673
792
If you're used to using the auto commit mode in the normal Kafka consumer, you can think of the `Asynchronous` mode being similar to this.
674
793
We suggest starting with this mode, and it is the default.
Copy file name to clipboardExpand all lines: parallel-consumer-examples/parallel-consumer-example-core/src/main/java/io/confluent/parallelconsumer/examples/core/CoreApp.java
0 commit comments