SIMPLE KV API. SHARED OBJECT STORAGE.

Object storage.
A simple
key-value API.

A simple key-value service with durable object storage and independent readers. Built in Rust on SlateDB.

Active local pilot · Validated with Docker LocalStack

01 / FOLLOW A REQUESTOne dataset. Separate roles.
Rockbee: one writer, independent readers, shared object storage Client requests reach the gateway. PUT and DELETE go to the writer, which persists through SlateDB to object storage. GET goes to a reader with its own RAM cache. Readers follow updates and fetch data from the same object storage. multiplexed TCP PUT / DELETE GET persist follow + fetch Client Gateway One writer SlateDB N readers SlateDB + RAM cache Object storage Shared WAL + SST files
PUT

The writer persists your bytes through SlateDB. A successful response means the write is durable; readers may still be catching up.

Architecture schematic · one writer per storage prefix

PUT · GET · DELETE Opaque bytesOptional TTL Expire on readsAsync Rust client Bounded multiplexing

THE ARCHITECTURE

Storage outlives
the process.

Provision read-serving compute separately from durable storage. Add readers as serving demand changes, while the dataset stays in shared object storage. SlateDB handles persistence; Rockbee provides the network service and client.

Read the architecture notes
01

Write through one writer.

The gateway routes PUT and DELETE to the writer for a storage prefix. Success is returned after SlateDB confirms durability in object storage.

02

Read through independent readers.

GET requests are distributed round-robin. Each reader has its own SlateDB state and RAM cache, backed by the same stored data. The gateway has no key-value cache.

03

Allow updates time to arrive.

Readers follow manifests and WAL updates asynchronously. A durable write can still read as an older value or a miss until a reader catches up.

WHY DISAGGREGATION MATTERS

Let data and traffic
grow independently.

Disaggregated architecture separates the machines serving requests from the storage that keeps data durable. Dataset size and serving capacity become separate planning decisions.

Size compute for demand.

Add readers against the same stored dataset as read traffic grows. Each new reader does not need its own full durable copy.

Keep hot data close.

Each reader caches frequently accessed data in RAM. The full dataset can exceed cache capacity; cold reads fetch from object storage.

Replace compute. Keep data.

A replacement reader opens the existing dataset. It still needs to load metadata, catch up, and warm its cache before steady-state performance.

The tradeoffs: cold-read latency, duplicated hot data across reader caches, and object-store request and transfer costs. Rockbee still has one writer per prefix and eventual reader visibility. Savings and throughput gains depend on the workload.

Explore the design in SlateDB

BYTES IN. BYTES OUT.

Your data.
Your encoding.

Keep the connection. Submit a write. Collect its result. The client correlates responses on a shared TCP connection.

Keys and values are nonempty byte arrays. Encode JSON, Protobuf, or your own format in your application.

Explore the Rust client
main.rs
use rockbee_client::{Client, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let gateway = std::env::var("GATEWAY_ADDR")?;
    let client = Client::connect(&gateway).await?;

    // Starts now. None means no TTL.
    let write = client.put(b"product:42", b"hello", None);
    write.await?; // Durable acknowledgement.

    // Reader visibility is eventual.
    let value = client.get(b"product:42").await?;
    println!("{value:?}");

    client.delete(b"product:42").await?;
    client.shutdown().await;
    Ok(())
}

EXPLICIT BEHAVIOR

Know what the
response means.

Failures are part of the API. These are behaviors you can inspect in the code and exercise locally.

DURABILITY

A successful write
has persisted.

PUT and DELETE wait for SlateDB’s durable acknowledgement before returning success.

Inspect the writer
UNCERTAINTY

A lost ACK is an
unknown outcome.

A write may have succeeded even if its reply never arrives. Mutation errors distinguish NotSent, Rejected, and Unknown. Uncertain writes are never automatically replayed.

Run the lost-ACK test

This local test drops replies between gateway and writer. It checks that persisted writes remain visible and are not replayed. It uses a filesystem object-store adapter.

cargo test --locked -p rockbee-gateway \
  --test gateway_durability \
  lost_gateway_writer_acks_are_unknown_without_replaying_durable_mutations
Read the test
VISIBILITY

Durable now.
Visible after catch-up.

