zerobus-sdk / java
CLAUDE.md94 starsChanged 10 days agoApache-2.0View on GitHub
Java wrapper around the Rust core via JNI. Ingestion is asynchronous. ingestRecordOffset() (and ingestRecordsOffset()) return as soon as the record/batch is queued — the SDK sends it and tracks its acknowledgment in the background. - Idiomatic flow: ingest in a loop, then call flush() once (after a bounded batch, or periodically for a long-running stream) to confirm durability. Equivalently, call waitForOffset() on the last returned offset — acks are ordered, so confirming the last offset confirms every prior record. -…
In this repository6 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- cpp/CLAUDE.mdCLAUDE.md
- go/CLAUDE.mdCLAUDE.md
- python/CLAUDE.mdCLAUDE.md
- rust/CLAUDE.mdCLAUDE.md
- typescript/CLAUDE.mdCLAUDE.md
The file
We split this CLAUDE.md into the rules it gives an agent
5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 17
Per-record
waitForOffset(): use when a specific record must be confirmed before continuing. - Very specific: line 95
Every PR must update java/NEXT_CHANGELOG.md under the appropriate section if it changes user-facing behavior.
- Very specific: line 96
Update java/README.md if the change affects usage, setup, or API surface.
- Very specific: line 97
Add or update examples in java/examples/ for new or modified APIs.
- Distinctive: line 98
Add Javadoc for all new public classes/methods.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Java SDK
Java wrapper around the Rust core via JNI.
Client code patterns (read before writing or reviewing examples/docs)
Ingestion is asynchronous. ingestRecordOffset() (and ingestRecordsOffset()) return as
soon as the record/batch is queued — the SDK sends it and tracks its acknowledgment in the
background.
- Idiomatic flow: ingest in a loop, then call
flush()once (after a bounded batch, or periodically for a long-running stream) to confirm durability. Equivalently, callwaitForOffset()on the last returned offset — acks are ordered, so confirming the last offset confirms every prior record. - Async monitoring: register an
AckCallback(onAck/onError) to track progress without blocking the ingest loop. - Per-record
waitForOffset(): use when a specific record must be confirmed before continuing. Avoid calling it after every record in a tight loop, since that limits throughput to one record per round-trip. - New code should use
ZerobusProtoStream/ZerobusJsonStream(offset-based) rather than the deprecatedZerobusStream. With the deprecated API, keep the last future and.join()once after the loop rather than joining per record (the latter is discouraged for the same throughput reason).
Structure
java/
├── src/main/java/com/databricks/zerobus/
│ ├── ZerobusSdk.java # Main SDK class
│ ├── ZerobusStream.java # Legacy generic stream (deprecated)
│ ├── ZerobusProtoStream.java # Proto ingestion stream
│ ├── ZerobusJsonStream.java # JSON ingestion stream
│ ├── ZerobusArrowStream.java # Arrow Flight stream
│ ├── NativeLoader.java # Platform-specific JNI library loading
│ └── ... # Config, exceptions, callbacks
├── src/test/ # JUnit 5 + Mockito tests
├── pom.xml # Maven config
└── tools/ # Schema generationThe JNI native library is built from rust/jni/. It's packaged inside the JAR for all supported platforms.
Build commands
Run from java/:
mvn compile— Compilemvn test— Run testsmvn test -Dtest=ClassName#method— Run specific testmvn clean package -Dzerobus.skipNativeLibCheck=true— Build local Java JARs without staged release JNI librariesmvn spotless:check— Check formatting (Google Java Format)mvn spotless:apply— Auto-format
Features
- Avro (Beta) —
ZerobusAvroStream, viastreamBuilder().avro(schemaJson). The Java side encodesGenericRecords with Apache Avro (AvroEncoder); pre-encodedbyte[]pass through. The record format is fixed at creation (RecordFormat→ native handle) and reused on ingest.org.apache.avro:avrois aprovideddependency (caller-supplied, likearrow-vector) and needs Java 11+; the rest of the SDK stays Java 8. Building therefore needs a JDK 11+ toolchain (enforced bymaven-enforcer); the artifact still targets Java 8.- The JNI crate's
avrocargo feature forwards to the SDK's;make build-jnipasses it. Never enable it in a plain--workspacebuild, or unification turns Avro on for the FFI crate too.
FFI boundary: JNI
JNI is the most manual of all the bindings. Key considerations:
- Handle pattern: Java holds a
long nativeHandlefield — an opaque pointer to the Rust object. All native methods pass this handle. If the handle is invalid (use-after-close), Rust panics or returns an error. - No finalizers: Unlike Go/Python/TypeScript, Java streams do not have GC-triggered cleanup. Users must call
close()explicitly or use try-with-resources. Forgetting this leaks native memory. - AutoCloseable: SDK and stream classes implement
AutoCloseable. Always prefer try-with-resources. - Async bridge:
CompletableFuturebridges Rust futures to Java. Stream creation and acknowledgment waiting return futures. - Thread safety: The Java SDK is not thread-safe. Do not share SDK or stream instances across threads without external synchronization.
- Native library loading (
NativeLoader.java): Detects OS + arch, extracts the correct.so/.dylib/.dllfrom the JAR classpath to a temp directory, loads viaSystem.load(). MarkeddeleteOnExit().
Breaking change rules
Public API is everything in com.databricks.zerobus with public visibility:
- Removing or renaming public classes, methods, or fields is breaking.
- Changing method signatures (parameter types, return types, checked exceptions) is breaking.
- Deprecate with
@Deprecatedannotation and Javadoc explaining the replacement. - JNI native method signatures (
private static native ...) are internal but must stay in sync withrust/jni/— changing one without the other causesUnsatisfiedLinkErrorat runtime.
Performance notes
- JNI calls have non-trivial overhead (~100ns per crossing). Batch APIs reduce crossings.
- Proto descriptors are passed as
byte[]— copied at the boundary. This is a one-time cost at stream creation. - Record payloads (
byte[]for proto,Stringfor JSON) are copied across JNI. For high-throughput, prefer proto with batch ingestion. - Native library extraction happens once at class load time — first-use latency includes temp file I/O.
Changelog and documentation
- Every PR must update
java/NEXT_CHANGELOG.mdunder the appropriate section if it changes user-facing behavior. - Update
java/README.mdif the change affects usage, setup, or API surface. - Add or update examples in
java/examples/for new or modified APIs. - Add Javadoc for all new public classes/methods.
Release
- Version source:
java/pom.xml(<version>x.y.z</version>). - Tag:
java/v<semver>→ triggersrelease-java.yml→ builds JNI native libs for all platforms, packages into JAR → publishes to Maven Central. - The JAR bundles native libraries for all 5 platforms. The JNI release depends on
rust/jni/— if Rust JNI code changed, both must be coordinated. - On version bump PR: update version in
pom.xml, moveNEXT_CHANGELOG.mdcontents toCHANGELOG.md, resetNEXT_CHANGELOG.md.
Config
- Java 8+ compatibility (source/target 1.8)
- Google Java Format via Spotless
- Arrow version: 17.0.0
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

