Use a new approach to completely avoid memory leaks

This commit is contained in:
Andrea Cavalli 2021-08-16 10:27:47 +02:00
parent 9d326f5a8b
commit 435e7d4886
2 changed files with 983 additions and 1099 deletions

View File

@ -23,17 +23,17 @@ public interface LLDictionary extends LLKeyValueDatabaseStructure {
ByteBufAllocator getAllocator(); ByteBufAllocator getAllocator();
Mono<ByteBuf> get(@Nullable LLSnapshot snapshot, ByteBuf key, boolean existsAlmostCertainly); Mono<ByteBuf> get(@Nullable LLSnapshot snapshot, Mono<ByteBuf> key, boolean existsAlmostCertainly);
default Mono<ByteBuf> get(@Nullable LLSnapshot snapshot, ByteBuf key) { default Mono<ByteBuf> get(@Nullable LLSnapshot snapshot, Mono<ByteBuf> key) {
return get(snapshot, key, false); return get(snapshot, key, false);
} }
Mono<ByteBuf> put(ByteBuf key, ByteBuf value, LLDictionaryResultType resultType); Mono<ByteBuf> put(Mono<ByteBuf> key, Mono<ByteBuf> value, LLDictionaryResultType resultType);
Mono<UpdateMode> getUpdateMode(); Mono<UpdateMode> getUpdateMode();
default Mono<ByteBuf> update(ByteBuf key, default Mono<ByteBuf> update(Mono<ByteBuf> key,
Function<@Nullable ByteBuf, @Nullable ByteBuf> updater, Function<@Nullable ByteBuf, @Nullable ByteBuf> updater,
UpdateReturnMode updateReturnMode, UpdateReturnMode updateReturnMode,
boolean existsAlmostCertainly) { boolean existsAlmostCertainly) {
@ -42,24 +42,24 @@ public interface LLDictionary extends LLKeyValueDatabaseStructure {
.transform(prev -> LLUtils.resolveDelta(prev, updateReturnMode)); .transform(prev -> LLUtils.resolveDelta(prev, updateReturnMode));
} }
default Mono<ByteBuf> update(ByteBuf key, default Mono<ByteBuf> update(Mono<ByteBuf> key,
Function<@Nullable ByteBuf, @Nullable ByteBuf> updater, Function<@Nullable ByteBuf, @Nullable ByteBuf> updater,
UpdateReturnMode returnMode) { UpdateReturnMode returnMode) {
return update(key, updater, returnMode, false); return update(key, updater, returnMode, false);
} }
Mono<Delta<ByteBuf>> updateAndGetDelta(ByteBuf key, Mono<Delta<ByteBuf>> updateAndGetDelta(Mono<ByteBuf> key,
Function<@Nullable ByteBuf, @Nullable ByteBuf> updater, Function<@Nullable ByteBuf, @Nullable ByteBuf> updater,
boolean existsAlmostCertainly); boolean existsAlmostCertainly);
default Mono<Delta<ByteBuf>> updateAndGetDelta(ByteBuf key, default Mono<Delta<ByteBuf>> updateAndGetDelta(Mono<ByteBuf> key,
Function<@Nullable ByteBuf, @Nullable ByteBuf> updater) { Function<@Nullable ByteBuf, @Nullable ByteBuf> updater) {
return updateAndGetDelta(key, updater, false); return updateAndGetDelta(key, updater, false);
} }
Mono<Void> clear(); Mono<Void> clear();
Mono<ByteBuf> remove(ByteBuf key, LLDictionaryResultType resultType); Mono<ByteBuf> remove(Mono<ByteBuf> key, LLDictionaryResultType resultType);
<K> Flux<Tuple3<K, ByteBuf, Optional<ByteBuf>>> getMulti(@Nullable LLSnapshot snapshot, <K> Flux<Tuple3<K, ByteBuf, Optional<ByteBuf>>> getMulti(@Nullable LLSnapshot snapshot,
Flux<Tuple2<K, ByteBuf>> keys, Flux<Tuple2<K, ByteBuf>> keys,
@ -74,34 +74,34 @@ public interface LLDictionary extends LLKeyValueDatabaseStructure {
<X> Flux<ExtraKeyOperationResult<ByteBuf, X>> updateMulti(Flux<Tuple2<ByteBuf, X>> entries, <X> Flux<ExtraKeyOperationResult<ByteBuf, X>> updateMulti(Flux<Tuple2<ByteBuf, X>> entries,
BiFunction<ByteBuf, X, ByteBuf> updateFunction); BiFunction<ByteBuf, X, ByteBuf> updateFunction);
Flux<Entry<ByteBuf, ByteBuf>> getRange(@Nullable LLSnapshot snapshot, LLRange range, boolean existsAlmostCertainly); Flux<Entry<ByteBuf, ByteBuf>> getRange(@Nullable LLSnapshot snapshot, Mono<LLRange> range, boolean existsAlmostCertainly);
default Flux<Entry<ByteBuf, ByteBuf>> getRange(@Nullable LLSnapshot snapshot, LLRange range) { default Flux<Entry<ByteBuf, ByteBuf>> getRange(@Nullable LLSnapshot snapshot, Mono<LLRange> range) {
return getRange(snapshot, range, false); return getRange(snapshot, range, false);
} }
Flux<List<Entry<ByteBuf, ByteBuf>>> getRangeGrouped(@Nullable LLSnapshot snapshot, Flux<List<Entry<ByteBuf, ByteBuf>>> getRangeGrouped(@Nullable LLSnapshot snapshot,
LLRange range, Mono<LLRange> range,
int prefixLength, int prefixLength,
boolean existsAlmostCertainly); boolean existsAlmostCertainly);
default Flux<List<Entry<ByteBuf, ByteBuf>>> getRangeGrouped(@Nullable LLSnapshot snapshot, default Flux<List<Entry<ByteBuf, ByteBuf>>> getRangeGrouped(@Nullable LLSnapshot snapshot,
LLRange range, Mono<LLRange> range,
int prefixLength) { int prefixLength) {
return getRangeGrouped(snapshot, range, prefixLength, false); return getRangeGrouped(snapshot, range, prefixLength, false);
} }
Flux<ByteBuf> getRangeKeys(@Nullable LLSnapshot snapshot, LLRange range); Flux<ByteBuf> getRangeKeys(@Nullable LLSnapshot snapshot, Mono<LLRange> range);
Flux<List<ByteBuf>> getRangeKeysGrouped(@Nullable LLSnapshot snapshot, LLRange range, int prefixLength); Flux<List<ByteBuf>> getRangeKeysGrouped(@Nullable LLSnapshot snapshot, Mono<LLRange> range, int prefixLength);
Flux<ByteBuf> getRangeKeyPrefixes(@Nullable LLSnapshot snapshot, LLRange range, int prefixLength); Flux<ByteBuf> getRangeKeyPrefixes(@Nullable LLSnapshot snapshot, Mono<LLRange> range, int prefixLength);
Flux<BadBlock> badBlocks(LLRange range); Flux<BadBlock> badBlocks(Mono<LLRange> range);
Mono<Void> setRange(LLRange range, Flux<Entry<ByteBuf, ByteBuf>> entries); Mono<Void> setRange(Mono<LLRange> range, Flux<Entry<ByteBuf, ByteBuf>> entries);
default Mono<Void> replaceRange(LLRange range, default Mono<Void> replaceRange(Mono<LLRange> range,
boolean canKeysChange, boolean canKeysChange,
Function<Entry<ByteBuf, ByteBuf>, Mono<Entry<ByteBuf, ByteBuf>>> entriesReplacer, Function<Entry<ByteBuf, ByteBuf>, Mono<Entry<ByteBuf, ByteBuf>>> entriesReplacer,
boolean existsAlmostCertainly) { boolean existsAlmostCertainly) {
@ -122,19 +122,19 @@ public interface LLDictionary extends LLKeyValueDatabaseStructure {
}); });
} }
default Mono<Void> replaceRange(LLRange range, default Mono<Void> replaceRange(Mono<LLRange> range,
boolean canKeysChange, boolean canKeysChange,
Function<Entry<ByteBuf, ByteBuf>, Mono<Entry<ByteBuf, ByteBuf>>> entriesReplacer) { Function<Entry<ByteBuf, ByteBuf>, Mono<Entry<ByteBuf, ByteBuf>>> entriesReplacer) {
return replaceRange(range, canKeysChange, entriesReplacer, false); return replaceRange(range, canKeysChange, entriesReplacer, false);
} }
Mono<Boolean> isRangeEmpty(@Nullable LLSnapshot snapshot, LLRange range); Mono<Boolean> isRangeEmpty(@Nullable LLSnapshot snapshot, Mono<LLRange> range);
Mono<Long> sizeRange(@Nullable LLSnapshot snapshot, LLRange range, boolean fast); Mono<Long> sizeRange(@Nullable LLSnapshot snapshot, Mono<LLRange> range, boolean fast);
Mono<Entry<ByteBuf, ByteBuf>> getOne(@Nullable LLSnapshot snapshot, LLRange range); Mono<Entry<ByteBuf, ByteBuf>> getOne(@Nullable LLSnapshot snapshot, Mono<LLRange> range);
Mono<ByteBuf> getOneKey(@Nullable LLSnapshot snapshot, LLRange range); Mono<ByteBuf> getOneKey(@Nullable LLSnapshot snapshot, Mono<LLRange> range);
Mono<Entry<ByteBuf, ByteBuf>> removeOne(LLRange range); Mono<Entry<ByteBuf, ByteBuf>> removeOne(Mono<LLRange> range);
} }

