Fix some memory leaks
This commit is contained in:
parent
82f8e91e99
commit
d253111233
@ -538,23 +538,6 @@ public class LLUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: remove this ugly method
|
|
||||||
/**
|
|
||||||
* cleanup resource
|
|
||||||
* @param cleanupOnSuccess if true the resource will be cleaned up if the function is successful
|
|
||||||
*/
|
|
||||||
public static <U, T extends Resource<T>> Mono<U> usingSend(Mono<Send<T>> resourceSupplier,
|
|
||||||
Function<Send<T>, Mono<U>> resourceClosure,
|
|
||||||
boolean cleanupOnSuccess) {
|
|
||||||
return Mono.usingWhen(resourceSupplier, resourceClosure, r -> {
|
|
||||||
if (cleanupOnSuccess) {
|
|
||||||
return Mono.fromRunnable(() -> r.close());
|
|
||||||
} else {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}, (r, ex) -> Mono.fromRunnable(() -> r.close()), r -> Mono.fromRunnable(() -> r.close()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: remove this ugly method
|
// todo: remove this ugly method
|
||||||
/**
|
/**
|
||||||
* cleanup resource
|
* cleanup resource
|
||||||
@ -613,36 +596,6 @@ public class LLUtils {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: remove this ugly method
|
|
||||||
/**
|
|
||||||
* cleanup resource
|
|
||||||
* @param cleanupOnSuccess if true the resource will be cleaned up if the function is successful
|
|
||||||
*/
|
|
||||||
public static <U, T extends Resource<T>, V extends T> Flux<U> usingEachResource(Flux<V> resourceSupplier,
|
|
||||||
Function<V, Mono<U>> resourceClosure,
|
|
||||||
boolean cleanupOnSuccess) {
|
|
||||||
return resourceSupplier
|
|
||||||
.concatMap(resource -> Mono.usingWhen(Mono.just(resource), resourceClosure, r -> {
|
|
||||||
if (cleanupOnSuccess) {
|
|
||||||
return Mono.fromRunnable(() -> {
|
|
||||||
if (r.isAccessible()) {
|
|
||||||
r.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}, (r, ex) -> Mono.fromRunnable(() -> {
|
|
||||||
if (r.isAccessible()) {
|
|
||||||
r.close();
|
|
||||||
}
|
|
||||||
}), r -> Mono.fromRunnable(() -> {
|
|
||||||
if (r.isAccessible()) {
|
|
||||||
r.close();
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: remove this ugly method
|
// todo: remove this ugly method
|
||||||
/**
|
/**
|
||||||
* cleanup resource
|
* cleanup resource
|
||||||
@ -668,35 +621,6 @@ public class LLUtils {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: remove this ugly method
|
|
||||||
/**
|
|
||||||
* cleanup resource
|
|
||||||
* @param cleanupOnSuccess if true the resource will be cleaned up if the function is successful
|
|
||||||
*/
|
|
||||||
public static <U, T extends Resource<T>> Flux<U> usingSendResources(Mono<Send<T>> resourceSupplier,
|
|
||||||
Function<T, Flux<U>> resourceClosure,
|
|
||||||
boolean cleanupOnSuccess) {
|
|
||||||
return Flux.usingWhen(resourceSupplier.map(Send::receive), resourceClosure, r -> {
|
|
||||||
if (cleanupOnSuccess) {
|
|
||||||
return Mono.fromRunnable(() -> {
|
|
||||||
if (r.isAccessible()) {
|
|
||||||
r.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
|
||||||
}, (r, ex) -> Mono.fromRunnable(() -> {
|
|
||||||
if (r.isAccessible()) {
|
|
||||||
r.close();
|
|
||||||
}
|
|
||||||
}), r -> Mono.fromRunnable(() -> {
|
|
||||||
if (r.isAccessible()) {
|
|
||||||
r.close();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isSet(ScoreDoc[] scoreDocs) {
|
public static boolean isSet(ScoreDoc[] scoreDocs) {
|
||||||
for (ScoreDoc scoreDoc : scoreDocs) {
|
for (ScoreDoc scoreDoc : scoreDocs) {
|
||||||
if (scoreDoc == null) {
|
if (scoreDoc == null) {
|
||||||
@ -873,28 +797,40 @@ public class LLUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Mono<Buffer> resolveLLDelta(Mono<LLDelta> prev, UpdateReturnMode updateReturnMode) {
|
public static Mono<Buffer> resolveLLDelta(Mono<LLDelta> prev, UpdateReturnMode updateReturnMode) {
|
||||||
return prev.handle((deltaToReceive, sink) -> {
|
return prev.handle((delta, sink) -> {
|
||||||
try (var delta = deltaToReceive) {
|
final Buffer previous = delta.previousUnsafe();
|
||||||
switch (updateReturnMode) {
|
final Buffer current = delta.currentUnsafe();
|
||||||
case GET_NEW_VALUE -> {
|
switch (updateReturnMode) {
|
||||||
var current = delta.currentUnsafe();
|
case GET_NEW_VALUE -> {
|
||||||
if (current != null) {
|
if (previous != null && previous.isAccessible()) {
|
||||||
sink.next(current.copy());
|
previous.close();
|
||||||
} else {
|
|
||||||
sink.complete();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case GET_OLD_VALUE -> {
|
if (current != null) {
|
||||||
var previous = delta.previousUnsafe();
|
sink.next(current);
|
||||||
if (previous != null) {
|
} else {
|
||||||
sink.next(previous.copy());
|
sink.complete();
|
||||||
} else {
|
|
||||||
sink.complete();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case NOTHING -> sink.complete();
|
|
||||||
default -> sink.error(new IllegalStateException());
|
|
||||||
}
|
}
|
||||||
|
case GET_OLD_VALUE -> {
|
||||||
|
if (current != null && current.isAccessible()) {
|
||||||
|
current.close();
|
||||||
|
}
|
||||||
|
if (previous != null) {
|
||||||
|
sink.next(previous);
|
||||||
|
} else {
|
||||||
|
sink.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case NOTHING -> {
|
||||||
|
if (previous != null && previous.isAccessible()) {
|
||||||
|
previous.close();
|
||||||
|
}
|
||||||
|
if (current != null && current.isAccessible()) {
|
||||||
|
current.close();
|
||||||
|
}
|
||||||
|
sink.complete();
|
||||||
|
}
|
||||||
|
default -> sink.error(new IllegalStateException());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -985,7 +921,7 @@ public class LLUtils {
|
|||||||
private static void onNextDropped(Object next) {
|
private static void onNextDropped(Object next) {
|
||||||
if (next instanceof Send<?> send) {
|
if (next instanceof Send<?> send) {
|
||||||
send.close();
|
send.close();
|
||||||
} else if (next instanceof Resource<?> resource) {
|
} else if (next instanceof Resource<?> resource && resource.isAccessible()) {
|
||||||
resource.close();
|
resource.close();
|
||||||
} else if (next instanceof Iterable<?> iterable) {
|
} else if (next instanceof Iterable<?> iterable) {
|
||||||
iterable.forEach(LLUtils::onNextDropped);
|
iterable.forEach(LLUtils::onNextDropped);
|
||||||
@ -995,12 +931,6 @@ public class LLUtils {
|
|||||||
if (rocksObj.isOwningHandle()) {
|
if (rocksObj.isOwningHandle()) {
|
||||||
rocksObj.close();
|
rocksObj.close();
|
||||||
}
|
}
|
||||||
} else if (next instanceof UpdateAtomicResultDelta delta) {
|
|
||||||
delta.delta().close();
|
|
||||||
} else if (next instanceof UpdateAtomicResultCurrent cur) {
|
|
||||||
cur.current().close();
|
|
||||||
} else if (next instanceof UpdateAtomicResultPrevious cur) {
|
|
||||||
cur.previous().close();
|
|
||||||
} else if (next instanceof Optional<?> optional) {
|
} else if (next instanceof Optional<?> optional) {
|
||||||
optional.ifPresent(LLUtils::onNextDropped);
|
optional.ifPresent(LLUtils::onNextDropped);
|
||||||
} else if (next instanceof Map.Entry<?, ?> entry) {
|
} else if (next instanceof Map.Entry<?, ?> entry) {
|
||||||
|
@ -63,8 +63,7 @@ public class DatabaseMapDictionary<T, U> extends DatabaseMapDictionaryDeep<T, U,
|
|||||||
SerializerFixedBinaryLength<T> keySerializer,
|
SerializerFixedBinaryLength<T> keySerializer,
|
||||||
Serializer<U> valueSerializer,
|
Serializer<U> valueSerializer,
|
||||||
Runnable onClose) {
|
Runnable onClose) {
|
||||||
return new DatabaseMapDictionary<>(dictionary, null, keySerializer,
|
return new DatabaseMapDictionary<>(dictionary, null, keySerializer, valueSerializer, onClose);
|
||||||
valueSerializer, onClose);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T, U> DatabaseMapDictionary<T, U> tail(LLDictionary dictionary,
|
public static <T, U> DatabaseMapDictionary<T, U> tail(LLDictionary dictionary,
|
||||||
@ -128,33 +127,31 @@ public class DatabaseMapDictionary<T, U> extends DatabaseMapDictionaryDeep<T, U,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void deserializeValue(T keySuffix, Buffer value, SynchronousSink<U> sink) {
|
private void deserializeValue(T keySuffix, Buffer value, SynchronousSink<U> sink) {
|
||||||
try (value) {
|
try {
|
||||||
try {
|
sink.next(valueSerializer.deserialize(value));
|
||||||
sink.next(valueSerializer.deserialize(value));
|
} catch (IndexOutOfBoundsException ex) {
|
||||||
} catch (IndexOutOfBoundsException ex) {
|
var exMessage = ex.getMessage();
|
||||||
var exMessage = ex.getMessage();
|
if (exMessage != null && exMessage.contains("read 0 to 0, write 0 to ")) {
|
||||||
if (exMessage != null && exMessage.contains("read 0 to 0, write 0 to ")) {
|
var totalZeroBytesErrors = this.totalZeroBytesErrors.incrementAndGet();
|
||||||
var totalZeroBytesErrors = this.totalZeroBytesErrors.incrementAndGet();
|
if (totalZeroBytesErrors < 512 || totalZeroBytesErrors % 10000 == 0) {
|
||||||
if (totalZeroBytesErrors < 512 || totalZeroBytesErrors % 10000 == 0) {
|
try (var keySuffixBytes = serializeKeySuffixToKey(keySuffix)) {
|
||||||
try (var keySuffixBytes = serializeKeySuffixToKey(keySuffix)) {
|
LOG.error("Unexpected zero-bytes value at " + dictionary.getDatabaseName()
|
||||||
LOG.error("Unexpected zero-bytes value at " + dictionary.getDatabaseName()
|
+ ":" + dictionary.getColumnName()
|
||||||
+ ":" + dictionary.getColumnName()
|
+ ":" + LLUtils.toStringSafe(this.keyPrefix)
|
||||||
+ ":" + LLUtils.toStringSafe(this.keyPrefix)
|
+ ":" + keySuffix + "(" + LLUtils.toStringSafe(keySuffixBytes) + ") total=" + totalZeroBytesErrors);
|
||||||
+ ":" + keySuffix + "(" + LLUtils.toStringSafe(keySuffixBytes) + ") total=" + totalZeroBytesErrors);
|
} catch (SerializationException e) {
|
||||||
} catch (SerializationException e) {
|
LOG.error("Unexpected zero-bytes value at " + dictionary.getDatabaseName()
|
||||||
LOG.error("Unexpected zero-bytes value at " + dictionary.getDatabaseName()
|
+ ":" + dictionary.getColumnName()
|
||||||
+ ":" + dictionary.getColumnName()
|
+ ":" + LLUtils.toStringSafe(this.keyPrefix)
|
||||||
+ ":" + LLUtils.toStringSafe(this.keyPrefix)
|
+ ":" + keySuffix + "(?) total=" + totalZeroBytesErrors);
|
||||||
+ ":" + keySuffix + "(?) total=" + totalZeroBytesErrors);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sink.complete();
|
|
||||||
} else {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
}
|
||||||
} catch (Throwable ex) {
|
sink.complete();
|
||||||
|
} else {
|
||||||
sink.error(ex);
|
sink.error(ex);
|
||||||
}
|
}
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
sink.error(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,10 +279,12 @@ public class DatabaseMapDictionary<T, U> extends DatabaseMapDictionaryDeep<T, U,
|
|||||||
@Override
|
@Override
|
||||||
public Mono<U> getValue(@Nullable CompositeSnapshot snapshot, T keySuffix, boolean existsAlmostCertainly) {
|
public Mono<U> getValue(@Nullable CompositeSnapshot snapshot, T keySuffix, boolean existsAlmostCertainly) {
|
||||||
return dictionary
|
return dictionary
|
||||||
.get(resolveSnapshot(snapshot),
|
.get(resolveSnapshot(snapshot), Mono.fromCallable(() -> serializeKeySuffixToKey(keySuffix)))
|
||||||
Mono.fromCallable(() -> serializeKeySuffixToKey(keySuffix))
|
.handle((valueToReceive, sink) -> {
|
||||||
)
|
try (valueToReceive) {
|
||||||
.handle((valueToReceive, sink) -> deserializeValue(keySuffix, valueToReceive, sink));
|
deserializeValue(keySuffix, valueToReceive, sink);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -304,20 +303,22 @@ public class DatabaseMapDictionary<T, U> extends DatabaseMapDictionaryDeep<T, U,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<U> updateValue(T keySuffix,
|
public Mono<U> updateValue(T keySuffix, UpdateReturnMode updateReturnMode,
|
||||||
UpdateReturnMode updateReturnMode,
|
|
||||||
SerializationFunction<@Nullable U, @Nullable U> updater) {
|
SerializationFunction<@Nullable U, @Nullable U> updater) {
|
||||||
var keyMono = Mono.fromCallable(() -> serializeKeySuffixToKey(keySuffix));
|
var keyMono = Mono.fromCallable(() -> serializeKeySuffixToKey(keySuffix));
|
||||||
return dictionary
|
return dictionary
|
||||||
.update(keyMono, getSerializedUpdater(updater), updateReturnMode)
|
.update(keyMono, getSerializedUpdater(updater), updateReturnMode)
|
||||||
.handle((valueToReceive, sink) -> deserializeValue(keySuffix, valueToReceive, sink));
|
.handle((valueToReceive, sink) -> {
|
||||||
|
try (valueToReceive) {
|
||||||
|
deserializeValue(keySuffix, valueToReceive, sink);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Delta<U>> updateValueAndGetDelta(T keySuffix,
|
public Mono<Delta<U>> updateValueAndGetDelta(T keySuffix, SerializationFunction<@Nullable U, @Nullable U> updater) {
|
||||||
SerializationFunction<@Nullable U, @Nullable U> updater) {
|
|
||||||
var keyMono = Mono.fromCallable(() -> serializeKeySuffixToKey(keySuffix));
|
var keyMono = Mono.fromCallable(() -> serializeKeySuffixToKey(keySuffix));
|
||||||
return dictionary
|
return dictionary
|
||||||
.updateAndGetDelta(keyMono, getSerializedUpdater(updater))
|
.updateAndGetDelta(keyMono, getSerializedUpdater(updater))
|
||||||
.transform(mono -> LLUtils.mapLLDelta(mono, serialized -> {
|
.transform(mono -> LLUtils.mapLLDelta(mono, serialized -> {
|
||||||
try (serialized) {
|
try (serialized) {
|
||||||
@ -326,21 +327,18 @@ public class DatabaseMapDictionary<T, U> extends DatabaseMapDictionaryDeep<T, U,
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
public BinarySerializationFunction getSerializedUpdater(
|
public BinarySerializationFunction getSerializedUpdater(SerializationFunction<@Nullable U, @Nullable U> updater) {
|
||||||
SerializationFunction<@Nullable U, @Nullable U> updater) {
|
|
||||||
return oldSerialized -> {
|
return oldSerialized -> {
|
||||||
try (oldSerialized) {
|
U result;
|
||||||
U result;
|
if (oldSerialized == null) {
|
||||||
if (oldSerialized == null) {
|
result = updater.apply(null);
|
||||||
result = updater.apply(null);
|
} else {
|
||||||
} else {
|
result = updater.apply(valueSerializer.deserialize(oldSerialized));
|
||||||
result = updater.apply(valueSerializer.deserialize(oldSerialized));
|
}
|
||||||
}
|
if (result == null) {
|
||||||
if (result == null) {
|
return null;
|
||||||
return null;
|
} else {
|
||||||
} else {
|
return serializeValue(result);
|
||||||
return serializeValue(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -372,7 +370,11 @@ public class DatabaseMapDictionary<T, U> extends DatabaseMapDictionaryDeep<T, U,
|
|||||||
var valueMono = Mono.fromCallable(() -> serializeValue(value));
|
var valueMono = Mono.fromCallable(() -> serializeValue(value));
|
||||||
return dictionary
|
return dictionary
|
||||||
.put(keyMono, valueMono, LLDictionaryResultType.PREVIOUS_VALUE)
|
.put(keyMono, valueMono, LLDictionaryResultType.PREVIOUS_VALUE)
|
||||||
.handle((valueToReceive, sink) -> deserializeValue(keySuffix, valueToReceive, sink));
|
.handle((valueToReceive, sink) -> {
|
||||||
|
try (valueToReceive) {
|
||||||
|
deserializeValue(keySuffix, valueToReceive, sink);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -381,7 +383,11 @@ public class DatabaseMapDictionary<T, U> extends DatabaseMapDictionaryDeep<T, U,
|
|||||||
var valueMono = Mono.fromCallable(() -> serializeValue(value));
|
var valueMono = Mono.fromCallable(() -> serializeValue(value));
|
||||||
return dictionary
|
return dictionary
|
||||||
.put(keyMono, valueMono, LLDictionaryResultType.PREVIOUS_VALUE)
|
.put(keyMono, valueMono, LLDictionaryResultType.PREVIOUS_VALUE)
|
||||||
.handle((Buffer valueBuf, SynchronousSink<U> sink) -> deserializeValue(keySuffix, valueBuf, sink))
|
.handle((Buffer valueBuf, SynchronousSink<U> sink) -> {
|
||||||
|
try (valueBuf) {
|
||||||
|
deserializeValue(keySuffix, valueBuf, sink);
|
||||||
|
}
|
||||||
|
})
|
||||||
.map(oldValue -> !Objects.equals(oldValue, value))
|
.map(oldValue -> !Objects.equals(oldValue, value))
|
||||||
.defaultIfEmpty(value != null);
|
.defaultIfEmpty(value != null);
|
||||||
}
|
}
|
||||||
@ -400,7 +406,11 @@ public class DatabaseMapDictionary<T, U> extends DatabaseMapDictionaryDeep<T, U,
|
|||||||
var keyMono = Mono.fromCallable(() -> serializeKeySuffixToKey(keySuffix));
|
var keyMono = Mono.fromCallable(() -> serializeKeySuffixToKey(keySuffix));
|
||||||
return dictionary
|
return dictionary
|
||||||
.remove(keyMono, LLDictionaryResultType.PREVIOUS_VALUE)
|
.remove(keyMono, LLDictionaryResultType.PREVIOUS_VALUE)
|
||||||
.handle((valueToReceive, sink) -> deserializeValue(keySuffix, valueToReceive, sink));
|
.handle((valueToReceive, sink) -> {
|
||||||
|
try (valueToReceive) {
|
||||||
|
deserializeValue(keySuffix, valueToReceive, sink);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -925,21 +925,6 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Buffer applyUpdateAndCloseIfNecessary(BinarySerializationFunction updater,
|
|
||||||
@Nullable Buffer prevDataToSendToUpdater)
|
|
||||||
throws SerializationException {
|
|
||||||
@Nullable Buffer newData = null;
|
|
||||||
try {
|
|
||||||
newData = updater.apply(prevDataToSendToUpdater);
|
|
||||||
} finally {
|
|
||||||
if (prevDataToSendToUpdater != newData && prevDataToSendToUpdater != null
|
|
||||||
&& prevDataToSendToUpdater.isAccessible()) {
|
|
||||||
prevDataToSendToUpdater.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newData;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getLevels() {
|
protected int getLevels() {
|
||||||
var closeReadLock = closeLock.readLock();
|
var closeReadLock = closeLock.readLock();
|
||||||
try {
|
try {
|
||||||
|
@ -257,87 +257,73 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Buffer> get(@Nullable LLSnapshot snapshot, Mono<Buffer> keyMono) {
|
public Mono<Buffer> get(@Nullable LLSnapshot snapshot, Mono<Buffer> keyMono) {
|
||||||
return keyMono
|
return Mono.usingWhen(keyMono, key -> runOnDb(false, () -> {
|
||||||
.publishOn(dbRScheduler)
|
logger.trace(MARKER_ROCKSDB, "Reading {}", () -> toStringSafe(key));
|
||||||
.<Buffer>handle((key, sink) -> {
|
try {
|
||||||
try (key) {
|
var readOptions = generateReadOptionsOrStatic(snapshot);
|
||||||
logger.trace(MARKER_ROCKSDB, "Reading {}", () -> toStringSafe(key));
|
Buffer result;
|
||||||
try {
|
startedGet.increment();
|
||||||
var readOptions = generateReadOptionsOrStatic(snapshot);
|
try {
|
||||||
Buffer result;
|
result = getTime.recordCallable(() -> db.get(readOptions, key));
|
||||||
startedGet.increment();
|
} finally {
|
||||||
try {
|
endedGet.increment();
|
||||||
result = getTime.recordCallable(() -> db.get(readOptions, key));
|
if (readOptions != EMPTY_READ_OPTIONS) {
|
||||||
} finally {
|
readOptions.close();
|
||||||
endedGet.increment();
|
|
||||||
if (readOptions != EMPTY_READ_OPTIONS) {
|
|
||||||
readOptions.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.trace(MARKER_ROCKSDB, "Read {}: {}", () -> toStringSafe(key), () -> toStringSafe(result));
|
|
||||||
if (result != null) {
|
|
||||||
sink.next(result);
|
|
||||||
} else {
|
|
||||||
sink.complete();
|
|
||||||
}
|
|
||||||
} catch (RocksDBException ex) {
|
|
||||||
sink.error(new IOException("Failed to read " + toStringSafe(key) + ": " + ex.getMessage()));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
logger.trace(MARKER_ROCKSDB, "Read {}: {}", () -> toStringSafe(key), () -> toStringSafe(result));
|
||||||
|
return result;
|
||||||
|
} catch (RocksDBException ex) {
|
||||||
|
throw new IOException("Failed to read " + toStringSafe(key) + ": " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}), key -> Mono.fromRunnable(key::close));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Boolean> isRangeEmpty(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono, boolean fillCache) {
|
public Mono<Boolean> isRangeEmpty(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono, boolean fillCache) {
|
||||||
return rangeMono.publishOn(dbRScheduler).handle((range, sink) -> {
|
return Mono.usingWhen(rangeMono, range -> runOnDb(false, () -> {
|
||||||
try (range) {
|
assert !Schedulers.isInNonBlockingThread() : "Called isRangeEmpty in a nonblocking thread";
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called isRangeEmpty in a nonblocking thread";
|
startedContains.increment();
|
||||||
startedContains.increment();
|
try {
|
||||||
try {
|
Boolean isRangeEmpty = containsTime.recordCallable(() -> {
|
||||||
Boolean isRangeEmpty = containsTime.recordCallable(() -> {
|
if (range.isSingle()) {
|
||||||
if (range.isSingle()) {
|
return !containsKey(snapshot, range.getSingleUnsafe());
|
||||||
return !containsKey(snapshot, range.getSingleUnsafe());
|
} else {
|
||||||
} else {
|
// Temporary resources to release after finished
|
||||||
// Temporary resources to release after finished
|
|
||||||
|
|
||||||
try (var readOpts = LLUtils.generateCustomReadOptions(generateReadOptionsOrNull(snapshot),
|
try (var readOpts = LLUtils.generateCustomReadOptions(generateReadOptionsOrNull(snapshot),
|
||||||
true,
|
true,
|
||||||
isBoundedRange(range),
|
isBoundedRange(range),
|
||||||
true
|
true
|
||||||
)) {
|
)) {
|
||||||
readOpts.setVerifyChecksums(VERIFY_CHECKSUMS_WHEN_NOT_NEEDED);
|
readOpts.setVerifyChecksums(VERIFY_CHECKSUMS_WHEN_NOT_NEEDED);
|
||||||
readOpts.setFillCache(fillCache);
|
readOpts.setFillCache(fillCache);
|
||||||
try (var rocksIterator = db.newIterator(readOpts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
try (var rocksIterator = db.newIterator(readOpts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
||||||
if (!LLLocalDictionary.PREFER_AUTO_SEEK_BOUND && range.hasMin()) {
|
if (!LLLocalDictionary.PREFER_AUTO_SEEK_BOUND && range.hasMin()) {
|
||||||
if (nettyDirect && isReadOnlyDirect(range.getMinUnsafe())) {
|
if (nettyDirect && isReadOnlyDirect(range.getMinUnsafe())) {
|
||||||
var seekBuf = ((ReadableComponent) range.getMinUnsafe()).readableBuffer();
|
var seekBuf = ((ReadableComponent) range.getMinUnsafe()).readableBuffer();
|
||||||
rocksIterator.seek(seekBuf);
|
rocksIterator.seek(seekBuf);
|
||||||
} else {
|
|
||||||
var seekArray = LLUtils.toArray(range.getMinUnsafe());
|
|
||||||
rocksIterator.seek(seekArray);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
rocksIterator.seekToFirst();
|
var seekArray = LLUtils.toArray(range.getMinUnsafe());
|
||||||
|
rocksIterator.seek(seekArray);
|
||||||
}
|
}
|
||||||
return !rocksIterator.isValid();
|
} else {
|
||||||
|
rocksIterator.seekToFirst();
|
||||||
}
|
}
|
||||||
|
return !rocksIterator.isValid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
assert isRangeEmpty != null;
|
});
|
||||||
sink.next(isRangeEmpty);
|
assert isRangeEmpty != null;
|
||||||
} catch (RocksDBException ex) {
|
return isRangeEmpty;
|
||||||
sink.error(new RocksDBException("Failed to read range " + LLUtils.toStringSafe(range)
|
} catch (RocksDBException ex) {
|
||||||
+ ": " + ex.getMessage()));
|
throw new RocksDBException("Failed to read range " + LLUtils.toStringSafe(range)
|
||||||
} finally {
|
+ ": " + ex.getMessage());
|
||||||
endedContains.increment();
|
} finally {
|
||||||
}
|
endedContains.increment();
|
||||||
} catch (Throwable ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
}
|
||||||
});
|
}), range -> Mono.fromRunnable(range::close));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsKey(@Nullable LLSnapshot snapshot, Buffer key) throws RocksDBException {
|
private boolean containsKey(@Nullable LLSnapshot snapshot, Buffer key) throws RocksDBException {
|
||||||
@ -375,24 +361,16 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
v.touch("put entry value")
|
v.touch("put entry value")
|
||||||
));
|
));
|
||||||
// Write the new entry to the database
|
// Write the new entry to the database
|
||||||
Mono<Buffer> putMono = entryMono
|
Mono<Buffer> putMono = Mono.usingWhen(entryMono, entry -> runOnDb(true, () -> {
|
||||||
.publishOn(dbWScheduler)
|
var key = entry.getKeyUnsafe();
|
||||||
.handle((entry, sink) -> {
|
var value = entry.getValueUnsafe();
|
||||||
try {
|
assert key != null : "Key is null";
|
||||||
try (entry) {
|
assert value != null : "Value is null";
|
||||||
var key = entry.getKeyUnsafe();
|
key.touch("Dictionary put key");
|
||||||
var value = entry.getValueUnsafe();
|
value.touch("Dictionary put value");
|
||||||
assert key != null : "Key is null";
|
put(key, value);
|
||||||
assert value != null : "Value is null";
|
return null;
|
||||||
key.touch("Dictionary put key");
|
}), entry -> Mono.fromRunnable(entry::close));
|
||||||
value.touch("Dictionary put value");
|
|
||||||
put(key, value);
|
|
||||||
}
|
|
||||||
sink.complete();
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Read the previous data, then write the new data, then return the previous data
|
// Read the previous data, then write the new data, then return the previous data
|
||||||
return Flux.concatDelayError(Flux.just(previousDataMono, putMono), true, 1).singleOrEmpty();
|
return Flux.concatDelayError(Flux.just(previousDataMono, putMono), true, 1).singleOrEmpty();
|
||||||
}
|
}
|
||||||
@ -433,87 +411,67 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
public Mono<Buffer> update(Mono<Buffer> keyMono,
|
public Mono<Buffer> update(Mono<Buffer> keyMono,
|
||||||
BinarySerializationFunction updater,
|
BinarySerializationFunction updater,
|
||||||
UpdateReturnMode updateReturnMode) {
|
UpdateReturnMode updateReturnMode) {
|
||||||
return keyMono
|
return Mono.usingWhen(keyMono, key -> runOnDb(true, () -> {
|
||||||
.publishOn(dbWScheduler)
|
assert !Schedulers.isInNonBlockingThread() : "Called update in a nonblocking thread";
|
||||||
.handle((key, sink) -> {
|
if (updateMode == UpdateMode.DISALLOW) {
|
||||||
try (key) {
|
throw new UnsupportedOperationException("update() is disallowed");
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called update in a nonblocking thread";
|
}
|
||||||
if (updateMode == UpdateMode.DISALLOW) {
|
UpdateAtomicResultMode returnMode = switch (updateReturnMode) {
|
||||||
sink.error(new UnsupportedOperationException("update() is disallowed"));
|
case NOTHING -> UpdateAtomicResultMode.NOTHING;
|
||||||
return;
|
case GET_NEW_VALUE -> UpdateAtomicResultMode.CURRENT;
|
||||||
}
|
case GET_OLD_VALUE -> UpdateAtomicResultMode.PREVIOUS;
|
||||||
UpdateAtomicResultMode returnMode = switch (updateReturnMode) {
|
};
|
||||||
case NOTHING -> UpdateAtomicResultMode.NOTHING;
|
UpdateAtomicResult result;
|
||||||
case GET_NEW_VALUE -> UpdateAtomicResultMode.CURRENT;
|
var readOptions = generateReadOptionsOrStatic(null);
|
||||||
case GET_OLD_VALUE -> UpdateAtomicResultMode.PREVIOUS;
|
startedUpdates.increment();
|
||||||
};
|
try (var writeOptions = new WriteOptions()) {
|
||||||
UpdateAtomicResult result;
|
result = updateTime.recordCallable(() -> db.updateAtomic(readOptions, writeOptions, key, updater, returnMode));
|
||||||
var readOptions = generateReadOptionsOrStatic(null);
|
} finally {
|
||||||
startedUpdates.increment();
|
endedUpdates.increment();
|
||||||
try (var writeOptions = new WriteOptions()) {
|
if (readOptions != EMPTY_READ_OPTIONS) {
|
||||||
result = updateTime.recordCallable(() ->
|
readOptions.close();
|
||||||
db.updateAtomic(readOptions, writeOptions, key, updater, returnMode));
|
}
|
||||||
} finally {
|
}
|
||||||
endedUpdates.increment();
|
assert result != null;
|
||||||
if (readOptions != EMPTY_READ_OPTIONS) {
|
return switch (updateReturnMode) {
|
||||||
readOptions.close();
|
case NOTHING -> {
|
||||||
}
|
result.close();
|
||||||
}
|
yield null;
|
||||||
assert result != null;
|
}
|
||||||
var previous = switch (updateReturnMode) {
|
case GET_NEW_VALUE -> ((UpdateAtomicResultCurrent) result).current();
|
||||||
case NOTHING -> null;
|
case GET_OLD_VALUE -> ((UpdateAtomicResultPrevious) result).previous();
|
||||||
case GET_NEW_VALUE -> ((UpdateAtomicResultCurrent) result).current();
|
};
|
||||||
case GET_OLD_VALUE -> ((UpdateAtomicResultPrevious) result).previous();
|
}), key -> Mono.fromRunnable(key::close));
|
||||||
};
|
|
||||||
if (previous != null) {
|
|
||||||
sink.next(previous);
|
|
||||||
} else {
|
|
||||||
sink.complete();
|
|
||||||
}
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("DuplicatedCode")
|
@SuppressWarnings("DuplicatedCode")
|
||||||
@Override
|
@Override
|
||||||
public Mono<LLDelta> updateAndGetDelta(Mono<Buffer> keyMono,
|
public Mono<LLDelta> updateAndGetDelta(Mono<Buffer> keyMono, BinarySerializationFunction updater) {
|
||||||
BinarySerializationFunction updater) {
|
return Mono.usingWhen(keyMono, key -> runOnDb(true, () -> {
|
||||||
return keyMono
|
key.touch("low-level dictionary update");
|
||||||
.publishOn(dbWScheduler)
|
assert !Schedulers.isInNonBlockingThread() : "Called update in a nonblocking thread";
|
||||||
.handle((key, sink) -> {
|
if (updateMode == UpdateMode.DISALLOW) {
|
||||||
try (key) {
|
throw new UnsupportedOperationException("update() is disallowed");
|
||||||
key.touch("low-level dictionary update");
|
}
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called update in a nonblocking thread";
|
if (updateMode == UpdateMode.ALLOW && !db.supportsTransactions()) {
|
||||||
if (updateMode == UpdateMode.DISALLOW) {
|
throw new UnsupportedOperationException("update() is disallowed because the database doesn't support"
|
||||||
sink.error(new UnsupportedOperationException("update() is disallowed"));
|
+ "safe atomic operations");
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
if (updateMode == UpdateMode.ALLOW && !db.supportsTransactions()) {
|
|
||||||
sink.error(new UnsupportedOperationException("update() is disallowed because the database doesn't support"
|
|
||||||
+ "safe atomic operations"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateAtomicResult result;
|
UpdateAtomicResult result;
|
||||||
var readOptions = generateReadOptionsOrStatic(null);
|
var readOptions = generateReadOptionsOrStatic(null);
|
||||||
startedUpdates.increment();
|
startedUpdates.increment();
|
||||||
try (var writeOptions = new WriteOptions()) {
|
try (var writeOptions = new WriteOptions()) {
|
||||||
result = updateTime.recordCallable(() ->
|
result = updateTime.recordCallable(() -> db.updateAtomic(readOptions, writeOptions, key, updater, DELTA));
|
||||||
db.updateAtomic(readOptions, writeOptions, key, updater, DELTA));
|
} finally {
|
||||||
} finally {
|
endedUpdates.increment();
|
||||||
endedUpdates.increment();
|
if (readOptions != EMPTY_READ_OPTIONS) {
|
||||||
if (readOptions != EMPTY_READ_OPTIONS) {
|
readOptions.close();
|
||||||
readOptions.close();
|
}
|
||||||
}
|
}
|
||||||
}
|
assert result != null;
|
||||||
assert result != null;
|
return ((UpdateAtomicResultDelta) result).delta();
|
||||||
sink.next(((UpdateAtomicResultDelta) result).delta());
|
}), key -> Mono.fromRunnable(key::close));
|
||||||
} catch (Throwable ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -521,67 +479,47 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
// Obtain the previous value from the database
|
// Obtain the previous value from the database
|
||||||
Mono<Buffer> previousDataMono = this.getPreviousData(keyMono, resultType);
|
Mono<Buffer> previousDataMono = this.getPreviousData(keyMono, resultType);
|
||||||
// Delete the value from the database
|
// Delete the value from the database
|
||||||
Mono<Buffer> removeMono = keyMono
|
Mono<Buffer> removeMono = Mono.usingWhen(keyMono, key -> runOnDb(true, () -> {
|
||||||
.publishOn(dbWScheduler)
|
try {
|
||||||
.handle((key, sink) -> {
|
logger.trace(MARKER_ROCKSDB, "Deleting {}", () -> toStringSafe(key));
|
||||||
try (key) {
|
startedRemove.increment();
|
||||||
logger.trace(MARKER_ROCKSDB, "Deleting {}", () -> toStringSafe(key));
|
try (var writeOptions = new WriteOptions()) {
|
||||||
startedRemove.increment();
|
removeTime.recordCallable(() -> {
|
||||||
try (var writeOptions = new WriteOptions()) {
|
db.delete(writeOptions, key);
|
||||||
removeTime.recordCallable(() -> {
|
return null;
|
||||||
db.delete(writeOptions, key);
|
});
|
||||||
return null;
|
} finally {
|
||||||
});
|
endedRemove.increment();
|
||||||
} finally {
|
}
|
||||||
endedRemove.increment();
|
return null;
|
||||||
}
|
} catch (RocksDBException ex) {
|
||||||
sink.complete();
|
throw new RocksDBException("Failed to delete: " + ex.getMessage());
|
||||||
} catch (RocksDBException ex) {
|
}
|
||||||
sink.error(new RocksDBException("Failed to delete: " + ex.getMessage()));
|
}), key -> Mono.fromRunnable(key::close));
|
||||||
} catch (Throwable ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Read the previous data, then delete the data, then return the previous data
|
// Read the previous data, then delete the data, then return the previous data
|
||||||
return Flux.concat(previousDataMono, removeMono).singleOrEmpty();
|
return Flux.concat(previousDataMono, removeMono).singleOrEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Buffer> getPreviousData(Mono<Buffer> keyMono, LLDictionaryResultType resultType) {
|
private Mono<Buffer> getPreviousData(Mono<Buffer> keyMono, LLDictionaryResultType resultType) {
|
||||||
return switch (resultType) {
|
return switch (resultType) {
|
||||||
case PREVIOUS_VALUE_EXISTENCE -> keyMono
|
case PREVIOUS_VALUE_EXISTENCE -> Mono.usingWhen(keyMono, key -> runOnDb(false, () -> {
|
||||||
.publishOn(dbRScheduler)
|
var contained = containsKey(null, key);
|
||||||
.handle((key, sink) -> {
|
return LLUtils.booleanToResponseByteBuffer(alloc, contained);
|
||||||
try (key) {
|
}), key -> Mono.fromRunnable(key::close));
|
||||||
var contained = containsKey(null, key);
|
case PREVIOUS_VALUE -> Mono.usingWhen(keyMono, key -> runOnDb(false, () -> {
|
||||||
sink.next(LLUtils.booleanToResponseByteBuffer(alloc, contained));
|
assert !Schedulers.isInNonBlockingThread() : "Called getPreviousData in a nonblocking thread";
|
||||||
} catch (Throwable ex) {
|
Buffer result;
|
||||||
sink.error(ex);
|
var readOptions = generateReadOptionsOrStatic(null);
|
||||||
}
|
try {
|
||||||
});
|
result = db.get(readOptions, key);
|
||||||
case PREVIOUS_VALUE -> keyMono
|
} finally {
|
||||||
.publishOn(dbRScheduler)
|
if (readOptions != EMPTY_READ_OPTIONS) {
|
||||||
.handle((key, sink) -> {
|
readOptions.close();
|
||||||
try (key) {
|
}
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called getPreviousData in a nonblocking thread";
|
}
|
||||||
Buffer result;
|
logger.trace(MARKER_ROCKSDB, "Read {}: {}", () -> toStringSafe(key), () -> toStringSafe(result));
|
||||||
var readOptions = generateReadOptionsOrStatic(null);
|
return result;
|
||||||
try {
|
}), key -> Mono.fromRunnable(key::close));
|
||||||
result = db.get(readOptions, key);
|
|
||||||
} finally {
|
|
||||||
if (readOptions != EMPTY_READ_OPTIONS) {
|
|
||||||
readOptions.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.trace(MARKER_ROCKSDB, "Read {}: {}", () -> toStringSafe(key), () -> toStringSafe(result));
|
|
||||||
if (result == null) {
|
|
||||||
sink.complete();
|
|
||||||
} else {
|
|
||||||
sink.next(result);
|
|
||||||
}
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
case VOID -> Mono.empty();
|
case VOID -> Mono.empty();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -818,7 +756,6 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
|
||||||
private Flux<LLEntry> getRangeSingle(LLSnapshot snapshot, Mono<Buffer> keyMono) {
|
private Flux<LLEntry> getRangeSingle(LLSnapshot snapshot, Mono<Buffer> keyMono) {
|
||||||
return Mono
|
return Mono
|
||||||
.zip(keyMono, this.get(snapshot, keyMono))
|
.zip(keyMono, this.get(snapshot, keyMono))
|
||||||
@ -963,20 +900,13 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Flux<Buffer> getRangeKeysSingle(LLSnapshot snapshot, Mono<Buffer> keyMono) {
|
private Flux<Buffer> getRangeKeysSingle(LLSnapshot snapshot, Mono<Buffer> keyMono) {
|
||||||
return keyMono
|
return Mono.usingWhen(keyMono, key -> runOnDb(false, () -> {
|
||||||
.publishOn(dbRScheduler)
|
if (containsKey(snapshot, key)) {
|
||||||
.<Buffer>handle((key, sink) -> {
|
return key;
|
||||||
try (key) {
|
} else {
|
||||||
if (containsKey(snapshot, key)) {
|
return null;
|
||||||
sink.next(key);
|
}
|
||||||
} else {
|
}), key -> Mono.fromRunnable(key::close)).flux();
|
||||||
sink.complete();
|
|
||||||
}
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flux();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private record RocksObjTuple<T extends AbstractNativeReference, U extends Resource<?>>(T t1, U t2) implements SafeCloseable {
|
private record RocksObjTuple<T extends AbstractNativeReference, U extends Resource<?>>(T t1, U t2) implements SafeCloseable {
|
||||||
@ -1006,30 +936,23 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
@Override
|
@Override
|
||||||
public Mono<Void> setRange(Mono<LLRange> rangeMono, Flux<LLEntry> entries, boolean smallRange) {
|
public Mono<Void> setRange(Mono<LLRange> rangeMono, Flux<LLEntry> entries, boolean smallRange) {
|
||||||
if (USE_WINDOW_IN_SET_RANGE) {
|
if (USE_WINDOW_IN_SET_RANGE) {
|
||||||
return rangeMono
|
return Mono
|
||||||
.publishOn(dbWScheduler)
|
.usingWhen(rangeMono, range -> runOnDb(true, () -> {
|
||||||
.<Boolean>handle((range, sink) -> {
|
|
||||||
try (var writeOptions = new WriteOptions(); range) {
|
try (var writeOptions = new WriteOptions(); range) {
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called setRange in a nonblocking thread";
|
assert !Schedulers.isInNonBlockingThread() : "Called setRange in a nonblocking thread";
|
||||||
if (!USE_WRITE_BATCH_IN_SET_RANGE_DELETE || !USE_WRITE_BATCHES_IN_SET_RANGE) {
|
if (!USE_WRITE_BATCH_IN_SET_RANGE_DELETE || !USE_WRITE_BATCHES_IN_SET_RANGE) {
|
||||||
try (var opts = LLUtils.generateCustomReadOptions(null,
|
try (var opts = LLUtils.generateCustomReadOptions(null, true, isBoundedRange(range), smallRange)) {
|
||||||
true,
|
try (var it = db.newIterator(opts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
||||||
isBoundedRange(range),
|
if (!PREFER_AUTO_SEEK_BOUND && range.hasMin()) {
|
||||||
smallRange
|
it.seekTo(range.getMinUnsafe());
|
||||||
)) {
|
} else {
|
||||||
SafeCloseable seekTo;
|
it.seekToFirst();
|
||||||
try (var it = db.newIterator(opts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
|
||||||
if (!PREFER_AUTO_SEEK_BOUND && range.hasMin()) {
|
|
||||||
it.seekTo(range.getMinUnsafe());
|
|
||||||
} else {
|
|
||||||
seekTo = null;
|
|
||||||
it.seekToFirst();
|
|
||||||
}
|
|
||||||
while (it.isValid()) {
|
|
||||||
db.delete(writeOptions, it.key());
|
|
||||||
it.next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
while (it.isValid()) {
|
||||||
|
db.delete(writeOptions, it.key());
|
||||||
|
it.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (USE_CAPPED_WRITE_BATCH_IN_SET_RANGE) {
|
} else if (USE_CAPPED_WRITE_BATCH_IN_SET_RANGE) {
|
||||||
try (var batch = new CappedWriteBatch(db,
|
try (var batch = new CappedWriteBatch(db,
|
||||||
@ -1057,60 +980,56 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
batch.clear();
|
batch.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sink.next(true);
|
return true;
|
||||||
} catch (RocksDBException ex) {
|
} catch (RocksDBException ex) {
|
||||||
sink.error(new RocksDBException("Failed to set a range: " + ex.getMessage()));
|
throw new RocksDBException("Failed to set a range: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
})
|
}), range -> Mono.fromRunnable(range::close))
|
||||||
.thenMany(entries.window(MULTI_GET_WINDOW))
|
.thenMany(entries.window(MULTI_GET_WINDOW))
|
||||||
.flatMap(keysWindowFlux -> keysWindowFlux
|
.flatMap(keysWindowFlux -> keysWindowFlux
|
||||||
.collectList()
|
.collectList()
|
||||||
.flatMap(entriesList -> this
|
.flatMap(entriesList -> this.<Void>runOnDb(true, () -> {
|
||||||
.<Void>runOnDb(true, () -> {
|
try (var writeOptions = new WriteOptions()) {
|
||||||
try (var writeOptions = new WriteOptions()) {
|
if (!USE_WRITE_BATCHES_IN_SET_RANGE) {
|
||||||
if (!USE_WRITE_BATCHES_IN_SET_RANGE) {
|
for (LLEntry entry : entriesList) {
|
||||||
for (LLEntry entry : entriesList) {
|
db.put(writeOptions, entry.getKeyUnsafe(), entry.getValueUnsafe());
|
||||||
db.put(writeOptions, entry.getKeyUnsafe(), entry.getValueUnsafe());
|
|
||||||
}
|
|
||||||
} else if (USE_CAPPED_WRITE_BATCH_IN_SET_RANGE) {
|
|
||||||
try (var batch = new CappedWriteBatch(db,
|
|
||||||
alloc,
|
|
||||||
CAPPED_WRITE_BATCH_CAP,
|
|
||||||
RESERVED_WRITE_BATCH_SIZE,
|
|
||||||
MAX_WRITE_BATCH_SIZE,
|
|
||||||
writeOptions
|
|
||||||
)) {
|
|
||||||
for (LLEntry entry : entriesList) {
|
|
||||||
if (nettyDirect) {
|
|
||||||
batch.put(cfh, entry.getKeyUnsafe().send(), entry.getValueUnsafe().send());
|
|
||||||
} else {
|
|
||||||
batch.put(cfh,
|
|
||||||
LLUtils.toArray(entry.getKeyUnsafe()),
|
|
||||||
LLUtils.toArray(entry.getValueUnsafe())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
batch.flush();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try (var batch = new WriteBatch(RESERVED_WRITE_BATCH_SIZE)) {
|
|
||||||
for (LLEntry entry : entriesList) {
|
|
||||||
batch.put(cfh, LLUtils.toArray(entry.getKeyUnsafe()),
|
|
||||||
LLUtils.toArray(entry.getValueUnsafe()));
|
|
||||||
}
|
|
||||||
db.write(writeOptions, batch);
|
|
||||||
batch.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
for (LLEntry entry : entriesList) {
|
|
||||||
entry.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
} else if (USE_CAPPED_WRITE_BATCH_IN_SET_RANGE) {
|
||||||
)
|
try (var batch = new CappedWriteBatch(db,
|
||||||
)
|
alloc,
|
||||||
|
CAPPED_WRITE_BATCH_CAP,
|
||||||
|
RESERVED_WRITE_BATCH_SIZE,
|
||||||
|
MAX_WRITE_BATCH_SIZE,
|
||||||
|
writeOptions
|
||||||
|
)) {
|
||||||
|
for (LLEntry entry : entriesList) {
|
||||||
|
if (nettyDirect) {
|
||||||
|
batch.put(cfh, entry.getKeyUnsafe().send(), entry.getValueUnsafe().send());
|
||||||
|
} else {
|
||||||
|
batch.put(cfh,
|
||||||
|
LLUtils.toArray(entry.getKeyUnsafe()),
|
||||||
|
LLUtils.toArray(entry.getValueUnsafe())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
batch.flush();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try (var batch = new WriteBatch(RESERVED_WRITE_BATCH_SIZE)) {
|
||||||
|
for (LLEntry entry : entriesList) {
|
||||||
|
batch.put(cfh, LLUtils.toArray(entry.getKeyUnsafe()), LLUtils.toArray(entry.getValueUnsafe()));
|
||||||
|
}
|
||||||
|
db.write(writeOptions, batch);
|
||||||
|
batch.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
for (LLEntry entry : entriesList) {
|
||||||
|
entry.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})))
|
||||||
.then()
|
.then()
|
||||||
.onErrorMap(cause -> new IOException("Failed to write range", cause));
|
.onErrorMap(cause -> new IOException("Failed to write range", cause));
|
||||||
} else {
|
} else {
|
||||||
@ -1131,19 +1050,16 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
})
|
})
|
||||||
.then(Mono.<Void>empty());
|
.then(Mono.<Void>empty());
|
||||||
|
|
||||||
var putMono = entries
|
var putMono = entries.publishOn(dbWScheduler).handle((entry, sink) -> {
|
||||||
.publishOn(dbWScheduler)
|
try (entry) {
|
||||||
.handle((entry, sink) -> {
|
if (entry.getKeyUnsafe() != null && entry.getValueUnsafe() != null) {
|
||||||
try (entry) {
|
this.put(entry.getKeyUnsafe(), entry.getValueUnsafe());
|
||||||
if (entry.getKeyUnsafe() != null && entry.getValueUnsafe() != null) {
|
}
|
||||||
this.put(entry.getKeyUnsafe(), entry.getValueUnsafe());
|
sink.next(true);
|
||||||
}
|
} catch (RocksDBException ex) {
|
||||||
sink.next(true);
|
sink.error(new RocksDBException("Failed to write range: " + ex.getMessage()));
|
||||||
} catch (RocksDBException ex) {
|
}
|
||||||
sink.error(new RocksDBException("Failed to write range: " + ex.getMessage()));
|
}).then(Mono.<Void>empty());
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(Mono.<Void>empty());
|
|
||||||
|
|
||||||
return deleteMono.then(putMono);
|
return deleteMono.then(putMono);
|
||||||
}
|
}
|
||||||
@ -1263,11 +1179,11 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Long> sizeRange(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono, boolean fast) {
|
public Mono<Long> sizeRange(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono, boolean fast) {
|
||||||
return rangeMono.publishOn(dbRScheduler).handle((range, sink) -> {
|
return Mono.usingWhen(rangeMono, range -> runOnDb(false, () -> {
|
||||||
try (range) {
|
try {
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called sizeRange in a nonblocking thread";
|
assert !Schedulers.isInNonBlockingThread() : "Called sizeRange in a nonblocking thread";
|
||||||
if (range.isAll()) {
|
if (range.isAll()) {
|
||||||
sink.next(fast ? fastSizeAll(snapshot) : exactSizeAll(snapshot));
|
return fast ? fastSizeAll(snapshot) : exactSizeAll(snapshot);
|
||||||
} else {
|
} else {
|
||||||
try (var readOpts = LLUtils.generateCustomReadOptions(generateReadOptionsOrNull(snapshot),
|
try (var readOpts = LLUtils.generateCustomReadOptions(generateReadOptionsOrNull(snapshot),
|
||||||
false,
|
false,
|
||||||
@ -1291,20 +1207,20 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
rocksIterator.next();
|
rocksIterator.next();
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
sink.next(i);
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (RocksDBException ex) {
|
} catch (RocksDBException ex) {
|
||||||
sink.error(new RocksDBException("Failed to get size of range: " + ex.getMessage()));
|
throw new RocksDBException("Failed to get size of range: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
});
|
}), range -> Mono.fromRunnable(range::close));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<LLEntry> getOne(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
|
public Mono<LLEntry> getOne(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
|
||||||
return rangeMono.publishOn(dbRScheduler).handle((range, sink) -> {
|
return Mono.usingWhen(rangeMono, range -> runOnDb(false, () -> {
|
||||||
try (range) {
|
try {
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called getOne in a nonblocking thread";
|
assert !Schedulers.isInNonBlockingThread() : "Called getOne in a nonblocking thread";
|
||||||
try (var readOpts = LLUtils.generateCustomReadOptions(generateReadOptionsOrNull(snapshot), true, true, true)) {
|
try (var readOpts = LLUtils.generateCustomReadOptions(generateReadOptionsOrNull(snapshot), true, true, true)) {
|
||||||
try (var rocksIterator = db.newIterator(readOpts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
try (var rocksIterator = db.newIterator(readOpts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
||||||
@ -1316,24 +1232,24 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
if (rocksIterator.isValid()) {
|
if (rocksIterator.isValid()) {
|
||||||
try (var key = LLUtils.readDirectNioBuffer(alloc, rocksIterator::key)) {
|
try (var key = LLUtils.readDirectNioBuffer(alloc, rocksIterator::key)) {
|
||||||
try (var value = LLUtils.readDirectNioBuffer(alloc, rocksIterator::value)) {
|
try (var value = LLUtils.readDirectNioBuffer(alloc, rocksIterator::value)) {
|
||||||
sink.next(LLEntry.of(key.touch("get-one key"), value.touch("get-one value")));
|
return LLEntry.of(key.touch("get-one key"), value.touch("get-one value"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sink.complete();
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (RocksDBException ex) {
|
} catch (RocksDBException ex) {
|
||||||
sink.error(new RocksDBException("Failed to get one entry: " + ex.getMessage()));
|
throw new RocksDBException("Failed to get one entry: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
});
|
}), range -> Mono.fromRunnable(range::close));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Buffer> getOneKey(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
|
public Mono<Buffer> getOneKey(@Nullable LLSnapshot snapshot, Mono<LLRange> rangeMono) {
|
||||||
return rangeMono.publishOn(dbRScheduler).handle((range, sink) -> {
|
return Mono.usingWhen(rangeMono, range -> runOnDb(false, () -> {
|
||||||
try (range) {
|
try {
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called getOneKey in a nonblocking thread";
|
assert !Schedulers.isInNonBlockingThread() : "Called getOneKey in a nonblocking thread";
|
||||||
try (var readOpts = LLUtils.generateCustomReadOptions(generateReadOptionsOrNull(snapshot), true, true, true)) {
|
try (var readOpts = LLUtils.generateCustomReadOptions(generateReadOptionsOrNull(snapshot), true, true, true)) {
|
||||||
try (var rocksIterator = db.newIterator(readOpts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
try (var rocksIterator = db.newIterator(readOpts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
||||||
@ -1343,16 +1259,16 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
rocksIterator.seekToFirst();
|
rocksIterator.seekToFirst();
|
||||||
}
|
}
|
||||||
if (rocksIterator.isValid()) {
|
if (rocksIterator.isValid()) {
|
||||||
sink.next(LLUtils.readDirectNioBuffer(alloc, rocksIterator::key));
|
return LLUtils.readDirectNioBuffer(alloc, rocksIterator::key);
|
||||||
} else {
|
} else {
|
||||||
sink.complete();
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (RocksDBException ex) {
|
} catch (RocksDBException ex) {
|
||||||
sink.error(new RocksDBException("Failed to get one key: " + ex.getMessage()));
|
throw new RocksDBException("Failed to get one key: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
});
|
}), range -> Mono.fromRunnable(range::close));
|
||||||
}
|
}
|
||||||
|
|
||||||
private long fastSizeAll(@Nullable LLSnapshot snapshot) throws RocksDBException {
|
private long fastSizeAll(@Nullable LLSnapshot snapshot) throws RocksDBException {
|
||||||
@ -1467,31 +1383,26 @@ public class LLLocalDictionary implements LLDictionary {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<LLEntry> removeOne(Mono<LLRange> rangeMono) {
|
public Mono<LLEntry> removeOne(Mono<LLRange> rangeMono) {
|
||||||
return rangeMono.publishOn(dbWScheduler).handle((range, sink) -> {
|
return Mono.usingWhen(rangeMono, range -> runOnDb(true, () -> {
|
||||||
try (range) {
|
assert !Schedulers.isInNonBlockingThread() : "Called removeOne in a nonblocking thread";
|
||||||
assert !Schedulers.isInNonBlockingThread() : "Called removeOne in a nonblocking thread";
|
try (var readOpts = new ReadOptions();
|
||||||
try (var readOpts = new ReadOptions();
|
var writeOpts = new WriteOptions()) {
|
||||||
var writeOpts = new WriteOptions()) {
|
try (var rocksIterator = db.newIterator(readOpts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
||||||
try (var rocksIterator = db.newIterator(readOpts, range.getMinUnsafe(), range.getMaxUnsafe())) {
|
if (!LLLocalDictionary.PREFER_AUTO_SEEK_BOUND && range.hasMin()) {
|
||||||
if (!LLLocalDictionary.PREFER_AUTO_SEEK_BOUND && range.hasMin()) {
|
rocksIterator.seekTo(range.getMinUnsafe());
|
||||||
rocksIterator.seekTo(range.getMinUnsafe());
|
} else {
|
||||||
} else {
|
rocksIterator.seekToFirst();
|
||||||
rocksIterator.seekToFirst();
|
|
||||||
}
|
|
||||||
if (!rocksIterator.isValid()) {
|
|
||||||
sink.complete();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Buffer key = LLUtils.readDirectNioBuffer(alloc, rocksIterator::key);
|
|
||||||
Buffer value = LLUtils.readDirectNioBuffer(alloc, rocksIterator::value);
|
|
||||||
db.delete(writeOpts, key);
|
|
||||||
sink.next(LLEntry.of(key, value));
|
|
||||||
}
|
}
|
||||||
|
if (!rocksIterator.isValid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Buffer key = LLUtils.readDirectNioBuffer(alloc, rocksIterator::key);
|
||||||
|
Buffer value = LLUtils.readDirectNioBuffer(alloc, rocksIterator::value);
|
||||||
|
db.delete(writeOpts, key);
|
||||||
|
return LLEntry.of(key, value);
|
||||||
}
|
}
|
||||||
} catch (RocksDBException ex) {
|
|
||||||
sink.error(ex);
|
|
||||||
}
|
}
|
||||||
});
|
}), range -> Mono.fromRunnable(range::close));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -139,13 +139,14 @@ public class LLLocalSingleton implements LLSingleton {
|
|||||||
case GET_OLD_VALUE -> UpdateAtomicResultMode.PREVIOUS;
|
case GET_OLD_VALUE -> UpdateAtomicResultMode.PREVIOUS;
|
||||||
};
|
};
|
||||||
UpdateAtomicResult result;
|
UpdateAtomicResult result;
|
||||||
try (key;
|
try (var readOptions = new ReadOptions(); var writeOptions = new WriteOptions()) {
|
||||||
var readOptions = new ReadOptions();
|
|
||||||
var writeOptions = new WriteOptions()) {
|
|
||||||
result = db.updateAtomic(readOptions, writeOptions, key, updater, returnMode);
|
result = db.updateAtomic(readOptions, writeOptions, key, updater, returnMode);
|
||||||
}
|
}
|
||||||
return switch (updateReturnMode) {
|
return switch (updateReturnMode) {
|
||||||
case NOTHING -> null;
|
case NOTHING -> {
|
||||||
|
result.close();
|
||||||
|
yield null;
|
||||||
|
}
|
||||||
case GET_NEW_VALUE -> ((UpdateAtomicResultCurrent) result).current();
|
case GET_NEW_VALUE -> ((UpdateAtomicResultCurrent) result).current();
|
||||||
case GET_OLD_VALUE -> ((UpdateAtomicResultPrevious) result).previous();
|
case GET_OLD_VALUE -> ((UpdateAtomicResultPrevious) result).previous();
|
||||||
};
|
};
|
||||||
@ -160,9 +161,7 @@ public class LLLocalSingleton implements LLSingleton {
|
|||||||
throw new UnsupportedOperationException("Called update in a nonblocking thread");
|
throw new UnsupportedOperationException("Called update in a nonblocking thread");
|
||||||
}
|
}
|
||||||
UpdateAtomicResult result;
|
UpdateAtomicResult result;
|
||||||
try (key;
|
try (var readOptions = new ReadOptions(); var writeOptions = new WriteOptions()) {
|
||||||
var readOptions = new ReadOptions();
|
|
||||||
var writeOptions = new WriteOptions()) {
|
|
||||||
result = db.updateAtomic(readOptions, writeOptions, key, updater, DELTA);
|
result = db.updateAtomic(readOptions, writeOptions, key, updater, DELTA);
|
||||||
}
|
}
|
||||||
return ((UpdateAtomicResultDelta) result).delta();
|
return ((UpdateAtomicResultDelta) result).delta();
|
||||||
|
@ -96,8 +96,8 @@ public final class OptimisticRocksDBColumn extends AbstractRocksDBColumn<Optimis
|
|||||||
boolean committedSuccessfully;
|
boolean committedSuccessfully;
|
||||||
int retries = 0;
|
int retries = 0;
|
||||||
ExponentialPageLimits retryTime = null;
|
ExponentialPageLimits retryTime = null;
|
||||||
Buffer sentPrevData;
|
Buffer sentPrevData = null;
|
||||||
Buffer sentCurData;
|
Buffer sentCurData = null;
|
||||||
boolean changed;
|
boolean changed;
|
||||||
do {
|
do {
|
||||||
var prevDataArray = tx.getForUpdate(readOptions, cfh, keyArray, true);
|
var prevDataArray = tx.getForUpdate(readOptions, cfh, keyArray, true);
|
||||||
@ -122,8 +122,14 @@ public final class OptimisticRocksDBColumn extends AbstractRocksDBColumn<Optimis
|
|||||||
} else {
|
} else {
|
||||||
prevDataToSendToUpdater = null;
|
prevDataToSendToUpdater = null;
|
||||||
}
|
}
|
||||||
|
@Nullable Buffer newData;
|
||||||
@Nullable Buffer newData = applyUpdateAndCloseIfNecessary(updater, prevDataToSendToUpdater);
|
try {
|
||||||
|
newData = updater.apply(prevDataToSendToUpdater);
|
||||||
|
} finally {
|
||||||
|
if (prevDataToSendToUpdater != null && prevDataToSendToUpdater.isAccessible()) {
|
||||||
|
prevDataToSendToUpdater.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
try (newData) {
|
try (newData) {
|
||||||
var newDataArray = newData == null ? null : LLUtils.toArray(newData);
|
var newDataArray = newData == null ? null : LLUtils.toArray(newData);
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
@ -157,15 +163,21 @@ public final class OptimisticRocksDBColumn extends AbstractRocksDBColumn<Optimis
|
|||||||
committedSuccessfully = true;
|
committedSuccessfully = true;
|
||||||
tx.rollback();
|
tx.rollback();
|
||||||
}
|
}
|
||||||
|
if (sentPrevData != null && sentPrevData.isAccessible()) {
|
||||||
|
sentPrevData.close();
|
||||||
|
}
|
||||||
|
if (sentCurData != null && sentCurData.isAccessible()) {
|
||||||
|
sentCurData.close();
|
||||||
|
}
|
||||||
sentPrevData = prevData == null ? null : prevData.copy();
|
sentPrevData = prevData == null ? null : prevData.copy();
|
||||||
sentCurData = newData == null ? null : newData.copy();
|
sentCurData = newData == null ? null : newData.copy();
|
||||||
if (!committedSuccessfully) {
|
if (!committedSuccessfully) {
|
||||||
tx.undoGetForUpdate(cfh, keyArray);
|
tx.undoGetForUpdate(cfh, keyArray);
|
||||||
tx.rollback();
|
tx.rollback();
|
||||||
if (sentPrevData != null) {
|
if (sentPrevData != null && sentPrevData.isAccessible()) {
|
||||||
sentPrevData.close();
|
sentPrevData.close();
|
||||||
}
|
}
|
||||||
if (sentCurData != null) {
|
if (sentCurData != null && sentCurData.isAccessible()) {
|
||||||
sentCurData.close();
|
sentCurData.close();
|
||||||
}
|
}
|
||||||
retries++;
|
retries++;
|
||||||
@ -220,7 +232,15 @@ public final class OptimisticRocksDBColumn extends AbstractRocksDBColumn<Optimis
|
|||||||
}
|
}
|
||||||
yield new UpdateAtomicResultPrevious(sentPrevData);
|
yield new UpdateAtomicResultPrevious(sentPrevData);
|
||||||
}
|
}
|
||||||
case BINARY_CHANGED -> new UpdateAtomicResultBinaryChanged(changed);
|
case BINARY_CHANGED -> {
|
||||||
|
if (sentPrevData != null) {
|
||||||
|
sentPrevData.close();
|
||||||
|
}
|
||||||
|
if (sentCurData != null) {
|
||||||
|
sentCurData.close();
|
||||||
|
}
|
||||||
|
yield new UpdateAtomicResultBinaryChanged(changed);
|
||||||
|
}
|
||||||
case DELTA -> new UpdateAtomicResultDelta(LLDelta.of(sentPrevData, sentCurData));
|
case DELTA -> new UpdateAtomicResultDelta(LLDelta.of(sentPrevData, sentCurData));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,14 @@ public final class PessimisticRocksDBColumn extends AbstractRocksDBColumn<Transa
|
|||||||
prevDataToSendToUpdater = null;
|
prevDataToSendToUpdater = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable Buffer newData = applyUpdateAndCloseIfNecessary(updater, prevDataToSendToUpdater);
|
@Nullable Buffer newData;
|
||||||
|
try {
|
||||||
|
newData = updater.apply(prevDataToSendToUpdater);
|
||||||
|
} finally {
|
||||||
|
if (prevDataToSendToUpdater != null && prevDataToSendToUpdater.isAccessible()) {
|
||||||
|
prevDataToSendToUpdater.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
try (newData) {
|
try (newData) {
|
||||||
var newDataArray = newData == null ? null : LLUtils.toArray(newData);
|
var newDataArray = newData == null ? null : LLUtils.toArray(newData);
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
@ -160,7 +167,15 @@ public final class PessimisticRocksDBColumn extends AbstractRocksDBColumn<Transa
|
|||||||
}
|
}
|
||||||
yield new UpdateAtomicResultPrevious(sentPrevData);
|
yield new UpdateAtomicResultPrevious(sentPrevData);
|
||||||
}
|
}
|
||||||
case BINARY_CHANGED -> new UpdateAtomicResultBinaryChanged(changed);
|
case BINARY_CHANGED -> {
|
||||||
|
if (sentPrevData != null) {
|
||||||
|
sentPrevData.close();
|
||||||
|
}
|
||||||
|
if (sentCurData != null) {
|
||||||
|
sentCurData.close();
|
||||||
|
}
|
||||||
|
yield new UpdateAtomicResultBinaryChanged(changed);
|
||||||
|
}
|
||||||
case DELTA -> new UpdateAtomicResultDelta(LLDelta.of(sentPrevData, sentCurData));
|
case DELTA -> new UpdateAtomicResultDelta(LLDelta.of(sentPrevData, sentCurData));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,14 @@ public final class StandardRocksDBColumn extends AbstractRocksDBColumn<RocksDB>
|
|||||||
prevDataToSendToUpdater = null;
|
prevDataToSendToUpdater = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable Buffer newData = applyUpdateAndCloseIfNecessary(updater, prevDataToSendToUpdater);
|
@Nullable Buffer newData;
|
||||||
|
try {
|
||||||
|
newData = updater.apply(prevDataToSendToUpdater);
|
||||||
|
} finally {
|
||||||
|
if (prevDataToSendToUpdater != null && prevDataToSendToUpdater.isAccessible()) {
|
||||||
|
prevDataToSendToUpdater.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
try (newData) {
|
try (newData) {
|
||||||
boolean changed;
|
boolean changed;
|
||||||
assert newData == null || newData.isAccessible();
|
assert newData == null || newData.isAccessible();
|
||||||
@ -112,18 +119,13 @@ public final class StandardRocksDBColumn extends AbstractRocksDBColumn<RocksDB>
|
|||||||
}
|
}
|
||||||
recordAtomicUpdateTime(changed, prevData != null, newData != null, initNanoTime);
|
recordAtomicUpdateTime(changed, prevData != null, newData != null, initNanoTime);
|
||||||
return switch (returnMode) {
|
return switch (returnMode) {
|
||||||
case NOTHING -> {
|
case NOTHING -> RESULT_NOTHING;
|
||||||
yield RESULT_NOTHING;
|
case CURRENT -> new UpdateAtomicResultCurrent(newData != null ? newData.copy() : null);
|
||||||
}
|
case PREVIOUS -> new UpdateAtomicResultPrevious(prevData != null ? prevData.copy() : null);
|
||||||
case CURRENT -> {
|
|
||||||
yield new UpdateAtomicResultCurrent(newData != null ? newData.copy() : null);
|
|
||||||
}
|
|
||||||
case PREVIOUS -> {
|
|
||||||
yield new UpdateAtomicResultPrevious(prevData != null ? prevData.copy() : null);
|
|
||||||
}
|
|
||||||
case BINARY_CHANGED -> new UpdateAtomicResultBinaryChanged(changed);
|
case BINARY_CHANGED -> new UpdateAtomicResultBinaryChanged(changed);
|
||||||
case DELTA -> new UpdateAtomicResultDelta(LLDelta
|
case DELTA -> new UpdateAtomicResultDelta(LLDelta.of(
|
||||||
.of(prevData != null ? prevData.copy() : null, newData != null ? newData.copy() : null));
|
prevData != null ? prevData.copy() : null,
|
||||||
|
newData != null ? newData.copy() : null));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
package it.cavallium.dbengine.database.disk;
|
package it.cavallium.dbengine.database.disk;
|
||||||
|
|
||||||
public sealed interface UpdateAtomicResult permits UpdateAtomicResultBinaryChanged, UpdateAtomicResultDelta,
|
import it.cavallium.dbengine.database.SafeCloseable;
|
||||||
UpdateAtomicResultNothing, UpdateAtomicResultPrevious, UpdateAtomicResultCurrent {}
|
|
||||||
|
public sealed interface UpdateAtomicResult extends SafeCloseable permits UpdateAtomicResultBinaryChanged,
|
||||||
|
UpdateAtomicResultDelta, UpdateAtomicResultNothing, UpdateAtomicResultPrevious, UpdateAtomicResultCurrent {}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
package it.cavallium.dbengine.database.disk;
|
package it.cavallium.dbengine.database.disk;
|
||||||
|
|
||||||
public final record UpdateAtomicResultBinaryChanged(boolean changed) implements UpdateAtomicResult {}
|
public record UpdateAtomicResultBinaryChanged(boolean changed) implements UpdateAtomicResult {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,4 +3,12 @@ package it.cavallium.dbengine.database.disk;
|
|||||||
import io.netty5.buffer.api.Buffer;
|
import io.netty5.buffer.api.Buffer;
|
||||||
import io.netty5.buffer.api.Send;
|
import io.netty5.buffer.api.Send;
|
||||||
|
|
||||||
public final record UpdateAtomicResultCurrent(Buffer current) implements UpdateAtomicResult {}
|
public record UpdateAtomicResultCurrent(Buffer current) implements UpdateAtomicResult {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (current != null && current.isAccessible()) {
|
||||||
|
current.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,4 +3,12 @@ package it.cavallium.dbengine.database.disk;
|
|||||||
import io.netty5.buffer.api.Send;
|
import io.netty5.buffer.api.Send;
|
||||||
import it.cavallium.dbengine.database.LLDelta;
|
import it.cavallium.dbengine.database.LLDelta;
|
||||||
|
|
||||||
public final record UpdateAtomicResultDelta(LLDelta delta) implements UpdateAtomicResult {}
|
public record UpdateAtomicResultDelta(LLDelta delta) implements UpdateAtomicResult {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (delta != null && delta.isAccessible()) {
|
||||||
|
delta.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
package it.cavallium.dbengine.database.disk;
|
package it.cavallium.dbengine.database.disk;
|
||||||
|
|
||||||
public final class UpdateAtomicResultNothing implements UpdateAtomicResult {}
|
public record UpdateAtomicResultNothing() implements UpdateAtomicResult {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,4 +3,12 @@ package it.cavallium.dbengine.database.disk;
|
|||||||
import io.netty5.buffer.api.Buffer;
|
import io.netty5.buffer.api.Buffer;
|
||||||
import io.netty5.buffer.api.Send;
|
import io.netty5.buffer.api.Send;
|
||||||
|
|
||||||
public final record UpdateAtomicResultPrevious(Buffer previous) implements UpdateAtomicResult {}
|
public record UpdateAtomicResultPrevious(Buffer previous) implements UpdateAtomicResult {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (previous != null && previous.isAccessible()) {
|
||||||
|
previous.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -77,7 +77,9 @@ public class HugePqPriorityQueue<T> implements PriorityQueue<T>, Reversable<Reve
|
|||||||
var keyBuf = serializeKey(element);
|
var keyBuf = serializeKey(element);
|
||||||
try (keyBuf) {
|
try (keyBuf) {
|
||||||
try (var readOptions = newReadOptions(); var writeOptions = newWriteOptions()) {
|
try (var readOptions = newReadOptions(); var writeOptions = newWriteOptions()) {
|
||||||
rocksDB.updateAtomic(readOptions, writeOptions, keyBuf, this::incrementOrAdd, UpdateAtomicResultMode.NOTHING);
|
rocksDB
|
||||||
|
.updateAtomic(readOptions, writeOptions, keyBuf, this::incrementOrAdd, UpdateAtomicResultMode.NOTHING)
|
||||||
|
.close();
|
||||||
}
|
}
|
||||||
++size;
|
++size;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -136,7 +138,9 @@ public class HugePqPriorityQueue<T> implements PriorityQueue<T>, Reversable<Reve
|
|||||||
if (it.isValid()) {
|
if (it.isValid()) {
|
||||||
var key = it.key();
|
var key = it.key();
|
||||||
try (var keyBuf = rocksDB.getAllocator().copyOf(key)) {
|
try (var keyBuf = rocksDB.getAllocator().copyOf(key)) {
|
||||||
rocksDB.updateAtomic(readOptions, writeOptions, keyBuf, this::reduceOrRemove, UpdateAtomicResultMode.NOTHING);
|
rocksDB
|
||||||
|
.updateAtomic(readOptions, writeOptions, keyBuf, this::reduceOrRemove, UpdateAtomicResultMode.NOTHING)
|
||||||
|
.close();
|
||||||
--size;
|
--size;
|
||||||
return deserializeKey(keyBuf);
|
return deserializeKey(keyBuf);
|
||||||
}
|
}
|
||||||
@ -210,22 +214,17 @@ public class HugePqPriorityQueue<T> implements PriorityQueue<T>, Reversable<Reve
|
|||||||
try (var readOptions = newReadOptions();
|
try (var readOptions = newReadOptions();
|
||||||
var writeOptions = newWriteOptions();
|
var writeOptions = newWriteOptions();
|
||||||
var keyBuf = serializeKey(element)) {
|
var keyBuf = serializeKey(element)) {
|
||||||
UpdateAtomicResultPrevious prev = (UpdateAtomicResultPrevious) rocksDB.updateAtomic(readOptions, writeOptions,
|
try (var prev = (UpdateAtomicResultPrevious) rocksDB.updateAtomic(readOptions, writeOptions,
|
||||||
keyBuf,
|
keyBuf,
|
||||||
this::reduceOrRemove,
|
this::reduceOrRemove,
|
||||||
UpdateAtomicResultMode.PREVIOUS
|
UpdateAtomicResultMode.PREVIOUS
|
||||||
);
|
)) {
|
||||||
try {
|
|
||||||
if (prev.previous() != null) {
|
if (prev.previous() != null) {
|
||||||
--size;
|
--size;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
if (prev.previous() != null) {
|
|
||||||
prev.previous().close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
throw new IllegalStateException(ex);
|
throw new IllegalStateException(ex);
|
||||||
|
Loading…
Reference in New Issue
Block a user