Give DBReduce a PostgreSQL database and a failing test.
It finds a smaller relational dataset and can reduce its schema while preserving the bug.
DBReduce reduces PostgreSQL data while checking that each candidate preserves the original failure identity. It supports output matchers and structured JSON verdicts, keeps oracle writes out of accepted data, and follows both PostgreSQL foreign keys and explicitly configured application relationships.
DBReduce v0.5.0 supports Python 3.10–3.14 and PostgreSQL 15–18. Use current patched,
matching-major pg_dump, pg_restore and psql tools; see the
compatibility report.
Install DBReduce as a standalone developer tool:
uv tool install dbreduce
dbreduce --helpuvx dbreduce --help also works without a persistent install. Standard installation
remains available with python -m pip install dbreduce. DBReduce itself is Python
software, but does not need to be installed in the application's environment or
added to its dependencies. The oracle can be Ruby, Java, Go, Node.js, Python,
shell, or any other executable.
Then reduce a database with a test that identifies the bug:
dbreduce reduce \
--database postgresql://localhost/app_bug \
--oracle 'uv run pytest tests/test_checkout.py::test_negative_total' \
--match-stdout 'AssertionError: negative total' \
--confirm 3The oracle must connect through DATABASE_URL or DBREDUCE_DATABASE_URL. DBReduce
sets both variables to its disposable workspace for every run. Use a database role
with CREATEDB permission. The source database is read only; reductions and oracle
executions run against a generated copy. Use trusted commands and dumps: SQL
functions, triggers, and external services can have effects outside that copy.
For an oracle that can report a stable signature directly, use --oracle-json and
have it write {"reproduced": true, "signature": "checkout-negative-total"} to
stdout. Structured oracles can also explicitly report
{"reproduced":false,"outcome":"candidate_invalid"} when schema/data removal
violates declared application preconditions. DBReduce rejects it separately from
target-bug disappearance and infrastructure failures. See the
candidate-validity contract and guide for details.
Use --oracle-framed-json when the application logs to stdout. Snapshot isolation
is the default; --candidate-backend clone is an experimental alternative on
PostgreSQL 15–18. Both WAL_LOG and FILE_COPY are tested; file_copy_method
requires PostgreSQL 18. --restore-jobs N enables parallel archive restores.
Use --reduce-schema for opt-in whole-table schema reduction.
It runs after row reduction, checks each proposed removal with
the same oracle, and exports only after a fresh logical restore. The default
data-only behavior is unchanged. Use --oracle-json or --oracle-framed-json so
an application startup error cannot count as the original bug.
- Checks failure identity so an unrelated error does not count as reproducing the bug.
- Isolates each oracle run so its database writes do not affect later candidates.
- Reduces through real foreign keys and declared single or composite relationships.
- Reports the final identity, candidate outcomes, timings, and row counts without saving raw oracle output.
- With
--reduce-schema, tries removal of application tables and their automatic dependencies; reports schema object counts and logical SQL size.
Without an identity matcher, legacy mode accepts any nonzero application exit status
and prints a warning. Reports claim local irreducibility under attempted
relationship-closed transformations only when no validity or unresolved-check
barrier prevents it; DBReduce does not promise a global minimum.
By default, DBReduce writes dbreduce.min.sql and dbreduce-report.json and never
overwrites existing files.
The opt-in v0.4.0 schema phase was validated with real Wagtail and NetBox application oracles on PostgreSQL 17. Wagtail used generated application noise; NetBox used the public 4.1 demo dataset with the issue condition added:
Wagtail #9208
Rows: 150,706 -> 3
Tables: 48 -> 1
Failure identity: preserved
Fresh SQL restore: PASS
NetBox #17498
Rows: 21,381 -> 2
Tables: 180 -> 4
Failure identity: preserved
Fresh SQL restore: PASS
The v0.5.0 NetBox rerun explicitly classified 70 schema candidates as
candidate_invalid through declared table preconditions, with zero infrastructure
errors. It retained the same four tables and two rows. These validity barriers
prevent a claim of local irreducibility. NetBox validation exercised its form
path, not a full HTTP flow. Earlier results remain documented in the case reports.
See the real-world validation report and case studies for commands, environment, performance numbers, and limitations.
Ruby/Rails validation uses Mastodon #37059: 1,757 rows become two; optional schema reduction leaves 12 of 109 tables. The exported SQL reproduces the same migration exception in v4.5.2 after fresh restore; v4.5.3 returns a negative verdict. With the documented application container and network already set up, the validated oracle command is:
dbreduce reduce \
--database postgresql://postgres@dbreduce-adoption-pg17/mastodon_case \
--oracle 'docker exec -e DATABASE_URL="$DATABASE_URL" dbreduce-adoption-mastodon bundle exec ruby /case/wrapper.rb' \
--oracle-framed-json --confirm 2 --timeout 60DBReduce runs separately from Rails. The v0.5.0 case records 26 explicitly invalid schema candidates, 524 invalid restores and 24 unresolved oracle infrastructure errors; it does not claim schema local irreducibility.
See CHANGELOG.md for release history.
- Usage, oracle identity, configuration, safety, and development
- Benchmark and demo
- Real-world validation report
- Candidate isolation and clone safety
- Tested compatibility and decisions
- Trying DBReduce and reporting adoption blockers
- Candidate validity and structured oracle protocol
Licensed under Apache-2.0.