Add ikv-store benchmark fixture

This commit is contained in:
Mikhail Yevchenko
2026-04-05 17:20:44 +00:00
parent 2799b3b12a
commit 6cffba9177
5 changed files with 200 additions and 30 deletions
@@ -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);
}
}
}