Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .claude/skills/system-adapter-builder/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: system-adapter-builder
description: Build or update a SpiceBench system adapter with JSON-RPC over stdio and HTTP, including setup/create_tables/query_method/teardown/metrics support and template validation.
description: Build or update a SpiceBench system adapter with JSON-RPC over stdio and HTTP, including setup/create_tables/teardown/metrics support and template validation.
---

# SpiceBench System Adapter Builder
Expand All @@ -18,7 +18,6 @@ Required methods:

- `setup(run_id, datasets)`
- `create_tables(run_id)`
- `query_method(run_id)`
- `teardown(run_id)`
- `metrics(run_id)`
- `rpc.methods`
Expand All @@ -27,7 +26,7 @@ Required methods:

- Target language (`python`, `nodejs`, `rust`, `go`, or `java`)
- SUT provisioning flow for `setup` and `teardown`
- How to resolve query endpoint and credentials for `query_method`
- How to resolve query endpoint and credentials for `setup`
- Where to source metrics (cloud APIs, DB telemetry, host exporters)
- Runtime/toolchain target (latest/LTS channel used by repository workflows)

Expand All @@ -36,10 +35,10 @@ Required methods:
1. Copy the nearest template from `system-adapters/templates/<language>`.
2. Keep request/response envelopes JSON-RPC 2.0 compliant (`jsonrpc`, `id`, `method`, `params`).
3. Implement `setup` with run-scoped resources keyed by `run_id`.
4. Implement `create_tables` so the adapter creates/registers benchmark destination tables.
5. Implement `query_method` to return:
4. Implement `setup` to return:
- `driver`: typically `flightsql` or `databricks`
- `db_kwargs`: real endpoint + auth kwargs for the SUT
5. Implement `create_tables` so the adapter creates/registers benchmark destination tables.
6. Implement `teardown` with run-scoped cleanup keyed by `run_id`.
7. Implement `metrics` to return both objects:
- `resource`: CPU, memory, disk bytes, disk IOPS
Expand Down Expand Up @@ -74,7 +73,7 @@ If any metric is unavailable, return `0`/`0.0` and document why.
- Adapter responds to all required methods over stdio and HTTP.
- `rpc.methods` includes every exposed method.
- `create_tables` creates/registers benchmark tables for each dataset.
- `query_method` returns a valid `driver` and complete `db_kwargs`.
- `setup` returns a valid `driver` and complete `db_kwargs`.
- `metrics` returns both `resource` and `ingestion` objects.
- Language build/syntax checks pass:
- Python: `python -m py_compile`
Expand Down
40 changes: 35 additions & 5 deletions .github/workflows/validate_system_adapter_templates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,18 @@ jobs:
done

grep -q '"jsonrpc":"2.0"' /tmp/python-resp.json
grep -q '"ok":true' /tmp/python-resp.json
grep -q '"driver":"flightsql"' /tmp/python-resp.json
grep -q '"db_kwargs"' /tmp/python-resp.json
curl -sS -o /tmp/python-create.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"create_tables","params":{"run_id":"00000000-0000-0000-0000-000000000000"}}' \
http://127.0.0.1:18080/jsonrpc
grep -q '"ok":true' /tmp/python-create.json
curl -sS -o /tmp/python-methods.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"rpc.methods","params":{}}' \
http://127.0.0.1:18080/jsonrpc
grep -q '"methods"' /tmp/python-methods.json
grep -q '"setup"' /tmp/python-methods.json
grep -q '"create_tables"' /tmp/python-methods.json
grep -q '"metrics"' /tmp/python-methods.json

nodejs-template:
Expand Down Expand Up @@ -91,12 +97,18 @@ jobs:
done

grep -q '"jsonrpc":"2.0"' /tmp/node-resp.json
grep -q '"ok":true' /tmp/node-resp.json
grep -q '"driver":"flightsql"' /tmp/node-resp.json
grep -q '"db_kwargs"' /tmp/node-resp.json
curl -sS -o /tmp/node-create.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"create_tables","params":{"run_id":"00000000-0000-0000-0000-000000000000"}}' \
http://127.0.0.1:18081/jsonrpc
grep -q '"ok":true' /tmp/node-create.json
curl -sS -o /tmp/node-methods.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"rpc.methods","params":{}}' \
http://127.0.0.1:18081/jsonrpc
grep -q '"methods"' /tmp/node-methods.json
grep -q '"setup"' /tmp/node-methods.json
grep -q '"create_tables"' /tmp/node-methods.json
grep -q '"metrics"' /tmp/node-methods.json

