Skip to content

Commit 8be2a8e

Browse files
authored
MINOR: Add javadocs to AbstractMergedSortedCacheStoreIterator (#18772)
While reviewing PR #18287, I wrote some javadocs to help me understand the AbstractMergedSortedCacheStoreIterator. Maybe we could add them to help the next developers getting into it. Reviewers: Anna Sophie Blee-Goldman <[email protected]>
1 parent 45c02d7 commit 8be2a8e

File tree

1 file changed

+133
-3
lines changed

1 file changed

+133
-3
lines changed

Diff for: streams/src/main/java/org/apache/kafka/streams/state/internals/AbstractMergedSortedCacheStoreIterator.java

+133-3
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,31 @@
2323
import java.util.NoSuchElementException;
2424

2525
/**
26-
* Merges two iterators. Assumes each of them is sorted by key
26+
* AbstractMergedSortedCacheStoreIterator is an abstract class for merging two sorted iterators, one from a cache and
27+
* the other from a store. It ensures the merged results maintain sorted order while resolving conflicts between cache
28+
* and store entries.
2729
*
28-
* @param <K>
29-
* @param <V>
30+
* <p>This iterator is used for state stores in Kafka Streams, which have an (optional) caching layer that needs to be
31+
* "merged" with the underlying state. It handles common scenarios like skipping records with cached tombstones (deleted
32+
* entries) and preferring cache entries over store entries when conflicts arise.</p>
33+
*
34+
* @param <K> The type of the resulting merged key.
35+
* @param <KS> The type of the store key.
36+
* @param <V> The type of the resulting merged value.
37+
* @param <VS> The type of the store value.
3038
*/
3139
abstract class AbstractMergedSortedCacheStoreIterator<K, KS, V, VS> implements KeyValueIterator<K, V> {
3240
private final PeekingKeyValueIterator<Bytes, LRUCacheEntry> cacheIterator;
3341
private final KeyValueIterator<KS, VS> storeIterator;
3442
private final boolean forward;
3543

44+
/**
45+
* Constructs an AbstractMergedSortedCacheStoreIterator.
46+
*
47+
* @param cacheIterator The iterator for the cache, assumed to be sorted by key.
48+
* @param storeIterator The iterator for the store, assumed to be sorted by key.
49+
* @param forward The direction of iteration. True for forward, false for reverse.
50+
*/
3651
AbstractMergedSortedCacheStoreIterator(final PeekingKeyValueIterator<Bytes, LRUCacheEntry> cacheIterator,
3752
final KeyValueIterator<KS, VS> storeIterator,
3853
final boolean forward) {
@@ -41,20 +56,79 @@ abstract class AbstractMergedSortedCacheStoreIterator<K, KS, V, VS> implements K
4156
this.forward = forward;
4257
}
4358

59+
/**
60+
* Compares the keys from the cache and store to determine their ordering.
61+
*
62+
* @param cacheKey The key from the cache.
63+
* @param storeKey The key from the store.
64+
*
65+
* @return A negative integer, zero, or a positive integer as the cache key is less than,
66+
* equal to, or greater than the store key.
67+
*/
4468
abstract int compare(final Bytes cacheKey, final KS storeKey);
4569

70+
/**
71+
* Deserializes a store key into a generic merged key type.
72+
*
73+
* @param key The store key to deserialize.
74+
*
75+
* @return The deserialized key.
76+
*/
4677
abstract K deserializeStoreKey(final KS key);
4778

79+
/**
80+
* Deserializes a key-value pair from the store into a generic merged key-value pair.
81+
*
82+
* @param pair The key-value pair from the store.
83+
*
84+
* @return The deserialized key-value pair.
85+
*/
4886
abstract KeyValue<K, V> deserializeStorePair(final KeyValue<KS, VS> pair);
4987

88+
/**
89+
* Deserializes a cache key into a generic merged key type.
90+
*
91+
* @param cacheKey The cache key to deserialize.
92+
*
93+
* @return The deserialized key.
94+
*/
5095
abstract K deserializeCacheKey(final Bytes cacheKey);
5196

97+
/**
98+
* Deserializes a cache entry into a generic value type.
99+
*
100+
* @param cacheEntry The cache entry to deserialize.
101+
*
102+
* @return The deserialized value.
103+
*/
52104
abstract V deserializeCacheValue(final LRUCacheEntry cacheEntry);
53105

106+
/**
107+
* Checks if a cache entry is a tombstone (representing a deleted value).
108+
*
109+
* @param nextFromCache The cache entry to check.
110+
*
111+
* @return True if the cache entry is a tombstone, false otherwise.
112+
*/
54113
private boolean isDeletedCacheEntry(final KeyValue<Bytes, LRUCacheEntry> nextFromCache) {
55114
return nextFromCache.value.value() == null;
56115
}
57116

117+
/**
118+
* Determines if there are more entries to iterate over, resolving conflicts between cache and store entries (e.g.,
119+
* skipping tombstones).
120+
*
121+
* <p>Conflict resolution scenarios:</p>
122+
*
123+
* <ul>
124+
* <li><b>Cache contains a tombstone for a key:</b> Skip both the cache tombstone and the corresponding store entry (if exists).</li>
125+
* <li><b>Cache contains a value for a key present in the store:</b> Prefer the cache value and skip the store entry.</li>
126+
* <li><b>Cache key is unique:</b> Return the cache value.</li>
127+
* <li><b>Store key is unique:</b> Return the store value.</li>
128+
* </ul>
129+
*
130+
* @return True if there are more entries, false otherwise.
131+
*/
58132
@Override
59133
public boolean hasNext() {
60134
// skip over items deleted from cache, and corresponding store items if they have the same key
@@ -86,6 +160,13 @@ public boolean hasNext() {
86160
return cacheIterator.hasNext() || storeIterator.hasNext();
87161
}
88162

163+
/**
164+
* Retrieves the next key-value pair in the merged iteration.
165+
*
166+
* @return The next key-value pair.
167+
*
168+
* @throws NoSuchElementException If there are no more elements to iterate.
169+
*/
89170
@Override
90171
public KeyValue<K, V> next() {
91172
if (!hasNext()) {
@@ -107,6 +188,15 @@ public KeyValue<K, V> next() {
107188
return chooseNextValue(nextCacheKey, nextStoreKey, comparison);
108189
}
109190

191+
/**
192+
* Resolves which source (cache or store) to fetch the next key-value pair when a comparison is performed.
193+
*
194+
* @param nextCacheKey The next key from the cache.
195+
* @param nextStoreKey The next key from the store.
196+
* @param comparison The comparison result between the cache and store keys.
197+
*
198+
* @return The next key-value pair.
199+
*/
110200
private KeyValue<K, V> chooseNextValue(final Bytes nextCacheKey,
111201
final KS nextStoreKey,
112202
final int comparison) {
@@ -133,6 +223,15 @@ private KeyValue<K, V> chooseNextValue(final Bytes nextCacheKey,
133223
}
134224
}
135225

226+
/**
227+
* Fetches the next value from the store, ensuring it matches the expected key.
228+
*
229+
* @param nextStoreKey The expected next key from the store.
230+
*
231+
* @return The next key-value pair from the store.
232+
*
233+
* @throws IllegalStateException If the key does not match the expected key.
234+
*/
136235
private KeyValue<K, V> nextStoreValue(final KS nextStoreKey) {
137236
final KeyValue<KS, VS> next = storeIterator.next();
138237

@@ -143,6 +242,15 @@ private KeyValue<K, V> nextStoreValue(final KS nextStoreKey) {
143242
return deserializeStorePair(next);
144243
}
145244

245+
/**
246+
* Fetches the next value from the cache, ensuring it matches the expected key.
247+
*
248+
* @param nextCacheKey The expected next key from the cache.
249+
*
250+
* @return The next key-value pair from the cache.
251+
*
252+
* @throws IllegalStateException If the key does not match the expected key.
253+
*/
146254
private KeyValue<K, V> nextCacheValue(final Bytes nextCacheKey) {
147255
final KeyValue<Bytes, LRUCacheEntry> next = cacheIterator.next();
148256

@@ -153,6 +261,13 @@ private KeyValue<K, V> nextCacheValue(final Bytes nextCacheKey) {
153261
return KeyValue.pair(deserializeCacheKey(next.key), deserializeCacheValue(next.value));
154262
}
155263

264+
/**
265+
* Peeks at the next key in the merged iteration without advancing the iterator.
266+
*
267+
* @return The next key in the iteration.
268+
*
269+
* @throws NoSuchElementException If there are no more elements to peek.
270+
*/
156271
@Override
157272
public K peekNextKey() {
158273
if (!hasNext()) {
@@ -174,6 +289,18 @@ public K peekNextKey() {
174289
return chooseNextKey(nextCacheKey, nextStoreKey, comparison);
175290
}
176291

292+
/**
293+
* Determines the next key to return from the merged iteration based on the comparison of the cache and store keys.
294+
* Resolves conflicts by considering the iteration direction and ensuring the merged order is maintained.
295+
*
296+
* @param nextCacheKey The next key from the cache.
297+
* @param nextStoreKey The next key from the store.
298+
* @param comparison The comparison result between the cache and store keys. A negative value indicates the cache
299+
* key is smaller, zero indicates equality, and a positive value indicates the store key is
300+
* smaller.
301+
*
302+
* @return The next key to return from the merged iteration.
303+
*/
177304
private K chooseNextKey(final Bytes nextCacheKey,
178305
final KS nextStoreKey,
179306
final int comparison) {
@@ -200,6 +327,9 @@ private K chooseNextKey(final Bytes nextCacheKey,
200327
}
201328
}
202329

330+
/**
331+
* Closes the iterators and releases any associated resources.
332+
*/
203333
@Override
204334
public void close() {
205335
cacheIterator.close();

0 commit comments

Comments
 (0)