nicktelford commented on code in PR #22625:
URL: https://github.com/apache/kafka/pull/22625#discussion_r3451267263


##########
streams/src/main/java/org/apache/kafka/streams/state/internals/RocksDBTransactionBuffer.java:
##########
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.kafka.streams.state.internals;
+
+import org.apache.kafka.common.utils.Bytes;
+import org.apache.kafka.streams.KeyValue;
+import org.apache.kafka.streams.errors.ProcessorStateException;
+
+import org.rocksdb.ColumnFamilyHandle;
+import org.rocksdb.RocksDB;
+import org.rocksdb.RocksDBException;
+import org.rocksdb.RocksIterator;
+import org.rocksdb.WriteBatch;
+import org.rocksdb.WriteOptions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.TreeMap;
+
+/**
+ * A {@link TransactionBuffer} implementation for RocksDB-backed stores.
+ * Uses a {@link WriteBatch} (without index) to accumulate writes for atomic 
commit.
+ * Reads are handled entirely by the shared {@code ConcurrentSkipListMap} in
+ * {@link AbstractTransactionBuffer}, so a {@code WriteBatchWithIndex} is not 
needed.
+ * <p>
+ * Range deletions ({@link #stageDeleteRange}) are only supported by 
RocksDB-backed stores
+ * and are owned entirely by this class; {@link AbstractTransactionBuffer} 
carries no
+ * tombstone state.
+ */
+class RocksDBTransactionBuffer extends AbstractTransactionBuffer<Bytes> {
+
+    private final RocksDB db;
+    private final ColumnFamilyHandle cfHandle;
+    private final WriteOptions wOptions;
+    private final String storeName;
+    private WriteBatch writeBatch;
+    private volatile NavigableMap<Bytes, List<Bytes>> rangeTombstones = 
Collections.emptyNavigableMap();
+
+    RocksDBTransactionBuffer(final RocksDB db,
+                             final ColumnFamilyHandle cfHandle,
+                             final WriteOptions wOptions,
+                             final String storeName) {
+        this.db = db;
+        this.cfHandle = cfHandle;
+        this.wOptions = wOptions;
+        this.storeName = storeName;
+        this.writeBatch = new WriteBatch();
+    }
+
+    @Override
+    int estimateKeySize(final Bytes key) {
+        return key.get().length;
+    }
+
+    @Override
+    void stageToBackend(final Bytes key, final byte[] value) {
+        stage(cfHandle, key, value);
+    }
+
+    /**
+     * Stages a write for an explicit column family. Updates the shared read 
buffer
+     * ({@code pendingWrites}) and appends the write to the shared {@link 
WriteBatch}
+     * under {@code cf}, so every staged CF is committed atomically on {@link 
#commit()}.
+     */
+    void stage(final ColumnFamilyHandle cf, final Bytes key, final byte[] 
value) {
+        pendingWrites.put(key, Optional.ofNullable(value));
+        pendingWritesBytes += estimateKeySize(key) + (value != null ? 
value.length : 0);
+        try {
+            if (value != null) {
+                writeBatch.put(cf, key.get(), value);
+            } else {
+                writeBatch.delete(cf, key.get());
+            }
+        } catch (final RocksDBException e) {
+            throw new ProcessorStateException("Error staging write in 
transaction buffer for store " + storeName, e);
+        }
+    }
+
+    /**
+     * Stages a range deletion for an explicit column family. Updates the 
shared
+     * {@code pendingWrites} and {@code rangeTombstones} so iterators opened 
before
+     * commit hide the deleted range, and appends the range delete to the 
shared
+     * {@link WriteBatch} under {@code cf}.
+     */
+    void stageDeleteRange(final ColumnFamilyHandle cf, final Bytes from, final 
Bytes to) {
+        pendingWrites.subMap(from, true, to, false).clear();
+        final TreeMap<Bytes, List<Bytes>> copy = new 
TreeMap<>(rangeTombstones);
+        copy.computeIfAbsent(from, k -> new ArrayList<>()).add(to);
+        rangeTombstones = copy;
+        pendingWritesBytes += estimateKeySize(from) + estimateKeySize(to);
+        try {
+            writeBatch.deleteRange(cf, from.get(), to.get());
+        } catch (final RocksDBException e) {
+            throw new ProcessorStateException("Error staging delete range in 
transaction buffer for store " + storeName, e);
+        }
+    }
+
+    @Override
+    public Optional<byte[]> get(final Bytes key) {
+        final Optional<byte[]> staged = pendingWrites.get(key);
+        if (staged != null) {
+            return staged;
+        }
+        if (isCoveredByRangeTombstone(key, rangeTombstones)) {
+            return Optional.empty();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return super.isEmpty() && rangeTombstones.isEmpty();
+    }
+
+    ManagedKeyValueIterator<Bytes, byte[]> all(final ColumnFamilyHandle cf, 
final boolean forward) {
+        if (Thread.currentThread() == ownerThread) {
+            final ManagedKeyValueIterator<Bytes, byte[]> baseIter = 
newBaseIterator(cf, null, null, forward, true);
+            return new StagedMergeIterator<>(pendingWrites, baseIter, forward);
+        }
+        return snapshotScan(cf, null, null, forward, true);
+    }
+
+    ManagedKeyValueIterator<Bytes, byte[]> range(final ColumnFamilyHandle cf,
+                                                 final Bytes from, final Bytes 
to,
+                                                 final boolean forward, final 
boolean toInclusive) {
+        if (Thread.currentThread() == ownerThread) {
+            final ManagedKeyValueIterator<Bytes, byte[]> baseIter = 
newBaseIterator(cf, from, to, forward, toInclusive);
+            final NavigableMap<Bytes, Optional<byte[]>> stagingView = 
boundStaging(from, to, toInclusive);
+            return new StagedMergeIterator<>(stagingView, baseIter, forward);

Review Comment:
   Ahh yes, same as in the AbstractTransactionBuffer  :smile: Fixed.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to