Federico Mariani created CAMEL-23997:
----------------------------------------
Summary: camel-kafka: medium-severity findings from code review
(config, consumer, producer batch mode, transforms)
Key: CAMEL-23997
URL: https://issues.apache.org/jira/browse/CAMEL-23997
Project: Camel
Issue Type: Bug
Components: camel-kafka
Reporter: Federico Mariani
Umbrella ticket for the remaining *medium-severity* findings from a full code
review of camel-kafka (main, 4.22.0-SNAPSHOT). The critical findings are
tracked separately in the linked issues (consumer message loss; saslAuthType;
idempotent/resume). Each item below can be fixed independently; sub-tasks can
be split out as needed.
h3. Configuration
*1. sslEndpointAlgorithm=none does NOT disable hostname verification*
({{KafkaConfiguration}} lines 551-554 producer, 639-642 consumer; from
CAMEL-18146)
When the value is {{none}}/{{false}} the property is simply omitted, but
kafka-clients resolves an absent {{ssl.endpoint.identification.algorithm}} to
its own default {{https}} (verified via {{ConsumerConfig}}). The documented way
to disable verification is an *empty string*. The option is silently
ineffective; users must work around via {{additionalProperties}}. Fix: put an
empty string when {{none}}/{{false}}; consider marking the option {{security =
"insecure:ssl"}} per design/security.adoc.
*2. offsetRepository no longer disables Kafka auto-commit* (line 581;
regression from CAMEL-13870)
The mapping uses {{getAutoCommitEnable()}} (checks only {{batching}}) instead
of the {{offsetRepository}}-aware {{isAutoCommitEnable()}}. With an
{{offsetRepository}} configured and {{autoCommitEnable}} at default true,
{{enable.auto.commit=true}} is passed to the client, whose background thread
commits to the broker in parallel with repository tracking — diverging offsets
on rebalance. The setter javadoc explicitly promises "Defining one will disable
the autocommit". Fix: make {{getAutoCommitEnable()}} also return false when
{{offsetRepository != null}}.
*3. sendBufferBytes only applied to consumers when SSL is enabled* (line ~650;
regression from CAMEL-17615 refactor)
{{ProducerConfig.SEND_BUFFER_CONFIG}} sits inside the SSL-only block of the
consumer path; on PLAINTEXT the option is silently ignored. Fix: move to the
general consumer section (and use the {{ConsumerConfig}} constant).
*4. queueBufferingMaxMessages is a dead option* (lines 183-184)
Declared {{@UriParam(label = "producer", defaultValue = "10000")}} but never
mapped into any Kafka property — it belongs to the old Scala producer removed
in CAMEL-9467. Deprecate/remove with an upgrade-guide entry pointing to
{{bufferMemorySize}}/{{maxBlockMs}}.
h3. Consumer
*5. reconnect flag never reset after a successful reconnect*
({{KafkaFetchRecords}} lines 191, 319-326, 396-402; from CAMEL-17244)
Once {{setReconnect(true)}} (breakOnFirstError path), nothing resets it;
{{initializeConsumer()}} carries the comment "set reconnect to false ..." but
calls {{setConnected(false)}} instead. Consequence: with
{{breakOnFirstError=true}} + {{pollOnError=STOP}} (or a later fatal
{{AuthenticationException}}), the outer loop keeps reconnecting forever because
{{isReconnect()}} stays true. Fix: {{setReconnect(false)}} after successful
re-connection.
*6. Auto-generated groupId is a new random UUID per fetcher task*
({{KafkaConsumer#getProps}}, line 112)
With {{consumersCount=3}} and no {{groupId}}, each task gets its own
{{UUID.randomUUID()}} group → 3 independent groups → every message processed 3
times. Fix: generate once in {{doStart()}} and share.
*7. Manual-commit mode with Sync/Async commit managers auto-commits offsets the
route never committed* ({{KafkaRecordStreamingProcessor}} lines 97-99 + facade
119-123)
{{processExchange}} records every offset (including failed exchanges) and the
facade calls {{commitManager.commit(partition)}} per partition — so with
{{allowManualCommit=true}} + a configured Default*CommitFactory, offsets are
committed even when the route deliberately did not call
{{KafkaManualCommit.commit()}}. Behavior silently differs from the default
({{NoopCommitManager}}) setup. Needs a decision: gate on
{{!isAllowManualCommit()}} or document.
h3. Producer (batch/iterator mode, {{KeyValueHolderIterator}})
*8. Plain list elements are not converted to the serializer type* (lines 78-81;
regression from CAMEL-14360, original behavior from CAMEL-10586)
The non-Exchange/Message branch passes the raw body into the {{ProducerRecord}}
despite the method's own comment. {{List.of(1, 2, 3)}} + default
StringSerializer → {{ClassCastException}} at send time, while the same Integer
as a single body works. Fix: {{tryConvertToSerializedType(exchange, body,
valueSerializer)}} in the plain branch.
*9. Keys of List<Message> elements are never converted* (lines 117-132)
{{getInnerKey}} passes {{innerExchange}} (null for {{Message}} elements) to
{{tryConvertToSerializedType}}, which returns the object unconverted when the
exchange is null — the value conversion three lines later correctly uses the
fallback exchange, the key conversion does not. {{List<Message>}} with Integer
{{CamelKafkaKey}} + StringSerializer → {{ClassCastException}}. Fix: pass the
fallback {{ex}}.
*10. Endpoint-configured key= ignored for batch elements lacking the
CamelKafkaKey header* (lines 117-132 vs 134-140)
{{kafkaConfiguration.getKey()}} is only consulted inside the {{if (innerKey !=
null)}} guard, so elements without the header get a null record key even when
{{key=fixedKey}} is configured — inconsistent with {{getInnerPartitionKey}} and
with single-message mode ({{getOverrideKey}}). Breaks ordering expectations
silently. (Behavior change — note in upgrade guide.)
*11. Per-element recomputation of propagated headers*
({{PropagatedHeadersProvider#getDefaultHeaders}}; perf regression from
CAMEL-18527)
The plain-element branch recomputes the filtered+serialized header list for
every list element even in the default {{batchWithIndividualHeaders=false}}
mode where it was already cached. 10k elements x 20 headers = 200k redundant
filter+serialize calls per exchange. Fix: return the cached list.
*12. DefaultKafkaHeaderSerializer's type-converter fallback (CAMEL-18380) never
activates*
The serializer is {{CamelContextAware}} but nothing in camel-kafka injects the
context into the default instance created in {{KafkaConfiguration}} — so
unsupported header types are silently dropped instead of converted (the unit
test passes only because it injects the context manually). Fix:
{{CamelContextAware.trySetCamelContext(configuration.getHeaderSerializer(),
context)}} in {{KafkaProducer.doStart()}}.
h3. Kamelet transforms
*13. ReplaceField: renames parsing guarded by the wrong variable*
({{ReplaceField.java:49-51}})
{{if (ObjectHelper.isNotEmpty(disabled)) \{ renameFields = renames.split(",")
\}}} — tests {{disabled}} instead of {{renames}} (copy-paste). Used as a plain
bean (the classes were made reusable in CAMEL-21843): {{disabled}} set +
{{renames}} null → NPE; {{renames}} set + {{disabled}} empty → renames (and the
whole enabled/disabled filter loop) silently skipped. The kamelet defaults mask
it.
*14. MessageTimestampRouter: takes the first configured key name instead of the
first field present* ({{MessageTimestampRouter.java:59-64}})
The loop breaks on the first non-empty key *name* without checking the payload
contains it; with {{timestampKeys=ts1,ts2}} and a payload containing only
{{ts2}}, the timestamp is null and the record silently goes to the default
topic instead of the time-bucketed one — contradicting the documented "first
found field". Also line 70 raw-casts the value to String
({{ClassCastException}} for numeric fields).
h3. Reference
The complete review (including ~20 further low-severity items and cleanups:
charset mismatch in header serde, InterruptException consumer leak, dead code,
per-exchange ObjectMapper allocations, mislabeled producer-only options, etc.)
is available as a markdown document attached to / referenced from this review;
low items can be fixed opportunistically or split out on demand.
----
_This issue was researched and written by Claude Code on behalf of Federico
Mariani (GitHub: Croway). Findings were verified against the source and git
history before filing._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)