rust-template:
Expand Down Expand Up @@ -130,12 +142,18 @@ jobs:
done

grep -q '"jsonrpc":"2.0"' /tmp/rust-resp.json
grep -q '"ok":true' /tmp/rust-resp.json
grep -q '"driver":"flightsql"' /tmp/rust-resp.json
grep -q '"db_kwargs"' /tmp/rust-resp.json
curl -sS -o /tmp/rust-create.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"create_tables","params":{"run_id":"00000000-0000-0000-0000-000000000000"}}' \
http://127.0.0.1:18082/jsonrpc
grep -q '"ok":true' /tmp/rust-create.json
curl -sS -o /tmp/rust-methods.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"rpc.methods","params":{}}' \
http://127.0.0.1:18082/jsonrpc
grep -q '"methods"' /tmp/rust-methods.json
grep -q '"setup"' /tmp/rust-methods.json
grep -q '"create_tables"' /tmp/rust-methods.json
grep -q '"metrics"' /tmp/rust-methods.json

go-template:
Expand Down Expand Up @@ -169,12 +187,18 @@ jobs:
done

grep -q '"jsonrpc":"2.0"' /tmp/go-resp.json
grep -q '"ok":true' /tmp/go-resp.json
grep -q '"driver":"flightsql"' /tmp/go-resp.json
grep -q '"db_kwargs"' /tmp/go-resp.json
curl -sS -o /tmp/go-create.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"create_tables","params":{"run_id":"00000000-0000-0000-0000-000000000000"}}' \
http://127.0.0.1:18083/jsonrpc
grep -q '"ok":true' /tmp/go-create.json
curl -sS -o /tmp/go-methods.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"rpc.methods","params":{}}' \
http://127.0.0.1:18083/jsonrpc
grep -q '"methods"' /tmp/go-methods.json
grep -q '"setup"' /tmp/go-methods.json
grep -q '"create_tables"' /tmp/go-methods.json
grep -q '"metrics"' /tmp/go-methods.json

java-template:
Expand Down Expand Up @@ -207,10 +231,16 @@ jobs:
done

grep -q '"jsonrpc":"2.0"' /tmp/java-resp.json
grep -q '"ok":true' /tmp/java-resp.json
grep -q '"driver":"flightsql"' /tmp/java-resp.json
grep -q '"db_kwargs"' /tmp/java-resp.json
curl -sS -o /tmp/java-create.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"create_tables","params":{"run_id":"00000000-0000-0000-0000-000000000000"}}' \
http://127.0.0.1:18084/jsonrpc
grep -q '"ok":true' /tmp/java-create.json
curl -sS -o /tmp/java-methods.json -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"rpc.methods","params":{}}' \
http://127.0.0.1:18084/jsonrpc
grep -q '"methods"' /tmp/java-methods.json
grep -q '"setup"' /tmp/java-methods.json
grep -q '"create_tables"' /tmp/java-methods.json
grep -q '"metrics"' /tmp/java-methods.json
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ version.workspace = true
[dependencies]
adbc_client = { path = "crates/adbc_client" }
arrow.workspace = true
arrow-schema.workspace = true
async-trait.workspace = true
clap.workspace = true
data-generation = { path = "crates/data-generation" }
Expand Down
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ flowchart TB
direction TB

subgraph setup_phase["1 · Setup (JSON-RPC)"]
adapter_iface["System Adapter Protocol\n(setup / create_tables /\nquery_method / teardown / metrics)"]
adapter_iface["System Adapter Protocol\n(setup / create_tables /\nteardown / metrics)"]
spice["Spice Cloud Adapter"]
databricks["Databricks Adapter"]
other["... Other Adapters"]
Expand Down Expand Up @@ -104,9 +104,8 @@ flowchart TB

orchestrator -->|"start run"| run

adapter_iface -->|"setup(run_id, datasets)"| sut
adapter_iface -->|"setup(run_id, datasets)\n→ ADBC driver + kwargs"| executors
adapter_iface -->|"create_tables(run_id)"| sut
adapter_iface -->|"query_method(run_id)\n→ ADBC driver + kwargs"| executors
setup_phase -->|"system ready"| bench_phase
bench_phase -->|"benchmark complete"| teardown_phase

Expand All @@ -123,7 +122,7 @@ A **Run** is a single end-to-end execution of the benchmark for one system. Each

| Phase | What happens | Timed? |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
| **1. Setup** | Connect to system adapter via JSON-RPC (stdio or HTTP). Call `setup(run_id, datasets)` to provision the SUT, then `create_tables(run_id)`, then `query_method(run_id)` to get ADBC driver config. | No |
| **1. Setup** | Connect to system adapter via JSON-RPC (stdio or HTTP). Call `setup(run_id, datasets)` to provision the SUT and return ADBC driver config, then `create_tables(run_id)`. | No |
| **2. Benchmark (timed)** | Three sequential stages — warm-up (1× query set), baseline (10% of duration, 60s–600s), and load test (full duration with concurrent clients). | Yes |
| **3. Teardown** | Call `teardown(run_id)` via the adapter to deprovision resources and clean up. | No |

Expand All @@ -148,7 +147,7 @@ Common CLI/workflow usage:
| Component | Responsibility |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **GitHub Actions** | Orchestrates Runs on schedule, PR, or manual dispatch. Manages the full Run lifecycle across phases. |
| **System Adapter Protocol** | JSON-RPC 2.0 interface (stdio or HTTP) for each platform. Methods: `setup`, `create_tables`, `query_method`, `teardown`, `metrics`. |
| **System Adapter Protocol** | JSON-RPC 2.0 interface (stdio or HTTP) for each platform. Methods: `setup`, `create_tables`, `teardown`, `metrics`. |
| **Query Executors** | Pluggable query execution: ADBC direct (FlightSQL/Databricks drivers), HTTP (`/v1/sql`), or distributed (`/v1/queries` with polling). |
| **Data Generator** | Standalone binary (`data-generation`) that produces TPC-H partitioned Parquet batches and writes them to S3. |
| **Test Framework** | Core engine managing the warm-up → baseline → load test pipeline, query sets (TPC-H, TPC-DS, ClickBench, parameterized, scenario), and statistics collection. |
Expand Down Expand Up @@ -218,9 +217,8 @@ To benchmark a new platform, implement the JSON-RPC 2.0 adapter with these metho

1. **`setup(run_id, datasets)`** — Provision infrastructure and configure the target system.
2. **`create_tables(run_id)`** — Create/register destination tables for the benchmark datasets.
3. **`query_method(run_id)`** — Return the ADBC driver type (`flightsql` or `databricks`) and connection kwargs so SpiceBench can establish a direct query connection.
4. **`teardown(run_id)`** — Clean up provisioned resources.
5. **`metrics(run_id)`** *(optional)* — Return current resource usage (CPU, memory, disk, IOPS) and ingestion progress (rows, bytes, rows/s, active connections).
3. **`teardown(run_id)`** — Clean up provisioned resources.
4. **`metrics(run_id)`** *(optional)* — Return current resource usage (CPU, memory, disk, IOPS) and ingestion progress (rows, bytes, rows/s, active connections).

The adapter can run as a **stdio** child process or as an **HTTP** server.

Expand All @@ -247,9 +245,8 @@ For each run, SpiceBench calls adapter JSON-RPC methods in this order:

1. `setup(run_id, datasets, metadata)`
2. `create_tables(run_id)`
3. `query_method(run_id)`
4. benchmark execution and optional periodic `metrics(run_id)` scraping
5. `teardown(run_id)`
3. benchmark execution and optional periodic `metrics(run_id)` scraping
4. `teardown(run_id)`

Tiny `create_tables` request example:

Expand Down
10 changes: 0 additions & 10 deletions crates/system-adapter-protocol/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,16 +207,6 @@ impl Client {
.ok_or_else(|| ClientError::InvalidResponse("Missing result".to_string()))
}

/// Get query method/driver information for a benchmark run
pub async fn query_method(&mut self, run_id: uuid::Uuid) -> Result<crate::QueryMethodResponse> {
let request = crate::QueryMethodRequest { run_id };
let rpc_request = JsonRpcRequest::new(1, crate::methods::QUERY_METHOD, request);
let response = self.call_typed(rpc_request).await?;
response
.result
.ok_or_else(|| ClientError::InvalidResponse("Missing result".to_string()))
}

/// Teardown a benchmark run
pub async fn teardown(&mut self, run_id: uuid::Uuid) -> Result<crate::TeardownResponse> {
let request = crate::TeardownRequest { run_id };
Expand Down
45 changes: 11 additions & 34 deletions crates/system-adapter-protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ limitations under the License.
//!
//! # Features
//!
//! - **Protocol types**: Request/response types for setup, create_tables, query_method, and teardown
//! - **Protocol types**: Request/response types for setup, create_tables, teardown, and metrics
//! - **Client**: Ready-to-use client with Stdio and HTTP transports (requires `client` feature)
//! - **Server**: Easy server implementation via Handler trait (requires `server` feature)
//! - **JSON-RPC**: Standard JSON-RPC 2.0 envelope types
Expand All @@ -44,9 +44,7 @@ limitations under the License.
//! let setup_response = client.setup(run_id, HashMap::new(), HashMap::new()).await?;
//! let create_tables_response = client.create_tables(run_id).await?;
//!
//! // Get query method information
//! let query_response = client.query_method(run_id).await?;
//! println!("Driver: {:?}", query_response.driver);
//! println!("Driver: {:?}", setup_response.driver);
//!
//! // Teardown the run
//! let teardown_response = client.teardown(run_id).await?;
Expand All @@ -60,8 +58,8 @@ limitations under the License.
//! # #[cfg(feature = "server")]
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! use system_adapter_protocol::{
//! AdbcDriver, CreateTablesResponse, DatasetConfig, Handler, QueryMethodResponse, Server,
//! SetupResponse, TeardownResponse,
//! AdbcDriver, CreateTablesResponse, DatasetConfig, Handler, Server, SetupResponse,
//! TeardownResponse,
//! };
//! use async_trait::async_trait;
//! use std::collections::HashMap;
Expand All @@ -79,11 +77,7 @@ limitations under the License.
//! ) -> Result<SetupResponse, String> {
//! // Your setup logic here
//! let _ = metadata;
//! Ok(SetupResponse { ok: true })
//! }
//!
//! async fn query_method(&mut self, run_id: Uuid) -> Result<QueryMethodResponse, String> {
//! Ok(QueryMethodResponse {
//! Ok(SetupResponse {
//! driver: AdbcDriver::Flightsql,
//! db_kwargs: HashMap::new(),
//! })
Expand Down Expand Up @@ -162,11 +156,13 @@ pub struct SetupRequest {
pub metadata: HashMap<String, serde_json::Value>,
}

/// Response from setup request
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
/// Response from setup request containing ADBC connection information
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SetupResponse {
/// Indicates if setup was successful
pub ok: bool,
/// ADBC driver to use for database connections
pub driver: AdbcDriver,
/// Driver-specific connection parameters
pub db_kwargs: HashMap<String, serde_json::Value>,
}

/// Request to create benchmark tables in the system under test.
Expand All @@ -185,24 +181,6 @@ pub struct CreateTablesResponse {
pub ok: bool,
}

/// Request to get query method/driver information
///
/// JSON-RPC method: `query_method`
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryMethodRequest {
/// Unique identifier for the benchmark run
pub run_id: Uuid,
}

/// Response containing database connection information
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct QueryMethodResponse {
/// ADBC driver to use for database connections
pub driver: AdbcDriver,
/// Driver-specific connection parameters
pub db_kwargs: HashMap<String, serde_json::Value>,
}

/// Request to teardown a benchmark run
///
/// JSON-RPC method: `teardown`
Expand Down Expand Up @@ -377,7 +355,6 @@ pub mod error_codes {
pub mod methods {
pub const SETUP: &str = "setup";
pub const CREATE_TABLES: &str = "create_tables";
pub const QUERY_METHOD: &str = "query_method";
pub const TEARDOWN: &str = "teardown";
pub const METRICS: &str = "metrics";
pub const RPC_METHODS: &str = "rpc.methods";
Expand Down
Loading
Loading