pg_play is the AI-ready orchestration layer for reproducible PostgreSQL
experiments.
- Recreates the same PostgreSQL stand, backend activity, and diagnostic window.
- Applies a versioned
pg_configuratorcandidate only through a reviewedpg_standplan. - Keeps human-facing component CLIs small while exposing a typed MCP workflow to an AI agent.
- Records immutable plan hashes, run state, component versions, and artifact hashes.
- Validates and compares
pg_diagJSON reports without treating a partial collection as a successful complete run. - Plans and runs
pg_perf_benchonly against an explicitly selected disposable database, then validates and compares benchmark artifacts and TPS evidence. - Installs every component while preserving their independent use.
The independently installable components are:
pg_stand— reproducible PostgreSQL stands;pg_workload— profile-driven backend activity emulation;pg_diag— diagnostic JSON and HTML artifacts;pg_configurator— version-aware PostgreSQL configuration candidates.pg_perf_bench— controlled pgbench execution and environment evidence.
pg_play
|
+-------------+-------------+
| |
v |
pg_configurator |
| versioned config |
v |
pg_stand ------------------------+
|
v
PostgreSQL stand
/ | \
v v v
pg_workload pg_perf_bench pg_diag
| | |
| benchmark report |
+--------- run ----------+----> diagnostic report
|
change one reviewed input
|
+----> rebuild, rerun, compare
The control layers are deliberately separate:
agent skills workflow and interpretation rules
|
v
pg-play-mcp thirteen typed, high-level operations
|
v
pg_play core validation, planning, state, comparison
|
v
component adapters argv arrays + strict JSON envelopes
|
+---- pg_configurator
+---- pg_stand
+---- pg_workload
+---- pg_diag
`---- pg_perf_bench
MCP does not expose arbitrary shell, SQL, Docker, or raw component-command tools. Each component remains pleasant to use directly: orchestration flags are hidden from its primary help and its normal human output is unchanged.
python -m pip install pg-playThis installs compatible versions of all five component distributions. They remain available through their own commands:
pg-stand --help
pg-workload --help
pg-diag --help
pg-configurator --help
pg-perf-bench --helppg_play/v1 is strict: unknown fields, secret-bearing configurator inputs,
missing projects, invalid profile names, and invalid diagnostic windows are
errors.
api_version: pg_play/v1
kind: PostgreSQLExperiment
metadata:
id: pg18-mixed-baseline
spec:
artifact_root: .pg_play/experiments/pg18-mixed-baseline
stand:
config: ../pg_stand/configs/single.yaml
project: ./stand
configurator:
inputs:
db_cpu: 4
db_ram: 8Gi
pg_version: "18"
db_duty: mixed
workload:
project: ./workload
profiles: [pagila, simple_stock]
scale: 1.0
database: workload_db
user: workload_user
install: true
stop_after_report: true
pgbench_duration_seconds: 30
job_interval_seconds: 5
resource_guard:
disk_max_used_pct: 90
mem_min_available_pct: 10
mem_min_available_mb: 2048
cpu_max_pct: 90
cpu_window_seconds: 60
check_interval: 5
diagnostics:
mode: snapshots
collection_mode: remote-db-only
duration_seconds: 60
interval_seconds: 10
report_name: pg18-mixed-diagnostics
benchmark: # optional
database: pg_perf_bench_test # dedicated and disposable
report_name: pg18-mixed-benchmark
benchmark_type: default
clients: [1, 4, 16] # or times_seconds, never both
init_command: >-
ARG_PGBENCH_PATH -i -s 10 -h ARG_PG_HOST -p ARG_PG_PORT
-U ARG_PG_USER ARG_PG_DATABASE
workload_command: >-
ARG_PGBENCH_PATH -T 60 -c ARG_PGBENCH_CLIENTS -j ARG_PGBENCH_CLIENTS
-h ARG_PG_HOST -p ARG_PG_PORT -U ARG_PG_USER ARG_PG_DATABASE
command_timeout: 120
system_metrics_interval: 1
drop_os_caches: false
collect_pg_logs: true
phases:
benchmark: true
workload_diagnostics: true
recreate_workload_database: trueTo use a packaged pg_perf_bench maximum-TPS profile, replace
benchmark_type, init_command, workload_command, and workload_path with:
workload_profile: imdb # imdb or pagila
workload_scale: 1.0
workload_duration_seconds: 30
clients: [1, 2, 4, 8, 16]The profile supplies its schema, deterministic generator, SQL query set and
command templates. pg_play includes the selected profile and scale in the
reviewed benchmark plan. pg_perf_bench selects the newest local pgbench/psql
pair automatically; optional pgbench_path and psql_path overrides are
accepted only when they are not older than the newest installed clients. The
pg_diag OS sampler runs during every benchmark window; use
system_metrics_interval to control its cadence and
system_metrics_duration only when a custom command has no pgbench -T or
--time option.
Paths are resolved relative to the manifest. spec.stand.project defaults to
the manifest directory and fixes where pg_stand stores state, credentials,
and storage regardless of the caller's current directory. Credentials never
belong in this file. pg_play obtains the stand-owned administrator credential, creates a
random workload credential, and stores project-local passfiles with mode
0600.
The workload resource guard is always enabled by pg_play. Its thresholds are
manifest inputs and therefore part of the reviewed plan hash; override them
only when the host policy is explicitly known.
The packaged JSON Schema is available as the MCP resource
pgplay://experiment-schema.
pg_diag naming is the reference for equivalent options. Components now use
--host, --port, --database, --user, --password, --out, and
--pg-version wherever those concepts apply. Existing pg_perf_bench --pg-*,
pg_workload --pg-major/--workload-user, pg_stand --postgres-version, and
pg_configurator --output-file-name spellings remain compatibility aliases.
Secrets are the deliberate exception: pg_workload continues to accept
passwords only through environment/passfile mechanisms.
All five components use the same hidden orchestration options:
--machine, --request-id, and --component-capabilities. Their advertised
machine_interface object makes these names machine-verifiable.
The pg-play CLI contains only complete experiment operations:
| Command | Effect |
|---|---|
capabilities |
Read installed component contracts |
validate MANIFEST |
Validate the manifest and non-mutating component inputs |
plan MANIFEST |
Calculate the current read-only plan and its hash |
run MANIFEST --plan-hash HASH --run-id ID |
Execute exactly that plan |
status MANIFEST --run-id ID |
Read durable run state |
inspect-report REPORT.json |
Validate and summarize one diagnostic artifact |
compare-reports BASELINE.json CANDIDATE.json |
Produce deterministic summary deltas |
inspect-benchmark-report REPORT.json |
Validate and summarize one benchmark artifact |
compare-benchmark-reports BASELINE.json CANDIDATE.json |
Check server, environment and methodology identity, then produce TPS deltas |
benchmark-profiles |
List packaged maximum-TPS workload profiles |
benchmark-join-tasks |
List documented benchmark JOIN scenarios |
join-benchmark-reports --report ... --join-task TASK --out DIR --report-name NAME |
Join only the explicitly named benchmark reports |
teardown MANIFEST [--clear-stand-data] |
Stop workload processes and remove the managed stand |
Typical flow:
mkdir -p stand workload
pg-workload init --directory workload
pg-play validate experiment.yaml
pg-play plan experiment.yaml > plan.json
# Copy plan_hash from the reviewed plan.
pg-play run experiment.yaml \
--plan-hash sha256:... \
--run-id baseline-001
pg-play status experiment.yaml --run-id baseline-001
pg-play inspect-report .pg_play/experiments/pg18-mixed-baseline/baseline-001/report.jsonrun recalculates the plan and rejects a stale hash. A run id is immutable;
retry a changed or failed experiment under a new id.
Start the stdio server with:
pg-play-mcpConfigure an MCP client to launch that executable with no shell wrapper. The server exposes only:
component_capabilitiesvalidate_experimentplan_experimentrun_experimentexperiment_statusinspect_diagnostic_reportcompare_diagnostic_reportsinspect_benchmark_reportcompare_benchmark_reportsbenchmark_profilesbenchmark_join_tasksjoin_benchmark_reportsteardown_experiment
An agent should validate, plan, show the mutation to the user, and then call
run_experiment with the unchanged hash and a new run id. The implementation
uses the stable MCP Python SDK
1.x line and intentionally excludes the 2.x prerelease API.
The wheel contains two optional workflow skills under pg_play/skills/:
run-postgres-experiment— validate, plan, execute, and recover a run;analyze-postgres-experiment— inspect reports, compare controlled runs, and design the next single-variable iteration.
Skills contain procedural guidance; they do not reimplement orchestration or invoke raw component commands. Skill registration is agent-runtime-specific, so installing a Python wheel does not automatically activate them in every agent product.
- Every component returns the exact
pg_play/component/v1envelope in hidden machine mode and advertisespg_play/capabilities/v1through the common--component-capabilitiesflag. - Plans hash normalized configuration, workload profile contents, scheduler state, and current stand state.
- Parameters owned by stand topology, TLS, fixed CSV logging, or diagnostic
preloads remain under
pg_stand; the plan records those candidate values separately and passes only non-owned parameters topg_stand. - Managed TLS stands are rejected during validation in
pg_play/v1: the component contract does not yet provision a client certificate for the dedicated workload role. Direct TLS use of each component remains available. pg_stand applyverifies its component plan hash;pg_play runverifies the combined plan hash. Machine-modepg_perf_bench benchmarkindependently verifies a content-sensitive benchmark plan hash before resetting its database.- Subprocesses receive argument arrays with
shell=False. - Password-bearing CLI arguments and password-bearing machine output are rejected.
- Background workload stop verifies PID ownership before signaling a process.
- A failed cleanup cannot hide the original collection failure; a cleanup failure after successful diagnostics marks the run partial.
pg_diagpartial artifacts are retained and explicitly marked partial.pg_playnever automatically destroys a stand.
Remote OS collection uses strict SSH host verification. For a newly created
local pg_stand, pg_play captures its host key into the run directory before
starting pg_diag; the captured file is permission-restricted and retained as
run evidence.
The first contract covers one configuration candidate, one managed stand, an optional controlled benchmark, selected workload profiles, a one-shot or snapshots report, and deterministic diagnostic and benchmark comparison. Automatic extraction of OS facts and generation or application of TuneD/systemd artifacts remains a roadmap item; it is not silently approximated by the current implementation.
python3 -m venv .venv
.venv/bin/pip install -e ../pg_stand -e ../pg_workload -e ../pg_diag \
-e ../pg_configurator -e ../pg_perf_bench
.venv/bin/pip install -e '.[dev]'
.venv/bin/ruff check .
.venv/bin/ruff format --check .
.venv/bin/pytestFor coordinated releases, publish the component distributions before tagging
pg_play: first pg_configurator, pg_diag, pg_stand, and pg_workload,
then pg_perf_bench (which depends on pg_diag), and finally pg_play.
Ordinary branch CI checks out the component sources so a coordinated source
change can be tested before those versions reach PyPI. Tagged publish jobs use
the package index deliberately and therefore enforce this release order.