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)

Reply via email to