package it.cavallium.dbengine.database.collections; import io.netty.buffer.ByteBuf; import io.netty.util.ReferenceCounted; import it.cavallium.dbengine.client.CompositeSnapshot; import it.cavallium.dbengine.database.LLDictionary; import it.cavallium.dbengine.database.LLDictionaryResultType; import it.cavallium.dbengine.database.LLUtils; import it.cavallium.dbengine.database.UpdateMode; import it.cavallium.dbengine.database.serialization.Serializer; import it.cavallium.dbengine.database.serialization.SerializerFixedBinaryLength; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.function.Function; import org.jetbrains.annotations.Nullable; import org.rocksdb.RocksDBException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; /** * Optimized implementation of "DatabaseMapDictionary with SubStageGetterSingle" */ public class DatabaseMapDictionary extends DatabaseMapDictionaryDeep> { private final Serializer valueSerializer; protected DatabaseMapDictionary(LLDictionary dictionary, ByteBuf prefixKey, SerializerFixedBinaryLength keySuffixSerializer, Serializer valueSerializer) { // Do not retain or release or use the prefixKey here super(dictionary, prefixKey, keySuffixSerializer, new SubStageGetterSingle<>(valueSerializer), 0); prefixKey = null; this.valueSerializer = valueSerializer; } public static DatabaseMapDictionary simple(LLDictionary dictionary, SerializerFixedBinaryLength keySerializer, Serializer valueSerializer) { return new DatabaseMapDictionary<>(dictionary, EMPTY_BYTES, keySerializer, valueSerializer); } public static DatabaseMapDictionary tail(LLDictionary dictionary, ByteBuf prefixKey, SerializerFixedBinaryLength keySuffixSerializer, Serializer valueSerializer) { return new DatabaseMapDictionary<>(dictionary, prefixKey, keySuffixSerializer, valueSerializer); } private ByteBuf toKey(ByteBuf suffixKey) { try { assert suffixKeyConsistency(suffixKey.readableBytes()); return LLUtils.directCompositeBuffer(dictionary.getAllocator(), keyPrefix.retain(), suffixKey.retain()); } finally { suffixKey.release(); } } @Override public Mono> get(@Nullable CompositeSnapshot snapshot, boolean existsAlmostCertainly) { return dictionary .getRange(resolveSnapshot(snapshot), range.retain(), existsAlmostCertainly) .collectMap( entry -> deserializeSuffix(stripPrefix(entry.getKey())), entry -> deserialize(entry.getValue()), HashMap::new) .filter(map -> !map.isEmpty()); } @Override public Mono> setAndGetPrevious(Map value) { return Mono .usingWhen( Mono.just(true), b -> get(null, false), b -> dictionary .setRange(range.retain(), Flux .fromIterable(value.entrySet()) .map(entry -> Map .entry(this.toKey(serializeSuffix(entry.getKey())), serialize(entry.getValue())) ) ) ); } @Override public Mono> clearAndGetPrevious() { return this .setAndGetPrevious(Map.of()); } @Override public Mono leavesCount(@Nullable CompositeSnapshot snapshot, boolean fast) { return dictionary.sizeRange(resolveSnapshot(snapshot), range.retain(), fast); } @Override public Mono isEmpty(@Nullable CompositeSnapshot snapshot) { return dictionary.isRangeEmpty(resolveSnapshot(snapshot), range.retain()); } @Override public Mono> at(@Nullable CompositeSnapshot snapshot, T keySuffix) { return Mono .fromSupplier(() -> new DatabaseSingle<>(dictionary, toKey(serializeSuffix(keySuffix)), Serializer.noop())) .>map(entry -> new DatabaseSingleMapped<>(entry, valueSerializer)); } @Override public Mono getValue(@Nullable CompositeSnapshot snapshot, T keySuffix, boolean existsAlmostCertainly) { return Mono .using( () -> toKey(serializeSuffix(keySuffix)), keyBuf -> dictionary .get(resolveSnapshot(snapshot), keyBuf.retain(), existsAlmostCertainly) .map(this::deserialize), ReferenceCounted::release ); } @Override public Mono putValue(T keySuffix, U value) { ByteBuf keySuffixBuf = serializeSuffix(keySuffix); ByteBuf keyBuf = toKey(keySuffixBuf.retain()); ByteBuf valueBuf = serialize(value); return dictionary.put(keyBuf.retain(), valueBuf.retain(), LLDictionaryResultType.VOID).doFinally(s -> { keyBuf.release(); keySuffixBuf.release(); valueBuf.release(); }).then(); } @Override public Mono getUpdateMode() { return dictionary.getUpdateMode(); } @Override public Mono updateValue(T keySuffix, boolean existsAlmostCertainly, Function<@Nullable U, @Nullable U> updater) { return Mono .using( () -> toKey(serializeSuffix(keySuffix)), keyBuf -> dictionary.update(keyBuf.retain(), oldSerialized -> { try { var result = updater.apply(oldSerialized == null ? null : this.deserialize(oldSerialized.retain())); if (result == null) { return null; } else { return this.serialize(result); } } finally { if (oldSerialized != null) { oldSerialized.release(); } } }, existsAlmostCertainly), ReferenceCounted::release ); } @Override public Mono putValueAndGetPrevious(T keySuffix, U value) { ByteBuf keySuffixBuf = serializeSuffix(keySuffix); ByteBuf keyBuf = toKey(keySuffixBuf.retain()); ByteBuf valueBuf = serialize(value); return dictionary .put(keyBuf.retain(), valueBuf.retain(), LLDictionaryResultType.PREVIOUS_VALUE) .map(this::deserialize) .doFinally(s -> { keyBuf.release(); keySuffixBuf.release(); valueBuf.release(); }); } @Override public Mono putValueAndGetChanged(T keySuffix, U value) { ByteBuf keySuffixBuf = serializeSuffix(keySuffix); ByteBuf keyBuf = toKey(keySuffixBuf.retain()); ByteBuf valueBuf = serialize(value); return dictionary .put(keyBuf.retain(), valueBuf.retain(), LLDictionaryResultType.PREVIOUS_VALUE) .map(this::deserialize) .map(oldValue -> !Objects.equals(oldValue, value)) .defaultIfEmpty(value != null) .doFinally(s -> { keyBuf.release(); keySuffixBuf.release(); valueBuf.release(); }); } @Override public Mono remove(T keySuffix) { return Mono .using( () -> toKey(serializeSuffix(keySuffix)), keyBuf -> dictionary.remove(keyBuf.retain(), LLDictionaryResultType.VOID).then(), ReferenceCounted::release ); } @Override public Mono removeAndGetPrevious(T keySuffix) { ByteBuf keySuffixBuf = serializeSuffix(keySuffix); ByteBuf keyBuf = toKey(keySuffixBuf.retain()); return dictionary .remove(keyBuf.retain(), LLDictionaryResultType.PREVIOUS_VALUE) .map(this::deserialize) .doFinally(s -> { keyBuf.release(); keySuffixBuf.release(); }); } @Override public Mono removeAndGetStatus(T keySuffix) { ByteBuf keySuffixBuf = serializeSuffix(keySuffix); ByteBuf keyBuf = toKey(keySuffixBuf.retain()); return dictionary .remove(keyBuf.retain(), LLDictionaryResultType.PREVIOUS_VALUE_EXISTENCE) .map(LLUtils::responseToBoolean) .doFinally(s -> { keyBuf.release(); keySuffixBuf.release(); }); } @Override public Flux> getMulti(@Nullable CompositeSnapshot snapshot, Flux keys, boolean existsAlmostCertainly) { return dictionary .getMulti(resolveSnapshot(snapshot), keys.flatMap(keySuffix -> Mono.fromCallable(() -> { ByteBuf keySuffixBuf = serializeSuffix(keySuffix); try { return toKey(keySuffixBuf.retain()); } finally { keySuffixBuf.release(); } })), existsAlmostCertainly) .flatMap(entry -> Mono .fromCallable(() -> Map.entry(deserializeSuffix(stripPrefix(entry.getKey())), deserialize(entry.getValue()))) ); } private Entry serializeEntry(T key, U value) { ByteBuf serializedKey = toKey(serializeSuffix(key)); try { ByteBuf serializedValue = serialize(value); try { return Map.entry(serializedKey.retain(), serializedValue.retain()); } finally { serializedValue.release(); } } finally { serializedKey.release(); } } @Override public Mono putMulti(Flux> entries) { var serializedEntries = entries .flatMap(entry -> Mono .fromCallable(() -> serializeEntry(entry.getKey(), entry.getValue())) ).doOnDiscard(Entry.class, entry -> { //noinspection unchecked var castedEntry = (Entry) entry; castedEntry.getKey().release(); castedEntry.getValue().release(); }); return dictionary .putMulti(serializedEntries, false) .then(); } @Override public Flux>> getAllStages(@Nullable CompositeSnapshot snapshot) { return dictionary .getRangeKeys(resolveSnapshot(snapshot), range.retain()) .map(key -> { try { return Map.entry(deserializeSuffix(stripPrefix(key.retain())), new DatabaseSingleMapped<>(new DatabaseSingle<>(dictionary, toKey(stripPrefix(key.retain())), Serializer.noop() ), valueSerializer) ); } finally { key.release(); } }); } @Override public Flux> getAllValues(@Nullable CompositeSnapshot snapshot) { return dictionary .getRange(resolveSnapshot(snapshot), range.retain()) .map(serializedEntry -> Map.entry( deserializeSuffix(stripPrefix(serializedEntry.getKey())), valueSerializer.deserialize(serializedEntry.getValue()) )) .doOnDiscard(Entry.class, entry -> { //noinspection unchecked var castedEntry = (Entry) entry; castedEntry.getKey().release(); castedEntry.getValue().release(); }); } @Override public Flux> setAllValuesAndGetPrevious(Flux> entries) { return Flux .usingWhen( Mono.just(true), b -> getAllValues(null), b -> dictionary .setRange(range.retain(), entries.map(entry -> Map.entry(toKey(serializeSuffix(entry.getKey())), serialize(entry.getValue())) ) ) ); } @Override public Mono clear() { if (range.isAll()) { return dictionary .clear(); } else if (range.isSingle()) { return dictionary .remove(range.getSingle().retain(), LLDictionaryResultType.VOID) .then(); } else { return dictionary .setRange(range.retain(), Flux.empty()); } } /** * This method is just a shorter version than valueSerializer::deserialize */ private U deserialize(ByteBuf bytes) { return valueSerializer.deserialize(bytes); } /** * This method is just a shorter version than valueSerializer::serialize */ private ByteBuf serialize(U bytes) { return valueSerializer.serialize(bytes); } }