rockserver/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java

395 lines
14 KiB
Java
Raw Normal View History

2023-12-06 00:43:35 +01:00
package it.cavallium.rockserver.core.impl.test;
2023-12-08 20:46:40 +01:00
import static it.cavallium.rockserver.core.common.Utils.toMemorySegmentSimple;
2023-12-06 00:43:35 +01:00
import it.cavallium.rockserver.core.client.EmbeddedConnection;
2024-03-31 01:54:34 +01:00
import it.cavallium.rockserver.core.common.Keys;
import it.cavallium.rockserver.core.common.RequestType;
2023-12-08 20:46:40 +01:00
import it.cavallium.rockserver.core.common.ColumnHashType;
2023-12-06 00:43:35 +01:00
import it.cavallium.rockserver.core.common.ColumnSchema;
2023-12-09 01:02:08 +01:00
import it.cavallium.rockserver.core.common.Delta;
2024-04-22 22:41:06 +02:00
import it.cavallium.rockserver.core.common.RocksDBException;
2023-12-09 17:45:47 +01:00
import it.cavallium.rockserver.core.common.RocksDBRetryException;
2023-12-07 00:14:28 +01:00
import it.cavallium.rockserver.core.common.Utils;
2023-12-08 20:46:40 +01:00
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.ObjectList;
2023-12-08 23:50:27 +01:00
import java.lang.foreign.Arena;
2023-12-09 13:47:26 +01:00
import java.util.Arrays;
2023-12-08 23:50:27 +01:00
import java.util.concurrent.ThreadLocalRandom;
2023-12-07 00:14:28 +01:00
import org.junit.jupiter.api.Assertions;
2023-12-06 00:43:35 +01:00
import java.io.IOException;
import java.lang.foreign.MemorySegment;
2023-12-09 01:02:08 +01:00
import org.junit.jupiter.api.Test;
2023-12-06 00:43:35 +01:00
2023-12-09 01:02:08 +01:00
abstract class EmbeddedDBTest {
2023-12-06 00:43:35 +01:00
2023-12-09 01:02:08 +01:00
protected EmbeddedConnection db;
protected long colId = 0L;
protected Arena arena;
protected MemorySegment bigValue;
2024-03-31 01:54:34 +01:00
protected Keys key1;
protected Keys collidingKey1;
protected Keys key2;
2023-12-09 01:02:08 +01:00
protected MemorySegment value1;
protected MemorySegment value2;
2023-12-06 00:43:35 +01:00
@org.junit.jupiter.api.BeforeEach
void setUp() throws IOException {
2023-12-07 00:14:28 +01:00
if (System.getProperty("rockserver.core.print-config", null) == null) {
System.setProperty("rockserver.core.print-config", "false");
}
2023-12-08 23:50:27 +01:00
arena = Arena.ofShared();
2023-12-07 00:14:28 +01:00
db = new EmbeddedConnection(null, "test", null);
2023-12-08 20:46:40 +01:00
createStandardColumn();
2023-12-08 23:50:27 +01:00
2023-12-09 01:02:08 +01:00
bigValue = getBigValue();
key1 = getKey1();
collidingKey1 = getCollidingKey1();
key2 = getKey2();
2023-12-08 23:50:27 +01:00
2023-12-09 01:02:08 +01:00
value1 = getValue1();
value2 = getValue2();
}
protected MemorySegment getBigValue() {
2023-12-08 23:50:27 +01:00
var bigValueArray = new byte[10_000];
ThreadLocalRandom.current().nextBytes(bigValueArray);
2023-12-09 01:02:08 +01:00
return Utils.toMemorySegment(arena, bigValueArray);
}
2024-03-31 01:54:34 +01:00
protected Keys getKey2() {
return new Keys(new MemorySegment[] {
2023-12-09 01:02:08 +01:00
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 4, 6),
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 1, 2, 3),
toMemorySegmentSimple(arena, 6, 7, 7)
2024-03-31 01:54:34 +01:00
});
2023-12-09 01:02:08 +01:00
}
2024-03-31 01:54:34 +01:00
protected Keys getCollidingKey1() {
return new Keys(new MemorySegment[] {
2023-12-09 01:02:08 +01:00
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 4, 6),
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 1, 2, 3),
toMemorySegmentSimple(arena, 6, 7, -48)
2024-03-31 01:54:34 +01:00
});
2023-12-09 01:02:08 +01:00
}
2024-03-31 01:54:34 +01:00
protected Keys getKey1() {
return new Keys(new MemorySegment[] {
2023-12-09 01:02:08 +01:00
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 4, 6),
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 1, 2, 3),
toMemorySegmentSimple(arena, 6, 7, 8)
2024-03-31 01:54:34 +01:00
});
2023-12-09 01:02:08 +01:00
}
protected boolean getHasValues() {
return true;
}
2023-12-08 23:50:27 +01:00
2023-12-09 01:02:08 +01:00
protected MemorySegment getValue1() {
return Utils.toMemorySegmentSimple(arena, 0, 0, 3);
}
protected MemorySegment getValue2() {
return Utils.toMemorySegmentSimple(arena, 0, 0, 5);
2023-12-08 20:46:40 +01:00
}
private void createStandardColumn() {
2023-12-09 01:02:08 +01:00
createColumn(getSchema());
2023-12-08 20:46:40 +01:00
}
private void createColumn(ColumnSchema schema) {
if (colId != 0L) {
db.deleteColumn(colId);
}
colId = db.createColumn("column-test", schema);
2023-12-06 00:43:35 +01:00
}
2023-12-08 23:50:27 +01:00
void fillSomeKeys() {
Assertions.assertNull(db.put(arena, 0, colId, key1, value1, RequestType.none()));
Assertions.assertNull(db.put(arena, 0, colId, collidingKey1, value2, RequestType.none()));
Assertions.assertNull(db.put(arena, 0, colId, key2, bigValue, RequestType.none()));
2023-12-08 23:50:27 +01:00
for (int i = 0; i < Byte.MAX_VALUE; i++) {
2023-12-09 01:02:08 +01:00
var keyI = getKeyI(i);
var valueI = getValueI(i);
Assertions.assertNull(db.put(arena, 0, colId, keyI, valueI, RequestType.none()));
2023-12-08 23:50:27 +01:00
}
}
2024-03-31 01:54:34 +01:00
protected Keys getKeyI(int i) {
return new Keys(new MemorySegment[] {
2023-12-09 01:02:08 +01:00
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 4, 6),
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 1, 2, 3),
toMemorySegmentSimple(arena, 8, 2, 5, 1, 7, i)
2024-03-31 01:54:34 +01:00
});
2023-12-09 01:02:08 +01:00
}
2024-03-31 01:54:34 +01:00
protected Keys getNotFoundKeyI(int i) {
return new Keys(new MemorySegment[] {
2023-12-09 01:02:08 +01:00
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 4, 6),
toMemorySegmentSimple(arena, 3),
toMemorySegmentSimple(arena, 1, 2, 3),
toMemorySegmentSimple(arena, 8, 2, 5, 1, 0, i)
2024-03-31 01:54:34 +01:00
});
2023-12-09 01:02:08 +01:00
}
protected MemorySegment getValueI(int i) {
return toMemorySegmentSimple(arena, i, i, i, i, i);
}
2023-12-06 00:43:35 +01:00
@org.junit.jupiter.api.AfterEach
void tearDown() throws IOException {
2023-12-07 00:14:28 +01:00
db.deleteColumn(colId);
db.close();
2023-12-08 23:50:27 +01:00
arena.close();
2023-12-06 00:43:35 +01:00
}
2023-12-09 01:02:08 +01:00
@SuppressWarnings("DataFlowIssue")
@Test
void putTestErrors() {
var key = getKey1();
if (!getHasValues()) {
2024-04-22 22:41:06 +02:00
Assertions.assertThrows(RocksDBException.class, () -> db.put(arena, 0, colId, key, toMemorySegmentSimple(arena, 123), RequestType.delta()));
2023-12-09 13:47:26 +01:00
} else {
2024-04-22 22:41:06 +02:00
Assertions.assertThrows(RocksDBException.class, () -> db.put(arena, 0, colId, key, MemorySegment.NULL, RequestType.delta()));
Assertions.assertThrows(RocksDBException.class, () -> db.put(arena, 0, colId, key, null, RequestType.delta()));
2023-12-09 01:02:08 +01:00
}
2024-04-22 22:41:06 +02:00
Assertions.assertThrows(RocksDBException.class, () -> db.put(arena, 0, colId, null, value1, RequestType.delta()));
Assertions.assertThrows(RocksDBException.class, () -> db.put(arena, 0, colId, null, null, RequestType.delta()));
Assertions.assertThrows(RocksDBException.class, () -> db.put(arena, 0, colId, key, value1, null));
Assertions.assertThrows(RocksDBException.class, () -> db.put(arena, 1, colId, key, value1, RequestType.delta()));
Assertions.assertThrows(RocksDBException.class, () -> db.put(arena, 0, 21203, key, value1, RequestType.delta()));
2023-12-09 01:02:08 +01:00
}
@Test
2023-12-08 20:46:40 +01:00
void putSameBucketSameKey() {
2023-12-09 01:02:08 +01:00
var key = getKey1();
var value1 = getValue1();
var value2 = getValue2();
Delta<MemorySegment> delta;
2023-12-07 16:54:12 +01:00
delta = db.put(arena, 0, colId, key, value1, RequestType.delta());
2023-12-07 16:54:12 +01:00
Assertions.assertNull(delta.previous());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value1, delta.current());
2023-12-07 16:54:12 +01:00
delta = db.put(arena, 0, colId, key, value2, RequestType.delta());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value1, delta.previous());
assertSegmentEquals(value2, delta.current());
2023-12-06 00:43:35 +01:00
}
2023-12-09 01:02:08 +01:00
@Test
2023-12-08 20:46:40 +01:00
void putSameBucketDifferentKey() {
2023-12-09 01:02:08 +01:00
if (getSchemaVarKeys().isEmpty()) {
return;
}
createColumn(ColumnSchema.of(getSchemaFixedKeys(), ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.ALLSAME8), getHasValues()));
2023-12-08 20:46:40 +01:00
2023-12-09 01:02:08 +01:00
var key1 = getKey1();
var key2 = getCollidingKey1();
2023-12-08 20:46:40 +01:00
2023-12-09 01:02:08 +01:00
var value1 = getValue1();
var value2 = getValue2();
2023-12-08 20:46:40 +01:00
2023-12-09 01:02:08 +01:00
Delta<MemorySegment> delta;
2023-12-08 20:46:40 +01:00
Assertions.assertFalse(db.put(arena, 0, colId, getKeyI(3), value2, RequestType.previousPresence()));
Assertions.assertFalse(db.put(arena, 0, colId, getKeyI(4), value2, RequestType.previousPresence()));
2023-12-09 13:47:26 +01:00
delta = db.put(arena, 0, colId, key1, value1, RequestType.delta());
2023-12-08 20:46:40 +01:00
Assertions.assertNull(delta.previous());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value1, delta.current());
2023-12-08 20:46:40 +01:00
delta = db.put(arena, 0, colId, key2, value2, RequestType.delta());
2023-12-08 20:46:40 +01:00
Assertions.assertNull(delta.previous());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value2, delta.current());
delta = db.put(arena, 0, colId, key2, value1, RequestType.delta());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value2, delta.previous());
assertSegmentEquals(value1, delta.current());
2023-12-08 20:46:40 +01:00
delta = db.put(arena, 0, colId, key2, value1, RequestType.delta());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value1, delta.previous());
assertSegmentEquals(value1, delta.current());
Assertions.assertTrue(db.put(arena, 0, colId, key1, value2, RequestType.previousPresence()));
Assertions.assertTrue(db.put(arena, 0, colId, key2, value2, RequestType.previousPresence()));
2023-12-09 13:47:26 +01:00
delta = db.put(arena, 0, colId, key1, value1, RequestType.delta());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value2, delta.previous());
assertSegmentEquals(value1, delta.current());
delta = db.put(arena, 0, colId, key2, value1, RequestType.delta());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value2, delta.previous());
assertSegmentEquals(value1, delta.current());
Assertions.assertNull(db.put(arena, 0, colId, key1, value2, RequestType.none()));
Assertions.assertNull(db.put(arena, 0, colId, key2, value2, RequestType.none()));
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value2, db.put(arena, 0, colId, key1, value1, RequestType.previous()));
assertSegmentEquals(value2, db.put(arena, 0, colId, key2, value1, RequestType.previous()));
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value1, db.put(arena, 0, colId, key1, value1, RequestType.previous()));
assertSegmentEquals(value1, db.put(arena, 0, colId, key2, value1, RequestType.previous()));
2023-12-09 13:47:26 +01:00
if (!Utils.valueEquals(value1, value2)) {
Assertions.assertTrue(db.put(arena, 0, colId, key1, value2, RequestType.changed()));
Assertions.assertTrue(db.put(arena, 0, colId, key2, value2, RequestType.changed()));
2023-12-09 13:47:26 +01:00
}
Assertions.assertFalse(db.put(arena, 0, colId, key1, value2, RequestType.changed()));
Assertions.assertFalse(db.put(arena, 0, colId, key2, value2, RequestType.changed()));
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value2, db.put(arena, 0, colId, key1, value1, RequestType.previous()));
assertSegmentEquals(value2, db.put(arena, 0, colId, key2, value1, RequestType.previous()));
2023-12-08 20:46:40 +01:00
}
2023-12-09 01:02:08 +01:00
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);
}
2023-12-08 20:46:40 +01:00
/**
* Some keys have same bucket, some not
*/
2023-12-09 01:02:08 +01:00
@Test
2023-12-08 20:46:40 +01:00
void putMixedBucketMixedKey() {
2023-12-09 01:02:08 +01:00
var key1 = getKey1();
var collidingKey1 = getCollidingKey1();
var key2 = getKey2();
2023-12-08 20:46:40 +01:00
2023-12-09 01:02:08 +01:00
var value1 = getValue1();
var value2 = getValue2();
2023-12-08 20:46:40 +01:00
var delta = db.put(arena, 0, colId, key1, value1, RequestType.delta());
2023-12-08 20:46:40 +01:00
Assertions.assertNull(delta.previous());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value1, delta.current());
2023-12-08 20:46:40 +01:00
delta = db.put(arena, 0, colId, collidingKey1, value2, RequestType.delta());
2023-12-08 20:46:40 +01:00
Assertions.assertNull(delta.previous());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value2, delta.current());
2023-12-08 20:46:40 +01:00
delta = db.put(arena, 0, colId, collidingKey1, value1, RequestType.delta());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value2, delta.previous());
assertSegmentEquals(value1, delta.current());
2023-12-08 20:46:40 +01:00
delta = db.put(arena, 0, colId, key2, value1, RequestType.delta());
2023-12-08 20:46:40 +01:00
Assertions.assertNull(delta.previous());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value1, delta.current());
2023-12-08 20:46:40 +01:00
delta = db.put(arena, 0, colId, key2, value2, RequestType.delta());
2023-12-09 13:47:26 +01:00
assertSegmentEquals(value1, delta.previous());
assertSegmentEquals(value2, delta.current());
2023-12-08 20:46:40 +01:00
}
2023-12-09 01:02:08 +01:00
@Test
2023-12-06 00:43:35 +01:00
void get() {
2023-12-09 13:47:26 +01:00
if (getHasValues()) {
Assertions.assertNull(db.get(arena, 0, colId, key1, RequestType.current()));
Assertions.assertNull(db.get(arena, 0, colId, collidingKey1, RequestType.current()));
Assertions.assertNull(db.get(arena, 0, colId, key2, RequestType.current()));
2023-12-09 13:47:26 +01:00
}
Assertions.assertFalse(db.get(arena, 0, colId, key1, RequestType.exists()));
Assertions.assertFalse(db.get(arena, 0, colId, collidingKey1, RequestType.exists()));
Assertions.assertFalse(db.get(arena, 0, colId, key2, RequestType.exists()));
2023-12-09 13:47:26 +01:00
2023-12-08 23:50:27 +01:00
fillSomeKeys();
2023-12-09 13:47:26 +01:00
if (getHasValues()) {
assertSegmentEquals(value1, db.get(arena, 0, colId, key1, RequestType.current()));
Assertions.assertNull(db.get(arena, 0, colId, getNotFoundKeyI(0), RequestType.current()));
assertSegmentEquals(value2, db.get(arena, 0, colId, collidingKey1, RequestType.current()));
assertSegmentEquals(bigValue, db.get(arena, 0, colId, key2, RequestType.current()));
2023-12-09 13:47:26 +01:00
}
Assertions.assertTrue(db.get(arena, 0, colId, key1, RequestType.exists()));
Assertions.assertFalse(db.get(arena, 0, colId, getNotFoundKeyI(0), RequestType.exists()));
Assertions.assertTrue(db.get(arena, 0, colId, collidingKey1, RequestType.exists()));
Assertions.assertTrue(db.get(arena, 0, colId, key2, RequestType.exists()));
2023-12-09 13:47:26 +01:00
}
2023-12-09 17:45:47 +01:00
@Test
void update() {
if (getHasValues()) {
var forUpdate = db.get(arena, 0, colId, key1, RequestType.forUpdate());
2023-12-09 17:45:47 +01:00
Assertions.assertNull(forUpdate.previous());
Assertions.assertTrue(forUpdate.updateId() != 0);
db.put(arena, forUpdate.updateId(), colId, key1, value1, RequestType.none());
2023-12-09 17:45:47 +01:00
Assertions.assertThrows(Exception.class, () -> db.put(arena, forUpdate.updateId(), colId, key1, value2, RequestType.none()));
2023-12-09 17:45:47 +01:00
}
}
@Test
void concurrentUpdate() {
if (getHasValues()) {
{
var forUpdate1 = db.get(arena, 0, colId, key1, RequestType.forUpdate());
2023-12-09 17:45:47 +01:00
try {
var forUpdate2 = db.get(arena, 0, colId, key1, RequestType.forUpdate());
2023-12-09 17:45:47 +01:00
try {
db.put(arena, forUpdate1.updateId(), colId, key1, value1, RequestType.none());
Assertions.assertThrowsExactly(RocksDBRetryException.class, () -> db.put(arena, forUpdate2.updateId(), colId, key1, value2, RequestType.none()));
2023-12-09 17:45:47 +01:00
// Retrying
var forUpdate3 = db.get(arena, forUpdate2.updateId(), colId, key1, RequestType.forUpdate());
2023-12-09 17:45:47 +01:00
try {
assertSegmentEquals(value1, forUpdate3.previous());
db.put(arena, forUpdate3.updateId(), colId, key1, value2, RequestType.none());
2023-12-09 17:45:47 +01:00
} catch (Throwable ex3) {
db.closeFailedUpdate(forUpdate3.updateId());
throw ex3;
}
} catch (Throwable ex2) {
db.closeFailedUpdate(forUpdate2.updateId());
throw ex2;
}
} catch (Throwable ex) {
throw ex;
}
}
}
}
2023-12-09 13:47:26 +01:00
public static void assertSegmentEquals(MemorySegment expected, MemorySegment input) {
if (!Utils.valueEquals(expected, input)) {
Assertions.fail(
"Memory segments are not equal! Expected: "
+ (expected != null ? Arrays.toString(Utils.toByteArray(expected)) : "null")
+ ", Input: " + (input != null ? Arrays.toString(Utils.toByteArray(input)) : "null"));
}
2023-12-06 00:43:35 +01:00
}
2023-12-09 01:02:08 +01:00
@SuppressWarnings("DataFlowIssue")
@Test
void getTestError() {
fillSomeKeys();
Assertions.assertThrows(Exception.class, () -> Utils.valueEquals(value1, db.get(arena, 0, colId, null, RequestType.current())));
Assertions.assertThrows(Exception.class, () -> Utils.valueEquals(value1, db.get(arena, 0, 18239, key1, RequestType.current())));
Assertions.assertThrows(Exception.class, () -> Utils.valueEquals(value1, db.get(arena, 1, colId, key1, RequestType.current())));
2023-12-09 01:02:08 +01:00
Assertions.assertThrows(Exception.class, () -> Utils.valueEquals(value1, db.get(arena, 0, colId, key1, null)));
}
2023-12-06 00:43:35 +01:00
}