[ 
https://issues.apache.org/jira/browse/KAFKA-21372?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Mohammad Reduwan reassigned KAFKA-21372:
----------------------------------------

    Assignee: Mohammad Reduwan

> PartitionGroup#readyToProcess formats log messages on every call, and most of 
> these messages are discarded
> ----------------------------------------------------------------------------------------------------------
>
>                 Key: KAFKA-21372
>                 URL: https://issues.apache.org/jira/browse/KAFKA-21372
>             Project: Kafka
>          Issue Type: Bug
>          Components: streams
>    Affects Versions: 4.2.0, 4.3.0, 4.4.0
>            Reporter: Nicholas Telford
>            Assignee: Mohammad Reduwan
>            Priority: Minor
>
> h3. Problem
> KAFKA-19682 (#20693, first released in 4.2.0) added a log message to 
> {{{}PartitionGroup#readyToProcess{}}}. The method returns this message in 
> {{{}ReadyToProcessResult{}}}. It builds the message with {{String.format}} 
> and a {{StringBuilder}} for each partition that has no buffered records:
> {code:java}
> final Long fetchedLag = fetchedLags.getOrDefault(partition, -1L);
> appendLog(logMessageBuilder, String.format("Partition %s has fetched lag of 
> %d", partition, fetchedLag));
> ...
> appendLog(logMessageBuilder,
>     String.format("Partition %s has current lag %d, but no data is buffered 
> locally. Waiting to buffer some records.",
>     partition, fetchedLag));
> ...
> appendLog(logMessageBuilder, String.format(
>     "Partition %s has current lag 0 and current time is %d. " +
>         "Waiting for new data to be produced for configured idle time %d 
> (deadline is %d).",
>     partition, wallClockTime, maxTaskIdleMs, deadline));
> {code}
> No log-level check protects this code. It runs each time 
> {{StreamTask#isProcessable}} calls {{{}readyToProcess{}}}, and the 
> {{TaskExecutor}} loop does this {*}for each record{*}. The code runs when a 
> task has more than one input partition and at least one of those partitions 
> has no buffered records. This is a normal condition for a task that reads a 
> low-volume topic together with a high-volume topic.
> The caller then discards almost every message:
>  * {{StreamTask#isProcessable}} discards the message when the task changes 
> from READY to NOT_READY ({{{}wasReady{}}}).
>  * {{StreamTask#maybeLogNotReady}} logs the message at INFO only when 
> {{NOT_READY_LOG_INTERVAL_MS}} (120 s) passed since the last log. It discards 
> all other messages.
>  * When a partition reaches its idle deadline and the task becomes ready 
> ({{{}enforced{}}}), the method returns {{{}ReadyToProcessResult(true, 
> Optional.empty()){}}}. It discards the "has fetched lag" text that it already 
> formatted for that partition.
> Before 4.2.0, this method logged the same states with 
> {{{}logger.trace("...{}...", ...){}}}. That code formatted no text when TRACE 
> was disabled.
> h3. Impact
> We upgraded a Kafka Streams application from 3.8 to 4.4.0. The application 
> runs EOS v2 and has several sub-topologies with more than one input partition 
> for each task.
> We recorded a 5-minute JFR profile on one instance during the catch-up after 
> a restart. In this profile:
>  * {{PartitionGroup#readyToProcess}} is 19.7% of the StreamThread CPU 
> samples. {{String.format}} called from it is 15.1%, and this includes 
> {{DecimalFormatSymbols.<init>}} and {{{}Formatter.parse{}}}.
>  * {{ArrayList}} allocations from {{Formatter.parse}} are 13.7% of the 
> sampled allocation weight.
> The 3.8 profile of the same application has no equivalent cost.
> h3. Proposed fix
> Do not build the message unless the caller will log it. Possible approaches:
>  # Return the not-ready reason as structured data in 
> {{{}ReadyToProcessResult{}}}: the partition, the fetched lag, the deadline 
> and the reason. Let {{StreamTask#maybeLogNotReady}} format the text only when 
> it logs.
>  # Alternatively, make {{readyToProcess}} build the message only when the 
> caller asks for it. For example, pass a flag that {{StreamTask}} sets when 
> {{NOT_READY_LOG_INTERVAL_MS}} passed.
> Both approaches keep the log output of KAFKA-19682 and remove the cost from 
> the processing loop.
> h3. Workaround
> {{max.task.idle.ms=-1}} makes {{readyToProcess}} return before the loop. But 
> this setting also disables the wait for unfetched data, so a task can process 
> records out of order. It is not a safe workaround for applications that need 
> the default behaviour.



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

Reply via email to