CavalliumDBEngine/src/main/java/it/cavallium/dbengine/database/collections/DatabaseStageMap.java

253 lines
8.5 KiB
Java

package it.cavallium.dbengine.database.collections;
import static it.cavallium.dbengine.utils.StreamUtils.collectOn;
import static it.cavallium.dbengine.utils.StreamUtils.count;
import static it.cavallium.dbengine.utils.StreamUtils.executing;
import it.cavallium.dbengine.client.CompositeSnapshot;
import it.cavallium.dbengine.database.Delta;
import it.cavallium.dbengine.database.LLUtils;
import it.cavallium.dbengine.database.SubStageEntry;
import it.cavallium.dbengine.database.UpdateMode;
import it.cavallium.dbengine.database.UpdateReturnMode;
import it.cavallium.dbengine.database.serialization.KVSerializationFunction;
import it.cavallium.dbengine.database.serialization.SerializationFunction;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMaps;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings("unused")
public interface DatabaseStageMap<T, U, US extends DatabaseStage<U>> extends DatabaseStageEntry<Object2ObjectSortedMap<T, U>> {
@NotNull US at(@Nullable CompositeSnapshot snapshot, T key);
default boolean containsKey(@Nullable CompositeSnapshot snapshot, T key) {
return !this.at(snapshot, key).isEmpty(snapshot);
}
default @Nullable U getValue(@Nullable CompositeSnapshot snapshot, T key) {
return this.at(snapshot, key).get(snapshot);
}
default U getValueOrDefault(@Nullable CompositeSnapshot snapshot, T key, U defaultValue) {
return Objects.requireNonNullElse(getValue(snapshot, key), defaultValue);
}
default U getValueOrDefault(@Nullable CompositeSnapshot snapshot, T key, Supplier<U> defaultValue) {
return Objects.requireNonNullElseGet(getValue(snapshot, key), defaultValue);
}
default void putValue(T key, U value) {
at(null, key).set(value);
}
UpdateMode getUpdateMode();
default U updateValue(T key,
UpdateReturnMode updateReturnMode,
SerializationFunction<@Nullable U, @Nullable U> updater) {
return at(null, key).update(updater, updateReturnMode);
}
default Stream<Boolean> updateMulti(Stream<T> keys, KVSerializationFunction<T, @Nullable U, @Nullable U> updater) {
return keys.map(key -> this.updateValue(key, prevValue -> updater.apply(key, prevValue)));
}
default boolean updateValue(T key, SerializationFunction<@Nullable U, @Nullable U> updater) {
return LLUtils.isDeltaChanged(updateValueAndGetDelta(key, updater));
}
default Delta<U> updateValueAndGetDelta(T key, SerializationFunction<@Nullable U, @Nullable U> updater) {
return this.at(null, key).updateAndGetDelta(updater);
}
default @Nullable U putValueAndGetPrevious(T key, @Nullable U value) {
return at(null, key).setAndGetPrevious(value);
}
/**
* @return true if the key was associated with any value, false if the key didn't exist.
*/
default boolean putValueAndGetChanged(T key, @Nullable U value) {
return at(null, key).setAndGetChanged(value);
}
default void remove(T key) {
removeAndGetStatus(key);
}
default @Nullable U removeAndGetPrevious(T key) {
return at(null, key).clearAndGetPrevious();
}
default boolean removeAndGetStatus(T key) {
return removeAndGetPrevious(key) != null;
}
/**
* GetMulti must return the elements in sequence!
*/
default Stream<Optional<U>> getMulti(@Nullable CompositeSnapshot snapshot, Stream<T> keys) {
return keys.map(key -> Optional.ofNullable(this.getValue(snapshot, key)));
}
default void putMulti(Stream<Entry<T, U>> entries) {
collectOn(getDbWritePool(), entries, executing(entry -> this.putValue(entry.getKey(), entry.getValue())));
}
Stream<SubStageEntry<T, US>> getAllStages(@Nullable CompositeSnapshot snapshot, boolean smallRange);
default Stream<Entry<T, U>> getAllEntries(@Nullable CompositeSnapshot snapshot,
boolean smallRange) {
return this.getAllStages(snapshot, smallRange).map(stage -> {
var val = stage.getValue().get(snapshot);
return val != null ? Map.entry(stage.getKey(), val) : null;
}).filter(Objects::nonNull);
}
default Stream<T> getAllKeys(@Nullable CompositeSnapshot snapshot, boolean smallRange) {
return this
.getAllStages(snapshot, smallRange)
.map(SubStageEntry::getKey)
.filter(Objects::nonNull);
}
default Stream<U> getAllValues(@Nullable CompositeSnapshot snapshot, boolean smallRange) {
return this
.getAllEntries(snapshot, smallRange)
.map(Entry::getValue)
.filter(Objects::nonNull);
}
default void setAllEntries(Stream<Entry<T, U>> entries) {
setAllEntriesAndGetPrevious(entries).close();
}
Stream<Entry<T, U>> setAllEntriesAndGetPrevious(Stream<Entry<T, U>> entries);
default void clear() {
setAllEntries(Stream.empty());
}
default void replaceAllEntries(boolean canKeysChange,
Function<Entry<T, U>, @NotNull Entry<T, U>> entriesReplacer,
boolean smallRange) {
if (canKeysChange) {
try (var entries = this.getAllEntries(null, smallRange)) {
this.setAllEntries(entries.map(entriesReplacer));
}
} else {
collectOn(getDbWritePool(),
this.getAllEntries(null, smallRange).map(entriesReplacer),
executing(replacedEntry -> this.at(null, replacedEntry.getKey()).set(replacedEntry.getValue()))
);
}
}
default void replaceAll(Consumer<Entry<T, US>> entriesReplacer) {
collectOn(getDbWritePool(), this.getAllStages(null, false), executing(entriesReplacer));
}
@Override
default Object2ObjectSortedMap<T, U> setAndGetPrevious(Object2ObjectSortedMap<T, U> value) {
Object2ObjectSortedMap<T, U> map;
if (value == null) {
map = this.clearAndGetPrevious();
} else {
try (var stream = this.setAllEntriesAndGetPrevious(value.entrySet().stream())) {
map = stream.collect(Collectors.toMap(Entry::getKey,
Entry::getValue,
(a, b) -> a,
Object2ObjectLinkedOpenHashMap::new
));
}
}
return map != null && map.isEmpty() ? null : map;
}
@Override
default boolean setAndGetChanged(@Nullable Object2ObjectSortedMap<T, U> value) {
if (value != null && value.isEmpty()) {
value = null;
}
var prev = this.setAndGetPrevious(value);
if (prev == null) {
return value != null;
} else {
return !Objects.equals(prev, value);
}
}
@Override
default Delta<Object2ObjectSortedMap<T, U>> updateAndGetDelta(
SerializationFunction<@Nullable Object2ObjectSortedMap<T, U>, @Nullable Object2ObjectSortedMap<T, U>> updater) {
var updateMode = this.getUpdateMode();
if (updateMode == UpdateMode.ALLOW_UNSAFE) {
Object2ObjectSortedMap<T, U> v;
try (var stream = this.getAllEntries(null, true)) {
v = stream
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> a, Object2ObjectLinkedOpenHashMap::new));
}
if (v.isEmpty()) {
v = null;
}
var result = updater.apply(v);
if (result != null && result.isEmpty()) {
result = null;
}
this.setAllEntries(result != null ? result.entrySet().stream() : null);
return new Delta<>(v, result);
} else if (updateMode == UpdateMode.ALLOW) {
throw new UnsupportedOperationException("Maps can't be updated atomically");
} else if (updateMode == UpdateMode.DISALLOW) {
throw new UnsupportedOperationException("Map can't be updated because updates are disabled");
} else {
throw new UnsupportedOperationException("Unknown update mode: " + updateMode);
}
}
@Override
default Object2ObjectSortedMap<T, U> clearAndGetPrevious() {
return this.setAndGetPrevious(Object2ObjectSortedMaps.emptyMap());
}
@Override
default Object2ObjectSortedMap<T, U> get(@Nullable CompositeSnapshot snapshot) {
try (var stream = this.getAllEntries(snapshot, true)) {
Object2ObjectSortedMap<T, U> map = stream
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> a, Object2ObjectLinkedOpenHashMap::new));
return map.isEmpty() ? null : map;
}
}
@Override
default long leavesCount(@Nullable CompositeSnapshot snapshot, boolean fast) {
return count(this.getAllStages(snapshot, false));
}
/**
* Value getter doesn't lock data. Please make sure to lock before getting data.
*/
default ValueGetterBlocking<T, U> getDbValueGetter(@Nullable CompositeSnapshot snapshot) {
return k -> getValue(snapshot, k);
}
default ValueGetter<T, U> getAsyncDbValueGetter(@Nullable CompositeSnapshot snapshot) {
return k -> getValue(snapshot, k);
}
}