Skip to content

test whether it is possible to consume before the producer gets an ack#97

Open
arov00 wants to merge 2 commits into
mainfrom
fix/ack-fetch-concurrency-experiment
Open

test whether it is possible to consume before the producer gets an ack#97
arov00 wants to merge 2 commits into
mainfrom
fix/ack-fetch-concurrency-experiment

Conversation

@arov00

@arov00 arov00 commented Mar 3, 2026

Copy link
Copy Markdown
Collaborator

spoiler: yes it's possible

We observe that, in practice, Ack latencies are sometimes higher than E2E latencies at the same time and for the same topic partition. This was initially a surprise because intuitively one would expect E2E latencies to dominate ACK latencies (especially when everything runs on localhost).

Not sure if we should merge this. But it's interesting as an experiment. Running the synth client on this branch shows that it is possible for an E2E latency of an event to be recorded before its Ack latency is recorded. Normally, we would expect E2E latency to be an upper bound for the Ack latency, but the results show that this is not necessarily always the case:

2026-03-03 12:50:28,017 INFO  [io.spoud.kafka.MessageConsumer] (kafka-consumer) Consumer 0: 12 messages/second
2026-03-03 12:50:31,016 WARN  [io.spoud.MetricService] (kafka-consumer) Consumed message 0:290080 before receiving an ACK! This means that Ack latency could be higher than E2E latency!
2026-03-03 12:50:31,018 WARN  [io.spoud.MetricService] (kafka-consumer) Consumed message 0:290081 before receiving an ACK! This means that Ack latency could be higher than E2E latency!
2026-03-03 12:50:40,022 INFO  [io.spoud.kafka.MessageConsumer] (kafka-consumer) Consumer 0: 12 messages/second
2026-03-03 12:50:52,021 INFO  [io.spoud.kafka.MessageConsumer] (kafka-consumer) Consumer 0: 12 messages/second

If 99% of the time, the recorded Ack latencies are upper-bounded by the E2E latency, and 1% of the time they're not, then this 1% of recorded latencies ends up forming the 99th percentile of ack latencies and will be greater or equal to the E2E latency.

image

@arov00
arov00 requested review from gaetancollaud and spike83 March 3, 2026 11:57
lastMessage.set(ack);
metricService.recordAckLatency(metadata.topic(), metadata.partition(), Duration.between(send, ack));
Instant ackTime = Instant.ofEpochMilli(timeService.currentTimeMillis());
Instant sendTime = Instant.ofEpochMilli(metadata.timestamp());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

metadata.timestamp is now set by broker? this would add another time source into the mix, no?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think metadata.timestamp is the timestamp we give during production, no?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, depending on the topic configuration log.message.timestamp.type or message.timestamp.type.
If we are unlucky we inherit the LogAppendTime timestamp from broker config as a default when creating topics. We should ensure we set/validate CreateTime on the topics.

Comment on lines +94 to +98
if (fromRack.equals(config.rack()) && !ackedOffsets.contains(new AckedMessage(offset, partition))) {
Log.warnf("Consumed message %d:%d before receiving an ACK! This means that Ack latency could be higher than E2E latency!", partition, offset);
} else {
ackedOffsets.remove(new AckedMessage(offset, partition));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we cause a potential memory leak as we never remove the item that was missing here if it's added to the list later on. list will grow forever.

.minimumExpectedValue(1.0)
.maximumExpectedValue(10_000.0)
.minimumExpectedValue(config.expectedMinLatency())
.maximumExpectedValue(config.expectedMaxLatency())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes sense. might be influencing the P9s to a certain extend. This change should survive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants