Skip to main content

DriftBench logo

DriftBench

DriftBench is a toolkit for generating and replaying data drift and workload drift with DriftSpec.

Who uses DriftBench:

  • Researcher — design reproducible drift experiments and ablations.
  • Database Vendor / Performance Team — run drift regression checks across targets before release.
  • New User — start from validated examples and get first outputs quickly.

Version history: CHANGELOG · Production site: driftbench.com


Install

pip install -U driftbench-db

Or from source:

git clone https://github.com/Liuguanli/DriftBench.git
cd DriftBench
pip install -e .

Verify:

driftbench --help
python -c "from driftbench.data.ycsb import data; print(data(record_count=10).generate('./artifacts').summary())"

Benchmark Adapters (driftbench.data)

Nine adapters generate local benchmark artifacts. Most synthetic generators need no external tool; TPC-H mode="generate" may auto-build an unpinned upstream dbgen, pgbench regression runs need PostgreSQL/pgbench, and BenchBase generates configuration rather than a local dataset.

Adapter Workload type Data format Tables Queries
tpch OLAP .tbl (pipe-delimited) 8 22 Python qgen-style SQL templates
tpcds OLAP / Decision support .dat (pipe-delimited) 5 synthetic 99 query IDs
tpcc OLTP .csv 9 5 transaction types
tpcc_skew OLTP + hotspot .csv + weight manifest 9 5 transaction types
job OLAP / join-order .csv 11 (IMDB-like) 20 SQL templates
ycsb Key-value .csv 1 6 workload mixes (A–F)
dsb Decision support .csv 3 star-schema 3 SQL templates
pgbench TPC-B (OLTP) .csv 4 3 workloads
benchbase Multi-benchmark XML + shell script via live DB 10 benchmarks

Related benchmark docs: complete adapter reference, target orchestration contract, and hands-on testing guide.

Adapter names describe modeled workload families, not official benchmark conformance. Most outputs are DriftBench synthetic fixtures, and the TPC-H auto-build neither pins the upstream revision nor records the dbgen binary hash. The canonical provenance, conformance, YAML-execution, and live-database boundaries explain what is generated, what benchmark_adapter means inside a DriftSpec, and which claims the artifacts do and do not support.

Generate data and queries

from pathlib import Path
from driftbench.data.tpch import data as tpch_data, queries as tpch_queries
from driftbench.data.tpcds import data as tpcds_data, queries as tpcds_queries
from driftbench.data.tpcc import data as tpcc_data, queries as tpcc_queries
from driftbench.data.tpcc_skew import data as tpcc_skew_data, queries as tpcc_skew_queries
from driftbench.data.job import data as job_data, queries as job_queries
from driftbench.data.ycsb import data as ycsb_data, queries as ycsb_queries
from driftbench.data.dsb import data as dsb_data, queries as dsb_queries
from driftbench.data.pgbench import data as pgbench_data, queries as pgbench_queries
from driftbench.data.benchbase import data as bb_data, queries as bb_queries

out = Path("./artifacts")

# TPC-H — auto-builds dbgen on first use; converts .tbl to .csv with .as_csv()
tpch_data(scale_factor=1, mode="generate").generate(output_dir=out)
tpch_queries(query_ids=[1, 3, 5], queries_per_template=2).generate(output_dir=out)

# TPC-DS — synthetic .dat files; converts to .csv with .as_csv()
tpcds_data(scale_factor=10).generate(output_dir=out)
tpcds_queries().generate(output_dir=out)

# TPC-C — scale_factor = number of warehouses
tpcc_data(scale_factor=4).generate(output_dir=out)
tpcc_queries().generate(output_dir=out)

# TPC-C Skew — Zipf hot-warehouse access distribution
tpcc_skew_data(scale_factor=10, hot_warehouse_fraction=0.2, skew_factor=0.99).generate(output_dir=out)
tpcc_skew_queries(scale_factor=10, hot_warehouse_fraction=0.2).generate(output_dir=out)

# JOB, YCSB, DSB, pgbench
job_data(scale_factor=1).generate(output_dir=out)
ycsb_data(scale_factor=1).generate(output_dir=out)
ycsb_queries(workload="B").generate(output_dir=out)
dsb_data(scale_factor=10).generate(output_dir=out)
pgbench_data(scale_factor=1).generate(output_dir=out)
pgbench_queries(workload="select_only", clients=2, duration=5).generate(output_dir=out)

# BenchBase — generates XML configs + shell scripts for a live database
bb_data(benchmark="tpcc", scale_factor=10).generate(output_dir=out)
bb_queries(benchmark="tpcc", terminals=8, duration=120).generate(output_dir=out)

Output layout

artifacts/
  tpch/data/sf_1/tables/   tpch/queries/
  tpcds/data/              tpcds/queries/
  tpcc/data/               tpcc/queries/
  tpcc_skew/data/          tpcc_skew/queries/
  job/data/                job/queries/
  ycsb/data/               ycsb/queries/
  dsb/data/                dsb/queries/
  pgbench/data/            pgbench/queries/
  benchbase/tpcc/data/     benchbase/tpcc/queries/

Each folder contains a *_manifest.json listing the generated files.

GenerationResult

generate() returns a GenerationResult:

result = tpch_data(scale_factor=1, mode="generate").generate(output_dir=out)
result.files      # list of generated file paths
result.metadata   # path to the manifest JSON

# Convert pipe-delimited .tbl / .dat to standard CSV (both kept on disk).
# Known TPC-H (8 tables) and TPC-DS (5 synthetic tables) get a proper
# header row, so the CSV is self-describing and usable directly by .drift().
csv_result = result.as_csv()

# Lightweight JSON-serializable summary for logs / dashboards / quick asserts.
result.summary()
# {'benchmark': 'tpch', 'artifact_type': 'data',
#  'output_dir': '/tmp/...', 'file_count': 8,
#  'tables': ['customer', 'lineitem', 'nation', ...]}

A second call reuses files only when its normalized generation parameters match the manifest and every managed file has the recorded path, byte count, and SHA-256. Older manifests rebuild once. Pass force=True to regenerate unconditionally; if an external source_dir changes in place, use force=True because source contents are not checksummed.

Only real-database regression gate: pgbench

This is currently DriftBench's only no-mock live-database regression gate. It covers PostgreSQL 16 select-only; the other adapter and example tests validate artifacts and do not establish database execution, performance, or benchmark conformance. driftbench benchmark pgbench runs a native pgbench -b select-only baseline and a DriftBench-generated select_only candidate against the same PostgreSQL instance. Each of three paired rounds runs an isolated warmup and measurement, retaining raw stdout/stderr and transaction logs. The version-controlled CI policy requires PostgreSQL/pgbench 16, scale 1, two clients, 3-second warmups and 5-second measurements. The PostgreSQL server must be running and its role/authentication configured first.

# Confirm that the pgbench client is major version 16, then create and initialize
# a new database using an already configured PostgreSQL role.
pgbench --version
createdb --host localhost --port 5432 --username driftbench driftbench_ci
pgbench --initialize --scale=1 \
  --host localhost --port 5432 --username driftbench driftbench_ci

# Generate the candidate used by the checked-in default policy.
python -c "from driftbench.data.pgbench import queries; queries(workload='select_only', clients=2, duration=5).generate(output_dir='artifacts', force=True)"

driftbench benchmark pgbench \
  --candidate-script artifacts/pgbench/queries/pgbench_select_only.sql \
  --output-dir benchmark-artifacts/results \
  --database driftbench_ci --host localhost --port 5432 --username driftbench \
  --json

# Verify a copied bundle later, without PostgreSQL, network access, or Git.
driftbench benchmark verify --bundle benchmark-artifacts/results --json

The command requires a new or empty output directory and writes baseline.json, candidate.json, decision.json, execution_order.json, hashed raw evidence, the canonical policy at inputs/policy.json, the exact executed candidate at inputs/candidate.sql, and environment.json. Both results record byte counts and SHA-256 digests for those inputs and the environment snapshot. The snapshot includes the DriftBench version/source revision, Python, OS/CPU, password-free connection identity, PostgreSQL/pgbench versions, key server settings, and the scale inferred from initialized pgbench tables. Producing a valid bundle requires a clean DriftBench source checkout: the runner checks the runtime-source paths before the first phase and again after the last measurement, requiring the same full 40-character HEAD both times. DRIFTBENCH_GIT_SHA is only an assertion that must equal that HEAD; it cannot bypass a dirty checkout, and an installed wheel never borrows the caller repository's SHA.

For each measurement (not warmup), authoritative TPS is successful transactions divided by runner-measured elapsed seconds. It must be within an inclusive 5% of pgbench-reported TPS before any aggregation or threshold decision; a mismatch is invalid evidence and exits 4. Exit codes are 0 (verified pass), 3 (configuration/path), 4 (execution/parser/provenance/integrity), and 5 (complete verified threshold regression). The offline verifier proves that a bundle is internally self-consistent with its captured bytes and policy; it does not authenticate who produced the bundle or where it originated. Incomplete capture fails closed while retaining evidence. DriftBench never updates a baseline or policy automatically.

Applying drift to benchmark data

GenerationResult exposes .drift() and .drift_multi() to apply data drift directly — no manual schema extraction or generator setup needed.

Single-table drift:

from driftbench.data.tpch import TPCHData

result = TPCHData(scale_factor=1, source_dir="path/to/tbls").generate().as_csv()

# Inject outliers into lineitem.l_quantity
drifted = result.drift("lineitem", "outlier_injection", column="l_quantity", n=500)

# Skew the price/discount distribution
drifted = result.drift("lineitem", "value_skew",
                       columns=["l_extendedprice", "l_discount"], skewness=2)

drift() writes the drifted CSV to <output_dir>/<table>_<drift_type>.csv by default. Pass output_path= to override. Returns a new GenerationResult pointing at the drifted file.

Every .drift() call also emits a reproducible DriftSpec YAML (<output_stem>.driftspec.yaml) next to the CSV — kept out of result.files but recorded under the manifest's driftspec key. Running that YAML through driftbench.spec.core.run_all regenerates byte-identical output, so a Python-generated drift can be shared or automated as a spec without rework. The function-call path (fast, imperative) and the spec path (declarative, version-controllable, reproducible) are the same engine and produce identical results for the same seed and parameters.

Multi-table drift:

# FK relationships for tpch / job are wired automatically
drifted = result.drift_multi([
    {"op": "skew_column", "target": "lineitem", "column": "l_quantity",
     "fraction": 0.2, "skewness": 2},
    {"op": "delete_keys", "target": "orders", "key_column": "o_orderkey",
     "fraction": 0.05,
     "propagate": [{"relationship": "lineitem_orders", "policy": "drop"}]},
])

Pass relationships=[] or a custom list to override the built-in FK maps. Supported benchmarks with auto-wiring: tpch, job. tpcc and tpcc_skew require explicit relationship definitions because their joins use composite keys.

DriftSpec YAMLs — five ready-to-run benchmark drift examples are in driftspec/examples/:

  • tpch_lineitem_drift.yaml
  • tpcc_drift.yaml
  • job_drift.yaml
  • ycsb_drift.yaml
  • pgbench_drift.yaml

Paper-ready executable TPC-H examples: data drift, query-workload drift, and the examples reproduction guide.


CLI Quickstart

The commands below use repository fixtures and must be run from a source checkout. PyPI installations do not include driftspec/examples/ or its fixture data.

# Validate a DriftSpec
python -m driftbench.cli validate-spec driftspec/examples/demo_data_single.yaml --json

# Deep local readiness check (schema, handler parameters, inputs, outputs, adapter import)
python -m driftbench.cli validate-spec driftspec/examples/demo_data_single.yaml --deep --json

# Dry-run (preview execution plan)
python -m driftbench.cli dry-run driftspec/examples/demo_data_single.yaml --json

# Execute
python -m driftbench.cli run-yaml driftspec/examples/demo_data_single.yaml

Deep validation is an opt-in, read-only preflight. Relative paths resolve from the current working directory, just as they do during execution. It checks local files/directories, output collisions and writability, all registered DriftSpec handler contracts, and benchmark-adapter availability. It does not generate data, execute handlers, create directories, connect to databases, or probe external tools. Existing outputs and unchecked external services are reported as warnings. Exit code 0 means no validation errors, 3 means the spec is not locally ready, and 4 means the validator itself failed.


Python API

from driftbench import run_spec, trace_to_spec

run_spec("driftspec/examples/demo_data_single.yaml")
trace_to_spec("driftspec/trace_inputs/trace_data_mock.csv", "driftspec/generated/from_trace.yaml")

MCP Server

python3 -m driftbench_mcp.server

Core workflow via MCP: trace_to_spec → validate_spec → run_spec → list_outputs


Testing

pip install -e ".[test]"
python -m unittest discover -s test -p 'test_*.py' -v

The five real PostgreSQL 16 integration tests are discovered by this command but skip by default whenever DRIFTBENCH_REQUIRE_PG_INTEGRATION is not exactly 1. The remote Benchmark Regression workflow explicitly sets it to 1 and runs those tests against PostgreSQL 16 with no mocks or skips.


Visualization results: DriftBench Visualization Gallery.


License

MIT — see LICENSE.

Release files for driftbench-db 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for driftbench-db 0.1.0
File Size Uploaded
driftbench_db-0.1.0.tar.gz 5.8 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for driftbench-db 0.1.0
File Interpreter ABI Platform
driftbench_db-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 11.7 MB

Release files / driftbench_db-0.1.0.tar.gz

Download URL driftbench_db-0.1.0.tar.gz
Size 5.8 MB
Tags Source
SHA-256 checksum
How to use checksums
ca7b275953e588c2b8a851f1cd28280984c54ab3582e7bd586c6632d64087fd5
BLAKE2b-256 checksum
How to use checksums
25bc824be87eb91425ffc5edd40f3f56237e3961e026e33cc4d2895fde4d3576
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 23, 2026.

Transparency log

Release files / driftbench_db-0.1.0-py3-none-any.whl

Download URL driftbench_db-0.1.0-py3-none-any.whl
Size 5.9 MB
Tags Python 3
SHA-256 checksum
How to use checksums
ff0a749165d727958974bec8a2a7fec64aeb8337e5be13132d527e10021306ff
BLAKE2b-256 checksum
How to use checksums
137d3d61a97fd3abbaa2324b61c2272dc72168eaa09f3e5f2d411bf1f9689e4c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 23, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page