Code cleanup, more tests
This commit is contained in:
parent
a803bad343
commit
127c1cc392
5
pom.xml
5
pom.xml
@ -39,11 +39,6 @@
|
||||
<artifactId>ipaddress</artifactId>
|
||||
<version>5.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.4.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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> T put(Arena arena,
|
||||
long transactionId,
|
||||
long columnId,
|
||||
MemorySegment[] keys,
|
||||
@Nullable MemorySegment value,
|
||||
@NotNull MemorySegment @NotNull [] keys,
|
||||
@NotNull MemorySegment value,
|
||||
PutCallback<? super MemorySegment, T> callback) throws RocksDBException {
|
||||
return db.put(arena, transactionId, columnId, keys, value, callback);
|
||||
}
|
||||
@ -75,7 +76,7 @@ public class EmbeddedConnection extends BaseConnection {
|
||||
public <T> T get(Arena arena,
|
||||
long transactionId,
|
||||
long columnId,
|
||||
MemorySegment[] keys,
|
||||
MemorySegment @NotNull [] keys,
|
||||
GetCallback<? super MemorySegment, T> 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<? super MemorySegment, T> callback) throws RocksDBException {
|
||||
@NotNull IteratorCallback<? super MemorySegment, T> callback) throws RocksDBException {
|
||||
return db.subsequent(arena, iterationId, skipCount, takeCount, callback);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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> T put(Arena arena,
|
||||
long transactionId,
|
||||
long columnId,
|
||||
MemorySegment[] keys,
|
||||
@Nullable MemorySegment value,
|
||||
MemorySegment @NotNull [] keys,
|
||||
@NotNull MemorySegment value,
|
||||
PutCallback<? super MemorySegment, T> callback) throws RocksDBException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@ -67,7 +68,7 @@ public abstract class SocketConnection extends BaseConnection {
|
||||
public <T> T get(Arena arena,
|
||||
long transactionId,
|
||||
long columnId,
|
||||
MemorySegment[] keys,
|
||||
MemorySegment @NotNull [] keys,
|
||||
GetCallback<? super MemorySegment, T> 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<? super MemorySegment, T> callback) throws RocksDBException {
|
||||
@NotNull IteratorCallback<? super MemorySegment, T> callback) throws RocksDBException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<METHOD_DATA_TYPE, RESULT_TYPE> {
|
||||
|
@ -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;
|
||||
|
@ -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<ColumnHashType> variableTailKeys, boolean hasValue) {
|
||||
|
||||
@ -44,7 +43,7 @@ public record ColumnSchema(IntList keys, ObjectList<ColumnHashType> 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() {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package it.cavallium.rockserver.core.common;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
|
||||
public interface HashFunction {
|
||||
|
@ -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> T put(Arena arena,
|
||||
long transactionId,
|
||||
long columnId,
|
||||
MemorySegment[] keys,
|
||||
@Nullable MemorySegment value,
|
||||
@NotNull MemorySegment @NotNull[] keys,
|
||||
@NotNull MemorySegment value,
|
||||
PutCallback<? super MemorySegment, T> callback) throws RocksDBException;
|
||||
|
||||
/**
|
||||
@ -73,7 +74,7 @@ public interface RocksDBAPI {
|
||||
<T> T get(Arena arena,
|
||||
long transactionId,
|
||||
long columnId,
|
||||
MemorySegment[] keys,
|
||||
@NotNull MemorySegment @NotNull[] keys,
|
||||
GetCallback<? super MemorySegment, T> 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<? super MemorySegment, T> callback) throws RocksDBException;
|
||||
@NotNull IteratorCallback<? super MemorySegment, T> callback) throws RocksDBException;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -88,13 +88,27 @@ public final class DataSize extends Number implements Comparable<DataSize> {
|
||||
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<DataSize> {
|
||||
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) {
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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<ColumnInstance> columns;
|
||||
private final ConcurrentMap<String, Long> columnNamesIndex;
|
||||
private final NonBlockingHashMapLong<REntry<Transaction>> 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<Transaction> tx, boolean commit) {
|
||||
private boolean closeTransaction(@NotNull REntry<Transaction> tx, boolean commit) {
|
||||
ops.beginOp();
|
||||
try {
|
||||
// Transaction found
|
||||
@ -211,7 +206,7 @@ public class EmbeddedDB implements RocksDBAPI, Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean commitTxOptimistically(REntry<Transaction> tx) throws RocksDBException {
|
||||
private boolean commitTxOptimistically(@NotNull REntry<Transaction> 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> T put(Arena arena,
|
||||
long transactionId,
|
||||
long columnId,
|
||||
MemorySegment[] keys,
|
||||
@Nullable MemorySegment value,
|
||||
@NotNull MemorySegment @NotNull [] keys,
|
||||
@NotNull MemorySegment value,
|
||||
PutCallback<? super MemorySegment, T> callback) throws it.cavallium.rockserver.core.common.RocksDBException {
|
||||
ops.beginOp();
|
||||
try {
|
||||
@ -320,10 +315,10 @@ public class EmbeddedDB implements RocksDBAPI, Closeable {
|
||||
}
|
||||
|
||||
private <U> U put(Arena arena,
|
||||
REntry<Transaction> tx,
|
||||
@Nullable REntry<Transaction> tx,
|
||||
ColumnInstance col,
|
||||
MemorySegment[] keys,
|
||||
@Nullable MemorySegment value,
|
||||
@NotNull MemorySegment @NotNull[] keys,
|
||||
@NotNull MemorySegment value,
|
||||
PutCallback<? super MemorySegment, U> 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> T get(Arena arena,
|
||||
long transactionId,
|
||||
long columnId,
|
||||
MemorySegment[] keys,
|
||||
MemorySegment @NotNull [] keys,
|
||||
GetCallback<? super MemorySegment, T> 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<? super MemorySegment, T> callback) throws it.cavallium.rockserver.core.common.RocksDBException {
|
||||
@NotNull IteratorCallback<? super MemorySegment, T> 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;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ package it.cavallium.rockserver.core.impl;
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
|
||||
/**
|
||||
|
@ -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 extends AbstractNativeReference>(T val, RocksDBObjects objs) implements Closeable {
|
||||
|
||||
|
@ -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<CompressionType> 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);
|
||||
|
@ -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;
|
||||
|
@ -0,0 +1,5 @@
|
||||
package it.cavallium.rockserver.core.impl.test;
|
||||
|
||||
public class EmbeddedDBFixedAndVarKeysWithValueTest extends EmbeddedDBTest {
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<ColumnHashType> getSchemaVarKeys() {
|
||||
return ObjectList.of();
|
||||
}
|
||||
|
||||
}
|
@ -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<ColumnHashType> 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;
|
||||
}
|
||||
}
|
@ -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<MemorySegment> 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<MemorySegment> 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<ColumnHashType> 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)));
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user