shashank created CAMEL-26822:
--------------------------------

             Summary: camel-kafka - the Kafka idempotent and key-value 
repositories force acks=1, so a leader failover can drop acknowledged records
                 Key: CAMEL-26822
                 URL: https://issues.apache.org/jira/browse/CAMEL-26822
             Project: Camel
          Issue Type: Improvement
          Components: camel-kafka
            Reporter: shashank
            Assignee: shashank


{{KafkaChangelog.start}} sets 
{{producerConfig.putIfAbsent(ProducerConfig.ACKS_CONFIG, "1")}} 
({{KafkaChangelog.java:162}}). {{KafkaIdempotentRepository}} and 
{{KafkaKeyValueRepository}} both use {{KafkaChangelog}}, so every repository 
whose producer config does not set {{acks}} runs with {{acks=1}}. This includes 
{{new KafkaIdempotentRepository(topic, bootstrapServers)}}.

Since Kafka 3.0 (KIP-679) the client default is {{acks=all}} with 
{{enable.idempotence=true}}. The line overrides that, and {{acks=1}} also turns 
the default idempotence off (the client logs "Idempotence will be disabled 
because acks is set to 1"). A producer config that sets only 
{{enable.idempotence=true}} fails at start with a {{ConfigException}}.

With {{acks=1}}, {{KafkaChangelog.send}} counts the leader's acknowledgement as 
success, and the repository has already changed its local cache. If the leader 
fails over before a follower has the record, the record is truncated. No 
instance ever read it, so the writer's cache and the topic disagree for good.

Example, replication factor 3, two instances:
# Instance A adds {{k1}}. The add is replicated and both instances apply it.
# A's processing fails and {{remove(k1)}} drops {{k1}} from A's cache. Only the 
leader has acknowledged the remove record.
# The leader stops (a crash, or a controlled shutdown in a rolling restart). A 
follower that was in sync becomes leader without the remove record.
# The redelivery of {{k1}} reaches B, which still holds {{k1}}, and is skipped 
as a duplicate. A also skips it after a restart.

A lost add lets a duplicate through, and a lost {{put}} of the key-value 
repository makes readers go back to the old value.

Found with Specula (a TLA+ model checked against traces of the real code). The 
failover itself was not reproduced with a test: it needs a cluster of several 
brokers and a leader change.

Why it matters: the default settings can hit it on any cluster with 
replication, on a normal rolling restart. The docs set no requirement on the 
replication factor, and the {{KafkaKeyValueRepository}} javadoc 
({{KafkaKeyValueRepository.java:52}}) calls it "durable" storage.

Workaround: set {{acks=all}} in the repository's {{producerConfig}}. The line 
uses {{putIfAbsent}}, so a user value wins.

Background: the line dates from the first implementation (CAMEL-10927, 2017), 
when the client default was already {{acks=1}}. Its comment is about 
synchronous sends without batching. CAMEL-18841 changed the endpoint default 
{{requestRequiredAcks}} to {{all}}, but the repositories were not changed.

Suggested change: remove the {{acks}} line so the client default applies, and 
add a note to the upgrade guide. Would you prefer that, or setting {{acks=all}} 
explicitly? Each send then waits for the in-sync replicas, which adds some 
latency to every add and remove.

_Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to