View File

@ -1,14 +1,15 @@
package it.cavallium.dbengine.database.disk; package it.cavallium.dbengine.database.disk;
import static io.netty.buffer.Unpooled.wrappedBuffer;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
import it.cavallium.dbengine.client.BadBlock; import it.cavallium.dbengine.client.BadBlock;
import it.cavallium.dbengine.database.Column;
import it.cavallium.dbengine.client.DatabaseOptions; import it.cavallium.dbengine.client.DatabaseOptions;
import it.cavallium.dbengine.database.Column;
import it.cavallium.dbengine.database.Delta; import it.cavallium.dbengine.database.Delta;
import it.cavallium.dbengine.database.ExtraKeyOperationResult; import it.cavallium.dbengine.database.ExtraKeyOperationResult;
import it.cavallium.dbengine.database.KeyOperationResult;
import it.cavallium.dbengine.database.LLDictionary; import it.cavallium.dbengine.database.LLDictionary;
import it.cavallium.dbengine.database.LLDictionaryResultType; import it.cavallium.dbengine.database.LLDictionaryResultType;
import it.cavallium.dbengine.database.LLRange; import it.cavallium.dbengine.database.LLRange;
@ -17,16 +18,13 @@ import it.cavallium.dbengine.database.LLUtils;
import it.cavallium.dbengine.database.RepeatedElementList; import it.cavallium.dbengine.database.RepeatedElementList;
import it.cavallium.dbengine.database.UpdateMode; import it.cavallium.dbengine.database.UpdateMode;
import it.cavallium.dbengine.database.UpdateReturnMode; import it.cavallium.dbengine.database.UpdateReturnMode;
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -68,7 +66,6 @@ import reactor.core.scheduler.Scheduler;
import reactor.util.function.Tuple2; import reactor.util.function.Tuple2;
import reactor.util.function.Tuple3; import reactor.util.function.Tuple3;
import reactor.util.function.Tuples; import reactor.util.function.Tuples;
import static io.netty.buffer.Unpooled.*;
@NotAtomic @NotAtomic
public class LLLocalDictionary implements LLDictionary { public class LLLocalDictionary implements LLDictionary {
@ -237,11 +234,14 @@ public class LLLocalDictionary implements LLDictionary {
return alloc; return alloc;
} }
private <T> Mono<T> runOnDb(Callable<@Nullable T> callable) {
return Mono.fromCallable(callable).subscribeOn(dbScheduler);
}
@Override @Override
public Mono<ByteBuf> get(@Nullable LLSnapshot snapshot, ByteBuf key, boolean existsAlmostCertainly) { public Mono<ByteBuf> get(@Nullable LLSnapshot snapshot, Mono<ByteBuf> keyMono, boolean existsAlmostCertainly) {
try { return Mono.usingWhen(keyMono,
return Mono key -> runOnDb(() -> {
.fromCallable(() -> {
StampedLock lock; StampedLock lock;
long stamp; long stamp;
if (updateMode == UpdateMode.ALLOW) { if (updateMode == UpdateMode.ALLOW) {
@ -262,14 +262,9 @@ public class LLLocalDictionary implements LLDictionary {
lock.unlockRead(stamp); lock.unlockRead(stamp);
} }
} }
}) }).onErrorMap(cause -> new IOException("Failed to read " + LLUtils.toStringSafe(key), cause)),
.subscribeOn(dbScheduler) key -> Mono.fromRunnable(key::release)
.onErrorMap(cause -> new IOException("Failed to read " + LLUtils.toStringSafe(key), cause)) );
.doFirst(key::retain)
.doAfterTerminate(key::release);
} finally {
key.release();
}
} }
private ByteBuf dbGet(ColumnFamilyHandle cfh, private ByteBuf dbGet(ColumnFamilyHandle cfh,
@ -287,9 +282,7 @@ public class LLLocalDictionary implements LLDictionary {
throw new RocksDBException("Key buffer must be direct"); throw new RocksDBException("Key buffer must be direct");
} }
ByteBuffer keyNioBuffer = LLUtils.toDirect(key); ByteBuffer keyNioBuffer = LLUtils.toDirect(key);
if (databaseOptions.enableDbAssertionsWhenUsingAssertions()) { assert !databaseOptions.enableDbAssertionsWhenUsingAssertions() || keyNioBuffer.isDirect();
assert keyNioBuffer.isDirect();
}
// Create a direct result buffer because RocksDB works only with direct buffers // Create a direct result buffer because RocksDB works only with direct buffers
ByteBuf resultBuf = alloc.directBuffer(LLLocalDictionary.INITIAL_DIRECT_READ_BYTE_BUF_SIZE_BYTES); ByteBuf resultBuf = alloc.directBuffer(LLLocalDictionary.INITIAL_DIRECT_READ_BYTE_BUF_SIZE_BYTES);
try { try {
@ -388,6 +381,7 @@ public class LLLocalDictionary implements LLDictionary {
} }
} }
@SuppressWarnings("SameParameterValue")
private void dbPut(ColumnFamilyHandle cfh, @Nullable WriteOptions writeOptions, ByteBuf key, ByteBuf value) private void dbPut(ColumnFamilyHandle cfh, @Nullable WriteOptions writeOptions, ByteBuf key, ByteBuf value)
throws RocksDBException { throws RocksDBException {
try { try {
@ -399,15 +393,11 @@ public class LLLocalDictionary implements LLDictionary {
throw new RocksDBException("Value buffer must be direct"); throw new RocksDBException("Value buffer must be direct");
} }
var keyNioBuffer = LLUtils.toDirect(key); var keyNioBuffer = LLUtils.toDirect(key);
if (databaseOptions.enableDbAssertionsWhenUsingAssertions()) { assert !databaseOptions.enableDbAssertionsWhenUsingAssertions() || keyNioBuffer.isDirect();
assert keyNioBuffer.isDirect();
}
var valueNioBuffer = LLUtils.toDirect(value); var valueNioBuffer = LLUtils.toDirect(value);
if (databaseOptions.enableDbAssertionsWhenUsingAssertions()) { assert !databaseOptions.enableDbAssertionsWhenUsingAssertions() || valueNioBuffer.isDirect();
assert valueNioBuffer.isDirect();
}
db.put(cfh, Objects.requireNonNullElse(writeOptions, EMPTY_WRITE_OPTIONS), keyNioBuffer, valueNioBuffer); db.put(cfh, Objects.requireNonNullElse(writeOptions, EMPTY_WRITE_OPTIONS), keyNioBuffer, valueNioBuffer);
} else { } else {
db.put(cfh, Objects.requireNonNullElse(writeOptions, EMPTY_WRITE_OPTIONS), LLUtils.toArray(key), LLUtils.toArray(value)); db.put(cfh, Objects.requireNonNullElse(writeOptions, EMPTY_WRITE_OPTIONS), LLUtils.toArray(key), LLUtils.toArray(value));
@ -419,28 +409,22 @@ public class LLLocalDictionary implements LLDictionary {
} }
@Override @Override
public Mono<Boolean> isRangeEmpty(@Nullable LLSnapshot snapshot, LLRange range) { public Mono<Boolean> isRangeEmpty(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
try { return Mono.usingWhen(rangeMono,
return Mono range -> {
.defer(() -> {
if (range.isSingle()) { if (range.isSingle()) {
return this.containsKey(snapshot, range.getSingle().retain()); return this.containsKey(snapshot, Mono.just(range.getSingle()).map(ByteBuf::retain));
} else { } else {
return this.containsRange(snapshot, range.retain()); return this.containsRange(snapshot, Mono.just(range).map(LLRange::retain));
}
})
.map(isContained -> !isContained)
.doFirst(range::retain)
.doAfterTerminate(range::release);
} finally {
range.release();
} }
},
range -> Mono.fromRunnable(range::release)
).map(isContained -> !isContained);
} }
public Mono<Boolean> containsRange(@Nullable LLSnapshot snapshot, LLRange range) { public Mono<Boolean> containsRange(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
try { return Mono.usingWhen(rangeMono,
return Mono range -> runOnDb(() -> {
.fromCallable(() -> {
try (var readOpts = new ReadOptions(resolveSnapshot(snapshot))) { try (var readOpts = new ReadOptions(resolveSnapshot(snapshot))) {
readOpts.setVerifyChecksums(VERIFY_CHECKSUMS_WHEN_NOT_NEEDED); readOpts.setVerifyChecksums(VERIFY_CHECKSUMS_WHEN_NOT_NEEDED);
readOpts.setFillCache(false); readOpts.setFillCache(false);
@ -478,20 +462,14 @@ public class LLLocalDictionary implements LLDictionary {
return rocksIterator.isValid(); return rocksIterator.isValid();
} }
} }
}) }).onErrorMap(cause -> new IOException("Failed to read range " + range.toString(), cause)),
.onErrorMap(cause -> new IOException("Failed to read range " + range.toString(), cause)) range -> Mono.fromRunnable(range::release));
.subscribeOn(dbScheduler)
.doFirst(range::retain)
.doAfterTerminate(range::release);
} finally {
range.release();
}
} }
private Mono<Boolean> containsKey(@Nullable LLSnapshot snapshot, ByteBuf key) { private Mono<Boolean> containsKey(@Nullable LLSnapshot snapshot, Mono<ByteBuf> keyMono) {
try { return Mono.usingWhen(keyMono,
return Mono key -> runOnDb(() -> {
.fromCallable(() -> {
StampedLock lock; StampedLock lock;
long stamp; long stamp;
if (updateMode == UpdateMode.ALLOW) { if (updateMode == UpdateMode.ALLOW) {
@ -520,23 +498,18 @@ public class LLLocalDictionary implements LLDictionary {
lock.unlockRead(stamp); lock.unlockRead(stamp);
} }
} }
}) }).onErrorMap(cause -> new IOException("Failed to read " + LLUtils.toStringSafe(key), cause)),
.onErrorMap(cause -> new IOException("Failed to read " + LLUtils.toStringSafe(key), cause)) key -> Mono.fromRunnable(key::release)
.subscribeOn(dbScheduler) );
.doFirst(key::retain)
.doAfterTerminate(key::release);
} finally {
key.release();
}
} }
@Override @Override
public Mono<ByteBuf> put(ByteBuf key, ByteBuf value, LLDictionaryResultType resultType) { public Mono<ByteBuf> put(Mono<ByteBuf> keyMono, Mono<ByteBuf> valueMono, LLDictionaryResultType resultType) {
try { return Mono.usingWhen(keyMono,
return Mono key -> this
.defer(() -> getPreviousData(key.retain(), resultType)) .getPreviousData(Mono.just(key).map(ByteBuf::retain), resultType)
.concatWith(Mono .concatWith(Mono.usingWhen(valueMono,
.<ByteBuf>fromCallable(() -> { value -> this.<ByteBuf>runOnDb(() -> {
StampedLock lock; StampedLock lock;
long stamp; long stamp;
if (updateMode == UpdateMode.ALLOW) { if (updateMode == UpdateMode.ALLOW) {
@ -558,23 +531,12 @@ public class LLLocalDictionary implements LLDictionary {
lock.unlockWrite(stamp); lock.unlockWrite(stamp);
} }
} }
}) }),
.subscribeOn(dbScheduler) value -> Mono.fromRunnable(value::release)
.onErrorMap(cause -> new IOException("Failed to write " + LLUtils.toStringSafe(key), cause)) ).onErrorMap(cause -> new IOException("Failed to write " + LLUtils.toStringSafe(key), cause)))
) .singleOrEmpty(),
.singleOrEmpty() key -> Mono.fromRunnable(key::release)
.doFirst(() -> { );
key.retain();
value.retain();
})
.doAfterTerminate(() -> {
key.release();
value.release();
});
} finally {
key.release();
value.release();
}
} }
@Override @Override
@ -585,13 +547,12 @@ public class LLLocalDictionary implements LLDictionary {
// Remember to change also updateAndGetDelta() if you are modifying this function // Remember to change also updateAndGetDelta() if you are modifying this function
@SuppressWarnings("DuplicatedCode") @SuppressWarnings("DuplicatedCode")
@Override @Override
public Mono<ByteBuf> update(ByteBuf key, public Mono<ByteBuf> update(Mono<ByteBuf> keyMono,
Function<@Nullable ByteBuf, @Nullable ByteBuf> updater, Function<@Nullable ByteBuf, @Nullable ByteBuf> updater,
UpdateReturnMode updateReturnMode, UpdateReturnMode updateReturnMode,
boolean existsAlmostCertainly) { boolean existsAlmostCertainly) {
try { return Mono.usingWhen(keyMono,
return Mono key -> runOnDb(() -> {
.fromCallable(() -> {
if (updateMode == UpdateMode.DISALLOW) { if (updateMode == UpdateMode.DISALLOW) {
throw new UnsupportedOperationException("update() is disallowed"); throw new UnsupportedOperationException("update() is disallowed");
} }
@ -704,25 +665,19 @@ public class LLLocalDictionary implements LLDictionary {
lock.unlock(stamp); lock.unlock(stamp);
} }
} }
}) }).onErrorMap(cause -> new IOException("Failed to read or write " + LLUtils.toStringSafe(key), cause)),
.onErrorMap(cause -> new IOException("Failed to read or write " + LLUtils.toStringSafe(key), cause)) key -> Mono.fromRunnable(key::release)
.subscribeOn(dbScheduler) );
.doFirst(key::retain)
.doAfterTerminate(key::release);
} finally {
key.release();
}
} }
// Remember to change also update() if you are modifying this function // Remember to change also update() if you are modifying this function
@SuppressWarnings("DuplicatedCode") @SuppressWarnings("DuplicatedCode")
@Override @Override
public Mono<Delta<ByteBuf>> updateAndGetDelta(ByteBuf key, public Mono<Delta<ByteBuf>> updateAndGetDelta(Mono<ByteBuf> keyMono,
Function<@Nullable ByteBuf, @Nullable ByteBuf> updater, Function<@Nullable ByteBuf, @Nullable ByteBuf> updater,
boolean existsAlmostCertainly) { boolean existsAlmostCertainly) {
try { return Mono.usingWhen(keyMono,
return Mono key -> this.runOnDb(() -> {
.fromCallable(() -> {
if (updateMode == UpdateMode.DISALLOW) throw new UnsupportedOperationException("update() is disallowed"); if (updateMode == UpdateMode.DISALLOW) throw new UnsupportedOperationException("update() is disallowed");
StampedLock lock; StampedLock lock;
long stamp; long stamp;
@ -760,11 +715,10 @@ public class LLLocalDictionary implements LLDictionary {
ByteBuf prevDataToSendToUpdater = prevData == null ? null : prevData.retainedSlice(); ByteBuf prevDataToSendToUpdater = prevData == null ? null : prevData.retainedSlice();
try { try {
newData = updater.apply(prevDataToSendToUpdater == null ? null : prevDataToSendToUpdater.retain()); newData = updater.apply(prevDataToSendToUpdater == null ? null : prevDataToSendToUpdater.retain());
if (databaseOptions.enableDbAssertionsWhenUsingAssertions()) { assert !databaseOptions.enableDbAssertionsWhenUsingAssertions()
assert prevDataToSendToUpdater == null || prevDataToSendToUpdater == null
|| prevDataToSendToUpdater.readerIndex() == 0 || prevDataToSendToUpdater.readerIndex() == 0
|| !prevDataToSendToUpdater.isReadable(); || !prevDataToSendToUpdater.isReadable();
}
} finally { } finally {
if (prevDataToSendToUpdater != null) { if (prevDataToSendToUpdater != null) {
prevDataToSendToUpdater.release(); prevDataToSendToUpdater.release();
@ -827,14 +781,9 @@ public class LLLocalDictionary implements LLDictionary {
lock.unlock(stamp); lock.unlock(stamp);
} }
} }
}) }).onErrorMap(cause -> new IOException("Failed to read or write " + LLUtils.toStringSafe(key), cause)),
.onErrorMap(cause -> new IOException("Failed to read or write " + LLUtils.toStringSafe(key), cause)) key -> Mono.fromRunnable(key::release)
.subscribeOn(dbScheduler) );
.doFirst(key::retain)
.doAfterTerminate(key::release);
} finally {
key.release();
}
} }
private void dbDelete(ColumnFamilyHandle cfh, @Nullable WriteOptions writeOptions, ByteBuf key) private void dbDelete(ColumnFamilyHandle cfh, @Nullable WriteOptions writeOptions, ByteBuf key)
@ -855,12 +804,12 @@ public class LLLocalDictionary implements LLDictionary {
} }
@Override @Override
public Mono<ByteBuf> remove(ByteBuf key, LLDictionaryResultType resultType) { public Mono<ByteBuf> remove(Mono<ByteBuf> keyMono, LLDictionaryResultType resultType) {
try { return Mono.usingWhen(keyMono,
return Mono key -> this
.defer(() -> getPreviousData(key.retain(), resultType)) .getPreviousData(Mono.just(key).map(ByteBuf::retain), resultType)
.concatWith(Mono .concatWith(this
.fromCallable(() -> { .<ByteBuf>runOnDb(() -> {
StampedLock lock; StampedLock lock;
long stamp; long stamp;
if (updateMode == UpdateMode.ALLOW) { if (updateMode == UpdateMode.ALLOW) {
@ -884,29 +833,21 @@ public class LLLocalDictionary implements LLDictionary {
} }
}) })
.onErrorMap(cause -> new IOException("Failed to delete " + LLUtils.toStringSafe(key), cause)) .onErrorMap(cause -> new IOException("Failed to delete " + LLUtils.toStringSafe(key), cause))
.subscribeOn(dbScheduler)
.then(Mono.empty())
) )
.singleOrEmpty() .singleOrEmpty(),
.doFirst(key::retain) key -> Mono.fromCallable(key::release));
.doAfterTerminate(key::release);
} finally {
key.release();
}
} }
private Mono<ByteBuf> getPreviousData(ByteBuf key, LLDictionaryResultType resultType) { private Mono<ByteBuf> getPreviousData(Mono<ByteBuf> keyMono, LLDictionaryResultType resultType) {
try {
return Mono return Mono
.defer(() -> switch (resultType) { .usingWhen(keyMono,
key -> switch (resultType) {
case PREVIOUS_VALUE_EXISTENCE -> this case PREVIOUS_VALUE_EXISTENCE -> this
.containsKey(null, key.retain()) .containsKey(null, Mono.just(key).map(ByteBuf::retain))
.single() .single()
.map(LLUtils::booleanToResponseByteBuffer) .map(LLUtils::booleanToResponseByteBuffer)
.doAfterTerminate(() -> { .doAfterTerminate(() -> {
if (databaseOptions.enableDbAssertionsWhenUsingAssertions()) { assert !databaseOptions.enableDbAssertionsWhenUsingAssertions() || key.refCnt() > 0;
assert key.refCnt() > 0;
}
}); });
case PREVIOUS_VALUE -> Mono case PREVIOUS_VALUE -> Mono
.fromCallable(() -> { .fromCallable(() -> {
@ -932,9 +873,7 @@ public class LLLocalDictionary implements LLDictionary {
try { try {
return dbGet(cfh, null, key.retain(), true); return dbGet(cfh, null, key.retain(), true);
} finally { } finally {
if (databaseOptions.enableDbAssertionsWhenUsingAssertions()) { assert !databaseOptions.enableDbAssertionsWhenUsingAssertions() || key.refCnt() > 0;
assert key.refCnt() > 0;
}
} }
} }
} else { } else {
@ -949,12 +888,9 @@ public class LLLocalDictionary implements LLDictionary {
.onErrorMap(cause -> new IOException("Failed to read " + LLUtils.toStringSafe(key), cause)) .onErrorMap(cause -> new IOException("Failed to read " + LLUtils.toStringSafe(key), cause))
.subscribeOn(dbScheduler); .subscribeOn(dbScheduler);
case VOID -> Mono.empty(); case VOID -> Mono.empty();
}) },
.doFirst(key::retain) key -> Mono.fromRunnable(key::release)
.doAfterTerminate(key::release); );
} finally {
key.release();
}
} }
@Override @Override
@ -1284,61 +1220,51 @@ public class LLLocalDictionary implements LLDictionary {
@Override @Override
public Flux<Entry<ByteBuf, ByteBuf>> getRange(@Nullable LLSnapshot snapshot, public Flux<Entry<ByteBuf, ByteBuf>> getRange(@Nullable LLSnapshot snapshot,
LLRange range, Mono<LLRange> rangeMono,
boolean existsAlmostCertainly) { boolean existsAlmostCertainly) {
try { return Flux.usingWhen(rangeMono,
return Flux range -> {
.defer(() -> {
if (range.isSingle()) { if (range.isSingle()) {
return getRangeSingle(snapshot, range.getMin().retain(), existsAlmostCertainly); return getRangeSingle(snapshot, Mono.just(range.getMin()).map(ByteBuf::retain), existsAlmostCertainly);
} else { } else {
return getRangeMulti(snapshot, range.retain()); return getRangeMulti(snapshot, Mono.just(range).map(LLRange::retain));
}
})
.doFirst(range::retain)
.doAfterTerminate(range::release);
} finally {
range.release();
} }
},
range -> Mono.fromRunnable(range::release)
);
} }
@Override @Override
public Flux<List<Entry<ByteBuf, ByteBuf>>> getRangeGrouped(@Nullable LLSnapshot snapshot, public Flux<List<Entry<ByteBuf, ByteBuf>>> getRangeGrouped(@Nullable LLSnapshot snapshot,
LLRange range, Mono<LLRange> rangeMono,
int prefixLength, boolean existsAlmostCertainly) { int prefixLength, boolean existsAlmostCertainly) {
try { return Flux.usingWhen(rangeMono,
return Flux range -> {
.defer(() -> {
if (range.isSingle()) { if (range.isSingle()) {
return getRangeSingle(snapshot, range.getMin().retain(), existsAlmostCertainly).map(List::of); var rangeSingleMono = Mono.just(range.getMin()).map(ByteBuf::retain);
return getRangeSingle(snapshot, rangeSingleMono, existsAlmostCertainly).map(List::of);
} else { } else {
return getRangeMultiGrouped(snapshot, range.retain(), prefixLength); return getRangeMultiGrouped(snapshot, Mono.just(range).map(LLRange::retain), prefixLength);
}
})
.doFirst(range::retain)
.doAfterTerminate(range::release);
} finally {
range.release();
} }
},
range -> Mono.fromRunnable(range::release)
);
} }
private Flux<Entry<ByteBuf, ByteBuf>> getRangeSingle(LLSnapshot snapshot, ByteBuf key, boolean existsAlmostCertainly) { private Flux<Entry<ByteBuf, ByteBuf>> getRangeSingle(LLSnapshot snapshot,
try { Mono<ByteBuf> keyMono,
return Mono boolean existsAlmostCertainly) {
.defer(() -> this.get(snapshot, key.retain(), existsAlmostCertainly)) return Flux.usingWhen(keyMono,
.map(value -> Map.entry(key.retain(), value)) key -> this
.flux() .get(snapshot, Mono.just(key).map(ByteBuf::retain), existsAlmostCertainly)
.doFirst(key::retain) .map(value -> Map.entry(key.retain(), value)),
.doAfterTerminate(key::release); key -> Mono.fromRunnable(key::release)
} finally { );
key.release();
}
} }
@SuppressWarnings("Convert2MethodRef") private Flux<Entry<ByteBuf, ByteBuf>> getRangeMulti(LLSnapshot snapshot, Mono<LLRange> rangeMono) {
private Flux<Entry<ByteBuf, ByteBuf>> getRangeMulti(LLSnapshot snapshot, LLRange range) { return Flux.usingWhen(rangeMono,
try { range -> Flux
return Flux
.using( .using(
() -> new LLLocalEntryReactiveRocksIterator(db, () -> new LLLocalEntryReactiveRocksIterator(db,
alloc, alloc,
@ -1348,27 +1274,18 @@ public class LLLocalDictionary implements LLDictionary {
resolveSnapshot(snapshot), resolveSnapshot(snapshot),
getRangeMultiDebugName getRangeMultiDebugName
), ),
llLocalEntryReactiveRocksIterator -> llLocalEntryReactiveRocksIterator.flux(), llLocalEntryReactiveRocksIterator -> llLocalEntryReactiveRocksIterator
.flux()
.subscribeOn(dbScheduler),
LLLocalReactiveRocksIterator::release LLLocalReactiveRocksIterator::release
) ),
.doOnDiscard(Entry.class, entry -> { range -> Mono.fromRunnable(range::release)
//noinspection unchecked );
var castedEntry = (Entry<ByteBuf, ByteBuf>) entry;
castedEntry.getKey().release();
castedEntry.getValue().release();
})
.subscribeOn(dbScheduler)
.doFirst(range::retain)
.doAfterTerminate(range::release);
} finally {
range.release();
}
} }
@SuppressWarnings("Convert2MethodRef") private Flux<List<Entry<ByteBuf, ByteBuf>>> getRangeMultiGrouped(LLSnapshot snapshot, Mono<LLRange> rangeMono, int prefixLength) {
private Flux<List<Entry<ByteBuf, ByteBuf>>> getRangeMultiGrouped(LLSnapshot snapshot, LLRange range, int prefixLength) { return Flux.usingWhen(rangeMono,
try { range -> Flux
return Flux
.using( .using(
() -> new LLLocalGroupedEntryReactiveRocksIterator(db, () -> new LLLocalGroupedEntryReactiveRocksIterator(db,
alloc, alloc,
@ -1379,39 +1296,35 @@ public class LLLocalDictionary implements LLDictionary {
resolveSnapshot(snapshot), resolveSnapshot(snapshot),
"getRangeMultiGrouped" "getRangeMultiGrouped"
), ),
llLocalGroupedEntryReactiveRocksIterator -> llLocalGroupedEntryReactiveRocksIterator.flux(), reactiveRocksIterator -> reactiveRocksIterator
.flux()
.subscribeOn(dbScheduler),
LLLocalGroupedReactiveRocksIterator::release LLLocalGroupedReactiveRocksIterator::release
) ),
.subscribeOn(dbScheduler) range -> Mono.fromRunnable(range::release)
.doFirst(range::retain) );
.doAfterTerminate(range::release);
} finally {
range.release();
}
} }
@Override @Override
public Flux<ByteBuf> getRangeKeys(@Nullable LLSnapshot snapshot, LLRange range) { public Flux<ByteBuf> getRangeKeys(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
try { return Flux.usingWhen(rangeMono,
return Flux range -> {
.defer(() -> {
if (range.isSingle()) { if (range.isSingle()) {
return this.getRangeKeysSingle(snapshot, range.getMin().retain()); return this.getRangeKeysSingle(snapshot, Mono.just(range.getMin()).map(ByteBuf::retain));
} else { } else {
return this.getRangeKeysMulti(snapshot, range.retain()); return this.getRangeKeysMulti(snapshot, Mono.just(range).map(LLRange::retain));
}
})
.doFirst(range::retain)
.doAfterTerminate(range::release);
} finally {
range.release();
} }
},
range -> Mono.fromRunnable(range::release)
);
} }
@Override @Override
public Flux<List<ByteBuf>> getRangeKeysGrouped(@Nullable LLSnapshot snapshot, LLRange range, int prefixLength) { public Flux<List<ByteBuf>> getRangeKeysGrouped(@Nullable LLSnapshot snapshot,
try { Mono<LLRange> rangeMono,
return Flux int prefixLength) {
return Flux.usingWhen(rangeMono,
range -> Flux
.using( .using(
() -> new LLLocalGroupedKeyReactiveRocksIterator(db, () -> new LLLocalGroupedKeyReactiveRocksIterator(db,
alloc, alloc,
@ -1421,21 +1334,18 @@ public class LLLocalDictionary implements LLDictionary {
databaseOptions.allowNettyDirect(), databaseOptions.allowNettyDirect(),
resolveSnapshot(snapshot), resolveSnapshot(snapshot),
"getRangeKeysGrouped" "getRangeKeysGrouped"
), ), reactiveRocksIterator -> reactiveRocksIterator.flux()
LLLocalGroupedReactiveRocksIterator::flux, .subscribeOn(dbScheduler),
LLLocalGroupedReactiveRocksIterator::release LLLocalGroupedReactiveRocksIterator::release
) ),
.subscribeOn(dbScheduler) range -> Mono.fromRunnable(range::release)
.doFirst(range::retain) );
.doAfterTerminate(range::release);
} finally {
range.release();
}
} }
@Override @Override
public Flux<BadBlock> badBlocks(LLRange range) { public Flux<BadBlock> badBlocks(Mono<LLRange> rangeMono) {
return Flux return Flux.usingWhen(rangeMono,
range -> Flux
.<BadBlock>create(sink -> { .<BadBlock>create(sink -> {
try (var ro = new ReadOptions(getReadOptions(null))) { try (var ro = new ReadOptions(getReadOptions(null))) {
ro.setFillCache(false); ro.setFillCache(false);
@ -1470,15 +1380,15 @@ public class LLLocalDictionary implements LLDictionary {
sink.error(ex); sink.error(ex);
} }
}) })
.subscribeOn(dbScheduler) .subscribeOn(dbScheduler),
.doFirst(range::retain) range -> Mono.fromRunnable(range::release)
.doAfterTerminate(range::release); );
} }
@Override @Override
public Flux<ByteBuf> getRangeKeyPrefixes(@Nullable LLSnapshot snapshot, LLRange range, int prefixLength) { public Flux<ByteBuf> getRangeKeyPrefixes(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono, int prefixLength) {
try { return Flux.usingWhen(rangeMono,
return Flux range -> Flux
.using( .using(
() -> new LLLocalKeyPrefixReactiveRocksIterator(db, () -> new LLLocalKeyPrefixReactiveRocksIterator(db,
alloc, alloc,
@ -1493,18 +1403,15 @@ public class LLLocalDictionary implements LLDictionary {
LLLocalKeyPrefixReactiveRocksIterator::flux, LLLocalKeyPrefixReactiveRocksIterator::flux,
LLLocalKeyPrefixReactiveRocksIterator::release LLLocalKeyPrefixReactiveRocksIterator::release
) )
.subscribeOn(dbScheduler) .subscribeOn(dbScheduler),
.doFirst(range::retain) range -> Mono.fromRunnable(range::release)
.doAfterTerminate(range::release); );
} finally {
range.release();
}
} }
private Flux<ByteBuf> getRangeKeysSingle(LLSnapshot snapshot, ByteBuf key) { private Flux<ByteBuf> getRangeKeysSingle(LLSnapshot snapshot, Mono<ByteBuf> keyMono) {
try { return Flux.usingWhen(keyMono,
return Mono key -> this
.defer(() -> this.containsKey(snapshot, key.retain())) .containsKey(snapshot, Mono.just(key).map(ByteBuf::retain))
.flux() .flux()
.<ByteBuf>handle((contains, sink) -> { .<ByteBuf>handle((contains, sink) -> {
if (contains) { if (contains) {
@ -1513,18 +1420,15 @@ public class LLLocalDictionary implements LLDictionary {
sink.complete(); sink.complete();
} }
}) })
.doOnDiscard(ByteBuf.class, ReferenceCounted::release) .doOnDiscard(ByteBuf.class, ReferenceCounted::release),
.doFirst(key::retain) key -> Mono.fromRunnable(key::release)
.doAfterTerminate(key::release); );
} finally {
key.release();
}
} }
@SuppressWarnings("Convert2MethodRef") @SuppressWarnings("Convert2MethodRef")
private Flux<ByteBuf> getRangeKeysMulti(LLSnapshot snapshot, LLRange range) { private Flux<ByteBuf> getRangeKeysMulti(LLSnapshot snapshot, Mono<LLRange> rangeMono) {
try { return Flux.usingWhen(rangeMono,
return Flux range -> Flux
.using( .using(
() -> new LLLocalKeyReactiveRocksIterator(db, () -> new LLLocalKeyReactiveRocksIterator(db,
alloc, alloc,
@ -1538,17 +1442,15 @@ public class LLLocalDictionary implements LLDictionary {
LLLocalReactiveRocksIterator::release LLLocalReactiveRocksIterator::release
) )
.doOnDiscard(ByteBuf.class, ReferenceCounted::release) .doOnDiscard(ByteBuf.class, ReferenceCounted::release)
.subscribeOn(dbScheduler) .subscribeOn(dbScheduler),
.doFirst(range::retain) range -> Mono.fromRunnable(range::release)
.doAfterTerminate(range::release); );
} finally {
range.release();
}
} }
@Override @Override
public Mono<Void> setRange(LLRange range, Flux<Entry<ByteBuf, ByteBuf>> entries) { public Mono<Void> setRange(Mono<LLRange> rangeMono, Flux<Entry<ByteBuf, ByteBuf>> entries) {
try { return Mono.usingWhen(rangeMono,
range -> {
if (USE_WINDOW_IN_SET_RANGE) { if (USE_WINDOW_IN_SET_RANGE) {
return Mono return Mono
.<Void>fromCallable(() -> { .<Void>fromCallable(() -> {
@ -1673,17 +1575,15 @@ public class LLLocalDictionary implements LLDictionary {
) )
) )
.then() .then()
.onErrorMap(cause -> new IOException("Failed to write range", cause)) .onErrorMap(cause -> new IOException("Failed to write range", cause));
.doFirst(range::retain)
.doAfterTerminate(range::release);
} else { } else {
if (USE_WRITE_BATCHES_IN_SET_RANGE) { if (USE_WRITE_BATCHES_IN_SET_RANGE) {
return Mono.fromCallable(() -> { return Mono.fromCallable(() -> {
throw new UnsupportedOperationException("Can't use write batches in setRange without window. Please fix params"); throw new UnsupportedOperationException("Can't use write batches in setRange without window. Please fix params");
}); });
} }
return Flux return this
.defer(() -> this.getRange(null, range.retain(), false)) .getRange(null, Mono.just(range).map(LLRange::retain), false)
.flatMap(oldValue -> Mono .flatMap(oldValue -> Mono
.<Void>fromCallable(() -> { .<Void>fromCallable(() -> {
try { try {
@ -1697,20 +1597,29 @@ public class LLLocalDictionary implements LLDictionary {
.subscribeOn(dbScheduler) .subscribeOn(dbScheduler)
) )
.then(entries .then(entries
.flatMap(entry -> this.put(entry.getKey(), entry.getValue(), LLDictionaryResultType.VOID)) .flatMap(entry -> Mono.using(
.doOnNext(ReferenceCounted::release) () -> entry,
releasableEntry -> this
.put(Mono.just(entry.getKey()).map(ByteBuf::retain),
Mono.just(entry.getValue()).map(ByteBuf::retain),
LLDictionaryResultType.VOID
)
.doOnNext(ReferenceCounted::release),
releasableEntry -> {
releasableEntry.getKey().release();
releasableEntry.getValue().release();
})
)
.then(Mono.<Void>empty()) .then(Mono.<Void>empty())
) )
.onErrorMap(cause -> new IOException("Failed to write range", cause)) .onErrorMap(cause -> new IOException("Failed to write range", cause));
.doFirst(range::retain)
.doAfterTerminate(range::release);
}
} finally {
range.release();
} }
},
range -> Mono.fromRunnable(range::release)
);
} }
//todo: this is broken, check why //todo: this is broken, check why. (is this still true?)
private void deleteSmallRangeWriteBatch(CappedWriteBatch writeBatch, LLRange range) private void deleteSmallRangeWriteBatch(CappedWriteBatch writeBatch, LLRange range)
throws RocksDBException { throws RocksDBException {
try (var readOpts = new ReadOptions(getReadOptions(null))) { try (var readOpts = new ReadOptions(getReadOptions(null))) {
@ -1932,18 +1841,15 @@ public class LLLocalDictionary implements LLDictionary {
} }
@Override @Override
public Mono<Long> sizeRange(@Nullable LLSnapshot snapshot, LLRange range, boolean fast) { public Mono<Long> sizeRange(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono, boolean fast) {
try { return Mono.usingWhen(rangeMono,
return Mono range -> {
.defer(() -> {
if (range.isAll()) { if (range.isAll()) {
return Mono return this
.fromCallable(() -> fast ? fastSizeAll(snapshot) : exactSizeAll(snapshot)) .runOnDb(() -> fast ? fastSizeAll(snapshot) : exactSizeAll(snapshot))
.onErrorMap(IOException::new) .onErrorMap(IOException::new);
.subscribeOn(dbScheduler);
} else { } else {
return Mono return runOnDb(() -> {
.fromCallable(() -> {
try (var readOpts = new ReadOptions(resolveSnapshot(snapshot))) { try (var readOpts = new ReadOptions(resolveSnapshot(snapshot))) {
readOpts.setFillCache(false); readOpts.setFillCache(false);
readOpts.setVerifyChecksums(VERIFY_CHECKSUMS_WHEN_NOT_NEEDED); readOpts.setVerifyChecksums(VERIFY_CHECKSUMS_WHEN_NOT_NEEDED);
@ -1998,24 +1904,17 @@ public class LLLocalDictionary implements LLDictionary {
minBound.release(); minBound.release();
} }
} }
}) }).onErrorMap(cause -> new IOException("Failed to get size of range " + range, cause));
.onErrorMap(cause -> new IOException("Failed to get size of range "
+ range, cause))
.subscribeOn(dbScheduler);
}
})
.doFirst(range::retain)
.doAfterTerminate(range::release);
} finally {
range.release();
} }
},
range -> Mono.fromRunnable(range::release)
);
} }
@Override @Override
public Mono<Entry<ByteBuf, ByteBuf>> getOne(@Nullable LLSnapshot snapshot, LLRange range) { public Mono<Entry<ByteBuf, ByteBuf>> getOne(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
try { return Mono.usingWhen(rangeMono,
return Mono range -> runOnDb(() -> {
.fromCallable(() -> {
try (var readOpts = new ReadOptions(resolveSnapshot(snapshot))) { try (var readOpts = new ReadOptions(resolveSnapshot(snapshot))) {
ReleasableSlice minBound; ReleasableSlice minBound;
if (range.hasMin()) { if (range.hasMin()) {
@ -2067,20 +1966,15 @@ public class LLLocalDictionary implements LLDictionary {
minBound.release(); minBound.release();
} }
} }
}) }),
.subscribeOn(dbScheduler) range -> Mono.fromRunnable(range::release)
.doFirst(range::retain) );
.doAfterTerminate(range::release);
} finally {
range.release();
}
} }
@Override @Override
public Mono<ByteBuf> getOneKey(@Nullable LLSnapshot snapshot, LLRange range) { public Mono<ByteBuf> getOneKey(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
try { return Mono.usingWhen(rangeMono,
return Mono range -> runOnDb(() -> {
.fromCallable(() -> {
try (var readOpts = new ReadOptions(resolveSnapshot(snapshot))) { try (var readOpts = new ReadOptions(resolveSnapshot(snapshot))) {
ReleasableSlice minBound; ReleasableSlice minBound;
if (range.hasMin()) { if (range.hasMin()) {
@ -2124,13 +2018,9 @@ public class LLLocalDictionary implements LLDictionary {
minBound.release(); minBound.release();
} }
} }
}) }),
.subscribeOn(dbScheduler) range -> Mono.fromRunnable(range::release)
.doFirst(range::retain) );
.doAfterTerminate(range::release);
} finally {
range.release();
}
} }
private long fastSizeAll(@Nullable LLSnapshot snapshot) throws RocksDBException { private long fastSizeAll(@Nullable LLSnapshot snapshot) throws RocksDBException {
@ -2242,10 +2132,9 @@ public class LLLocalDictionary implements LLDictionary {
} }
@Override @Override
public Mono<Entry<ByteBuf, ByteBuf>> removeOne(LLRange range) { public Mono<Entry<ByteBuf, ByteBuf>> removeOne(Mono<LLRange> rangeMono) {
try { return Mono.usingWhen(rangeMono,
return Mono range -> runOnDb(() -> {
.fromCallable(() -> {
try (var readOpts = new ReadOptions(getReadOptions(null))) { try (var readOpts = new ReadOptions(getReadOptions(null))) {
ReleasableSlice minBound; ReleasableSlice minBound;
if (range.hasMin()) { if (range.hasMin()) {
@ -2289,14 +2178,9 @@ public class LLLocalDictionary implements LLDictionary {
minBound.release(); minBound.release();
} }
} }
}) }).onErrorMap(cause -> new IOException("Failed to delete " + range.toString(), cause)),
.onErrorMap(cause -> new IOException("Failed to delete " + range.toString(), cause)) range -> Mono.fromRunnable(range::release)
.subscribeOn(dbScheduler) );
.doFirst(range::retain)
.doAfterTerminate(range::release);
} finally {
range.release();
}
} }
@NotNull @NotNull