pg-bloat-detective
Read-only Postgres bloat detective: cheap timeline, named blocker, index bloat, evidence-linked report, before/after proof.
Quickstart
docker compose up -d
pip install -e ".[dev]"
python -m bloatdetective collect --db timeline.db # snapshot prod (read-only)
python workload/churn.py --mode idle-xact --secs 60 & # create the failure
python -m bloatdetective collect --db timeline.db
python -m bloatdetective report --db timeline.db --html --out report.html
python -m bloatdetective serve --db timeline.db & # :9187/metrics for Prometheus
pytest
bash scripts/benchmark.sh # full before/after proof -> REPORT.md
Verdicts
Heap: normal (steady-state, leave alone) · vacuum-starved · blocked-by-idle-xact|slot|prepared|active-xact · needs-rewrite.
Index: index-bloated (pgstatindex bloat >30% → needs REINDEX, VACUUM can't fix it) · index-unused (idx_scan=0 across snapshots → DROP candidate).
Index bloat is the differentiator (pganalyze admits the gap): collect runs exact
pgstatindex only on indexes under the 1GB cost guard (--max-bytes tunes it,
--allow-large overrides), metric = 100 − avg_leaf_density. Exported as
pgbloat_index_bloat_pct / pgbloat_index_scans, charted in the shadcn dashboard,
Graphed in Grafana.
Benchmark (measured 2026-10-05, local PG16 — heap + index, live run just now)
30s churn, no blocker (autovacuum simply lost) → VACUUM ANALYZE + REINDEX:
| moment | pg_stat dead | approx dead% | index bloat% (density) | verdict |
|---|---|---|---|---|
| before fix | 5,108,201 | — (single-snapshot lag) | 41.9% | vacuum-starved + index-bloated churn_pkey |
| after fix | 0 | 0.0% | 9.9% | normal — steady state, leave alone |
Earlier hole, now closed: an index at 83.7% bloat (density 16.3) dropped to 9.9%
(density 90.1) after REINDEX — VACUUM alone never touches that. Full log in
REPORT.md, visual in report.html.
See PLAN.md for the detailed 3-week plan + competitor gap.
MCP server (Claude Desktop / Cursor / any MCP client)
5 tools, read-only on Postgres (5s statement timeout, read-only tx). bloat_check
returns verdict + evidence + fix per table/index; bloat_live_diagnose snapshots a
throwaway DB so your timeline file is never touched — safest for prod DSNs.
pip install -e . # pulls mcp + psycopg
bloat-mcp # stdio transport
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{"mcpServers": {"bloat-detective": {
"command": "/abs/path/pg-bloat-detective/.venv/bin/python",
"args": ["-m", "bloatdetective.mcp_server"],
"env": {"BLOAT_DSN": "dbname=bloatdemo user=postgres host=localhost port=5433",
"BLOAT_DB": "/abs/path/pg-bloat-detective/timeline.db"}}}}
| tool | what the agent gets |
|---|---|
bloat_check |
verdicts + evidence + action per table/index |
bloat_collect |
snapshot timeline, return fresh verdicts |
bloat_timeline |
dead/approx/index-bloat series — when it started |
bloat_blockers |
pid + query + xmin age + slot — who to kill |
bloat_live_diagnose |
one-shot throwaway snapshot, diagnose, discard |
Public listing: mcp.so + PulseMCP accept GitHub submissions (server.json + README badge) — repo is ready, submit the URL after push.
Metadata
Release files for pg-bloat-detective 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pg_bloat_detective-0.1.1.tar.gz | 18.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pg_bloat_detective-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 34.2 kB
Release files / pg_bloat_detective-0.1.1.tar.gz
| Download URL | pg_bloat_detective-0.1.1.tar.gz |
|---|---|
| Size | 18.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7465a7b08e864c95f9fc86392dafbff27d76ac078a254d235b58c77d1243f2ef
|
|
BLAKE2b-256 checksum How to use checksums |
88f4efd58fbed361597757212d4061e2f77160a472ae4445da0879139df994f2
|
| 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 Oct 7, 2026.
Transparency logRelease files / pg_bloat_detective-0.1.1-py3-none-any.whl
| Download URL | pg_bloat_detective-0.1.1-py3-none-any.whl |
|---|---|
| Size | 15.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6db504c567a7db958834d3d5e868e199f209d792342b953fb0948222b0223cf8
|
|
BLAKE2b-256 checksum How to use checksums |
b4752614f4cfd4e2bd0d8226472bb48dfa251b1477edabe9c72b93b82af3c9f8
|
| 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 Oct 7, 2026.
Transparency log