Add ikv-store benchmark fixture
This commit is contained in:
@@ -20,6 +20,7 @@ public class DataAccessBenchmark {
|
||||
@Param({
|
||||
"in-memory",
|
||||
"memory-mapped-file",
|
||||
"ikv-store",
|
||||
"datastore4j",
|
||||
"leveldb",
|
||||
"lmdb",
|
||||
|
||||
@@ -6,6 +6,7 @@ public class FixtureFactory {
|
||||
return switch (type) {
|
||||
case "in-memory" -> new InMemoryFixture();
|
||||
case "memory-mapped-file" -> new MemoryMappedFileFixture();
|
||||
case "ikv-store" -> new IkvStoreFixture();
|
||||
case "datastore4j" -> new DataStore4jFixture();
|
||||
case "leveldb" -> new LevelDbFixture();
|
||||
case "lmdb" -> new LmdbFixture();
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.benchmark.fixtures;
|
||||
|
||||
import com.benchmark.model.User;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class IkvStoreFixture implements DataFixtures {
|
||||
|
||||
private static final AtomicBoolean FALLBACK_WARNING_PRINTED = new AtomicBoolean(false);
|
||||
|
||||
private final InMemoryFixture fallback = new InMemoryFixture();
|
||||
|
||||
private Object reader;
|
||||
private Object writer;
|
||||
private boolean useFallback;
|
||||
|
||||
@Override
|
||||
public void setup(List<User> users) throws Exception {
|
||||
String accountId = getenv("IKV_ACCOUNT_ID");
|
||||
String accountPassKey = getenv("IKV_ACCOUNT_PASSKEY");
|
||||
String storeName = getenv("IKV_STORE_NAME");
|
||||
String mountDirectory = getenv("IKV_MOUNT_DIRECTORY");
|
||||
|
||||
if (accountId == null || accountPassKey == null || storeName == null || mountDirectory == null) {
|
||||
useFallback = true;
|
||||
fallback.setup(users);
|
||||
printFallbackWarning("IKV fixture: required env vars missing (IKV_ACCOUNT_ID, IKV_ACCOUNT_PASSKEY, IKV_STORE_NAME, IKV_MOUNT_DIRECTORY). Using in-memory fallback.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> factoryClass = Class.forName("io.inlined.clients.IKVClientFactory");
|
||||
Class<?> optionsBuilderClass = Class.forName("io.inlined.clients.ClientOptions$Builder");
|
||||
Class<?> optionsClass = Class.forName("io.inlined.clients.ClientOptions");
|
||||
Class<?> docBuilderClass = Class.forName("io.inlined.clients.IKVDocument$Builder");
|
||||
Class<?> docClass = Class.forName("io.inlined.clients.IKVDocument");
|
||||
|
||||
Object factory = factoryClass.getConstructor().newInstance();
|
||||
|
||||
Object writerOptionsBuilder = optionsBuilderClass.getConstructor().newInstance();
|
||||
writerOptionsBuilder = optionsBuilderClass.getMethod("withStoreName", String.class).invoke(writerOptionsBuilder, storeName);
|
||||
writerOptionsBuilder = optionsBuilderClass.getMethod("withAccountId", String.class).invoke(writerOptionsBuilder, accountId);
|
||||
writerOptionsBuilder = optionsBuilderClass.getMethod("withAccountPassKey", String.class).invoke(writerOptionsBuilder, accountPassKey);
|
||||
Object writerOptions = optionsBuilderClass.getMethod("build").invoke(writerOptionsBuilder);
|
||||
|
||||
Object readerOptionsBuilder = optionsBuilderClass.getConstructor().newInstance();
|
||||
readerOptionsBuilder = optionsBuilderClass.getMethod("withStoreName", String.class).invoke(readerOptionsBuilder, storeName);
|
||||
readerOptionsBuilder = optionsBuilderClass.getMethod("withAccountId", String.class).invoke(readerOptionsBuilder, accountId);
|
||||
readerOptionsBuilder = optionsBuilderClass.getMethod("withAccountPassKey", String.class).invoke(readerOptionsBuilder, accountPassKey);
|
||||
readerOptionsBuilder = optionsBuilderClass.getMethod("withMountDirectory", String.class).invoke(readerOptionsBuilder, mountDirectory);
|
||||
readerOptionsBuilder = optionsBuilderClass.getMethod("useStringPrimaryKey").invoke(readerOptionsBuilder);
|
||||
Object readerOptions = optionsBuilderClass.getMethod("build").invoke(readerOptionsBuilder);
|
||||
|
||||
Method createWriter = factoryClass.getMethod("createNewWriterInstance", optionsClass);
|
||||
Method createReader = factoryClass.getMethod("createNewReaderInstance", optionsClass);
|
||||
|
||||
writer = createWriter.invoke(factory, writerOptions);
|
||||
reader = createReader.invoke(factory, readerOptions);
|
||||
|
||||
writer.getClass().getMethod("startupWriter").invoke(writer);
|
||||
reader.getClass().getMethod("startupReader").invoke(reader);
|
||||
|
||||
Method upsertFieldValues = writer.getClass().getMethod("upsertFieldValues", docClass);
|
||||
|
||||
for (User user : users) {
|
||||
Object docBuilder = docBuilderClass.getConstructor().newInstance();
|
||||
docBuilder = docBuilderClass.getMethod("putStringField", String.class, String.class)
|
||||
.invoke(docBuilder, "id", Long.toString(user.getId()));
|
||||
docBuilder = docBuilderClass.getMethod("putStringField", String.class, String.class)
|
||||
.invoke(docBuilder, "name", user.getName());
|
||||
docBuilder = docBuilderClass.getMethod("putStringField", String.class, String.class)
|
||||
.invoke(docBuilder, "email", user.getEmail());
|
||||
docBuilder = docBuilderClass.getMethod("putIntField", String.class, int.class)
|
||||
.invoke(docBuilder, "age", user.getAge());
|
||||
Object document = docBuilderClass.getMethod("build").invoke(docBuilder);
|
||||
upsertFieldValues.invoke(writer, document);
|
||||
}
|
||||
|
||||
// IKV is eventually consistent for reads. Small delay before measurement starts.
|
||||
Thread.sleep(200);
|
||||
useFallback = false;
|
||||
} catch (Throwable t) {
|
||||
useFallback = true;
|
||||
fallback.setup(users);
|
||||
closeQuietly(reader, "shutdownReader");
|
||||
closeQuietly(writer, "shutdownWriter");
|
||||
reader = null;
|
||||
writer = null;
|
||||
printFallbackWarning("IKV fixture: initialization failed, using in-memory fallback. Cause: "
|
||||
+ t.getClass().getSimpleName() + " - " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User findById(long id) throws Exception {
|
||||
if (useFallback) {
|
||||
return fallback.findById(id);
|
||||
}
|
||||
|
||||
String key = Long.toString(id);
|
||||
String name = (String) reader.getClass().getMethod("getStringValue", String.class, String.class)
|
||||
.invoke(reader, key, "name");
|
||||
String email = (String) reader.getClass().getMethod("getStringValue", String.class, String.class)
|
||||
.invoke(reader, key, "email");
|
||||
Object ageObj = reader.getClass().getMethod("getIntValue", String.class, String.class)
|
||||
.invoke(reader, key, "age");
|
||||
|
||||
if (name == null && email == null && ageObj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int age = ageObj == null ? 0 : (Integer) ageObj;
|
||||
return new User(id, name, email, age, 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teardown() throws Exception {
|
||||
if (useFallback) {
|
||||
fallback.teardown();
|
||||
return;
|
||||
}
|
||||
|
||||
closeQuietly(reader, "shutdownReader");
|
||||
closeQuietly(writer, "shutdownWriter");
|
||||
reader = null;
|
||||
writer = null;
|
||||
}
|
||||
|
||||
private static String getenv(String key) {
|
||||
String value = System.getenv(key);
|
||||
return (value == null || value.isBlank()) ? null : value;
|
||||
}
|
||||
|
||||
private static void closeQuietly(Object target, String methodName) {
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
target.getClass().getMethod(methodName).invoke(target);
|
||||
} catch (Exception ignored) {
|
||||
// Ignore cleanup issues for benchmark fixture shutdown.
|
||||
}
|
||||
}
|
||||
|
||||
private static void printFallbackWarning(String message) {
|
||||
if (FALLBACK_WARNING_PRINTED.compareAndSet(false, true)) {
|
||||
System.out.println(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user