pg-perf-check 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pg_perf_check-0.1.0/LICENSE +21 -0
- pg_perf_check-0.1.0/MANIFEST.in +3 -0
- pg_perf_check-0.1.0/PKG-INFO +187 -0
- pg_perf_check-0.1.0/README.md +165 -0
- pg_perf_check-0.1.0/pg_perf_check/__init__.py +9 -0
- pg_perf_check-0.1.0/pg_perf_check/cli.py +2896 -0
- pg_perf_check-0.1.0/pg_perf_check/sql/01-table-sizes-and-bloat.sql +286 -0
- pg_perf_check-0.1.0/pg_perf_check/sql/02-unused-and-missing-indexes.sql +287 -0
- pg_perf_check-0.1.0/pg_perf_check/sql/03-slow-queries.sql +376 -0
- pg_perf_check-0.1.0/pg_perf_check/sql/04-index-usage.sql +320 -0
- pg_perf_check-0.1.0/pg_perf_check/sql/05-connections-and-locks.sql +479 -0
- pg_perf_check-0.1.0/pg_perf_check/sql/06-cache-hit-and-io.sql +329 -0
- pg_perf_check-0.1.0/pg_perf_check/sql/07-vacuum-and-autovacuum.sql +548 -0
- pg_perf_check-0.1.0/pg_perf_check.egg-info/PKG-INFO +187 -0
- pg_perf_check-0.1.0/pg_perf_check.egg-info/SOURCES.txt +20 -0
- pg_perf_check-0.1.0/pg_perf_check.egg-info/dependency_links.txt +1 -0
- pg_perf_check-0.1.0/pg_perf_check.egg-info/entry_points.txt +2 -0
- pg_perf_check-0.1.0/pg_perf_check.egg-info/requires.txt +6 -0
- pg_perf_check-0.1.0/pg_perf_check.egg-info/top_level.txt +1 -0
- pg_perf_check-0.1.0/pyproject.toml +46 -0
- pg_perf_check-0.1.0/setup.cfg +4 -0
- pg_perf_check-0.1.0/tests/test_cli.py +224 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pg-perf-check
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Diagnose why a PostgreSQL database is slow: 24 read-only threshold checks plus 7 SQL files, each finding carrying its evidence, likely impact and a concrete action
|
|
5
|
+
Author: duke5am
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/duke5am/pg-perf-check
|
|
8
|
+
Project-URL: Issues, https://github.com/duke5am/pg-perf-check/issues
|
|
9
|
+
Keywords: postgres,postgresql,performance,diagnostics,tuning,bloat,autovacuum,indexes,slow-queries,locks,dba
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Topic :: Database
|
|
12
|
+
Classifier: Topic :: System :: Monitoring
|
|
13
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Provides-Extra: psycopg
|
|
18
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == "psycopg"
|
|
19
|
+
Provides-Extra: psycopg2
|
|
20
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == "psycopg2"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# pg-perf-check
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/pg-perf-check/)
|
|
26
|
+
|
|
27
|
+
Answer **"why is this database slow?"** with a scripted diagnostic report instead
|
|
28
|
+
of guesswork. 24 threshold checks plus 7 read-only SQL files, each answering one
|
|
29
|
+
question.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install pg-perf-check[psycopg] # from PyPI, Python 3.9+
|
|
33
|
+
pg-perf-check --database-url "$DATABASE_URL"
|
|
34
|
+
pg-perf-check --list-checks
|
|
35
|
+
pg-perf-check --self-test # no database needed
|
|
36
|
+
|
|
37
|
+
# or straight from a clone, no install:
|
|
38
|
+
pip install psycopg2-binary # or: apt install python3-psycopg2
|
|
39
|
+
python3 run_diagnostics.py --database-url "$DATABASE_URL"
|
|
40
|
+
python3 run_diagnostics.py --list-checks
|
|
41
|
+
python3 run_diagnostics.py --self-test # no database needed
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Installing without the extra pulls no dependencies at all — the driver is optional
|
|
45
|
+
and imported lazily, because the tool accepts either `psycopg` (v3) or `psycopg2`
|
|
46
|
+
and every no-database mode has to work on an interpreter with neither. The extras
|
|
47
|
+
are `[psycopg]` and `[psycopg2]`.
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
Connected to mydb on 127.0.0.1/5433 (PostgreSQL 17.11)
|
|
51
|
+
Running 24 checks and the sql/ files...
|
|
52
|
+
|
|
53
|
+
connections CONNECTIONS
|
|
54
|
+
84 of 100 connection slots in use, 12 idle in transaction
|
|
55
|
+
...
|
|
56
|
+
Suggested action:
|
|
57
|
+
Put a connection pooler in transaction mode in front of PostgreSQL and cap
|
|
58
|
+
the pool at roughly (2 x CPU cores) + effective_spindle_count. Do not raise
|
|
59
|
+
max_connections as the first move: it multiplies memory use per backend.
|
|
60
|
+
Source: sql/05-connections-and-locks.sql (Q5.0)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Everything here is read-only
|
|
64
|
+
|
|
65
|
+
No check writes, alters or drops anything, and none holds a lock beyond a normal
|
|
66
|
+
`SELECT`. Each SQL file says so in its header so you can confirm before pointing
|
|
67
|
+
it at production.
|
|
68
|
+
|
|
69
|
+
## What it checks
|
|
70
|
+
|
|
71
|
+
**24 threshold checks** across connections, idle-in-transaction sessions, lock
|
|
72
|
+
blocking chains, long-running queries, the oldest `xmin` horizon, replication
|
|
73
|
+
slots pinning `xmin`, table and index bloat, unused and duplicate indexes, cache
|
|
74
|
+
hit ratios, autovacuum health, and more — each with the evidence, the likely
|
|
75
|
+
impact and a concrete action.
|
|
76
|
+
|
|
77
|
+
**7 SQL files**, each answering one question and safe to run alone:
|
|
78
|
+
|
|
79
|
+
| File | Question |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `01-table-sizes-and-bloat` | what is big, and what is dead weight? |
|
|
82
|
+
| `02-unused-and-missing-indexes` | which indexes earn their keep, and what is missing? |
|
|
83
|
+
| `03-slow-queries` | where is the time actually going? |
|
|
84
|
+
| `04-index-usage` | scan counts, index sizes, redundancy |
|
|
85
|
+
| `05-connections-and-locks` | who is waiting on whom? |
|
|
86
|
+
| `06-cache-hit-and-io` | is it reading from cache or from disk? |
|
|
87
|
+
| `07-vacuum-and-autovacuum` | is bloat accumulating, and why? |
|
|
88
|
+
|
|
89
|
+
## Verified against a real PostgreSQL 17.11
|
|
90
|
+
|
|
91
|
+
**All 24 checks were executed against a live PostgreSQL 17.11 server, and the
|
|
92
|
+
whole report was produced end to end from that server.** Where
|
|
93
|
+
`pg_stat_statements` is unavailable, the four extension-dependent checks are
|
|
94
|
+
reported as *skipped* with the two-step install instructions, rather than failing
|
|
95
|
+
or silently reporting nothing.
|
|
96
|
+
|
|
97
|
+
The 7 SQL files are executed by the runner too, which prints a per-file result
|
|
98
|
+
line so you can see the state of your own server version. Exactly what that
|
|
99
|
+
showed, and what was not verified:
|
|
100
|
+
|
|
101
|
+
* `01`, `02`, `04`, `05`, `06` and `07` ran every statement cleanly on 17.11.
|
|
102
|
+
`07-vacuum-and-autovacuum.sql` reports **10/10 statements OK**.
|
|
103
|
+
* Two statements in `07-vacuum-and-autovacuum.sql` did **not**, and are fixed:
|
|
104
|
+
Q7.6 named `pg_stat_progress_vacuum` columns that PostgreSQL 17 replaced —
|
|
105
|
+
`max_dead_tuples` / `num_dead_tuples` became `max_dead_tuple_bytes`,
|
|
106
|
+
`dead_tuple_bytes` and `num_dead_item_ids`, a rename *and* a change of unit, so
|
|
107
|
+
naming either set breaks the other branch. Q7.6 now reads those fields out of
|
|
108
|
+
the row's JSON form (`to_jsonb(p) ->> '…'`), so it parses on every supported
|
|
109
|
+
version and reports which branch you are on in a `dead_store_units` column.
|
|
110
|
+
Q7.7 selected `datname` from `pg_stat_user_tables`, a column that has never
|
|
111
|
+
existed on **any** release — that view is already scoped to the current
|
|
112
|
+
database — and now selects `schemaname` / `relname`. Q7.6's 17+ branch was
|
|
113
|
+
verified by catching a live throttled `VACUUM` (`dead_store_units = bytes`,
|
|
114
|
+
`dead_store_capacity = 67108864`, i.e. the default 64 MiB
|
|
115
|
+
`maintenance_work_mem`); Q7.7 was verified directly. **Not verified:** the
|
|
116
|
+
pre-17 branch of Q7.6, because no PostgreSQL 16-or-older server was available.
|
|
117
|
+
* Two check-output defects were found during that same verification and are also
|
|
118
|
+
fixed: `shared_buffers_low` reported `approximately 0 MB` for a stock 128 MB
|
|
119
|
+
`shared_buffers` because the parser ignored `pg_settings.unit` (`setting = 16384,
|
|
120
|
+
unit = 8kB`) — it now prints `approximately 128 MB` — and `autovacuum_throttled`
|
|
121
|
+
printed `vacuum_cost_page_miss = None, vacuum_cost_page_dirty = None` because the
|
|
122
|
+
settings query selected neither; it now prints `2` and `20`.
|
|
123
|
+
* `03-slow-queries.sql` needs the `pg_stat_statements` extension. On a server
|
|
124
|
+
without it, seven of its eight statements fail with *relation
|
|
125
|
+
"pg_stat_statements" does not exist*, and the runner reports exactly that
|
|
126
|
+
alongside the install instructions. That is a missing extension on the server,
|
|
127
|
+
not a broken statement, and it is the only remaining failure the runner prints
|
|
128
|
+
on a stock 17.11 server without that extension.
|
|
129
|
+
|
|
130
|
+
## The advice it gives about its own output
|
|
131
|
+
|
|
132
|
+
Worth quoting, because it is the part most tooling gets wrong:
|
|
133
|
+
|
|
134
|
+
> Change ONE thing, then re-run this script and compare. Two changes at once means
|
|
135
|
+
> you cannot tell which one helped. […] Changing a setting is not the same as
|
|
136
|
+
> fixing a query.
|
|
137
|
+
|
|
138
|
+
Counters are cumulative since the last statistics reset, so the counter-based
|
|
139
|
+
findings (unused indexes, sequential scans, cache ratios) only mean something
|
|
140
|
+
after a full business cycle.
|
|
141
|
+
|
|
142
|
+
## What it is not
|
|
143
|
+
|
|
144
|
+
It diagnoses; it does not tune for you. It cannot know your workload, so it cannot
|
|
145
|
+
tell you whether a missing index is worth the write cost. `EXPLAIN-GUIDE.md`
|
|
146
|
+
covers reading plans; designing an index is your call.
|
|
147
|
+
|
|
148
|
+
Results also depend on your statistics being current — on a server where `ANALYZE`
|
|
149
|
+
has not run recently, estimated row counts will mislead you.
|
|
150
|
+
|
|
151
|
+
## Requirements
|
|
152
|
+
|
|
153
|
+
Python 3.9+ (the package declares `>=3.9`; the code itself runs on 3.8),
|
|
154
|
+
PostgreSQL 12+, and a Postgres driver — `psycopg` (v3) or `psycopg2`, either one,
|
|
155
|
+
installed explicitly or via `pip install pg-perf-check[psycopg]`. With no driver
|
|
156
|
+
and no database, `--help`, `--list-checks` and `--self-test` still work; a
|
|
157
|
+
connection attempt without a driver exits 2 with the install command to run.
|
|
158
|
+
|
|
159
|
+
## The full pack
|
|
160
|
+
|
|
161
|
+
The paid kit adds the two playbooks this repository does not ship —
|
|
162
|
+
`INDEXING-PLAYBOOK.md` (how to design an index the planner actually uses, with
|
|
163
|
+
page counts and WAL volumes measured on a live PostgreSQL 17.11 server rather than
|
|
164
|
+
asserted) and `REMEDIATION-PLAYBOOK.md` (one section per check: the threshold that
|
|
165
|
+
fired, what the counter really counts, the false positives to rule out, the SQL
|
|
166
|
+
with the guards that stop it being an outage, and how to verify the fix worked) —
|
|
167
|
+
plus the CI harness that turns the `--json` report into a baseline comparison,
|
|
168
|
+
failing a build only on findings that are new or worse.
|
|
169
|
+
|
|
170
|
+
<!-- RELATED:START -->
|
|
171
|
+
|
|
172
|
+
## Related tools
|
|
173
|
+
|
|
174
|
+
- **[pg-restore-drill](https://github.com/duke5am/pg-restore-drill)** — Prove your PostgreSQL backup actually restores: a scripted point-in-time recovery drill with a measured RPO/RTO report and a negative control.
|
|
175
|
+
*(if you were searching for "test postgres backup restore")*
|
|
176
|
+
- **[postgres-migration-safety-lint](https://github.com/duke5am/postgres-migration-safety-lint)** — Lint SQL migrations before they run: finds statements that take an ACCESS EXCLUSIVE lock, rewrite a table, or destroy data, and gives the safe rewrite.
|
|
177
|
+
*(if you were searching for "postgres migration lock")*
|
|
178
|
+
- **[rls-policy-tester](https://github.com/duke5am/rls-policy-tester)** — Prove user A cannot read user B's rows in Postgres or Supabase with row level security, including negative controls that fail on a missing policy.
|
|
179
|
+
*(if you were searching for "supabase rls test")*
|
|
180
|
+
|
|
181
|
+
All 28 tools in this set, grouped by what they check: **[dev-tools-index](https://duke5am.github.io/dev-tools-index/)**
|
|
182
|
+
|
|
183
|
+
If you arrived here searching for one of these, this is the tool: **postgres performance tuning queries** · **find missing indexes postgres** · **table bloat check postgresql** · **why is my postgres query slow**
|
|
184
|
+
|
|
185
|
+
<!-- RELATED:END -->
|
|
186
|
+
|
|
187
|
+
→ **[Postgres Performance Toolkit](https://duke5am.gumroad.com/l/05-postgres-perf-toolkit)** — $29 on Gumroad <!-- GUMROAD-LINK -->
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# pg-perf-check
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/pg-perf-check/)
|
|
4
|
+
|
|
5
|
+
Answer **"why is this database slow?"** with a scripted diagnostic report instead
|
|
6
|
+
of guesswork. 24 threshold checks plus 7 read-only SQL files, each answering one
|
|
7
|
+
question.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install pg-perf-check[psycopg] # from PyPI, Python 3.9+
|
|
11
|
+
pg-perf-check --database-url "$DATABASE_URL"
|
|
12
|
+
pg-perf-check --list-checks
|
|
13
|
+
pg-perf-check --self-test # no database needed
|
|
14
|
+
|
|
15
|
+
# or straight from a clone, no install:
|
|
16
|
+
pip install psycopg2-binary # or: apt install python3-psycopg2
|
|
17
|
+
python3 run_diagnostics.py --database-url "$DATABASE_URL"
|
|
18
|
+
python3 run_diagnostics.py --list-checks
|
|
19
|
+
python3 run_diagnostics.py --self-test # no database needed
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Installing without the extra pulls no dependencies at all — the driver is optional
|
|
23
|
+
and imported lazily, because the tool accepts either `psycopg` (v3) or `psycopg2`
|
|
24
|
+
and every no-database mode has to work on an interpreter with neither. The extras
|
|
25
|
+
are `[psycopg]` and `[psycopg2]`.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Connected to mydb on 127.0.0.1/5433 (PostgreSQL 17.11)
|
|
29
|
+
Running 24 checks and the sql/ files...
|
|
30
|
+
|
|
31
|
+
connections CONNECTIONS
|
|
32
|
+
84 of 100 connection slots in use, 12 idle in transaction
|
|
33
|
+
...
|
|
34
|
+
Suggested action:
|
|
35
|
+
Put a connection pooler in transaction mode in front of PostgreSQL and cap
|
|
36
|
+
the pool at roughly (2 x CPU cores) + effective_spindle_count. Do not raise
|
|
37
|
+
max_connections as the first move: it multiplies memory use per backend.
|
|
38
|
+
Source: sql/05-connections-and-locks.sql (Q5.0)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Everything here is read-only
|
|
42
|
+
|
|
43
|
+
No check writes, alters or drops anything, and none holds a lock beyond a normal
|
|
44
|
+
`SELECT`. Each SQL file says so in its header so you can confirm before pointing
|
|
45
|
+
it at production.
|
|
46
|
+
|
|
47
|
+
## What it checks
|
|
48
|
+
|
|
49
|
+
**24 threshold checks** across connections, idle-in-transaction sessions, lock
|
|
50
|
+
blocking chains, long-running queries, the oldest `xmin` horizon, replication
|
|
51
|
+
slots pinning `xmin`, table and index bloat, unused and duplicate indexes, cache
|
|
52
|
+
hit ratios, autovacuum health, and more — each with the evidence, the likely
|
|
53
|
+
impact and a concrete action.
|
|
54
|
+
|
|
55
|
+
**7 SQL files**, each answering one question and safe to run alone:
|
|
56
|
+
|
|
57
|
+
| File | Question |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `01-table-sizes-and-bloat` | what is big, and what is dead weight? |
|
|
60
|
+
| `02-unused-and-missing-indexes` | which indexes earn their keep, and what is missing? |
|
|
61
|
+
| `03-slow-queries` | where is the time actually going? |
|
|
62
|
+
| `04-index-usage` | scan counts, index sizes, redundancy |
|
|
63
|
+
| `05-connections-and-locks` | who is waiting on whom? |
|
|
64
|
+
| `06-cache-hit-and-io` | is it reading from cache or from disk? |
|
|
65
|
+
| `07-vacuum-and-autovacuum` | is bloat accumulating, and why? |
|
|
66
|
+
|
|
67
|
+
## Verified against a real PostgreSQL 17.11
|
|
68
|
+
|
|
69
|
+
**All 24 checks were executed against a live PostgreSQL 17.11 server, and the
|
|
70
|
+
whole report was produced end to end from that server.** Where
|
|
71
|
+
`pg_stat_statements` is unavailable, the four extension-dependent checks are
|
|
72
|
+
reported as *skipped* with the two-step install instructions, rather than failing
|
|
73
|
+
or silently reporting nothing.
|
|
74
|
+
|
|
75
|
+
The 7 SQL files are executed by the runner too, which prints a per-file result
|
|
76
|
+
line so you can see the state of your own server version. Exactly what that
|
|
77
|
+
showed, and what was not verified:
|
|
78
|
+
|
|
79
|
+
* `01`, `02`, `04`, `05`, `06` and `07` ran every statement cleanly on 17.11.
|
|
80
|
+
`07-vacuum-and-autovacuum.sql` reports **10/10 statements OK**.
|
|
81
|
+
* Two statements in `07-vacuum-and-autovacuum.sql` did **not**, and are fixed:
|
|
82
|
+
Q7.6 named `pg_stat_progress_vacuum` columns that PostgreSQL 17 replaced —
|
|
83
|
+
`max_dead_tuples` / `num_dead_tuples` became `max_dead_tuple_bytes`,
|
|
84
|
+
`dead_tuple_bytes` and `num_dead_item_ids`, a rename *and* a change of unit, so
|
|
85
|
+
naming either set breaks the other branch. Q7.6 now reads those fields out of
|
|
86
|
+
the row's JSON form (`to_jsonb(p) ->> '…'`), so it parses on every supported
|
|
87
|
+
version and reports which branch you are on in a `dead_store_units` column.
|
|
88
|
+
Q7.7 selected `datname` from `pg_stat_user_tables`, a column that has never
|
|
89
|
+
existed on **any** release — that view is already scoped to the current
|
|
90
|
+
database — and now selects `schemaname` / `relname`. Q7.6's 17+ branch was
|
|
91
|
+
verified by catching a live throttled `VACUUM` (`dead_store_units = bytes`,
|
|
92
|
+
`dead_store_capacity = 67108864`, i.e. the default 64 MiB
|
|
93
|
+
`maintenance_work_mem`); Q7.7 was verified directly. **Not verified:** the
|
|
94
|
+
pre-17 branch of Q7.6, because no PostgreSQL 16-or-older server was available.
|
|
95
|
+
* Two check-output defects were found during that same verification and are also
|
|
96
|
+
fixed: `shared_buffers_low` reported `approximately 0 MB` for a stock 128 MB
|
|
97
|
+
`shared_buffers` because the parser ignored `pg_settings.unit` (`setting = 16384,
|
|
98
|
+
unit = 8kB`) — it now prints `approximately 128 MB` — and `autovacuum_throttled`
|
|
99
|
+
printed `vacuum_cost_page_miss = None, vacuum_cost_page_dirty = None` because the
|
|
100
|
+
settings query selected neither; it now prints `2` and `20`.
|
|
101
|
+
* `03-slow-queries.sql` needs the `pg_stat_statements` extension. On a server
|
|
102
|
+
without it, seven of its eight statements fail with *relation
|
|
103
|
+
"pg_stat_statements" does not exist*, and the runner reports exactly that
|
|
104
|
+
alongside the install instructions. That is a missing extension on the server,
|
|
105
|
+
not a broken statement, and it is the only remaining failure the runner prints
|
|
106
|
+
on a stock 17.11 server without that extension.
|
|
107
|
+
|
|
108
|
+
## The advice it gives about its own output
|
|
109
|
+
|
|
110
|
+
Worth quoting, because it is the part most tooling gets wrong:
|
|
111
|
+
|
|
112
|
+
> Change ONE thing, then re-run this script and compare. Two changes at once means
|
|
113
|
+
> you cannot tell which one helped. […] Changing a setting is not the same as
|
|
114
|
+
> fixing a query.
|
|
115
|
+
|
|
116
|
+
Counters are cumulative since the last statistics reset, so the counter-based
|
|
117
|
+
findings (unused indexes, sequential scans, cache ratios) only mean something
|
|
118
|
+
after a full business cycle.
|
|
119
|
+
|
|
120
|
+
## What it is not
|
|
121
|
+
|
|
122
|
+
It diagnoses; it does not tune for you. It cannot know your workload, so it cannot
|
|
123
|
+
tell you whether a missing index is worth the write cost. `EXPLAIN-GUIDE.md`
|
|
124
|
+
covers reading plans; designing an index is your call.
|
|
125
|
+
|
|
126
|
+
Results also depend on your statistics being current — on a server where `ANALYZE`
|
|
127
|
+
has not run recently, estimated row counts will mislead you.
|
|
128
|
+
|
|
129
|
+
## Requirements
|
|
130
|
+
|
|
131
|
+
Python 3.9+ (the package declares `>=3.9`; the code itself runs on 3.8),
|
|
132
|
+
PostgreSQL 12+, and a Postgres driver — `psycopg` (v3) or `psycopg2`, either one,
|
|
133
|
+
installed explicitly or via `pip install pg-perf-check[psycopg]`. With no driver
|
|
134
|
+
and no database, `--help`, `--list-checks` and `--self-test` still work; a
|
|
135
|
+
connection attempt without a driver exits 2 with the install command to run.
|
|
136
|
+
|
|
137
|
+
## The full pack
|
|
138
|
+
|
|
139
|
+
The paid kit adds the two playbooks this repository does not ship —
|
|
140
|
+
`INDEXING-PLAYBOOK.md` (how to design an index the planner actually uses, with
|
|
141
|
+
page counts and WAL volumes measured on a live PostgreSQL 17.11 server rather than
|
|
142
|
+
asserted) and `REMEDIATION-PLAYBOOK.md` (one section per check: the threshold that
|
|
143
|
+
fired, what the counter really counts, the false positives to rule out, the SQL
|
|
144
|
+
with the guards that stop it being an outage, and how to verify the fix worked) —
|
|
145
|
+
plus the CI harness that turns the `--json` report into a baseline comparison,
|
|
146
|
+
failing a build only on findings that are new or worse.
|
|
147
|
+
|
|
148
|
+
<!-- RELATED:START -->
|
|
149
|
+
|
|
150
|
+
## Related tools
|
|
151
|
+
|
|
152
|
+
- **[pg-restore-drill](https://github.com/duke5am/pg-restore-drill)** — Prove your PostgreSQL backup actually restores: a scripted point-in-time recovery drill with a measured RPO/RTO report and a negative control.
|
|
153
|
+
*(if you were searching for "test postgres backup restore")*
|
|
154
|
+
- **[postgres-migration-safety-lint](https://github.com/duke5am/postgres-migration-safety-lint)** — Lint SQL migrations before they run: finds statements that take an ACCESS EXCLUSIVE lock, rewrite a table, or destroy data, and gives the safe rewrite.
|
|
155
|
+
*(if you were searching for "postgres migration lock")*
|
|
156
|
+
- **[rls-policy-tester](https://github.com/duke5am/rls-policy-tester)** — Prove user A cannot read user B's rows in Postgres or Supabase with row level security, including negative controls that fail on a missing policy.
|
|
157
|
+
*(if you were searching for "supabase rls test")*
|
|
158
|
+
|
|
159
|
+
All 28 tools in this set, grouped by what they check: **[dev-tools-index](https://duke5am.github.io/dev-tools-index/)**
|
|
160
|
+
|
|
161
|
+
If you arrived here searching for one of these, this is the tool: **postgres performance tuning queries** · **find missing indexes postgres** · **table bloat check postgresql** · **why is my postgres query slow**
|
|
162
|
+
|
|
163
|
+
<!-- RELATED:END -->
|
|
164
|
+
|
|
165
|
+
→ **[Postgres Performance Toolkit](https://duke5am.gumroad.com/l/05-postgres-perf-toolkit)** — $29 on Gumroad <!-- GUMROAD-LINK -->
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""pg_perf_check -- PostgreSQL performance diagnostics.
|
|
2
|
+
|
|
3
|
+
The implementation lives in :mod:`pg_perf_check.cli`; the `pg-perf-check`
|
|
4
|
+
console script and the repository-root `run_diagnostics.py` wrapper both call
|
|
5
|
+
``pg_perf_check.cli.main``, so there is one copy of the code, not two.
|
|
6
|
+
|
|
7
|
+
The read-only SQL files the report cites are package data in
|
|
8
|
+
``pg_perf_check/sql/``.
|
|
9
|
+
"""
|