shashank created CAMEL-26856:
--------------------------------
Summary: camel-kafka - exactlyOnce: a Kafka send whose retry was
rejected, in a route called from a doTry, is replayed after the doCatch instead
of skipped
Key: CAMEL-26856
URL: https://issues.apache.org/jira/browse/CAMEL-26856
Project: Camel
Issue Type: Bug
Components: camel-kafka
Reporter: shashank
Assignee: shashank
With {{exactlyOnce}} on a Kafka consumer, the route calls another route from a
{{doTry}} block (for example with {{direct}}), and that route sends to Kafka
with an error handler that redelivers ({{maximumRedeliveries}} > 0). When the
send fails, its retry is rejected inside the open transaction:
{{KafkaProducer}} (around line 709-726) sets {{rollbackOnly}} and the
{{CamelKafkaExactlyOnceRetryRollback}} property, and throws. The called route
fails unhandled, the {{doCatch}} of the calling route catches the failure, and
{{CatchProcessor}} sets {{rollbackOnly}} again after the {{doCatch}} block.
{{KafkaTransactionSynchronization.onDone}} skips the rollback of such a
rejected retry only when the exchange is failure handled
({{handledRedeliveryRejection}}, lines 123-125 and 142-143). Since CAMEL-26447
the exchange is no longer marked failure handled after a {{doCatch}} caught the
failure of a called route, as when the {{doTry}} fails in the route itself. So
the source transaction is aborted and the source record is replayed, where
before CAMEL-26447 it took the offsets-only path (abort the output, commit the
source offsets, skip the record). For a send that keeps failing (for example a
record that is too large), every replay ends the same way, so the consumer does
not get past that record.
A {{doTry}} that fails in the route itself is not affected: no redelivery runs
inside the {{doTry}}, so no retry is rejected, and the recorded send failure
takes the offsets-only path.
Expected: a rejected retry whose failure was handled, by an error handler or by
a {{doCatch}}, takes the offsets-only path, as before CAMEL-26447 and as for a
{{doTry}} that fails in the route.
Possible fix, in camel-kafka: treat the rejected retry as handled when the
exchange has no exception left, instead of requiring the failure handled mark:
{code:java}
boolean handledRedeliveryRejection = exchange.getException() == null
&&
Boolean.TRUE.equals(exchange.getProperty(KafkaExactlyOnceMetadata.RETRY_ROLLBACK_PROPERTY));
{code}
A rejection that nothing handled keeps its exception and is still rolled back
by the branch on {{exchange.getException() != null}}. A rollback that the route
asks for itself after the rejection ({{markRollbackOnly}}) is not told apart
from the rejection, which is already the case today when an error handler
handled it.
Test: a variant of
{{KafkaTransactionSynchronizationTest.handledInTransactionRedeliveryRejectionCommitsOffsetsAfterAbortingOutput}}
with {{isFailureHandled()}} returning false (the {{doCatch}} case), and if
practical a case in {{KafkaExactlyOnceIT}} with the {{doTry}} calling a route
that redelivers a failing send.
CAMEL-26447 exposes this, so it should be fixed in the same release (4.24.0).
Found in the review of CAMEL-26447 (apache/camel#29584), from reading the code;
not reproduced with a broker.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)