diff --git a/dhfs-parent/objects/pom.xml b/dhfs-parent/objects/pom.xml
index b11658fb..4396fadb 100644
--- a/dhfs-parent/objects/pom.xml
+++ b/dhfs-parent/objects/pom.xml
@@ -69,6 +69,11 @@
lmdbjava
0.9.1
+
+ org.rocksdb
+ rocksdbjni
+ 9.10.0
+
org.apache.commons
commons-collections4
diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/LmdbObjectPersistentStore.java b/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/LmdbObjectPersistentStore.java
index 2d76febd..cfa7d73d 100644
--- a/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/LmdbObjectPersistentStore.java
+++ b/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/LmdbObjectPersistentStore.java
@@ -1,8 +1,9 @@
package com.usatiuk.objects.stores;
import com.google.protobuf.ByteString;
+import com.usatiuk.dhfs.supportlib.UninitializedByteBuffer;
+import com.usatiuk.dhfs.utils.RefcountedCloseable;
import com.usatiuk.objects.JObjectKey;
-import com.usatiuk.objects.JObjectKeyImpl;
import com.usatiuk.objects.JObjectKeyMax;
import com.usatiuk.objects.JObjectKeyMin;
import com.usatiuk.objects.iterators.CloseableKvIterator;
@@ -10,8 +11,6 @@ import com.usatiuk.objects.iterators.IteratorStart;
import com.usatiuk.objects.iterators.KeyPredicateKvIterator;
import com.usatiuk.objects.iterators.ReversibleKvIterator;
import com.usatiuk.objects.snapshot.Snapshot;
-import com.usatiuk.dhfs.supportlib.UninitializedByteBuffer;
-import com.usatiuk.dhfs.utils.RefcountedCloseable;
import io.quarkus.arc.properties.IfBuildProperty;
import io.quarkus.logging.Log;
import io.quarkus.runtime.ShutdownEvent;
diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/RocksDbObjectPersistentStore.java b/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/RocksDbObjectPersistentStore.java
new file mode 100644
index 00000000..09852fc8
--- /dev/null
+++ b/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/RocksDbObjectPersistentStore.java
@@ -0,0 +1,280 @@
+package com.usatiuk.objects.stores;
+
+import com.google.protobuf.ByteString;
+import com.usatiuk.objects.JObjectKey;
+import com.usatiuk.objects.JObjectKeyMax;
+import com.usatiuk.objects.JObjectKeyMin;
+import com.usatiuk.objects.iterators.CloseableKvIterator;
+import com.usatiuk.objects.iterators.IteratorStart;
+import com.usatiuk.objects.iterators.ReversibleKvIterator;
+import com.usatiuk.objects.snapshot.Snapshot;
+import io.quarkus.arc.properties.IfBuildProperty;
+import io.quarkus.logging.Log;
+import io.quarkus.runtime.ShutdownEvent;
+import io.quarkus.runtime.StartupEvent;
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import org.apache.commons.lang3.tuple.Pair;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.rocksdb.*;
+
+import javax.annotation.Nonnull;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+
+@ApplicationScoped
+@IfBuildProperty(name = "dhfs.objects.persistence", stringValue = "rocks")
+public class RocksDbObjectPersistentStore implements ObjectPersistentStore {
+ private static final String DB_NAME = "objects";
+ private static final byte[] DB_VER_OBJ_NAME = "__DB_VER_OBJ".getBytes(StandardCharsets.UTF_8);
+ private final Path _root;
+ private Options _options;
+ private TransactionDBOptions _transactionDBOptions;
+ private TransactionDB _db;
+ private boolean _ready = false;
+
+ public RocksDbObjectPersistentStore(@ConfigProperty(name = "dhfs.objects.persistence.files.root") String root) {
+ _root = Path.of(root).resolve("objects");
+ }
+
+ void init(@Observes @Priority(100) StartupEvent event) throws RocksDBException {
+ if (!_root.toFile().exists()) {
+ Log.info("Initializing with root " + _root);
+ _root.toFile().mkdirs();
+ }
+
+ RocksDB.loadLibrary();
+
+ _options = new Options().setCreateIfMissing(true);
+ _transactionDBOptions = new TransactionDBOptions();
+ _db = TransactionDB.open(_options, _transactionDBOptions, _root.toString());
+
+ try (var txn = _db.beginTransaction(new WriteOptions())) {
+ var read = readTxId(txn);
+ if (read.isPresent()) {
+ Log.infov("Read tx id {0}", read.get());
+ } else {
+ txn.put(DB_VER_OBJ_NAME, ByteBuffer.allocate(8).putLong(0).array());
+ txn.commit();
+ }
+ }
+
+ _ready = true;
+ }
+
+ private Optional readTxId(Transaction txn) throws RocksDBException {
+ var value = txn.get(new ReadOptions(), DB_VER_OBJ_NAME);
+ return Optional.ofNullable(value).map(ByteBuffer::wrap).map(ByteBuffer::getLong);
+ }
+
+ void shutdown(@Observes @Priority(900) ShutdownEvent event) {
+ _ready = false;
+ _db.close();
+ }
+
+ private void verifyReady() {
+ if (!_ready) throw new IllegalStateException("Wrong service order!");
+ }
+
+ @Nonnull
+ @Override
+ public Optional readObject(JObjectKey name) {
+ verifyReady();
+ byte[] got = null;
+ try {
+ got = _db.get(new ReadOptions(), name.bytes());
+ } catch (RocksDBException e) {
+ throw new RuntimeException(e);
+ }
+ return Optional.ofNullable(got).map(ByteString::copyFrom);
+ }
+
+ @Override
+ public Snapshot getSnapshot() {
+ var txn = _db.beginTransaction(new WriteOptions());
+ txn.setSnapshot();
+ var rocksDbSnapshot = txn.getSnapshot();
+ long commitId = 0;
+ try {
+ commitId = readTxId(txn).orElseThrow();
+ } catch (RocksDBException e) {
+ throw new RuntimeException(e);
+ }
+ long finalCommitId = commitId;
+ return new Snapshot() {
+ private final Transaction _txn = txn;
+ private final long _id = finalCommitId;
+ private final org.rocksdb.Snapshot _rocksDbSnapshot = rocksDbSnapshot;
+ private boolean _closed = false;
+
+ @Override
+ public CloseableKvIterator getIterator(IteratorStart start, JObjectKey key) {
+ assert !_closed;
+ return new RocksDbKvIterator(_txn, start, key, _rocksDbSnapshot);
+ }
+
+ @Nonnull
+ @Override
+ public Optional readObject(JObjectKey name) {
+ assert !_closed;
+ try (var readOptions = new ReadOptions().setSnapshot(_rocksDbSnapshot)) {
+ var got = _txn.get(readOptions, name.bytes());
+ return Optional.ofNullable(got).map(ByteString::copyFrom);
+ } catch (RocksDBException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public long id() {
+ assert !_closed;
+ return _id;
+ }
+
+ @Override
+ public void close() {
+ assert !_closed;
+ _closed = true;
+ _txn.close();
+ }
+ };
+ }
+
+ @Override
+ public Runnable prepareTx(TxManifestRaw names, long txId) {
+ verifyReady();
+ var txn = _db.beginTransaction(new WriteOptions());
+ try {
+ for (var written : names.written()) {
+ txn.put(written.getKey().bytes(), written.getValue().toByteArray());
+ }
+ for (JObjectKey key : names.deleted()) {
+ txn.delete(key.bytes());
+ }
+
+ assert txId > readTxId(txn).orElseThrow();
+
+ txn.put(DB_VER_OBJ_NAME, ByteBuffer.allocate(8).putLong(txId).array());
+ } catch (Throwable t) {
+ txn.close();
+ throw new RuntimeException(t);
+ }
+ return () -> {
+ try {
+ txn.commit();
+ } catch (RocksDBException e) {
+ throw new RuntimeException(e);
+ } finally {
+ txn.close();
+ }
+ };
+ }
+
+ @Override
+ public long getTotalSpace() {
+ verifyReady();
+ return _root.toFile().getTotalSpace();
+ }
+
+ @Override
+ public long getFreeSpace() {
+ verifyReady();
+ return _root.toFile().getFreeSpace();
+ }
+
+ @Override
+ public long getUsableSpace() {
+ verifyReady();
+ return _root.toFile().getUsableSpace();
+ }
+
+ private class RocksDbKvIterator extends ReversibleKvIterator {
+ private final RocksIterator _iterator;
+ private final org.rocksdb.Snapshot _rocksDbSnapshot;
+ private final ReadOptions _readOptions;
+ private boolean _hasNext;
+
+ RocksDbKvIterator(Transaction txn, IteratorStart start, JObjectKey key, org.rocksdb.Snapshot rocksDbSnapshot) {
+ _rocksDbSnapshot = rocksDbSnapshot;
+ _readOptions = new ReadOptions().setSnapshot(_rocksDbSnapshot);
+ _iterator = txn.getIterator(_readOptions);
+ verifyReady();
+
+ if (key instanceof JObjectKeyMin) {
+ _iterator.seekToFirst();
+ } else if (key instanceof JObjectKeyMax) {
+ _iterator.seekToLast();
+ } else {
+ _iterator.seek(key.bytes());
+ }
+ _hasNext = _iterator.isValid();
+ }
+
+ @Override
+ public void close() {
+ _iterator.close();
+ }
+
+ @Override
+ protected void reverse() {
+ if (_hasNext) {
+ if (_goingForward) {
+ _iterator.prev();
+ } else {
+ _iterator.next();
+ }
+ } else {
+ if (_goingForward) {
+ _iterator.seekToLast();
+ } else {
+ _iterator.seekToFirst();
+ }
+ }
+ _goingForward = !_goingForward;
+ _hasNext = _iterator.isValid();
+ }
+
+ @Override
+ protected JObjectKey peekImpl() {
+ if (!_hasNext) {
+ throw new NoSuchElementException("No more elements");
+ }
+ return JObjectKey.fromByteBuffer(ByteBuffer.wrap(_iterator.key()));
+ }
+
+ @Override
+ protected void skipImpl() {
+ if (_goingForward) {
+ _iterator.next();
+ } else {
+ _iterator.prev();
+ }
+ _hasNext = _iterator.isValid();
+ }
+
+ @Override
+ protected boolean hasImpl() {
+ return _hasNext;
+ }
+
+ @Override
+ protected Pair nextImpl() {
+ if (!_hasNext) {
+ throw new NoSuchElementException("No more elements");
+ }
+ var key = JObjectKey.fromByteBuffer(ByteBuffer.wrap(_iterator.key()));
+ var value = ByteString.copyFrom(_iterator.value());
+ if (_goingForward) {
+ _iterator.next();
+ } else {
+ _iterator.prev();
+ }
+ _hasNext = _iterator.isValid();
+ return Pair.of(key, value);
+ }
+ }
+}
diff --git a/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/SerializingObjectPersistentStore.java b/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/SerializingObjectPersistentStore.java
index fdc1b4e0..c3a3a052 100644
--- a/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/SerializingObjectPersistentStore.java
+++ b/dhfs-parent/objects/src/main/java/com/usatiuk/objects/stores/SerializingObjectPersistentStore.java
@@ -24,10 +24,6 @@ public class SerializingObjectPersistentStore {
@Inject
ObjectPersistentStore delegateStore;
- @Nonnull
- Optional readObject(JObjectKey name) {
- return delegateStore.readObject(name).map(serializer::deserialize);
- }
public Snapshot getSnapshot() {
return new Snapshot() {
diff --git a/dhfs-parent/objects/src/main/resources/application.properties b/dhfs-parent/objects/src/main/resources/application.properties
index 2b79369f..d30533d1 100644
--- a/dhfs-parent/objects/src/main/resources/application.properties
+++ b/dhfs-parent/objects/src/main/resources/application.properties
@@ -1,4 +1,4 @@
-dhfs.objects.persistence=lmdb
+dhfs.objects.persistence=rocks
dhfs.objects.writeback.limit=134217728
dhfs.objects.lru.limit=134217728
dhfs.objects.lru.print-stats=true
diff --git a/dhfs-parent/objects/src/test/java/com/usatiuk/objects/stores/LmdbKvIteratorTest.java b/dhfs-parent/objects/src/test/java/com/usatiuk/objects/stores/LmdbKvIteratorTest.java
index 5e9cc04d..696b0ece 100644
--- a/dhfs-parent/objects/src/test/java/com/usatiuk/objects/stores/LmdbKvIteratorTest.java
+++ b/dhfs-parent/objects/src/test/java/com/usatiuk/objects/stores/LmdbKvIteratorTest.java
@@ -1,129 +1,129 @@
-package com.usatiuk.objects.stores;
-
-
-import com.google.protobuf.ByteString;
-import com.usatiuk.objects.JObjectKey;
-import com.usatiuk.objects.Just;
-import com.usatiuk.objects.TempDataProfile;
-import com.usatiuk.objects.iterators.IteratorStart;
-import io.quarkus.test.junit.QuarkusTest;
-import io.quarkus.test.junit.TestProfile;
-import jakarta.inject.Inject;
-import org.apache.commons.lang3.tuple.Pair;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.RepeatedTest;
-
-import java.util.List;
-
-class Profiles {
- public static class LmdbKvIteratorTestProfile extends TempDataProfile {
- }
-}
-
-@QuarkusTest
-@TestProfile(Profiles.LmdbKvIteratorTestProfile.class)
-public class LmdbKvIteratorTest {
-
- @Inject
- LmdbObjectPersistentStore store;
-
- long getNextTxId() {
- try (var s = store.getSnapshot()) {
- return s.id() + 1;
- }
- }
-
- @RepeatedTest(100)
- public void iteratorTest1() {
- store.prepareTx(
- new TxManifestRaw(
- List.of(Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})),
- Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})),
- Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))),
- List.of()
- ), getNextTxId()
- ).run();
-
- try (var snapshot = store.getSnapshot()) {
- var iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(""));
- Just.checkIterator(iterator, List.of(Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})),
- Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})),
- Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(3)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(2)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(Long.toString(2)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.GT, JObjectKey.of(Long.toString(2)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(3)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(2)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(1)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(1)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.GT, JObjectKey.of(Long.toString(3)));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.GT, JObjectKey.of(Long.toString(4)));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(0)));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertFalse(iterator.hasNext());
- iterator.close();
-
- iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(Long.toString(2)));
- Assertions.assertTrue(iterator.hasNext());
- Assertions.assertEquals(JObjectKey.of(Long.toString(2)), iterator.peekNextKey());
- Assertions.assertEquals(JObjectKey.of(Long.toString(1)), iterator.peekPrevKey());
- Assertions.assertEquals(JObjectKey.of(Long.toString(2)), iterator.peekNextKey());
- Assertions.assertEquals(JObjectKey.of(Long.toString(1)), iterator.peekPrevKey());
- Just.checkIterator(iterator.reversed(), Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})));
- Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
- Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})), iterator.prev());
- Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), iterator.prev());
- Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), iterator.next());
- iterator.close();
- }
-
- store.prepareTx(new TxManifestRaw(
- List.of(),
- List.of(JObjectKey.of(Long.toString(1)), JObjectKey.of(Long.toString(2)), JObjectKey.of(Long.toString(3)))
- ),
- getNextTxId()
- ).run();
- }
-}
+//package com.usatiuk.objects.stores;
+//
+//
+//import com.google.protobuf.ByteString;
+//import com.usatiuk.objects.JObjectKey;
+//import com.usatiuk.objects.Just;
+//import com.usatiuk.objects.TempDataProfile;
+//import com.usatiuk.objects.iterators.IteratorStart;
+//import io.quarkus.test.junit.QuarkusTest;
+//import io.quarkus.test.junit.TestProfile;
+//import jakarta.inject.Inject;
+//import org.apache.commons.lang3.tuple.Pair;
+//import org.junit.jupiter.api.Assertions;
+//import org.junit.jupiter.api.RepeatedTest;
+//
+//import java.util.List;
+//
+//class Profiles {
+// public static class LmdbKvIteratorTestProfile extends TempDataProfile {
+// }
+//}
+//
+//@QuarkusTest
+//@TestProfile(Profiles.LmdbKvIteratorTestProfile.class)
+//public class LmdbKvIteratorTest {
+//
+// @Inject
+// LmdbObjectPersistentStore store;
+//
+// long getNextTxId() {
+// try (var s = store.getSnapshot()) {
+// return s.id() + 1;
+// }
+// }
+//
+// @RepeatedTest(100)
+// public void iteratorTest1() {
+// store.prepareTx(
+// new TxManifestRaw(
+// List.of(Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})),
+// Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})),
+// Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))),
+// List.of()
+// ), getNextTxId()
+// ).run();
+//
+// try (var snapshot = store.getSnapshot()) {
+// var iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(""));
+// Just.checkIterator(iterator, List.of(Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})),
+// Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})),
+// Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(3)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(2)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(Long.toString(2)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.GT, JObjectKey.of(Long.toString(2)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(3)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(2)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(1)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(1)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.GT, JObjectKey.of(Long.toString(3)));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.GT, JObjectKey.of(Long.toString(4)));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(0)));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertFalse(iterator.hasNext());
+// iterator.close();
+//
+// iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(Long.toString(2)));
+// Assertions.assertTrue(iterator.hasNext());
+// Assertions.assertEquals(JObjectKey.of(Long.toString(2)), iterator.peekNextKey());
+// Assertions.assertEquals(JObjectKey.of(Long.toString(1)), iterator.peekPrevKey());
+// Assertions.assertEquals(JObjectKey.of(Long.toString(2)), iterator.peekNextKey());
+// Assertions.assertEquals(JObjectKey.of(Long.toString(1)), iterator.peekPrevKey());
+// Just.checkIterator(iterator.reversed(), Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})));
+// Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})));
+// Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})), iterator.prev());
+// Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), iterator.prev());
+// Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), iterator.next());
+// iterator.close();
+// }
+//
+// store.prepareTx(new TxManifestRaw(
+// List.of(),
+// List.of(JObjectKey.of(Long.toString(1)), JObjectKey.of(Long.toString(2)), JObjectKey.of(Long.toString(3)))
+// ),
+// getNextTxId()
+// ).run();
+// }
+//}