Skip to content

SidePlugin Java Binding

rockeet edited this page Aug 18, 2023 · 45 revisions

SidePlugin Java Binding 只需要极小的工作量

只需要 Bind SidePluginRepo,并且只需要 bind 极少的方法(此处仅列出声明):

class SidePluginRepo extends RocksObject {
    public SidePluginRepo(); // constructor
    public void importAutoFile(String fname) throws RocksDBException;
    public RocksDB openDB(String js) throws RocksDBException;
    public RocksDB openDB(String js, List<ColumnFamilyHandle> out_cfhs) throws RocksDBException;
    public RocksDB openDB() throws RocksDBException;
    public RocksDB openDB(List<ColumnFamilyHandle> out_cfhs) throws RocksDBException;
    public void startHttpServer() throws RocksDBException;
    public void closeHttpServer();
    public void closeAllDB(); // conform to C++ native name CloseAllDB
    public void close(); // synonym to closeAllDB
    public void put(String name, String spec, Options opt); // rarely used
    public void put(String name, String spec, DBOptions dbo);  // rarely used
    public void put(String name, String spec, ColumnFamilyOptions cfo); // rarely used
}

这么小的工作量,我已经多年没碰过 java,jni 更是从来没用过,实现这个 Binding,现学现卖,从头至尾,也只花了不到一天的时间。所以,良好的架构设计,可以极大地减小工作量,如果按照 SidePlugin 的架构,rocksdbjava 中的代码(java 代码与 c++ 各约 4 万行)至少可以缩减 90%。

参考 C++ 版的 101,Java 版的写法没啥大的不同;再参考实际的例子 SideGetBenchmarks,相信大家很容易使用 java 调用 ToplingDB。

其中的 put 方法是为了兼容已有代码:如果现存代码对各种 Option 进行了一些设置,并且这些设置无法通过配置文件进行配置(例如自定义了 java 版的 CompactionFilter),就先把 option put 到 SidePluginRepo,然后再调用 importAutoFile —— importAutoFile 会补充 put option 中的缺失选项,并覆盖同名选项!

将这个 Binding 再精简归纳总结:

    void importAutoFile(String fname) throws RocksDBException;
    RocksDB openDB(4 个重载) throws RocksDBException;
    void startHttpServer() throws RocksDBException;
    void close();
    void put(String name, String spec, 三种 option); // 很少使用

openDB 的四个重载中:

  1. js 参数一般是 json/yaml 中定义的 dbname,极少数情况下可以是 db 的 json 定义
    • 没有该参数的 openDB 指的是打开 json 中 open 对象指定的 db, 这是多数情况
  2. out_cfhs 指的是打开包含多个 ColumnFamily 的 db
    • 没有该参数的 openDB 指的是打开仅使用 default cf 的 db

一般情况下,在 openDB ... startHttpServer ... closeHttpServer ... closeAllDB 中,closeHttpServer 可以省略,因为 http 会在 closeAllDB 中自动关闭,同时,close 是 closeAllDB 的同义词,从而,可以简化为 openDB ... startHttpServer ... close,在使用了 try (...) {} 的情况下,close 也可以省略。

仍然提供 closeHttpServer 的原因在于,用户可能并不想在 db 的整个生存期内一直开启 http

运行 SideGetBenchmarks

步骤 0:安装必备工具和前置依赖

sudo yum -y install git gcc-c++ jemalloc-devel
sudo yum -y install java-latest-openjdk java-latest-openjdk-devel maven
sudo yum -y install libaio-devel gflags-devel zlib-devel bzip2-devel libcurl-devel liburing-devel

步骤 1:编译并安装 toplingdb

git clone https://github.com/topling/toplingdb.git --depth 1
cd toplingdb
git submodule update --init --recursive
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts # silent git
make -j`nproc` DEBUG_LEVEL=0 shared_lib
sudo make install-shared PREFIX=/opt

步骤 2: 编译 rocksdbjava

# 务必设置正确的环境变量 JAVA_HOME, 这里设置的是 open jdk 的默认路径
export JAVA_HOME=/usr/lib/jvm/jre-openjdk
make rocksdbjava -j`nproc` DEBUG_LEVEL=0

步骤 3: 将 rocksdbjava 加入 maven 仓库

你现在应该位于 toplingdb 仓库的根目录

cd java/target
cp rocksdbjni-7.10.0-linux64.jar rocksdbjni-7.10.0-SNAPSHOT-linux64.jar
mvn install:install-file -Dfile=rocksdbjni-7.10.0-SNAPSHOT-linux64.jar -DgroupId=org.rocksdb -DartifactId=rocksdbjni -Dversion=7.10.0-SNAPSHOT -Dpackaging=jar

步骤 4: 编译 SideGetBenchmarks

假定你现在位于 toplingdb 仓库的根目录

cd java/jmh
mvn clean package

步骤 5: 运行 SideGetBenchmarks

假定你现在已经位于 toplingdb/java/jmh

mkdir -p /dev/shm/db_bench_enterprise
cp ../../sideplugin/rockside/src/topling/web/{style.css,index.html} /dev/shm/db_bench_enterprise
export LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH # for libterark-*
export LD_PRELOAD=libterark-zbs-g++-12.1-r.so:libterark-fsa-g++-12.1-r.so:libjemalloc.so
java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar \
     -p keyCount=1000 -p keySize=128 -p valueSize=32768 \
     -p sideConf=../../sideplugin/rockside/sample-conf/db_bench_enterprise.yaml SideGetBenchmarks

运行起来之后,可以访问 http://127.0.0.1:2011 观测 db 状态。

说明 1:社区版用户请将 db_bench_enterprise.yaml 替换为 db_bench_community.yaml,这两个文件都在 toplingdb/sideplugin/rockside/sample-conf 中, sideplugin/rockside 是 ToplingDB 的 submodule。

社区版强行使用 db_bench_enterprise.yaml 也可以正常工作,只是 compact 中不会创建 ToplingZipTable 的 SST,与 yaml 中 level_writers 定义的不一致。

说明 2:设置 LD_PRELOAD 是因为 toplingdb 和 jemalloc 动态库使用了 init exec 类型的 TLS(线程局部存储),这种 TLS 访问速度最快,但使用了这种 TLS 的动态库必须预先加载,或者由可执行文件显式链接。而 java 加载 jni 是由 jvm 动态加载的,如果不用 LD_PRELOAD,会导致动态库中的这种 TLS 初始化失败。

限制

目前使用 Java 实现的组件无法用 Java 代码注册到 SidePlugin 的插件库中,一般情况下这并不是什么严重问题,唯一的问题在于:无法使用分布式 Compact。

例如在 Java 中实现了自定义的 CompactionFilter/MergeOperator ,因为无法注册到 SidePlugin 的插件库中,所以无法为 dcompact_worker 预加载相应的动态库以运行分布式 Compact。除非(1)实现专门的动态库,在其中用 jni 创建 JVM 并调用 Java 代码,并创建 C++ 包装类,把相应的组件注册到 SidePlugin 中。或者(2)使用 C++ 实现相同功能的插件并注册到 SidePlugin 中,例如数据都是 protobuf 格式,用 C++ 实现相应插件并不困难,而且性能还更高。

例如 flink rocksdb state backend 就是用 C++ 实现的 CompactionFilter,用 jni 包装后再通过 java 来管理

Clone this wiki locally