SBK

Storage Benchmark Kit

Storage driver guide

Drivers are the primary SBK extension point. A driver converts the harness’s generic payload operations into calls to one storage API while leaving scheduling, rate control, timing, and percentile calculation to SBK and PerL.

Runtime inventory

The aggregate distribution currently enables 53 driver projects. The source tree also contains disabled drivers and a template.

Category Enabled drivers
Object and blob storage cephs3, minio, openio, seaweeds3
Files and distributed filesystems asyncfile, file, filestream, hdfs
Streaming and messaging activemq, artemis, bookkeeper, kafka, nats, natsStream, nsq, pravega, pulsar, rabbitmq, redpanda, rocketmq
Relational and SQL systems db2, derby, exasol, h2, hive, jdbc, mariadb, mssql, mysql, postgresql, sqlite
Document, search, and analytical systems chromadb, couchbase, couchdb, dynamodb, elasticsearch, mongodb, solr
Key-value and embedded stores fdbrecord, foundationdb, leveldb, memcached, redis, rocksdb
Harness and local data structures atomicq, cassandra, concurrentq, conqueue, csv, linkedbq, null, perlbench, syncq

ignite is disabled in the Gradle registration files. halodb is disabled because its GitHub Packages dependency can be unavailable without credentials or package quota. sbktemplate is intentionally excluded because it is a scaffold.

The registration files—not this table—are authoritative:

Driver contract

A storage class implements io.sbk.api.Storage<T>:

public interface Storage<T> {
    void addArgs(InputOptions params);
    void parseArgs(ParameterOptions params);
    void openStorage(ParameterOptions params) throws IOException;
    void closeStorage(ParameterOptions params) throws IOException;
    DataWriter<T> createWriter(int id, ParameterOptions params) throws IOException;
    DataReader<T> createReader(int id, ParameterOptions params) throws IOException;
    DataType<T> getDataType();
}

getDataType() has a default suitable for byte[]; drivers using String, ByteBuffer, protobuf ByteString, or another representation override it.

Lifecycle ownership

Writer contract

The simplest writer implements Writer<T>.writeAsync(T data):

Reader contract

The simplest reader implements Reader<T>.read() and returns one payload. Backends with callback, batching, or embedded producer timestamps can use or override the specialized reader helpers. End-of-stream should use the behavior expected by the selected reader abstraction, typically EOFException for finite sources.

drivers/acmekv/
├── build.gradle
├── README.md
└── src/main/
    ├── java/io/sbk/driver/AcmeKv/
    │   ├── AcmeKv.java
    │   ├── AcmeKvConfig.java
    │   ├── AcmeKvWriter.java
    │   └── AcmeKvReader.java
    └── resources/acmekv.properties

Discovery uses simple Java class names. Keep directory, package, public class, resource name, and -class spelling consistent with existing drivers.

Choose a reference driver

Need Reference
Small synchronous local implementation drivers/file or drivers/filestream
Async file API drivers/asyncfile
S3-compatible HTTP SDK and shutdown handling drivers/minio
Message producer/consumer drivers/kafka, drivers/pulsar, or drivers/rabbitmq
Relational operations drivers/jdbc and a concrete SQL driver
Custom payload type drivers/file, drivers/csv, or drivers/fdbrecord
Callback reader Search for implementations of AbstractCallbackReader
Idle windows, pending futures, timeout, and shutdown drivers/null
Immediate synthetic completions and end-to-end SBK/PerL queue comparison drivers/perlbench

Prefer the driver whose SDK and completion semantics resemble the new backend, not merely the driver with the shortest source.

null and perlbench are not interchangeable no-op baselines. The default Null write intentionally remains incomplete, whereas every PerlBench operation completes immediately and feeds a timestamp into PerL. Use their READMEs to avoid interpreting an idle-control result as measurement-pipeline throughput.

Add a driver

  1. Write a driver specification using DRIVER_SPECIFICATION.md for non-trivial backends.
  2. Copy drivers/sbktemplate or a closer existing driver.
  3. Add the vendor client dependency to the driver build.gradle.
  4. Add include 'drivers:<name>' to settings-drivers.gradle.
  5. Add api project(':drivers:<name>') to build-drivers.gradle.
  6. Add any new top-level Java package to checkstyle/import-control.xml.
  7. Implement storage lifecycle, writers, readers, configuration, and documentation.
  8. Add focused unit tests for parsing, key generation, serialization, or error classification that do not require a live service.
  9. Run module, full-build, distribution, discovery, and real-backend verification.

The exact procedure is in AGENT_RECIPES.md.

Performance and correctness rules

Configuration rules

Dependency and compatibility rules

Adding a vendor SDK may require an import allow-list entry. It also changes the distribution’s pathing-JAR manifest, so use a clean rebuild before runtime testing.

The MinIO client is intentionally pinned at 8.5.17 because later checksum-header behavior can be incompatible with older S3 implementations. Do not upgrade it as a routine dependency refresh.

Do not enable HaloDB without confirming access to its GitHub Packages artifact.

Verification

# Fast feedback
./gradlew :drivers:<name>:compileJava
./gradlew :drivers:<name>:check

# Integration with every enabled module
./gradlew check

# Runtime packaging and discovery
./gradlew clean :pathingJar installDist --rerun-tasks
./build/install/sbk/bin/sbk -class <name> -help

# Backend smoke test; use non-production data and credentials
./build/install/sbk/bin/sbk -class <name> <connection-options> \
  -writers 1 -size 1024 -seconds 15

When reads are supported, read the records created by the write smoke test. Exercise at least one expected failure such as an invalid endpoint or credentials and confirm that it terminates clearly.

Driver definition of done