A write acknowledgement and reader visibility are different events. Measure the gap against your running local setup with the visibility probe.

make visibility BENCH_ADDR="$GATEWAY_ADDR"
Inspect the probe

WHERE IT FITS

Prepare once.
Look up by key.

For read-heavy applications that know the key and can tolerate a delay before updates become visible.

Catalog & content

Enriched product cards, descriptive catalog data, and prepared page fragments.

product:42:card

Recommendations & ML

Precomputed recommendations, feature vectors, and reusable inference results.

user:123:recommendations

Reporting & lookups

Published report snapshots and slowly changing reference data.

report:daily:2026-10-11

Your application prepares and refreshes the values. Rockbee serves them by key. Transactions, compare-and-swap, SQL, and immediate read-after-write visibility are outside this API.

OPTIONAL CHANGE DATA CAPTURE

Follow the
durable changes.

rockbee-cdc uses SlateDB’s WAL reader to export durable PUT and DELETE changes as one JSON event per line. Run it separately when you need a change feed; Rockbee readers work without it.

Connect stdout to your own consumer to forward changes to another service. Downstream delivery is handled by that consumer.

Run the CDC exporter
  1. Object storage WAL
  2. rockbee-cdc
  3. JSON lines
stdout · illustrative events
{"op":"put","key":[107],"value":[118],"seq":1,"wal_file_id":2}
{"op":"delete","key":[107],"value":null,"seq":2,"wal_file_id":3}

Keys and values are byte arrays: [107] is k, and [118] is v. Sequence and WAL numbers vary.

Delivery, retention & TTL
  • A local cursor resumes after the last completed WAL file. A crash can repeat events; consumers must handle duplicates. Stdout does not confirm downstream persistence.
  • The exporter needs retained WAL files. A missing required WAL stops the export; there is no built-in snapshot or backfill.
  • TTL PUTs include expires_at_unix_ms. Expiration itself does not emit a DELETE event; the consumer must check the timestamp.

TAKE IT FOR A SPIN

From source
to your first PUT.

Start a writer, reader, and gateway on your machine. You’ll need Rust through rustup and Docker Desktop.

The first build compiles SlateDB and the service binaries. The local runner prints dynamic addresses and uses a unique storage prefix.

Full setup guide & existing installations
01 / GET THE SOURCE
git clone https://github.com/umit/rockbee.git
cd rockbee
02 / START THE LOCAL STACK

For a fresh setup, with port 4566 free:

docker compose up -d --wait
make local

Already using LocalStack? Follow the setup guide before starting another container. Keep this terminal open.

03 / WRITE, THEN READ

In a second terminal at the repository root, replace the address below with the gateway address printed above:

export GATEWAY_ADDR='127.0.0.1:PORT_FROM_READY_OUTPUT'
./target/release/rockbee-client --addr "$GATEWAY_ADDR" put demo:key hello
./target/release/rockbee-client --addr "$GATEWAY_ADDR" get demo:key

The first GET can be a miss. Repeat it after the reader catches up. Stop the local runner with Ctrl-C.

A MEASURED PATH TO ADOPTION

Start small.
Measure what matters.

Choose one read-heavy workload that can tolerate eventual reads. Evaluate it against your current system before expanding.

  1. Define the contract. Set acceptable reader lag, latency targets, and the application's handling of uncertain writes.
  2. Use representative traffic. Measure warm and cold reads, sustained writes, recovery, and behavior under peak load.
  3. Account for the whole system. Include reader memory, compute, object-store requests, and transfer costs. Adding readers does not guarantee linear gains.
Inspect, reproduce, contribute
CURRENT SCOPE

One writer per prefix. Independent eventual readers. Byte-oriented PUT, GET, and DELETE, with optional TTL.

Production AWS operation, TLS, automatic writer failover, sharding, and capacity limits remain outside the verified scope. LocalStack measurements do not establish production S3 performance.

Ideas behind the project

SLATEDBAn Object-Native LSM for Online Systems CHRIS RICCOMINIThe Cloud Storage Triad: Latency, Cost, Durability MURAT DEMIRBASAtlas Infinite: MongoDB’s Disaggregated Architecture More on Neon, turbopuffer, and WarpStream