CavalliumDBEngine/src/test/java/it/cavallium/dbengine/DbTestUtils.java

245 lines
8.8 KiB
Java
Raw Normal View History

2021-05-02 19:18:15 +02:00
package it.cavallium.dbengine;
2021-08-28 22:42:51 +02:00
import static org.junit.jupiter.api.Assertions.assertEquals;
2021-09-04 02:19:10 +02:00
import io.netty5.buffer.api.Buffer;
import io.netty5.buffer.api.MemoryManager;
import io.netty5.buffer.api.Send;
import io.netty5.buffer.api.pool.BufferAllocatorMetric;
import io.netty5.buffer.api.pool.PooledBufferAllocator;
import io.netty5.util.internal.PlatformDependent;
import it.cavallium.dbengine.database.Column;
2021-08-28 22:42:51 +02:00
import it.cavallium.dbengine.database.LLDatabaseConnection;
import it.cavallium.dbengine.database.LLDictionary;
import it.cavallium.dbengine.database.LLKeyValueDatabase;
import it.cavallium.dbengine.database.UpdateMode;
import it.cavallium.dbengine.database.collections.DatabaseMapDictionary;
import it.cavallium.dbengine.database.collections.DatabaseMapDictionaryDeep;
import it.cavallium.dbengine.database.collections.DatabaseMapDictionaryHashed;
2021-05-03 18:07:18 +02:00
import it.cavallium.dbengine.database.collections.DatabaseStageEntry;
import it.cavallium.dbengine.database.collections.DatabaseStageMap;
import it.cavallium.dbengine.database.collections.SubStageGetterHashMap;
import it.cavallium.dbengine.database.collections.SubStageGetterMap;
2021-07-01 21:19:52 +02:00
import it.cavallium.dbengine.client.DatabaseOptions;
import it.cavallium.dbengine.database.disk.LLLocalDatabaseConnection;
2021-09-02 21:14:26 +02:00
import it.cavallium.dbengine.database.disk.MemorySegmentUtils;
import it.cavallium.dbengine.database.serialization.Serializer;
import it.cavallium.dbengine.database.serialization.SerializerFixedBinaryLength;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
2021-05-03 18:07:18 +02:00
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
public class DbTestUtils {
2021-08-31 15:50:11 +02:00
public static record TestAllocator(PooledBufferAllocator allocator) {}
2021-08-28 22:42:51 +02:00
2021-08-29 01:15:51 +02:00
public static TestAllocator newAllocator() {
2021-08-31 15:50:11 +02:00
return new TestAllocator(new PooledBufferAllocator(MemoryManager.instance(), true, 1, 8192, 9, 0, 0, false));
2021-08-28 22:42:51 +02:00
}
2021-08-29 01:15:51 +02:00
public static void destroyAllocator(TestAllocator testAllocator) {
2021-08-31 15:50:11 +02:00
testAllocator.allocator().close();
2021-08-28 22:42:51 +02:00
}
public static final AtomicInteger dbId = new AtomicInteger(0);
2021-08-28 22:42:51 +02:00
@SuppressWarnings("SameParameterValue")
2021-08-31 15:50:11 +02:00
private static long getUsedMemory(PooledBufferAllocator allocator, boolean printStats) {
allocator.trimCurrentThreadCache();
var usedMemory = ((BufferAllocatorMetric) allocator.metric()).usedMemory();
2021-08-29 01:15:51 +02:00
if (printStats) {
2021-08-31 15:50:11 +02:00
System.out.println("usedMemory=" + usedMemory);
2021-08-29 01:15:51 +02:00
}
2021-08-31 15:50:11 +02:00
return usedMemory;
2021-08-28 22:42:51 +02:00
}
2021-08-29 01:15:51 +02:00
public static <U> Flux<U> tempDb(TestAllocator alloc, Function<LLKeyValueDatabase, Publisher<U>> action) {
return Flux.usingWhen(openTempDb(alloc),
2021-08-28 22:42:51 +02:00
tempDb -> action.apply(tempDb.db()),
DbTestUtils::closeTempDb
);
}
2021-05-02 19:18:15 +02:00
2021-08-29 01:15:51 +02:00
public static record TempDb(TestAllocator allocator, LLDatabaseConnection connection, LLKeyValueDatabase db,
2021-08-28 22:42:51 +02:00
Path path) {}
2021-08-29 01:15:51 +02:00
public static Mono<TempDb> openTempDb(TestAllocator alloc) {
2021-09-02 21:14:26 +02:00
boolean canUseNettyDirect = computeCanUseNettyDirect();
2021-08-28 22:42:51 +02:00
return Mono.defer(() -> {
var wrkspcPath = Path.of("/tmp/.cache/tempdb-" + dbId.incrementAndGet() + "/");
return Mono
.<LLKeyValueDatabase>fromCallable(() -> {
if (Files.exists(wrkspcPath)) {
Files.walk(wrkspcPath).sorted(Comparator.reverseOrder()).forEach(file -> {
try {
Files.delete(file);
} catch (IOException ex) {
throw new CompletionException(ex);
}
});
}
Files.createDirectories(wrkspcPath);
return null;
})
.subscribeOn(Schedulers.boundedElastic())
2021-08-29 01:15:51 +02:00
.then(new LLLocalDatabaseConnection(alloc.allocator(), wrkspcPath).connect())
2021-08-28 22:42:51 +02:00
.flatMap(conn -> conn
.getDatabase("testdb",
List.of(Column.dictionary("testmap"), Column.special("ints"), Column.special("longs")),
2021-09-02 21:14:26 +02:00
new DatabaseOptions(Map.of(), true, false, true, false, true, canUseNettyDirect, canUseNettyDirect, -1)
2021-08-28 22:42:51 +02:00
)
.map(db -> new TempDb(alloc, conn, db, wrkspcPath))
);
});
}
2021-09-02 21:14:26 +02:00
private static boolean computeCanUseNettyDirect() {
boolean canUse = true;
if (!PlatformDependent.hasUnsafe()) {
System.err.println("Warning! Unsafe is not available!"
+ " Netty direct buffers will not be used in tests!");
canUse = false;
}
if (!MemorySegmentUtils.isSupported()) {
System.err.println("Warning! Foreign Memory Access API is not available!"
+ " Netty direct buffers will not be used in tests!"
2021-09-03 02:22:55 +02:00
+ " Please set \"--enable-preview --add-modules jdk.incubator.foreign -Dforeign.restricted=permit\"");
2021-09-02 21:14:26 +02:00
if (MemorySegmentUtils.getUnsupportedCause() != null) {
System.err.println("\tCause: " + MemorySegmentUtils.getUnsupportedCause().getClass().getName()
+ ":" + MemorySegmentUtils.getUnsupportedCause().getLocalizedMessage());
}
canUse = false;
}
return canUse;
}
2021-08-28 22:42:51 +02:00
public static Mono<Void> closeTempDb(TempDb tempDb) {
return tempDb.db().close().then(tempDb.connection().disconnect()).then(Mono.fromCallable(() -> {
2021-08-29 01:15:51 +02:00
ensureNoLeaks(tempDb.allocator().allocator(), false);
2021-08-28 22:42:51 +02:00
if (Files.exists(tempDb.path())) {
Files.walk(tempDb.path()).sorted(Comparator.reverseOrder()).forEach(file -> {
try {
Files.delete(file);
} catch (IOException ex) {
throw new CompletionException(ex);
}
});
}
return null;
}).subscribeOn(Schedulers.boundedElastic())).then();
}
2021-08-31 15:50:11 +02:00
public static void ensureNoLeaks(PooledBufferAllocator allocator, boolean printStats) {
2021-08-28 22:42:51 +02:00
if (allocator != null) {
2021-08-31 15:50:11 +02:00
assertEquals(0L, getUsedMemory(allocator, printStats));
2021-08-28 22:42:51 +02:00
}
}
public static Mono<? extends LLDictionary> tempDictionary(LLKeyValueDatabase database, UpdateMode updateMode) {
return tempDictionary(database, "testmap", updateMode);
}
public static Mono<? extends LLDictionary> tempDictionary(LLKeyValueDatabase database,
String name,
UpdateMode updateMode) {
return database.getDictionary(name, updateMode);
}
2021-05-03 18:07:18 +02:00
public enum DbType {
MAP,
HASH_MAP
}
public static DatabaseStageMap<String, String, DatabaseStageEntry<String>> tempDatabaseMapDictionaryMap(
LLDictionary dictionary,
2021-05-03 18:07:18 +02:00
DbType dbType,
int keyBytes) {
2021-05-04 01:21:29 +02:00
if (dbType == DbType.MAP) {
2021-05-03 21:41:51 +02:00
return DatabaseMapDictionary.simple(dictionary,
2021-08-28 22:42:51 +02:00
SerializerFixedBinaryLength.utf8(dictionary.getAllocator(), keyBytes),
Serializer.utf8(dictionary.getAllocator())
2021-05-03 21:41:51 +02:00
);
2021-05-03 18:07:18 +02:00
} else {
return DatabaseMapDictionaryHashed.simple(dictionary,
2021-08-28 22:42:51 +02:00
Serializer.utf8(dictionary.getAllocator()),
Serializer.utf8(dictionary.getAllocator()),
2021-05-08 03:09:00 +02:00
s -> (short) s.hashCode(),
2021-05-03 18:07:18 +02:00
new SerializerFixedBinaryLength<>() {
@Override
public int getSerializedBinaryLength() {
2021-05-08 03:09:00 +02:00
return Short.BYTES;
2021-05-03 18:07:18 +02:00
}
@Override
2021-09-02 17:15:40 +02:00
public @NotNull DeserializationResult<Short> deserialize(@NotNull Send<Buffer> serializedToReceive) {
2021-08-31 15:50:11 +02:00
try (var serialized = serializedToReceive.receive()) {
2021-05-08 03:09:00 +02:00
var val = serialized.readShort();
2021-09-02 17:15:40 +02:00
return new DeserializationResult<>(val, Short.BYTES);
2021-05-03 18:07:18 +02:00
}
}
@Override
2021-08-31 15:50:11 +02:00
public @NotNull Send<Buffer> serialize(@NotNull Short deserialized) {
try (var out = dictionary.getAllocator().allocate(Short.BYTES)) {
2021-05-08 03:09:00 +02:00
out.writeShort(deserialized);
2021-08-31 15:50:11 +02:00
out.writerOffset(Short.BYTES);
return out.send();
2021-05-03 18:07:18 +02:00
}
}
}
);
}
}
2021-08-28 22:42:51 +02:00
public static DatabaseMapDictionaryDeep<String, Map<String, String>,
2021-05-08 03:09:00 +02:00
DatabaseMapDictionary<String, String>> tempDatabaseMapDictionaryDeepMap(
LLDictionary dictionary,
int key1Bytes,
int key2Bytes) {
return DatabaseMapDictionaryDeep.deepTail(dictionary,
2021-08-28 22:42:51 +02:00
SerializerFixedBinaryLength.utf8(dictionary.getAllocator(), key1Bytes),
key2Bytes,
2021-08-28 22:42:51 +02:00
new SubStageGetterMap<>(SerializerFixedBinaryLength.utf8(dictionary.getAllocator(), key2Bytes),
Serializer.utf8(dictionary.getAllocator())
2021-05-08 03:09:00 +02:00
)
);
}
2021-08-28 22:42:51 +02:00
public static DatabaseMapDictionaryDeep<String, Map<String, String>,
2021-05-08 03:09:00 +02:00
DatabaseMapDictionaryHashed<String, String, Integer>> tempDatabaseMapDictionaryDeepMapHashMap(
LLDictionary dictionary,
int key1Bytes) {
return DatabaseMapDictionaryDeep.deepTail(dictionary,
2021-08-28 22:42:51 +02:00
SerializerFixedBinaryLength.utf8(dictionary.getAllocator(), key1Bytes),
2021-05-08 03:09:00 +02:00
Integer.BYTES,
2021-08-28 22:42:51 +02:00
new SubStageGetterHashMap<>(Serializer.utf8(dictionary.getAllocator()),
Serializer.utf8(dictionary.getAllocator()),
2021-05-08 03:09:00 +02:00
String::hashCode,
2021-08-28 22:42:51 +02:00
SerializerFixedBinaryLength.intSerializer(dictionary.getAllocator())
2021-05-08 03:09:00 +02:00
)
);
}
public static <T, U> DatabaseMapDictionaryHashed<String, String, Integer> tempDatabaseMapDictionaryHashMap(
LLDictionary dictionary) {
return DatabaseMapDictionaryHashed.simple(dictionary,
2021-08-28 22:42:51 +02:00
Serializer.utf8(dictionary.getAllocator()),
Serializer.utf8(dictionary.getAllocator()),
String::hashCode,
2021-08-28 22:42:51 +02:00
SerializerFixedBinaryLength.intSerializer(dictionary.getAllocator())
);
}
}