Andrea Cosentino created CAMEL-24779:
----------------------------------------

             Summary: camel-kafka - reduce per-message allocations on the 
producer, consumer and transform hot paths
                 Key: CAMEL-24779
                 URL: https://issues.apache.org/jira/browse/CAMEL-24779
             Project: Camel
          Issue Type: Improvement
          Components: camel-kafka
            Reporter: Andrea Cosentino


This is a small, behaviour-preserving performance clean-up that bundles three 
per-message hot-path allocation reductions in camel-kafka. No public API or 
option changes.

h3. 1. Producer: redundant callback allocation on the single-message async path
{{KafkaProducer.process(Exchange, AsyncCallback)}} sends a single record via 
{{doSend(exchange, record, producerCallBack)}}. Because the key (the exchange) 
is non-null, {{doSend}} allocates a {{KafkaProducerMetadataCallBack}} *and* a 
{{DelegatingCallback}} for every message. For the single-message case this is 
redundant: the parent {{KafkaProducerCallBack}} already sets the exception and 
the {{CamelKafkaRecordMeta}} header (a {{List<RecordMetadata>}}) on the same 
exchange. Sending with the parent callback alone removes two short-lived 
allocations per message and produces an identical {{CamelKafkaRecordMeta}} 
result. The iterator/batch path is left unchanged (it genuinely needs a 
per-element metadata callback).

h3. 2. Consumer: per-record Stream/lambda allocations in header propagation
{{KafkaRecordProcessor.propagateHeaders}} builds a {{Stream}} + spliterator + 
two capturing lambdas for every consumed record, and re-resolves 
{{exchange.getIn()}} per header. Replacing it with a plain enhanced-for loop 
over {{consumerRecord.headers()}} with a hoisted {{Message}} removes the 
per-record pipeline and lambda garbage. Behaviour is identical.

h3. 3. Transforms: ObjectMapper allocated per message
Six transform classes ({{HoistField}}, {{MaskField}}, {{ExtractField}}, 
{{ReplaceField}}, {{MessageTimestampRouter}}, {{ValueToKey}}) construct a {{new 
ObjectMapper()}} on every invocation. {{ObjectMapper}} is expensive to 
construct and thread-safe once configured; these back the corresponding Kamelet 
actions and therefore run per message. Reusing a single shared static 
{{ObjectMapper}} removes the allocation.

All three changes are behaviour-preserving and covered by existing unit tests 
plus additions.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to