Skip to content

Commit f52f2b9

Browse files
KAFKA-19476: Removing AtomicBoolean for findNextFetchOfffset (1/N) (#20207)
The PR refactors the findNextFetchOffset variable from AtomicBoolean to boolean itself as the access is always done while holding a lock. This also improves handling of `writeShareGroupState` method response where now complete lock is not required, rather on sub-section. Reviewers: Abhinav Dixit <[email protected]>, Andrew Schofield <[email protected]>
1 parent f188a31 commit f52f2b9

File tree

2 files changed

+36
-27
lines changed

2 files changed

+36
-27
lines changed

core/src/main/java/kafka/server/share/SharePartition.java

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
import java.util.Set;
8080
import java.util.concurrent.CompletableFuture;
8181
import java.util.concurrent.ConcurrentSkipListMap;
82-
import java.util.concurrent.atomic.AtomicBoolean;
8382
import java.util.concurrent.atomic.AtomicReference;
8483
import java.util.concurrent.locks.ReadWriteLock;
8584
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -237,11 +236,6 @@ private enum DeliveryCountOps {
237236
*/
238237
private final ReadWriteLock lock;
239238

240-
/**
241-
* The find next fetch offset is used to indicate if the next fetch offset should be recomputed.
242-
*/
243-
private final AtomicBoolean findNextFetchOffset;
244-
245239
/**
246240
* The lock to ensure that the same share partition does not enter a fetch queue
247241
* while another one is being fetched within the queue. The caller's id that acquires the fetch
@@ -275,6 +269,11 @@ private enum DeliveryCountOps {
275269
*/
276270
private final int defaultRecordLockDurationMs;
277271

272+
/**
273+
* The find next fetch offset is used to indicate if the next fetch offset should be recomputed.
274+
*/
275+
private boolean findNextFetchOffset;
276+
278277
/**
279278
* Timer is used to implement acquisition lock on records that guarantees the movement of records from
280279
* acquired to available/archived state upon timeout
@@ -410,7 +409,7 @@ private enum DeliveryCountOps {
410409
this.maxDeliveryCount = maxDeliveryCount;
411410
this.cachedState = new ConcurrentSkipListMap<>();
412411
this.lock = new ReentrantReadWriteLock();
413-
this.findNextFetchOffset = new AtomicBoolean(false);
412+
this.findNextFetchOffset = false;
414413
this.fetchLock = new AtomicReference<>(null);
415414
this.defaultRecordLockDurationMs = defaultRecordLockDurationMs;
416415
this.timer = timer;
@@ -536,7 +535,7 @@ public CompletableFuture<Void> maybeInitialize() {
536535
if (!cachedState.isEmpty()) {
537536
// If the cachedState is not empty, findNextFetchOffset flag is set to true so that any AVAILABLE records
538537
// in the cached state are not missed
539-
findNextFetchOffset.set(true);
538+
updateFindNextFetchOffset(true);
540539
endOffset = cachedState.lastEntry().getValue().lastOffset();
541540
// initialReadGapOffset is not required, if there are no gaps in the read state response
542541
if (gapStartOffset != -1) {
@@ -599,7 +598,7 @@ public long nextFetchOffset() {
599598
lock.writeLock().lock();
600599
try {
601600
// When none of the records in the cachedState are in the AVAILABLE state, findNextFetchOffset will be false
602-
if (!findNextFetchOffset.get()) {
601+
if (!findNextFetchOffset) {
603602
if (cachedState.isEmpty() || startOffset > cachedState.lastEntry().getValue().lastOffset()) {
604603
// 1. When cachedState is empty, endOffset is set to the next offset of the last
605604
// offset removed from batch, which is the next offset to be fetched.
@@ -618,7 +617,7 @@ public long nextFetchOffset() {
618617
// If cachedState is empty, there is no need of re-computing next fetch offset in future fetch requests.
619618
// Same case when startOffset has moved beyond the in-flight records, startOffset and endOffset point to the LSO
620619
// and the cached state is fresh.
621-
findNextFetchOffset.set(false);
620+
updateFindNextFetchOffset(false);
622621
log.trace("The next fetch offset for the share partition {}-{} is {}", groupId, topicIdPartition, endOffset);
623622
return endOffset;
624623
}
@@ -663,7 +662,7 @@ public long nextFetchOffset() {
663662
// If nextFetchOffset is -1, then no AVAILABLE records are found in the cachedState, so there is no need of
664663
// re-computing next fetch offset in future fetch requests
665664
if (nextFetchOffset == -1) {
666-
findNextFetchOffset.set(false);
665+
updateFindNextFetchOffset(false);
667666
nextFetchOffset = endOffset + 1;
668667
}
669668
log.trace("The next fetch offset for the share partition {}-{} is {}", groupId, topicIdPartition, nextFetchOffset);
@@ -1064,7 +1063,7 @@ private Optional<Throwable> releaseAcquiredRecordsForPerOffsetBatch(String membe
10641063
// If the maxDeliveryCount limit has been exceeded, the record will be transitioned to ARCHIVED state.
10651064
// This should not change the next fetch offset because the record is not available for acquisition
10661065
if (updateResult.state != RecordState.ARCHIVED) {
1067-
findNextFetchOffset.set(true);
1066+
updateFindNextFetchOffset(true);
10681067
}
10691068
}
10701069
}
@@ -1109,7 +1108,7 @@ private Optional<Throwable> releaseAcquiredRecordsForCompleteBatch(String member
11091108
// If the maxDeliveryCount limit has been exceeded, the record will be transitioned to ARCHIVED state.
11101109
// This should not change the next fetch offset because the record is not available for acquisition
11111110
if (updateResult.state != RecordState.ARCHIVED) {
1112-
findNextFetchOffset.set(true);
1111+
updateFindNextFetchOffset(true);
11131112
}
11141113
}
11151114
return Optional.empty();
@@ -1144,7 +1143,7 @@ void updateCacheAndOffsets(long logStartOffset) {
11441143
// If we have transitioned the state of any batch/offset from AVAILABLE to ARCHIVED,
11451144
// then there is a chance that the next fetch offset can change.
11461145
if (anyRecordArchived) {
1147-
findNextFetchOffset.set(true);
1146+
updateFindNextFetchOffset(true);
11481147
}
11491148

11501149
// The new startOffset will be the log start offset.
@@ -1206,7 +1205,7 @@ private void maybeArchiveStaleBatches(long fetchOffset, long baseOffset) {
12061205
// If we have transitioned the state of any batch/offset from AVAILABLE to ARCHIVED,
12071206
// then there is a chance that the next fetch offset can change.
12081207
if (anyRecordArchived) {
1209-
findNextFetchOffset.set(true);
1208+
updateFindNextFetchOffset(true);
12101209
}
12111210
} finally {
12121211
lock.writeLock().unlock();
@@ -1944,7 +1943,7 @@ private Optional<Throwable> acknowledgePerOffsetBatchRecords(
19441943
// This should not change the next fetch offset because the record is not available for acquisition
19451944
if (recordState == RecordState.AVAILABLE
19461945
&& updateResult.state != RecordState.ARCHIVED) {
1947-
findNextFetchOffset.set(true);
1946+
updateFindNextFetchOffset(true);
19481947
}
19491948
}
19501949
} finally {
@@ -2000,7 +1999,7 @@ private Optional<Throwable> acknowledgeCompleteBatch(
20001999
// This should not change the nextFetchOffset because the record is not available for acquisition
20012000
if (recordState == RecordState.AVAILABLE
20022001
&& updateResult.state != RecordState.ARCHIVED) {
2003-
findNextFetchOffset.set(true);
2002+
updateFindNextFetchOffset(true);
20042003
}
20052004
} finally {
20062005
lock.writeLock().unlock();
@@ -2086,11 +2085,11 @@ void rollbackOrProcessStateUpdates(
20862085
// Cancel the acquisition lock timeout task for the state since it is acknowledged/released successfully.
20872086
state.cancelAndClearAcquisitionLockTimeoutTask();
20882087
if (state.state == RecordState.AVAILABLE) {
2089-
findNextFetchOffset.set(true);
2088+
updateFindNextFetchOffset(true);
20902089
}
20912090
});
20922091
// Update the cached state and start and end offsets after acknowledging/releasing the acquired records.
2093-
cacheStateUpdated = maybeUpdateCachedStateAndOffsets();
2092+
cacheStateUpdated = maybeUpdateCachedStateAndOffsets();
20942093
future.complete(null);
20952094
} finally {
20962095
lock.writeLock().unlock();
@@ -2467,7 +2466,7 @@ private void releaseAcquisitionLockOnTimeoutForCompleteBatch(InFlightBatch inFli
24672466
// Cancel the acquisition lock timeout task for the batch since it is completed now.
24682467
updateResult.cancelAndClearAcquisitionLockTimeoutTask();
24692468
if (updateResult.state != RecordState.ARCHIVED) {
2470-
findNextFetchOffset.set(true);
2469+
updateFindNextFetchOffset(true);
24712470
}
24722471
return;
24732472
}
@@ -2514,7 +2513,7 @@ private void releaseAcquisitionLockOnTimeoutForPerOffsetBatch(InFlightBatch inFl
25142513
// Cancel the acquisition lock timeout task for the offset since it is completed now.
25152514
updateResult.cancelAndClearAcquisitionLockTimeoutTask();
25162515
if (updateResult.state != RecordState.ARCHIVED) {
2517-
findNextFetchOffset.set(true);
2516+
updateFindNextFetchOffset(true);
25182517
}
25192518
}
25202519
}
@@ -2748,12 +2747,22 @@ NavigableMap<Long, InFlightBatch> cachedState() {
27482747

27492748
// Visible for testing.
27502749
boolean findNextFetchOffset() {
2751-
return findNextFetchOffset.get();
2750+
lock.readLock().lock();
2751+
try {
2752+
return findNextFetchOffset;
2753+
} finally {
2754+
lock.readLock().unlock();
2755+
}
27522756
}
27532757

2754-
// Visible for testing. Should only be used for testing purposes.
2755-
void findNextFetchOffset(boolean findNextOffset) {
2756-
findNextFetchOffset.getAndSet(findNextOffset);
2758+
// Visible for testing.
2759+
void updateFindNextFetchOffset(boolean value) {
2760+
lock.writeLock().lock();
2761+
try {
2762+
findNextFetchOffset = value;
2763+
} finally {
2764+
lock.writeLock().unlock();
2765+
}
27572766
}
27582767

27592768
// Visible for testing

core/src/test/java/kafka/server/share/SharePartitionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,7 +1564,7 @@ public void testNextFetchOffsetWithCachedStateAcquired() {
15641564
@Test
15651565
public void testNextFetchOffsetWithFindAndCachedStateEmpty() {
15661566
SharePartition sharePartition = SharePartitionBuilder.builder().withState(SharePartitionState.ACTIVE).build();
1567-
sharePartition.findNextFetchOffset(true);
1567+
sharePartition.updateFindNextFetchOffset(true);
15681568
assertTrue(sharePartition.findNextFetchOffset());
15691569
assertEquals(0, sharePartition.nextFetchOffset());
15701570
assertFalse(sharePartition.findNextFetchOffset());
@@ -1573,7 +1573,7 @@ public void testNextFetchOffsetWithFindAndCachedStateEmpty() {
15731573
@Test
15741574
public void testNextFetchOffsetWithFindAndCachedState() {
15751575
SharePartition sharePartition = SharePartitionBuilder.builder().withState(SharePartitionState.ACTIVE).build();
1576-
sharePartition.findNextFetchOffset(true);
1576+
sharePartition.updateFindNextFetchOffset(true);
15771577
assertTrue(sharePartition.findNextFetchOffset());
15781578

15791579
fetchAcquiredRecords(sharePartition, memoryRecords(5), 5);

0 commit comments

Comments
 (0)