RFC: Modernizing RocksDB’s Java API using Panama (FFM)
java-api
Hello
I'm a long-time user of rocksdbjni (since version 6.x I believe) and I'm happily running it in production since 7 years.
Lately I started to use JDK 25 in production and wanted to experiment the new possibilities, especially after reading this excellent article https://rocksdb.org/blog/2024/02/20/foreign-function-interface.html.
This RFC proposes a direction for a next‑generation Java API for RocksDB based on the Foreign Function & Memory API (FFM/Panama). The goal is to reduce JNI surface area, improve safety, and make contributions easier — without rewriting the C++ core or breaking existing users.
*Motivation*
My main motivation is to reduce the lag between the RocksDB C++ and Java: historically every time a new API is defined, some time is needed before it appears.
*Goals*
- move logic to pure Java, remove JVM crashes in case of bad memory access
- Use Panama MemorySegment for zero‑copy
- Make contributions easier
*Non‑Goals*
- Rewriting RocksDB C++
- Breaking existing Java API immediately
- Removing JNI before Panama is stable
*Proposed Architecture*
- Use `c.h` via `jextract` or AI (I've beenusing Claude Code so far and it is quite good)
- Wrap it into a Java API that looks really 1:1 with the current rocksdbjni
- Any Java class is holding the `MemorySegment` , the code can be generated in a very mechanical way + few tweaks
around ownership of some related objects (to be clarified).
Example
```java
public final class FlushOptions implements AutoCloseable {
static final MethodHandle MH_CREATE;
private static final MethodHandle MH_DESTROY;
private static final MethodHandle MH_SET_WAIT;
private static final MethodHandle MH_GET_WAIT;
static {
MH_CREATE = RocksDB.lookup("rocksdb_flushoptions_create",
FunctionDescriptor.of(ValueLayout.ADDRESS));
MH_DESTROY = RocksDB.lookup("rocksdb_flushoptions_destroy",
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
// void rocksdb_flushoptions_set_wait(opts*, unsigned char)
MH_SET_WAIT = RocksDB.lookup("rocksdb_flushoptions_set_wait",
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_BYTE));
// unsigned char rocksdb_flushoptions_get_wait(opts*)
MH_GET_WAIT = RocksDB.lookup("rocksdb_flushoptions_get_wait",
FunctionDescriptor.of(ValueLayout.JAVA_BYTE, ValueLayout.ADDRESS));
}
private final MemorySegment ptr;
/**
* Creates FlushOptions with {@code wait = true} (the default).
*/
public FlushOptions() {
try {
this.ptr = (MemorySegment) MH_CREATE.invokeExact();
} catch (Throwable t) {
throw new RocksDBException("flushoptions create failed", t);
}
}
/**
* If {@code true} (default), {@code flush()} blocks until the memtable flush
* completes. If {@code false}, the flush is submitted asynchronously.
*
* @return {@code this} for chaining
*/
public FlushOptions setWait(boolean wait) {
try {
MH_SET_WAIT.invokeExact(ptr, wait ? (byte) 1 : (byte) 0);
return this;
} catch (Throwable t) {
throw new RocksDBException("flushoptions setWait failed", t);
}
}
/** Returns whether flush waits for completion. */
public boolean isWait() {
try {
return ((byte) MH_GET_WAIT.invokeExact(ptr)) != 0;
} catch (Throwable t) {
throw new RocksDBException("flushoptions getWait failed", t);
}
}
@Override
public void close() {
try {
MH_DESTROY.invokeExact(ptr);
} catch (Throwable t) {
throw new RocksDBException("flushoptions destroy failed", t);
}
}
}
```
*Call for Contributors*
There is an experimental repository, created in few hours of work (mostly by Claude):
https://github.com/dfa1/rocksdbffm
Performance numbers are not bad but they need extra validation.
Feedback from the RocksDB maintainers and the community would be extremely valuable at this stage
/cc @adamretter @alanpaxton
3 条评论