This is an automated email from the ASF dual-hosted git repository.
chia7712 pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/trunk by this push:
new 937add834bc MINOR: Avoid eager readNextOffset() call in
Cleaner.cleanSegments (#22200)
937add834bc is described below
commit 937add834bcddbc31f714159eab85cbb04ebccae
Author: KC.H <[email protected]>
AuthorDate: Mon May 4 17:50:12 2026 +0800
MINOR: Avoid eager readNextOffset() call in Cleaner.cleanSegments (#22200)
In Cleaner.cleanSegments, the upper bound offset for each LogSegment is
computed as the next segment's baseOffset when available, falling back
to the current segment's readNextOffset() for the last segment.
This is intentional: readNextOffset() reads from disk and is documented
as expensive, so the next segment's baseOffset (an O(1) field access) is
preferred whenever possible.
However, the previous code used:
```java
long upperBoundOffset = nextSegmentOpt.map(LogSegment::baseOffset)
.orElse(currentSegment.readNextOffset());
```
Optional.orElse evaluates its argument eagerly, so readNextOffset() was
invoked once per segment regardless of whether nextSegmentOpt was
present, defeating the optimization.
Replace orElse with a conditional expression so the fallback is only
evaluated when needed. orElseGet cannot be used here because
readNextOffset() declares IOException, which Supplier does not allow.
Reviewers: PoAn Yang <[email protected]>, Chia-Ping Tsai
<[email protected]>
---
.../main/java/org/apache/kafka/storage/internals/log/Cleaner.java | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git
a/storage/src/main/java/org/apache/kafka/storage/internals/log/Cleaner.java
b/storage/src/main/java/org/apache/kafka/storage/internals/log/Cleaner.java
index 5c111cc50f3..7dfb36ce915 100644
--- a/storage/src/main/java/org/apache/kafka/storage/internals/log/Cleaner.java
+++ b/storage/src/main/java/org/apache/kafka/storage/internals/log/Cleaner.java
@@ -253,7 +253,11 @@ public class Cleaner {
// Note that it is important to collect aborted transactions
from the full log segment
// range since we need to rebuild the full transaction index
for the new segment.
long startOffset = currentSegment.baseOffset();
- long upperBoundOffset =
nextSegmentOpt.map(LogSegment::baseOffset).orElse(currentSegment.readNextOffset());
+ // readNextOffset() is expensive — keep it lazy. orElse()
evaluates eagerly,
+ // and orElseGet() can't be used because readNextOffset()
throws IOException.
+ long upperBoundOffset = nextSegmentOpt.isPresent()
+ ? nextSegmentOpt.get().baseOffset()
+ : currentSegment.readNextOffset();
List<AbortedTxn> abortedTransactions =
log.collectAbortedTransactions(startOffset, upperBoundOffset);
transactionMetadata.addAbortedTransactions(abortedTransactions);