From 127c1cc392ab1c81402fc0525a6a186a93baf072 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Sat, 9 Dec 2023 01:02:08 +0100 Subject: [PATCH] Code cleanup, more tests --- pom.xml | 5 - .../it/cavallium/rockserver/core/Main.java | 1 - .../core/client/EmbeddedConnection.java | 17 +- .../core/client/RocksDBConnection.java | 1 - .../core/client/SocketConnection.java | 17 +- .../core/client/SocketConnectionUnix.java | 1 - .../rockserver/core/common/Callback.java | 3 - .../core/common/ColumnHashType.java | 4 +- .../rockserver/core/common/ColumnSchema.java | 3 +- .../rockserver/core/common/HashFunction.java | 1 - .../rockserver/core/common/RocksDBAPI.java | 19 +- .../rockserver/core/common/Utils.java | 33 ++- .../rockserver/core/config/DataSize.java | 21 +- .../rockserver/core/impl/Bucket.java | 1 - .../rockserver/core/impl/ColumnInstance.java | 3 +- .../rockserver/core/impl/DataSizeDecoder.java | 1 - .../core/impl/DbCompressionDecoder.java | 2 - .../rockserver/core/impl/EmbeddedDB.java | 66 ++--- .../rockserver/core/impl/XXHash32.java | 1 - .../rockserver/core/impl/rocksdb/REntry.java | 1 - .../core/impl/rocksdb/RocksDBLoader.java | 8 +- src/main/java/module-info.java | 1 - ...mbeddedDBFixedAndVarKeysWithValueTest.java | 5 + ...ddedDBFixedAndVarKeysWithoutValueTest.java | 31 ++ .../test/EmbeddedDBFixedWithValueTest.java | 61 ++++ .../test/EmbeddedDBFixedWithoutValueTest.java | 85 ++++++ .../core/impl/test/EmbeddedDBTest.java | 269 +++++++++++------- .../rockserver/core/test/XXHash32Test.java | 2 - 28 files changed, 450 insertions(+), 213 deletions(-) create mode 100644 src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedAndVarKeysWithValueTest.java create mode 100644 src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedAndVarKeysWithoutValueTest.java create mode 100644 src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedWithValueTest.java create mode 100644 src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedWithoutValueTest.java diff --git a/pom.xml b/pom.xml index e3122bd..b20e8bd 100644 --- a/pom.xml +++ b/pom.xml @@ -39,11 +39,6 @@ ipaddress 5.4.0 - - com.typesafe - config - 1.4.3 - org.jetbrains annotations diff --git a/src/main/java/it/cavallium/rockserver/core/Main.java b/src/main/java/it/cavallium/rockserver/core/Main.java index dcf1985..2f6d187 100644 --- a/src/main/java/it/cavallium/rockserver/core/Main.java +++ b/src/main/java/it/cavallium/rockserver/core/Main.java @@ -18,7 +18,6 @@ import net.sourceforge.argparse4j.ArgumentParsers; import net.sourceforge.argparse4j.inf.ArgumentParser; import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.Namespace; -import org.rocksdb.RocksDB; public class Main { diff --git a/src/main/java/it/cavallium/rockserver/core/client/EmbeddedConnection.java b/src/main/java/it/cavallium/rockserver/core/client/EmbeddedConnection.java index 6f974a8..0a1eb87 100644 --- a/src/main/java/it/cavallium/rockserver/core/client/EmbeddedConnection.java +++ b/src/main/java/it/cavallium/rockserver/core/client/EmbeddedConnection.java @@ -13,6 +13,7 @@ import java.net.URI; import java.nio.file.Path; import java.util.Optional; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class EmbeddedConnection extends BaseConnection { @@ -47,7 +48,7 @@ public class EmbeddedConnection extends BaseConnection { } @Override - public long createColumn(String name, ColumnSchema schema) { + public long createColumn(String name, @NotNull ColumnSchema schema) { return db.createColumn(name, schema); } @@ -57,7 +58,7 @@ public class EmbeddedConnection extends BaseConnection { } @Override - public long getColumnId(String name) { + public long getColumnId(@NotNull String name) { return db.getColumnId(name); } @@ -65,8 +66,8 @@ public class EmbeddedConnection extends BaseConnection { public T put(Arena arena, long transactionId, long columnId, - MemorySegment[] keys, - @Nullable MemorySegment value, + @NotNull MemorySegment @NotNull [] keys, + @NotNull MemorySegment value, PutCallback callback) throws RocksDBException { return db.put(arena, transactionId, columnId, keys, value, callback); } @@ -75,7 +76,7 @@ public class EmbeddedConnection extends BaseConnection { public T get(Arena arena, long transactionId, long columnId, - MemorySegment[] keys, + MemorySegment @NotNull [] keys, GetCallback callback) throws RocksDBException { return db.get(arena, transactionId, columnId, keys, callback); } @@ -84,7 +85,7 @@ public class EmbeddedConnection extends BaseConnection { public long openIterator(Arena arena, long transactionId, long columnId, - MemorySegment[] startKeysInclusive, + @NotNull MemorySegment @NotNull [] startKeysInclusive, @Nullable MemorySegment[] endKeysExclusive, boolean reverse, long timeoutMs) throws RocksDBException { @@ -97,7 +98,7 @@ public class EmbeddedConnection extends BaseConnection { } @Override - public void seekTo(Arena arena, long iterationId, MemorySegment[] keys) throws RocksDBException { + public void seekTo(Arena arena, long iterationId, MemorySegment @NotNull [] keys) throws RocksDBException { db.seekTo(arena, iterationId, keys); } @@ -106,7 +107,7 @@ public class EmbeddedConnection extends BaseConnection { long iterationId, long skipCount, long takeCount, - IteratorCallback callback) throws RocksDBException { + @NotNull IteratorCallback callback) throws RocksDBException { return db.subsequent(arena, iterationId, skipCount, takeCount, callback); } } diff --git a/src/main/java/it/cavallium/rockserver/core/client/RocksDBConnection.java b/src/main/java/it/cavallium/rockserver/core/client/RocksDBConnection.java index 369aaee..768b0f5 100644 --- a/src/main/java/it/cavallium/rockserver/core/client/RocksDBConnection.java +++ b/src/main/java/it/cavallium/rockserver/core/client/RocksDBConnection.java @@ -3,7 +3,6 @@ package it.cavallium.rockserver.core.client; import it.cavallium.rockserver.core.common.RocksDBAPI; import java.io.Closeable; import java.net.URI; -import org.jetbrains.annotations.Nullable; public interface RocksDBConnection extends Closeable, RocksDBAPI { diff --git a/src/main/java/it/cavallium/rockserver/core/client/SocketConnection.java b/src/main/java/it/cavallium/rockserver/core/client/SocketConnection.java index c1d757e..4021a2b 100644 --- a/src/main/java/it/cavallium/rockserver/core/client/SocketConnection.java +++ b/src/main/java/it/cavallium/rockserver/core/client/SocketConnection.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.net.SocketAddress; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public abstract class SocketConnection extends BaseConnection { @@ -39,7 +40,7 @@ public abstract class SocketConnection extends BaseConnection { } @Override - public long createColumn(String name, ColumnSchema schema) { + public long createColumn(String name, @NotNull ColumnSchema schema) { throw new UnsupportedOperationException(); } @@ -49,7 +50,7 @@ public abstract class SocketConnection extends BaseConnection { } @Override - public long getColumnId(String name) { + public long getColumnId(@NotNull String name) { throw new UnsupportedOperationException(); } @@ -57,8 +58,8 @@ public abstract class SocketConnection extends BaseConnection { public T put(Arena arena, long transactionId, long columnId, - MemorySegment[] keys, - @Nullable MemorySegment value, + MemorySegment @NotNull [] keys, + @NotNull MemorySegment value, PutCallback callback) throws RocksDBException { throw new UnsupportedOperationException(); } @@ -67,7 +68,7 @@ public abstract class SocketConnection extends BaseConnection { public T get(Arena arena, long transactionId, long columnId, - MemorySegment[] keys, + MemorySegment @NotNull [] keys, GetCallback callback) throws RocksDBException { throw new UnsupportedOperationException(); } @@ -76,7 +77,7 @@ public abstract class SocketConnection extends BaseConnection { public long openIterator(Arena arena, long transactionId, long columnId, - MemorySegment[] startKeysInclusive, + MemorySegment @NotNull [] startKeysInclusive, @Nullable MemorySegment[] endKeysExclusive, boolean reverse, long timeoutMs) throws RocksDBException { @@ -89,7 +90,7 @@ public abstract class SocketConnection extends BaseConnection { } @Override - public void seekTo(Arena arena, long iterationId, MemorySegment[] keys) throws RocksDBException { + public void seekTo(Arena arena, long iterationId, MemorySegment @NotNull [] keys) throws RocksDBException { throw new UnsupportedOperationException(); } @@ -98,7 +99,7 @@ public abstract class SocketConnection extends BaseConnection { long iterationId, long skipCount, long takeCount, - IteratorCallback callback) throws RocksDBException { + @NotNull IteratorCallback callback) throws RocksDBException { throw new UnsupportedOperationException(); } } diff --git a/src/main/java/it/cavallium/rockserver/core/client/SocketConnectionUnix.java b/src/main/java/it/cavallium/rockserver/core/client/SocketConnectionUnix.java index 119777e..9f92c4f 100644 --- a/src/main/java/it/cavallium/rockserver/core/client/SocketConnectionUnix.java +++ b/src/main/java/it/cavallium/rockserver/core/client/SocketConnectionUnix.java @@ -1,7 +1,6 @@ package it.cavallium.rockserver.core.client; import java.io.IOException; -import java.net.SocketAddress; import java.net.URI; import java.net.UnixDomainSocketAddress; import java.nio.file.Files; diff --git a/src/main/java/it/cavallium/rockserver/core/common/Callback.java b/src/main/java/it/cavallium/rockserver/core/common/Callback.java index eabd243..fe5e006 100644 --- a/src/main/java/it/cavallium/rockserver/core/common/Callback.java +++ b/src/main/java/it/cavallium/rockserver/core/common/Callback.java @@ -1,9 +1,6 @@ package it.cavallium.rockserver.core.common; -import java.lang.foreign.MemorySegment; import java.util.List; -import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicReference; import org.jetbrains.annotations.Nullable; public sealed interface Callback { diff --git a/src/main/java/it/cavallium/rockserver/core/common/ColumnHashType.java b/src/main/java/it/cavallium/rockserver/core/common/ColumnHashType.java index 9e9fc99..e4923ec 100644 --- a/src/main/java/it/cavallium/rockserver/core/common/ColumnHashType.java +++ b/src/main/java/it/cavallium/rockserver/core/common/ColumnHashType.java @@ -11,9 +11,7 @@ public enum ColumnHashType implements HashFunction { var xxHash = XXHash32.getInstance().hash(Utils.toByteArray(inputData), 0, Math.toIntExact(inputData.byteSize()), 0); hashResult.set(ColumnInstance.BIG_ENDIAN_BYTES, 0, (byte) xxHash); }), - ALLSAME8(Byte.BYTES, (inputData, hashResult) -> { - hashResult.set(ColumnInstance.BIG_ENDIAN_BYTES, 0, (byte) 0); - }); + ALLSAME8(Byte.BYTES, (inputData, hashResult) -> hashResult.set(ColumnInstance.BIG_ENDIAN_BYTES, 0, (byte) 0)); private final int bytes; private final HashFunction hashFunction; diff --git a/src/main/java/it/cavallium/rockserver/core/common/ColumnSchema.java b/src/main/java/it/cavallium/rockserver/core/common/ColumnSchema.java index 5080af0..56e7691 100644 --- a/src/main/java/it/cavallium/rockserver/core/common/ColumnSchema.java +++ b/src/main/java/it/cavallium/rockserver/core/common/ColumnSchema.java @@ -4,7 +4,6 @@ import it.cavallium.rockserver.core.common.RocksDBException.RocksDBErrorType; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.ObjectList; -import java.lang.foreign.MemorySegment; public record ColumnSchema(IntList keys, ObjectList variableTailKeys, boolean hasValue) { @@ -44,7 +43,7 @@ public record ColumnSchema(IntList keys, ObjectList variableTail /** * Keys with their length - * @return an array with the length of each key, variable-length keys must have the length of their hash + * @return an int list with the length of each key, variable-length keys must have the length of their hash */ @Override public IntList keys() { diff --git a/src/main/java/it/cavallium/rockserver/core/common/HashFunction.java b/src/main/java/it/cavallium/rockserver/core/common/HashFunction.java index e389ff0..f7dfe25 100644 --- a/src/main/java/it/cavallium/rockserver/core/common/HashFunction.java +++ b/src/main/java/it/cavallium/rockserver/core/common/HashFunction.java @@ -1,6 +1,5 @@ package it.cavallium.rockserver.core.common; -import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; public interface HashFunction { diff --git a/src/main/java/it/cavallium/rockserver/core/common/RocksDBAPI.java b/src/main/java/it/cavallium/rockserver/core/common/RocksDBAPI.java index 2d3bcb6..3e613e4 100644 --- a/src/main/java/it/cavallium/rockserver/core/common/RocksDBAPI.java +++ b/src/main/java/it/cavallium/rockserver/core/common/RocksDBAPI.java @@ -5,6 +5,7 @@ import it.cavallium.rockserver.core.common.Callback.IteratorCallback; import it.cavallium.rockserver.core.common.Callback.PutCallback; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public interface RocksDBAPI { @@ -31,7 +32,7 @@ public interface RocksDBAPI { * @param schema column key-value schema * @return column id */ - long createColumn(String name, ColumnSchema schema) throws RocksDBException; + long createColumn(String name, @NotNull ColumnSchema schema) throws RocksDBException; /** * Delete a column @@ -44,7 +45,7 @@ public interface RocksDBAPI { * @param name column name * @return column id */ - long getColumnId(String name) throws RocksDBException; + long getColumnId(@NotNull String name) throws RocksDBException; /** * Put an element into the specified position @@ -58,8 +59,8 @@ public interface RocksDBAPI { T put(Arena arena, long transactionId, long columnId, - MemorySegment[] keys, - @Nullable MemorySegment value, + @NotNull MemorySegment @NotNull[] keys, + @NotNull MemorySegment value, PutCallback callback) throws RocksDBException; /** @@ -73,7 +74,7 @@ public interface RocksDBAPI { T get(Arena arena, long transactionId, long columnId, - MemorySegment[] keys, + @NotNull MemorySegment @NotNull[] keys, GetCallback callback) throws RocksDBException; /** @@ -90,8 +91,8 @@ public interface RocksDBAPI { long openIterator(Arena arena, long transactionId, long columnId, - MemorySegment[] startKeysInclusive, - @Nullable MemorySegment[] endKeysExclusive, + @NotNull MemorySegment @NotNull[] startKeysInclusive, + @NotNull MemorySegment @Nullable[] endKeysExclusive, boolean reverse, long timeoutMs) throws RocksDBException; @@ -107,7 +108,7 @@ public interface RocksDBAPI { * @param iterationId iteration id * @param keys keys, inclusive. [] means "the beginning" */ - void seekTo(Arena arena, long iterationId, MemorySegment[] keys) throws RocksDBException; + void seekTo(Arena arena, long iterationId, @NotNull MemorySegment @NotNull[] keys) throws RocksDBException; /** * Get the subsequent element during an iteration @@ -121,5 +122,5 @@ public interface RocksDBAPI { long iterationId, long skipCount, long takeCount, - IteratorCallback callback) throws RocksDBException; + @NotNull IteratorCallback callback) throws RocksDBException; } diff --git a/src/main/java/it/cavallium/rockserver/core/common/Utils.java b/src/main/java/it/cavallium/rockserver/core/common/Utils.java index bd273c8..7d3dde1 100644 --- a/src/main/java/it/cavallium/rockserver/core/common/Utils.java +++ b/src/main/java/it/cavallium/rockserver/core/common/Utils.java @@ -5,7 +5,9 @@ import static java.lang.foreign.MemorySegment.NULL; import static java.util.Objects.requireNonNullElse; import java.io.IOException; +import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -16,9 +18,17 @@ import java.util.function.Function; import java.util.stream.Stream; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public class Utils { + @SuppressWarnings("resource") + private static final MemorySegment DUMMY_EMPTY_VALUE = Arena + .global() + .allocate(ValueLayout.JAVA_BYTE, (byte) -1) + .asReadOnly(); + + public static MemorySegment dummyEmptyValue() { + return DUMMY_EMPTY_VALUE; + } /** * Returns the value of the {@code int} argument, throwing an exception if the value overflows an {@code char}. @@ -61,6 +71,14 @@ public class Utils { } } + public static MemorySegment toMemorySegment(Arena arena, byte... array) { + if (array != null) { + return arena.allocateArray(ValueLayout.JAVA_BYTE, array); + } else { + return MemorySegment.NULL; + } + } + @NotNull public static MemorySegment toMemorySegmentSimple(int... array) { if (array != null) { @@ -74,6 +92,19 @@ public class Utils { } } + @NotNull + public static MemorySegment toMemorySegmentSimple(Arena arena, int... array) { + if (array != null) { + var newArray = new byte[array.length]; + for (int i = 0; i < array.length; i++) { + newArray[i] = (byte) array[i]; + } + return arena.allocateArray(ValueLayout.JAVA_BYTE, newArray); + } else { + return MemorySegment.NULL; + } + } + public static byte[] toByteArray(MemorySegment memorySegment) { return memorySegment.toArray(BIG_ENDIAN_BYTES); } diff --git a/src/main/java/it/cavallium/rockserver/core/config/DataSize.java b/src/main/java/it/cavallium/rockserver/core/config/DataSize.java index 7fb8940..f5353a4 100644 --- a/src/main/java/it/cavallium/rockserver/core/config/DataSize.java +++ b/src/main/java/it/cavallium/rockserver/core/config/DataSize.java @@ -88,13 +88,27 @@ public final class DataSize extends Number implements Comparable { return; } // Measurements are like B, MB, or MiB, not longer + final var scale = getScale(size, numberEndOffset); + this.size = (negative ? -1 : 1) * number * scale; + } + + private static int getScale(String size, int numberEndOffset) { if (size.length() - numberEndOffset > 3) { throw new IllegalArgumentException("Wrong measurement unit"); } var scaleChar = size.charAt(numberEndOffset); boolean powerOf2 = numberEndOffset + 1 < size.length() && size.charAt(numberEndOffset + 1) == 'i'; + final var scale = getScale(powerOf2, scaleChar); + // if scale is 1, the unit should be "B", nothing more + if (scale == 1 && numberEndOffset + 1 != size.length()) { + throw new IllegalArgumentException("Invalid unit"); + } + return scale; + } + + private static int getScale(boolean powerOf2, char scaleChar) { int k = powerOf2 ? 1024 : 1000; - var scale = switch (scaleChar) { + return switch (scaleChar) { case 'B' -> 1; case 'b' -> throw new IllegalArgumentException("Bits are not allowed"); case 'K', 'k' -> k; @@ -107,11 +121,6 @@ public final class DataSize extends Number implements Comparable { case 'Y', 'y' -> k * k * k * k * k * k * k * k; default -> throw new IllegalStateException("Unexpected value: " + scaleChar); }; - // if scale is 1, the unit should be "B", nothing more - if (scale == 1 && numberEndOffset + 1 != size.length()) { - throw new IllegalArgumentException("Invalid unit"); - } - this.size = (negative ? -1 : 1) * number * scale; } public static Long get(DataSize value) { diff --git a/src/main/java/it/cavallium/rockserver/core/impl/Bucket.java b/src/main/java/it/cavallium/rockserver/core/impl/Bucket.java index 3e64e33..b590ec7 100644 --- a/src/main/java/it/cavallium/rockserver/core/impl/Bucket.java +++ b/src/main/java/it/cavallium/rockserver/core/impl/Bucket.java @@ -7,7 +7,6 @@ import static java.lang.Math.toIntExact; import it.cavallium.rockserver.core.common.Utils; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; import java.util.ArrayList; import java.util.Map; import java.util.Map.Entry; diff --git a/src/main/java/it/cavallium/rockserver/core/impl/ColumnInstance.java b/src/main/java/it/cavallium/rockserver/core/impl/ColumnInstance.java index 187d2f3..e0b48b3 100644 --- a/src/main/java/it/cavallium/rockserver/core/impl/ColumnInstance.java +++ b/src/main/java/it/cavallium/rockserver/core/impl/ColumnInstance.java @@ -1,7 +1,6 @@ package it.cavallium.rockserver.core.impl; import static it.cavallium.rockserver.core.common.Utils.toCharExact; -import static java.lang.Math.toIntExact; import it.cavallium.rockserver.core.common.ColumnSchema; import it.cavallium.rockserver.core.common.RocksDBException; @@ -141,7 +140,7 @@ public record ColumnInstance(ColumnFamilyHandle cfh, ColumnSchema schema, int fi } public void checkNullableValue(MemorySegment value) { - if (schema.hasValue() == (value == null)) { + if (schema.hasValue() == (value == null || value == MemorySegment.NULL)) { if (schema.hasValue()) { throw RocksDBException.of(RocksDBErrorType.UNEXPECTED_NULL_VALUE, "Schema expects a value, but a null value has been passed"); diff --git a/src/main/java/it/cavallium/rockserver/core/impl/DataSizeDecoder.java b/src/main/java/it/cavallium/rockserver/core/impl/DataSizeDecoder.java index f4480ef..de3969f 100644 --- a/src/main/java/it/cavallium/rockserver/core/impl/DataSizeDecoder.java +++ b/src/main/java/it/cavallium/rockserver/core/impl/DataSizeDecoder.java @@ -4,7 +4,6 @@ import it.cavallium.rockserver.core.config.DataSize; import java.util.List; import org.github.gestalt.config.decoder.Decoder; import org.github.gestalt.config.decoder.DecoderContext; -import org.github.gestalt.config.decoder.DecoderService; import org.github.gestalt.config.decoder.Priority; import org.github.gestalt.config.entity.ValidationError; import org.github.gestalt.config.node.ConfigNode; diff --git a/src/main/java/it/cavallium/rockserver/core/impl/DbCompressionDecoder.java b/src/main/java/it/cavallium/rockserver/core/impl/DbCompressionDecoder.java index a33a611..c603439 100644 --- a/src/main/java/it/cavallium/rockserver/core/impl/DbCompressionDecoder.java +++ b/src/main/java/it/cavallium/rockserver/core/impl/DbCompressionDecoder.java @@ -1,11 +1,9 @@ package it.cavallium.rockserver.core.impl; -import it.cavallium.rockserver.core.config.DataSize; import it.cavallium.rockserver.core.config.DatabaseCompression; import java.util.List; import org.github.gestalt.config.decoder.Decoder; import org.github.gestalt.config.decoder.DecoderContext; -import org.github.gestalt.config.decoder.DecoderService; import org.github.gestalt.config.decoder.Priority; import org.github.gestalt.config.entity.ValidationError; import org.github.gestalt.config.node.ConfigNode; diff --git a/src/main/java/it/cavallium/rockserver/core/impl/EmbeddedDB.java b/src/main/java/it/cavallium/rockserver/core/impl/EmbeddedDB.java index cfa7b5c..7ca031e 100644 --- a/src/main/java/it/cavallium/rockserver/core/impl/EmbeddedDB.java +++ b/src/main/java/it/cavallium/rockserver/core/impl/EmbeddedDB.java @@ -2,8 +2,6 @@ package it.cavallium.rockserver.core.impl; import static it.cavallium.rockserver.core.common.Utils.toMemorySegment; import static it.cavallium.rockserver.core.impl.ColumnInstance.BIG_ENDIAN_BYTES; -import static java.lang.foreign.MemorySegment.NULL; -import static java.util.Objects.requireNonNullElse; import static org.rocksdb.KeyMayExist.KeyMayExistEnum.kExistsWithValue; import static org.rocksdb.KeyMayExist.KeyMayExistEnum.kExistsWithoutValue; @@ -36,10 +34,10 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import org.cliffc.high_scale_lib.NonBlockingHashMapLong; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.rocksdb.ColumnFamilyDescriptor; import org.rocksdb.ColumnFamilyHandle; @@ -57,9 +55,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { private static final boolean USE_FAST_GET = true; private final Logger logger; private final @Nullable Path path; - private final @Nullable Path embeddedConfigPath; - private final DatabaseConfig config; - private TransactionalDB db; + private final TransactionalDB db; private final NonBlockingHashMapLong columns; private final ConcurrentMap columnNamesIndex; private final NonBlockingHashMapLong> txs; @@ -69,17 +65,16 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { public EmbeddedDB(@Nullable Path path, String name, @Nullable Path embeddedConfigPath) { this.path = path; - this.embeddedConfigPath = embeddedConfigPath; this.logger = Logger.getLogger("db." + name); this.columns = new NonBlockingHashMapLong<>(); this.txs = new NonBlockingHashMapLong<>(); this.its = new NonBlockingHashMapLong<>(); this.columnNamesIndex = new ConcurrentHashMap<>(); this.ops = new SafeShutdown(); - this.config = ConfigParser.parse(this.embeddedConfigPath); + DatabaseConfig config = ConfigParser.parse(embeddedConfigPath); this.db = RocksDBLoader.load(path, config, logger); if (Boolean.parseBoolean(System.getProperty("rockserver.core.print-config", "true"))) { - logger.log(Level.INFO, "Database configuration: {0}", ConfigPrinter.stringify(this.config)); + logger.log(Level.INFO, "Database configuration: {0}", ConfigPrinter.stringify(config)); } } @@ -87,7 +82,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { * The column must be registered once!!! * Do not try to register a column that may already be registered */ - private long registerColumn(ColumnInstance column) { + private long registerColumn(@NotNull ColumnInstance column) { try { var columnName = new String(column.cfh().getName(), StandardCharsets.UTF_8); long id = FastRandomUtils.allocateNewValue(this.columns, column, 1, Long.MAX_VALUE); @@ -182,7 +177,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } } - private boolean closeTransaction(REntry tx, boolean commit) { + private boolean closeTransaction(@NotNull REntry tx, boolean commit) { ops.beginOp(); try { // Transaction found @@ -211,7 +206,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } } - private boolean commitTxOptimistically(REntry tx) throws RocksDBException { + private boolean commitTxOptimistically(@NotNull REntry tx) throws RocksDBException { try { tx.val().commit(); return true; @@ -225,7 +220,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } @Override - public long createColumn(String name, ColumnSchema schema) throws it.cavallium.rockserver.core.common.RocksDBException { + public long createColumn(String name, @NotNull ColumnSchema schema) throws it.cavallium.rockserver.core.common.RocksDBException { ops.beginOp(); try { synchronized (columnEditLock) { @@ -272,7 +267,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } @Override - public long getColumnId(String name) { + public long getColumnId(@NotNull String name) { var columnId = getColumnIdOrNull(name); if (columnId == null) { throw it.cavallium.rockserver.core.common.RocksDBException.of(RocksDBErrorType.COLUMN_NOT_FOUND, @@ -282,7 +277,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } } - private Long getColumnIdOrNull(String name) { + private Long getColumnIdOrNull(@NotNull String name) { var columnId = (long) columnNamesIndex.getOrDefault(name, -1L); ColumnInstance col; if (columnId == -1L || (col = columns.get(columnId)) == null || !col.cfh().isOwningHandle()) { @@ -296,8 +291,8 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { public T put(Arena arena, long transactionId, long columnId, - MemorySegment[] keys, - @Nullable MemorySegment value, + @NotNull MemorySegment @NotNull [] keys, + @NotNull MemorySegment value, PutCallback callback) throws it.cavallium.rockserver.core.common.RocksDBException { ops.beginOp(); try { @@ -320,10 +315,10 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } private U put(Arena arena, - REntry tx, + @Nullable REntry tx, ColumnInstance col, - MemorySegment[] keys, - @Nullable MemorySegment value, + @NotNull MemorySegment @NotNull[] keys, + @NotNull MemorySegment value, PutCallback callback) throws it.cavallium.rockserver.core.common.RocksDBException { // Check for null value col.checkNullableValue(value); @@ -335,7 +330,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { var bucketElementKeys = col.getBucketElementKeys(keys); try (var readOptions = new ReadOptions()) { var previousRawBucketByteArray = tx.val().getForUpdate(readOptions, col.cfh(), calculatedKey.toArray(BIG_ENDIAN_BYTES), true); - MemorySegment previousRawBucket = toMemorySegment(previousRawBucketByteArray); + MemorySegment previousRawBucket = toMemorySegment(arena, previousRawBucketByteArray); var bucket = new Bucket(col, previousRawBucket); previousValue = bucket.addElement(bucketElementKeys, value); tx.val().put(col.cfh(), Utils.toByteArray(calculatedKey), Utils.toByteArray(bucket.toSegment(arena))); @@ -364,7 +359,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } else { previousValueByteArray = db.get().get(col.cfh(), readOptions, calculatedKey.toArray(BIG_ENDIAN_BYTES)); } - previousValue = toMemorySegment(previousValueByteArray); + previousValue = toMemorySegment(arena, previousValueByteArray); } catch (RocksDBException e) { throw it.cavallium.rockserver.core.common.RocksDBException.of(RocksDBErrorType.PUT_2, e); } @@ -372,10 +367,12 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { previousValue = null; } if (tx != null) { - tx.val().put(col.cfh(), Utils.toByteArray(calculatedKey), Utils.toByteArray(requireNonNullElse(value, NULL))); + tx.val().put(col.cfh(), Utils.toByteArray(calculatedKey), Utils.toByteArray(value)); } else { try (var w = new WriteOptions()) { - db.get().put(col.cfh(), w, calculatedKey.asByteBuffer(), requireNonNullElse(value, NULL).asByteBuffer()); + var keyBB = calculatedKey.asByteBuffer(); + ByteBuffer valueBB = (col.schema().hasValue() ? value : Utils.dummyEmptyValue()).asByteBuffer(); + db.get().put(col.cfh(), w, keyBB, valueBB); } } } @@ -396,7 +393,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { public T get(Arena arena, long transactionId, long columnId, - MemorySegment[] keys, + MemorySegment @NotNull [] keys, GetCallback callback) throws it.cavallium.rockserver.core.common.RocksDBException { ops.beginOp(); try { @@ -465,7 +462,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { public long openIterator(Arena arena, long transactionId, long columnId, - MemorySegment[] startKeysInclusive, + MemorySegment @NotNull [] startKeysInclusive, @Nullable MemorySegment[] endKeysExclusive, boolean reverse, long timeoutMs) throws it.cavallium.rockserver.core.common.RocksDBException { @@ -501,7 +498,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } @Override - public void seekTo(Arena arena, long iterationId, MemorySegment[] keys) + public void seekTo(Arena arena, long iterationId, @NotNull MemorySegment @NotNull [] keys) throws it.cavallium.rockserver.core.common.RocksDBException { ops.beginOp(); try { @@ -516,7 +513,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { long iterationId, long skipCount, long takeCount, - IteratorCallback callback) throws it.cavallium.rockserver.core.common.RocksDBException { + @NotNull IteratorCallback callback) throws it.cavallium.rockserver.core.common.RocksDBException { ops.beginOp(); try { throw new UnsupportedOperationException(); @@ -532,14 +529,14 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { MemorySegment calculatedKey) throws RocksDBException { if (tx != null) { var previousRawBucketByteArray = tx.val().get(col.cfh(), readOptions, calculatedKey.toArray(BIG_ENDIAN_BYTES)); - return toMemorySegment(previousRawBucketByteArray); + return toMemorySegment(arena, previousRawBucketByteArray); } else { var db = this.db.get(); if (USE_FAST_GET) { return dbGetDirect(arena, col.cfh(), readOptions, calculatedKey); } else { var previousRawBucketByteArray = db.get(col.cfh(), readOptions, calculatedKey.toArray(BIG_ENDIAN_BYTES)); - return toMemorySegment(previousRawBucketByteArray); + return toMemorySegment(arena, previousRawBucketByteArray); } } } @@ -557,12 +554,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { case kNotExist -> null; case kExistsWithValue, kExistsWithoutValue -> { // At the beginning, size reflects the expected size, then it becomes the real data size - int size; - if (keyMayExist.exists == kExistsWithValue) { - size = keyMayExist.valueLength; - } else { - size = -1; - } + int size = keyMayExist.exists == kExistsWithValue ? keyMayExist.valueLength : -1; if (keyMayExist.exists == kExistsWithoutValue || size > resultBuffer.limit()) { if (size > resultBuffer.capacity()) { resultBuffer = arena.allocate(size).asByteBuffer(); @@ -600,7 +592,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable { } } - public Path getPath() { + public @Nullable Path getPath() { return path; } diff --git a/src/main/java/it/cavallium/rockserver/core/impl/XXHash32.java b/src/main/java/it/cavallium/rockserver/core/impl/XXHash32.java index 5e0c008..363f45b 100644 --- a/src/main/java/it/cavallium/rockserver/core/impl/XXHash32.java +++ b/src/main/java/it/cavallium/rockserver/core/impl/XXHash32.java @@ -14,7 +14,6 @@ package it.cavallium.rockserver.core.impl; * limitations under the License. */ -import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; /** diff --git a/src/main/java/it/cavallium/rockserver/core/impl/rocksdb/REntry.java b/src/main/java/it/cavallium/rockserver/core/impl/rocksdb/REntry.java index e3d1190..eb62771 100644 --- a/src/main/java/it/cavallium/rockserver/core/impl/rocksdb/REntry.java +++ b/src/main/java/it/cavallium/rockserver/core/impl/rocksdb/REntry.java @@ -2,7 +2,6 @@ package it.cavallium.rockserver.core.impl.rocksdb; import java.io.Closeable; import org.rocksdb.AbstractNativeReference; -import org.rocksdb.Status.Code; public record REntry(T val, RocksDBObjects objs) implements Closeable { diff --git a/src/main/java/it/cavallium/rockserver/core/impl/rocksdb/RocksDBLoader.java b/src/main/java/it/cavallium/rockserver/core/impl/rocksdb/RocksDBLoader.java index 5524e5a..daa68fb 100644 --- a/src/main/java/it/cavallium/rockserver/core/impl/rocksdb/RocksDBLoader.java +++ b/src/main/java/it/cavallium/rockserver/core/impl/rocksdb/RocksDBLoader.java @@ -6,7 +6,6 @@ import org.github.gestalt.config.exceptions.GestaltException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.rocksdb.*; -import org.rocksdb.util.Environment; import org.rocksdb.util.SizeUnit; import java.io.IOException; @@ -141,7 +140,6 @@ public class RocksDBLoader { .orElse(0L); if (isDisableAutoCompactions()) { - options.setMaxBackgroundCompactions(0); options.setMaxBackgroundJobs(0); } else { var backgroundJobs = Integer.parseInt(System.getProperty("it.cavallium.dbengine.jobs.background.num", "-1")); @@ -371,7 +369,7 @@ public class RocksDBLoader { columnFamilyOptions.setBottommostCompressionOptions(lastLevelOptions.compressionOptions); List compressionPerLevel = new ArrayList<>(); - for (ColumnLevelConfig columnLevelConfig : List.of(columnOptions.levels())) { + for (ColumnLevelConfig columnLevelConfig : columnOptions.levels()) { CompressionType compression = columnLevelConfig.compression(); compressionPerLevel.add(compression); } @@ -532,9 +530,7 @@ public class RocksDBLoader { logger.log(Level.FINE, "Failed to obtain stats", ex); } return TransactionalDB.create(definitiveDbPath.toString(), db); - } catch (IOException ex) { - throw it.cavallium.rockserver.core.common.RocksDBException.of(it.cavallium.rockserver.core.common.RocksDBException.RocksDBErrorType.ROCKSDB_LOAD_ERROR, "Failed to load rocksdb", ex); - } catch (RocksDBException ex) { + } catch (IOException | RocksDBException ex) { throw it.cavallium.rockserver.core.common.RocksDBException.of(it.cavallium.rockserver.core.common.RocksDBException.RocksDBErrorType.ROCKSDB_LOAD_ERROR, "Failed to load rocksdb", ex); } catch (GestaltException e) { throw it.cavallium.rockserver.core.common.RocksDBException.of(it.cavallium.rockserver.core.common.RocksDBException.RocksDBErrorType.CONFIG_ERROR, "Failed to load rocksdb", e); diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index ee9bf6b..457dc21 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -3,7 +3,6 @@ module rockserver.core { requires net.sourceforge.argparse4j; requires inet.ipaddr; requires java.logging; - requires typesafe.config; requires org.jetbrains.annotations; requires high.scale.lib; requires org.github.gestalt.core; diff --git a/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedAndVarKeysWithValueTest.java b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedAndVarKeysWithValueTest.java new file mode 100644 index 0000000..f58add9 --- /dev/null +++ b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedAndVarKeysWithValueTest.java @@ -0,0 +1,5 @@ +package it.cavallium.rockserver.core.impl.test; + +public class EmbeddedDBFixedAndVarKeysWithValueTest extends EmbeddedDBTest { + +} diff --git a/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedAndVarKeysWithoutValueTest.java b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedAndVarKeysWithoutValueTest.java new file mode 100644 index 0000000..088ee8b --- /dev/null +++ b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedAndVarKeysWithoutValueTest.java @@ -0,0 +1,31 @@ +package it.cavallium.rockserver.core.impl.test; + +import java.lang.foreign.MemorySegment; + +public class EmbeddedDBFixedAndVarKeysWithoutValueTest extends EmbeddedDBTest { + + @Override + protected MemorySegment getValue1() { + return MemorySegment.NULL; + } + + @Override + protected MemorySegment getValue2() { + return MemorySegment.NULL; + } + + @Override + protected MemorySegment getValueI(int i) { + return MemorySegment.NULL; + } + + @Override + protected boolean getHasValues() { + return false; + } + + @Override + protected MemorySegment getBigValue() { + return MemorySegment.NULL; + } +} diff --git a/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedWithValueTest.java b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedWithValueTest.java new file mode 100644 index 0000000..e6ab89b --- /dev/null +++ b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedWithValueTest.java @@ -0,0 +1,61 @@ +package it.cavallium.rockserver.core.impl.test; + +import static it.cavallium.rockserver.core.common.Utils.toMemorySegmentSimple; + +import it.cavallium.rockserver.core.common.ColumnHashType; +import it.unimi.dsi.fastutil.objects.ObjectList; +import java.lang.foreign.MemorySegment; + +public class EmbeddedDBFixedWithValueTest extends EmbeddedDBTest { + + @Override + protected MemorySegment[] getKeyI(int i) { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 7), + toMemorySegmentSimple(arena, i) + }; + } + + @Override + protected MemorySegment[] getNotFoundKeyI(int i) { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 5, 6), + toMemorySegmentSimple(arena, i) + }; + } + + @Override + protected MemorySegment[] getKey1() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 3) + }; + } + + @Override + protected MemorySegment[] getKey2() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 4) + }; + } + + @Override + protected MemorySegment[] getCollidingKey1() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 5) + }; + } + + @Override + protected ObjectList getSchemaVarKeys() { + return ObjectList.of(); + } + +} diff --git a/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedWithoutValueTest.java b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedWithoutValueTest.java new file mode 100644 index 0000000..f36c195 --- /dev/null +++ b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBFixedWithoutValueTest.java @@ -0,0 +1,85 @@ +package it.cavallium.rockserver.core.impl.test; + +import static it.cavallium.rockserver.core.common.Utils.toMemorySegmentSimple; + +import it.cavallium.rockserver.core.common.ColumnHashType; +import it.unimi.dsi.fastutil.objects.ObjectList; +import java.lang.foreign.MemorySegment; + +public class EmbeddedDBFixedWithoutValueTest extends EmbeddedDBTest { + + @Override + protected boolean getHasValues() { + return false; + } + + @Override + protected MemorySegment[] getKeyI(int i) { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 7), + toMemorySegmentSimple(arena, i) + }; + } + + @Override + protected MemorySegment[] getNotFoundKeyI(int i) { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 5, 6), + toMemorySegmentSimple(arena, i) + }; + } + + @Override + protected MemorySegment[] getKey1() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 3) + }; + } + + @Override + protected MemorySegment[] getKey2() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 4) + }; + } + + @Override + protected MemorySegment[] getCollidingKey1() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 5) + }; + } + + @Override + protected ObjectList getSchemaVarKeys() { + return ObjectList.of(); + } + + @Override + protected MemorySegment getValue1() { + return MemorySegment.NULL; + } + + @Override + protected MemorySegment getValue2() { + return MemorySegment.NULL; + } + + @Override + protected MemorySegment getValueI(int i) { + return MemorySegment.NULL; + } + + @Override + protected MemorySegment getBigValue() { + return MemorySegment.NULL; + } +} diff --git a/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java index b7ff879..9fd1f49 100644 --- a/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java +++ b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java @@ -6,6 +6,7 @@ import it.cavallium.rockserver.core.client.EmbeddedConnection; import it.cavallium.rockserver.core.common.Callback; import it.cavallium.rockserver.core.common.ColumnHashType; import it.cavallium.rockserver.core.common.ColumnSchema; +import it.cavallium.rockserver.core.common.Delta; import it.cavallium.rockserver.core.common.Utils; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.ObjectList; @@ -15,18 +16,19 @@ import org.junit.jupiter.api.Assertions; import java.io.IOException; import java.lang.foreign.MemorySegment; +import org.junit.jupiter.api.Test; -class EmbeddedDBTest { +abstract class EmbeddedDBTest { - private EmbeddedConnection db; - private long colId = 0L; - private Arena arena; - private MemorySegment bigValue; - private MemorySegment[] key1; - private MemorySegment[] collidingKey1; - private MemorySegment[] key2; - private MemorySegment value1; - private MemorySegment value2; + protected EmbeddedConnection db; + protected long colId = 0L; + protected Arena arena; + protected MemorySegment bigValue; + protected MemorySegment[] key1; + protected MemorySegment[] collidingKey1; + protected MemorySegment[] key2; + protected MemorySegment value1; + protected MemorySegment value2; @org.junit.jupiter.api.BeforeEach void setUp() throws IOException { @@ -37,41 +39,65 @@ class EmbeddedDBTest { db = new EmbeddedConnection(null, "test", null); createStandardColumn(); + bigValue = getBigValue(); + key1 = getKey1(); + collidingKey1 = getCollidingKey1(); + key2 = getKey2(); + value1 = getValue1(); + value2 = getValue2(); + } + + protected MemorySegment getBigValue() { var bigValueArray = new byte[10_000]; ThreadLocalRandom.current().nextBytes(bigValueArray); - bigValue = MemorySegment.ofArray(bigValueArray); - key1 = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - toMemorySegmentSimple(6, 7, 8) - }; - collidingKey1 = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - toMemorySegmentSimple(6, 7, -48) - }; - key2 = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - toMemorySegmentSimple(6, 7, 7) - }; + return Utils.toMemorySegment(arena, bigValueArray); + } - value1 = MemorySegment.ofArray(new byte[] {0, 0, 3}); - value2 = MemorySegment.ofArray(new byte[] {0, 0, 5}); + protected MemorySegment[] getKey2() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 1, 2, 3), + toMemorySegmentSimple(arena, 6, 7, 7) + }; + } + + protected MemorySegment[] getCollidingKey1() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 1, 2, 3), + toMemorySegmentSimple(arena, 6, 7, -48) + }; + } + + protected MemorySegment[] getKey1() { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 1, 2, 3), + toMemorySegmentSimple(arena, 6, 7, 8) + }; + } + + protected boolean getHasValues() { + return true; + } + + protected MemorySegment getValue1() { + return Utils.toMemorySegmentSimple(arena, 0, 0, 3); + } + + protected MemorySegment getValue2() { + return Utils.toMemorySegmentSimple(arena, 0, 0, 5); } private void createStandardColumn() { - createColumn(ColumnSchema.of(IntList.of(1, 2, 1), - ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.XXHASH32), - true - )); + createColumn(getSchema()); } private void createColumn(ColumnSchema schema) { @@ -86,18 +112,36 @@ class EmbeddedDBTest { Assertions.assertNull(db.put(arena, 0, colId, collidingKey1, value2, Callback.none())); Assertions.assertNull(db.put(arena, 0, colId, key2, bigValue, Callback.none())); for (int i = 0; i < Byte.MAX_VALUE; i++) { - var keyI = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - toMemorySegmentSimple(8, 2, 5, 1, 7, i) - }; - var valueI = toMemorySegmentSimple(i, i, i, i, i); + var keyI = getKeyI(i); + var valueI = getValueI(i); Assertions.assertNull(db.put(arena, 0, colId, keyI, valueI, Callback.none())); } } + protected MemorySegment[] getKeyI(int i) { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 1, 2, 3), + toMemorySegmentSimple(arena, 8, 2, 5, 1, 7, i) + }; + } + + protected MemorySegment[] getNotFoundKeyI(int i) { + return new MemorySegment[] { + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 4, 6), + toMemorySegmentSimple(arena, 3), + toMemorySegmentSimple(arena, 1, 2, 3), + toMemorySegmentSimple(arena, 8, 2, 5, 1, 0, i) + }; + } + + protected MemorySegment getValueI(int i) { + return toMemorySegmentSimple(arena, i, i, i, i, i); + } + @org.junit.jupiter.api.AfterEach void tearDown() throws IOException { db.deleteColumn(colId); @@ -105,19 +149,32 @@ class EmbeddedDBTest { arena.close(); } - @org.junit.jupiter.api.Test - void putSameBucketSameKey() { - var key = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - toMemorySegmentSimple(0, 0, 3, 6, 7, 8) - }; - var value1 = MemorySegment.ofArray(new byte[] {0, 0, 3}); - var value2 = MemorySegment.ofArray(new byte[] {0, 0, 5}); + @SuppressWarnings("DataFlowIssue") + @Test + void putTestErrors() { + var key = getKey1(); - var delta = db.put(arena, 0, colId, key, value1, Callback.delta()); + if (!getHasValues()) { + Assertions.assertThrows(Exception.class, () -> db.put(arena, 0, colId, key, toMemorySegmentSimple(arena, 123), Callback.delta())); + } + + Assertions.assertThrows(Exception.class, () -> db.put(arena, 0, colId, key, null, Callback.delta())); + Assertions.assertThrows(Exception.class, () -> db.put(arena, 0, colId, null, value1, Callback.delta())); + Assertions.assertThrows(Exception.class, () -> db.put(arena, 0, colId, null, null, Callback.delta())); + Assertions.assertThrows(Exception.class, () -> db.put(arena, 0, colId, key, value1, null)); + Assertions.assertThrows(Exception.class, () -> db.put(arena, 1, colId, key, value1, Callback.delta())); + Assertions.assertThrows(Exception.class, () -> db.put(arena, 0, 21203, key, value1, Callback.delta())); + } + + @Test + void putSameBucketSameKey() { + var key = getKey1(); + var value1 = getValue1(); + var value2 = getValue2(); + + Delta delta; + + delta = db.put(arena, 0, colId, key, value1, Callback.delta()); Assertions.assertNull(delta.previous()); Assertions.assertTrue(Utils.valueEquals(delta.current(), value1)); @@ -126,32 +183,22 @@ class EmbeddedDBTest { Assertions.assertTrue(Utils.valueEquals(delta.current(), value2)); } - @org.junit.jupiter.api.Test + @Test void putSameBucketDifferentKey() { - createColumn(ColumnSchema.of(IntList.of(1, 2, 1), ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.ALLSAME8), true)); + if (getSchemaVarKeys().isEmpty()) { + return; + } + createColumn(ColumnSchema.of(getSchemaFixedKeys(), ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.ALLSAME8), getHasValues())); - var lastKey1 = toMemorySegmentSimple(6, 7, 8); - var lastKey2 = toMemorySegmentSimple(6, 7, -48); + var key1 = getKey1(); + var key2 = getCollidingKey1(); - var key1 = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - lastKey1 - }; - var key2 = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - lastKey2 - }; + var value1 = getValue1(); + var value2 = getValue2(); - var value1 = MemorySegment.ofArray(new byte[] {0, 0, 3}); - var value2 = MemorySegment.ofArray(new byte[] {0, 0, 5}); + Delta delta; - var delta = db.put(arena, 0, colId, key1, value1, Callback.delta()); + delta = db.put(arena, 0, colId, key1, value1, Callback.delta()); Assertions.assertNull(delta.previous()); Assertions.assertTrue(Utils.valueEquals(delta.current(), value1)); @@ -164,41 +211,29 @@ class EmbeddedDBTest { Assertions.assertTrue(Utils.valueEquals(delta.current(), value1)); } + protected ColumnSchema getSchema() { + return ColumnSchema.of(getSchemaFixedKeys(), getSchemaVarKeys(), getHasValues()); + } + + protected IntList getSchemaFixedKeys() { + return IntList.of(1, 2, 1); + } + + protected ObjectList getSchemaVarKeys() { + return ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.XXHASH8); + } + /** * Some keys have same bucket, some not */ - @org.junit.jupiter.api.Test + @Test void putMixedBucketMixedKey() { - createColumn(ColumnSchema.of(IntList.of(1, 2, 1), ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.XXHASH8), true)); + var key1 = getKey1(); + var collidingKey1 = getCollidingKey1(); + var key2 = getKey2(); - var lastKey1 = toMemorySegmentSimple(6, 7, 8); - var collidingLastKey1 = toMemorySegmentSimple(6, 7, -48); - var lastKey2 = toMemorySegmentSimple(6, 7, 7); - - var key1 = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - lastKey1 - }; - var collidingKey1 = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - collidingLastKey1 - }; - var key2 = new MemorySegment[] { - toMemorySegmentSimple(3), - toMemorySegmentSimple(4, 6), - toMemorySegmentSimple(3), - toMemorySegmentSimple(1, 2, 3), - lastKey2 - }; - - var value1 = MemorySegment.ofArray(new byte[] {0, 0, 3}); - var value2 = MemorySegment.ofArray(new byte[] {0, 0, 5}); + var value1 = getValue1(); + var value2 = getValue2(); var delta = db.put(arena, 0, colId, key1, value1, Callback.delta()); Assertions.assertNull(delta.previous()); @@ -221,11 +256,25 @@ class EmbeddedDBTest { Assertions.assertTrue(Utils.valueEquals(delta.current(), value2)); } - @org.junit.jupiter.api.Test + @Test void get() { + Assertions.assertNull(db.get(arena, 0, colId, key1, Callback.current())); + Assertions.assertNull(db.get(arena, 0, colId, collidingKey1, Callback.current())); + Assertions.assertNull(db.get(arena, 0, colId, key2, Callback.current())); fillSomeKeys(); Assertions.assertTrue(Utils.valueEquals(value1, db.get(arena, 0, colId, key1, Callback.current()))); + Assertions.assertNull(db.get(arena, 0, colId, getNotFoundKeyI(0), Callback.current())); Assertions.assertTrue(Utils.valueEquals(value2, db.get(arena, 0, colId, collidingKey1, Callback.current()))); Assertions.assertTrue(Utils.valueEquals(bigValue, db.get(arena, 0, colId, key2, Callback.current()))); } + + @SuppressWarnings("DataFlowIssue") + @Test + void getTestError() { + fillSomeKeys(); + Assertions.assertThrows(Exception.class, () -> Utils.valueEquals(value1, db.get(arena, 0, colId, null, Callback.current()))); + Assertions.assertThrows(Exception.class, () -> Utils.valueEquals(value1, db.get(arena, 0, 18239, key1, Callback.current()))); + Assertions.assertThrows(Exception.class, () -> Utils.valueEquals(value1, db.get(arena, 1, colId, key1, Callback.current()))); + Assertions.assertThrows(Exception.class, () -> Utils.valueEquals(value1, db.get(arena, 0, colId, key1, null))); + } } \ No newline at end of file diff --git a/src/test/java/it/cavallium/rockserver/core/test/XXHash32Test.java b/src/test/java/it/cavallium/rockserver/core/test/XXHash32Test.java index fdb5e8e..4cdae91 100644 --- a/src/test/java/it/cavallium/rockserver/core/test/XXHash32Test.java +++ b/src/test/java/it/cavallium/rockserver/core/test/XXHash32Test.java @@ -3,9 +3,7 @@ package it.cavallium.rockserver.core.test; import it.cavallium.rockserver.core.impl.ColumnInstance; import it.cavallium.rockserver.core.impl.XXHash32; import java.lang.foreign.Arena; -import java.lang.foreign.ValueLayout; import java.lang.foreign.ValueLayout.OfByte; -import java.nio.ByteOrder; import java.util.concurrent.ThreadLocalRandom; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test;