A pure-Java, framework-free library for CockroachDB. Its headline feature:
custom annotations plus runtime dynamic proxies that make CockroachDB
anomaly-handling boilerplate — the try/catch retry loops around serialization
errors (SQLSTATE 40001) — disappear.
It is the framework-free sibling of the
cockroachdb-spring-boot-starter, and part
of a family of CockroachDB client libraries (Go, Python, .NET, Ktor). Its
roadmap tracks Cockroach Continuum (Plenum, vCluster, Aegis, and an
Agent/MCP surface) — see docs/CONTINUUM_PLAN.md.
Built for Java 25 (uses ScopedValue, sealed types, records, pattern-matching
switch) with Gradle 9.8 (multi-module).
Modules are served from this repository's GitHub Pages Maven repository — no token required. Each module is published as com.cockroachlabs:cockroachdb-java-library-<module> (modules: core, continuum, agent, cdc, metrics, ai, testing).
<repositories>
<repository>
<id>cockroachlabs</id>
<url>https://cockroachlabs.github.io/cockroachdb-java-library/maven/</url>
</repository>
</repositories>
<dependency>
<groupId>com.cockroachlabs</groupId>
<artifactId>cockroachdb-java-library-core</artifactId>
<version>0.1.1</version>
</dependency>repositories {
mavenCentral()
maven { url 'https://cockroachlabs.github.io/cockroachdb-java-library/maven/' }
}
dependencies {
implementation 'com.cockroachlabs:cockroachdb-java-library-core:0.1.1'
}API reference: https://cockroachlabs.github.io/cockroachdb-java-library/
Annotate a method, wrap the object with Crdb.enhance(...), and stop writing
retry loops:
public interface AccountService {
void transfer(long from, long to, long cents);
}
public class AccountServiceImpl implements AccountService {
private final DataSource ds;
public AccountServiceImpl(DataSource ds) { this.ds = ds; }
@Transactional(retry = true) // SERIALIZABLE + auto-retry on 40001
public void transfer(long from, long to, long cents) {
Crdb.tx(ds, c -> {
// ... debit `from`, credit `to` ...
return null;
});
}
}
// Assign to the INTERFACE type: interface targets are enhanced with a JDK proxy.
AccountService svc = Crdb.enhance((AccountService) new AccountServiceImpl(ds));
svc.transfer(1, 2, 5_00); // serialization conflicts retried transparentlyNo interface? Crdb.enhance(pojo) generates a ByteBuddy subclass instead and
copies the target's field state, so the same annotations work on concrete
classes.
You never need a proxy to get retry — Crdb.tx(...) is a SERIALIZABLE,
auto-retrying transaction on its own:
long balance = Crdb.tx(ds, c -> {
try (var ps = c.prepareStatement("SELECT balance FROM accounts WHERE id = ?")) {
ps.setLong(1, 1);
var rs = ps.executeQuery();
rs.next();
return rs.getLong(1);
}
});| Annotation | Effect |
|---|---|
@Transactional(retry, isolation) |
Run in a JDBC transaction (SERIALIZABLE by default); retry on serialization conflicts using cockroach_restart semantics |
@CrdbRetry(maxRetries, baseDelayMillis, maxDelayMillis) |
Retry on serialization anomalies with adaptive backoff |
@CircuitBreaker(failureThreshold, resetTimeoutMillis) |
Fast-fail after repeated failures |
@FollowerRead(staleness) |
Read from the nearest replica with AS OF SYSTEM TIME |
@Region(value) |
Pin execution context to a region |
@VirtualCluster(value) / @Workload(Priority) |
Cockroach vCluster tagging: SET LOCAL application_name + workload QoS on the transaction |
@AgentTool(value, destructive) |
Expose a method as an agent-callable tool (see the agent module) |
@VirtualCluster / @Workload tag a transaction's session: the vCluster name
becomes application_name and the workload priority maps to
default_transaction_quality_of_service (HIGH→critical, NORMAL→regular,
LOW→background), applied with SET LOCAL so pooled connections stay clean.
@VirtualCluster("analytics")
@Workload(Priority.LOW)
public List<Row> nightlyRollup() { return Crdb.tx(ds, c -> ...); }
// or via the facade:
Crdb.vcluster("analytics").workload(Priority.LOW).tx(ds, c -> ...);v1 does workload tagging/QoS; routing to a distinct vCluster endpoint is platform-dependent and out of scope. Cockroach vCluster hosts cannot use Cockroach Plenum — requesting
Crdb.storage(...)under a vCluster context logs a warning.
for (TableStorage t : Crdb.storage(ds).summary()) {
System.out.printf("%s.%s rows=%s bytes=%s%n",
t.schema(), t.table(), t.rows(), t.bytes()); // OptionalLong, may be empty
}Customer-safe SQL only (SHOW TABLES / SHOW RANGES ... WITH TABLES, DETAILS);
missing columns degrade to OptionalLong.empty() instead of throwing.
AegisClient aegis = Crdb.aegis(AegisConfig.defaults()); // simulated backend
aegis.findings();
aegis.emit(AppEvent.of("deploy", "app", Map.of("version", "1.2.0"), System.currentTimeMillis()));
// Feed retry/circuit signals into Aegis automatically:
new AegisExhaust(aegis).register();AegisConfig.defaults() (or any config without an endpoint) uses the in-memory
simulated backend; a real HTTP client is stubbed until the Aegis API is public.
CrdbAgent agent = CrdbAgent.from(new CrdbOpsTools(ds));
String tools = agent.listTools(); // MCP tools/list JSON
String result = agent.invoke("retry_stats", "{}"); // MCP tools/call JSON
// destructive tools are hidden until: agent.allowDestructive(true)The continuum module adds a Cloud control-plane client (simulated until an API key is set), typed metric-export builders, a virtual-cluster lifecycle manager, and a health probe.
import com.cockroachlabs.crdb.continuum.cloud.*;
CloudClient cloud = ContinuumCloudFactory.make(new CloudClient.Config(true, "", apiKey));
cloud.listClusters();
cloud.configureMetricExport(clusterId, MetricExport.datadog(ddKey, "US1")); // datadog/cloudWatch/prometheus
boolean ready = CloudHealth.isReachable(cloud);
VirtualClusterManager vm = VirtualClusterManager.forDataSource(ds);
vm.create("analytics");
String dsn = VirtualClusterManager.routingConnString(baseUrl, "analytics"); // adds -ccluster optionMCP is a public, versioned protocol, so the client speaks the real wire format (2025-06-18 initialize handshake, tools/list, tools/call, SSE + session handling). The Aegis REST API is not yet public, so Aegis is reached read-only over MCP with caller-supplied tool names (discover them via listTools()) rather than an invented wire protocol.
import com.cockroachlabs.crdb.continuum.mcp.*;
import com.cockroachlabs.crdb.continuum.aegis.McpAegisClient;
McpClient mcp = ContinuumMcpFactory.create(
new McpClient.Config(true, "https://cockroachlabs.cloud/mcp", token));
mcp.listTools();
McpAegisClient aegis = new McpAegisClient(mcp,
new McpAegisClient.ToolNames("list_findings", "list_recommendations", "emit_event"));
aegis.findings();| Module | Status | Purpose |
|---|---|---|
core |
implemented | Annotations, sealed anomalies + classifier, adaptive retry, circuit breaker, ScopedValue context, transaction template, dynamic-proxy engine, event hooks, Crdb facade |
continuum |
implemented | Cockroach Plenum storage insights, vCluster tagging/QoS, Aegis client (simulated) + exhaust |
agent |
implemented | Agent / MCP tool surface consuming @AgentTool |
cdc |
stub (P4) | Changefeed webhook handling + test harness |
metrics |
stub (P4) | Micrometer / OpenTelemetry instrumentation |
ai |
stub (P4) | pgvector similarity + semantic search |
testing |
stub (P4) | Testcontainers harness + concurrency runner |
./gradlew build # compile all modules + run tests
./gradlew :core:test # core tests onlyIntegration tests connect to a local CockroachDB. Override the target with the
CRDB_URL environment variable
(default jdbc:postgresql://localhost:26257/defaultdb?user=root&sslmode=disable).
If the cluster is unreachable, integration tests are skipped, not failed.
- JDK 25 (toolchain-enforced)
- Gradle 9.8+ (wrapper included; Gradle 8.x cannot run on JDK 25)
- A CockroachDB cluster for integration tests
cockroachdb-spring-boot-starter— Spring Boot auto-configuration siblingcockroachdb-go-library,cockroachdb-python-library,cockroachdb-dotnet-library,cockroachdb-ktor-dsldocs/CONTINUUM_PLAN.md— Cockroach Continuum feature roadmap