@evertonferreira96/agents 0.0.0-stage → 0.1.0
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.
- package/.claude/agents/api.md +45 -0
- package/.claude/agents/app.md +41 -0
- package/.claude/agents/architect.md +49 -0
- package/.claude/agents/infra.md +27 -0
- package/.claude/agents/product.md +58 -0
- package/.claude/agents/quick.md +33 -0
- package/.claude/agents/security.md +39 -0
- package/.claude/agents/ui.md +54 -0
- package/.claude/agents/verifier.md +56 -0
- package/.claude/agents/web.md +41 -0
- package/.claude/hooks/config-update-check.mjs +14 -0
- package/.claude/hooks/context-budget.mjs +51 -0
- package/.claude/hooks/graphify-update.sh +16 -0
- package/.claude/hooks/guard-bash.sh +64 -0
- package/.claude/hooks/orchestrator-guard.sh +49 -0
- package/.claude/hooks/post-compact.sh +26 -0
- package/.claude/hooks/readonly-guard.sh +54 -0
- package/.claude/hooks/stop-verify.sh +52 -0
- package/.claude/rules/api.md +21 -0
- package/.claude/rules/db.md +9 -0
- package/.claude/rules/delivery-diagram.md +16 -0
- package/.claude/rules/git-workflow.md +34 -0
- package/.claude/rules/graphify.md +3 -0
- package/.claude/rules/mobile.md +22 -0
- package/.claude/rules/tickets.md +9 -0
- package/.claude/rules/ui.md +14 -0
- package/.claude/rules/web.md +27 -0
- package/.claude/settings.json +114 -0
- package/.claude/skills/animate/RECIPES.md +324 -0
- package/.claude/skills/animate/SKILL.md +208 -0
- package/.claude/skills/animate/agents/openai.yaml +5 -0
- package/.claude/skills/animate-expo/RECIPES.md +385 -0
- package/.claude/skills/animate-expo/SKILL.md +264 -0
- package/.claude/skills/animate-expo/agents/openai.yaml +5 -0
- package/.claude/skills/animation-performance/SKILL.md +223 -0
- package/.claude/skills/animation-performance/agents/openai.yaml +5 -0
- package/.claude/skills/apple-design/SKILL.md +291 -0
- package/.claude/skills/apple-design/agents/openai.yaml +5 -0
- package/.claude/skills/arch-api/SKILL.md +291 -0
- package/.claude/skills/arch-api/agents/openai.yaml +3 -0
- package/.claude/skills/arch-api/references/common.md +174 -0
- package/.claude/skills/arch-api/references/contract.md +289 -0
- package/.claude/skills/arch-api/references/domain.md +301 -0
- package/.claude/skills/arch-api/references/endpoints.md +184 -0
- package/.claude/skills/arch-api/references/review.md +228 -0
- package/.claude/skills/arch-api/references/security.md +151 -0
- package/.claude/skills/arch-app/SKILL.md +278 -0
- package/.claude/skills/arch-app/agents/openai.yaml +3 -0
- package/.claude/skills/arch-app/references/navigation-session.md +257 -0
- package/.claude/skills/arch-app/references/review.md +155 -0
- package/.claude/skills/arch-app/references/screens.md +214 -0
- package/.claude/skills/arch-app/references/services.md +301 -0
- package/.claude/skills/arch-app/references/ui-mobile.md +124 -0
- package/.claude/skills/arch-db/SKILL.md +181 -0
- package/.claude/skills/arch-db/agents/openai.yaml +3 -0
- package/.claude/skills/arch-db/references/indexes-queries.md +251 -0
- package/.claude/skills/arch-db/references/migrations.md +191 -0
- package/.claude/skills/arch-db/references/modeling.md +214 -0
- package/.claude/skills/arch-db/references/mysql.md +262 -0
- package/.claude/skills/arch-db/references/pooling-ops.md +112 -0
- package/.claude/skills/arch-db/references/review.md +143 -0
- package/.claude/skills/arch-db/references/transactions-locks.md +141 -0
- package/.claude/skills/arch-web/SKILL.md +307 -0
- package/.claude/skills/arch-web/agents/openai.yaml +3 -0
- package/.claude/skills/arch-web/references/auth-session-env.md +165 -0
- package/.claude/skills/arch-web/references/blocks.md +198 -0
- package/.claude/skills/arch-web/references/cache.md +124 -0
- package/.claude/skills/arch-web/references/review.md +142 -0
- package/.claude/skills/arch-web/references/services.md +297 -0
- package/.claude/skills/arch-web/references/tests.md +159 -0
- package/.claude/skills/arch-web/references/ui-forms.md +143 -0
- package/.claude/skills/break-ui/CATALOG.md +135 -0
- package/.claude/skills/break-ui/SKILL.md +176 -0
- package/.claude/skills/break-ui/agents/openai.yaml +5 -0
- package/.claude/skills/brief/SKILL.md +91 -0
- package/.claude/skills/brief/agents/openai.yaml +3 -0
- package/.claude/skills/diagnosing-bugs/SKILL.md +138 -0
- package/.claude/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/.claude/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/.claude/skills/emil-design-eng/SKILL.md +675 -0
- package/.claude/skills/emil-design-eng/agents/openai.yaml +5 -0
- package/.claude/skills/find-animation-opportunities/SKILL.md +141 -0
- package/.claude/skills/find-animation-opportunities/agents/openai.yaml +5 -0
- package/.claude/skills/graphify/.graphify_version +1 -0
- package/.claude/skills/graphify/SKILL.md +742 -0
- package/.claude/skills/graphify/agents/openai.yaml +3 -0
- package/.claude/skills/graphify/references/add-watch.md +56 -0
- package/.claude/skills/graphify/references/exports.md +91 -0
- package/.claude/skills/graphify/references/extraction-spec.md +70 -0
- package/.claude/skills/graphify/references/github-and-merge.md +46 -0
- package/.claude/skills/graphify/references/hooks.md +33 -0
- package/.claude/skills/graphify/references/query.md +317 -0
- package/.claude/skills/graphify/references/transcribe.md +52 -0
- package/.claude/skills/graphify/references/update.md +221 -0
- package/.claude/skills/grill-me/SKILL.md +7 -0
- package/.claude/skills/grill-me/agents/openai.yaml +5 -0
- package/.claude/skills/grill-with-docs/SKILL.md +64 -0
- package/.claude/skills/grill-with-docs/agents/openai.yaml +5 -0
- package/.claude/skills/grilling/SKILL.md +30 -0
- package/.claude/skills/grilling/agents/openai.yaml +3 -0
- package/.claude/skills/handoff/SKILL.md +28 -0
- package/.claude/skills/handoff/agents/openai.yaml +5 -0
- package/.claude/skills/impeccable/SKILL.md +89 -0
- package/.claude/skills/impeccable/agents/openai.yaml +5 -0
- package/.claude/skills/impeccable/reference/adapt.md +318 -0
- package/.claude/skills/impeccable/reference/adapt.native.md +58 -0
- package/.claude/skills/impeccable/reference/android.md +46 -0
- package/.claude/skills/impeccable/reference/animate.md +89 -0
- package/.claude/skills/impeccable/reference/audit.md +137 -0
- package/.claude/skills/impeccable/reference/audit.native.md +139 -0
- package/.claude/skills/impeccable/reference/bolder.md +33 -0
- package/.claude/skills/impeccable/reference/clarify.md +94 -0
- package/.claude/skills/impeccable/reference/colorize.md +86 -0
- package/.claude/skills/impeccable/reference/component-review.md +63 -0
- package/.claude/skills/impeccable/reference/craft-floor.md +44 -0
- package/.claude/skills/impeccable/reference/craft.md +5 -0
- package/.claude/skills/impeccable/reference/critique.md +809 -0
- package/.claude/skills/impeccable/reference/degraded/asset-producer.md +42 -0
- package/.claude/skills/impeccable/reference/degraded/documenter.md +24 -0
- package/.claude/skills/impeccable/reference/degraded/finish-reviewer.md +38 -0
- package/.claude/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/.claude/skills/impeccable/reference/delight.md +70 -0
- package/.claude/skills/impeccable/reference/distill.md +111 -0
- package/.claude/skills/impeccable/reference/doctor.md +54 -0
- package/.claude/skills/impeccable/reference/document.md +416 -0
- package/.claude/skills/impeccable/reference/extract.md +69 -0
- package/.claude/skills/impeccable/reference/generate.md +101 -0
- package/.claude/skills/impeccable/reference/harden.md +345 -0
- package/.claude/skills/impeccable/reference/hooks.md +113 -0
- package/.claude/skills/impeccable/reference/init.md +131 -0
- package/.claude/skills/impeccable/reference/ios.md +51 -0
- package/.claude/skills/impeccable/reference/layout.md +84 -0
- package/.claude/skills/impeccable/reference/live-setup.md +104 -0
- package/.claude/skills/impeccable/reference/live.md +325 -0
- package/.claude/skills/impeccable/reference/mode-operate.md +21 -0
- package/.claude/skills/impeccable/reference/mode-persuade.md +19 -0
- package/.claude/skills/impeccable/reference/mode-read.md +21 -0
- package/.claude/skills/impeccable/reference/new-work.md +154 -0
- package/.claude/skills/impeccable/reference/onboard.md +234 -0
- package/.claude/skills/impeccable/reference/operate.md +61 -0
- package/.claude/skills/impeccable/reference/optimize.md +258 -0
- package/.claude/skills/impeccable/reference/overdrive.md +127 -0
- package/.claude/skills/impeccable/reference/polish.md +105 -0
- package/.claude/skills/impeccable/reference/quieter.md +99 -0
- package/.claude/skills/impeccable/reference/region-map.md +26 -0
- package/.claude/skills/impeccable/reference/routing.md +24 -0
- package/.claude/skills/impeccable/reference/shape.md +59 -0
- package/.claude/skills/impeccable/reference/typeset.md +80 -0
- package/.claude/skills/impeccable/reference/visualize.md +47 -0
- package/.claude/skills/impeccable/scripts/VERSION +1 -0
- package/.claude/skills/impeccable/scripts/command-metadata.json +98 -0
- package/.claude/skills/impeccable/scripts/data/font-index-failures.json +121 -0
- package/.claude/skills/impeccable/scripts/data/font-index.json +1 -0
- package/.claude/skills/impeccable/scripts/impeccable +206 -0
- package/.claude/skills/impeccable/scripts/impeccable.cmd +214 -0
- package/.claude/skills/impeccable/scripts/live-browser-dom.js +281 -0
- package/.claude/skills/impeccable/scripts/live-browser-ignores.js +242 -0
- package/.claude/skills/impeccable/scripts/live-browser-session.js +148 -0
- package/.claude/skills/impeccable/scripts/live-browser.js +13532 -0
- package/.claude/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/.claude/skills/improve-animations/AUDIT.md +115 -0
- package/.claude/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
- package/.claude/skills/improve-animations/SKILL.md +110 -0
- package/.claude/skills/improve-animations/agents/openai.yaml +5 -0
- package/.claude/skills/mobile-native/SKILL.md +311 -0
- package/.claude/skills/mobile-native/agents/openai.yaml +3 -0
- package/.claude/skills/orchestrate/SKILL.md +181 -0
- package/.claude/skills/orchestrate/agents/openai.yaml +3 -0
- package/.claude/skills/pick-ui-library/SKILL.md +85 -0
- package/.claude/skills/pick-ui-library/agents/openai.yaml +5 -0
- package/.claude/skills/ponytail/SKILL.md +52 -0
- package/.claude/skills/ponytail/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-audit/SKILL.md +103 -0
- package/.claude/skills/ponytail-audit/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-debt/SKILL.md +44 -0
- package/.claude/skills/ponytail-debt/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-review/SKILL.md +101 -0
- package/.claude/skills/ponytail-review/agents/openai.yaml +3 -0
- package/.claude/skills/postgres-best-practices/SKILL.md +42 -0
- package/.claude/skills/postgres-best-practices/agents/openai.yaml +3 -0
- package/.claude/skills/postgres-best-practices/references/backup-restore.md +372 -0
- package/.claude/skills/postgres-best-practices/references/bulk-loading.md +315 -0
- package/.claude/skills/postgres-best-practices/references/connection-pooling.md +310 -0
- package/.claude/skills/postgres-best-practices/references/hot-standby.md +221 -0
- package/.claude/skills/postgres-best-practices/references/indexing.md +318 -0
- package/.claude/skills/postgres-best-practices/references/logical-replication.md +610 -0
- package/.claude/skills/postgres-best-practices/references/major-version-upgrades.md +334 -0
- package/.claude/skills/postgres-best-practices/references/performance-diagnostics.md +553 -0
- package/.claude/skills/postgres-best-practices/references/query-optimization.md +294 -0
- package/.claude/skills/postgres-best-practices/references/query-patterns.md +566 -0
- package/.claude/skills/postgres-best-practices/references/schema-design.md +362 -0
- package/.claude/skills/postgres-best-practices/references/security-roles.md +489 -0
- package/.claude/skills/postgres-best-practices/references/transaction-isolation.md +344 -0
- package/.claude/skills/project-docs/SKILL.md +330 -0
- package/.claude/skills/project-docs/agents/openai.yaml +3 -0
- package/.claude/skills/prototype/PICKER.md +197 -0
- package/.claude/skills/prototype/SKILL.md +98 -0
- package/.claude/skills/prototype/agents/openai.yaml +5 -0
- package/.claude/skills/retro/SKILL.md +43 -0
- package/.claude/skills/retro/agents/openai.yaml +5 -0
- package/.claude/skills/review/SKILL.md +74 -0
- package/.claude/skills/review/agents/openai.yaml +3 -0
- package/.claude/skills/review/references/smells.md +23 -0
- package/.claude/skills/review-animations/SKILL.md +120 -0
- package/.claude/skills/review-animations/STANDARDS.md +187 -0
- package/.claude/skills/review-animations/agents/openai.yaml +5 -0
- package/.claude/skills/tdd/SKILL.md +38 -0
- package/.claude/skills/tdd/agents/openai.yaml +3 -0
- package/.claude/skills/tdd/mocking.md +59 -0
- package/.claude/skills/tdd/tests.md +77 -0
- package/.claude/skills/triage/SKILL.md +58 -0
- package/.claude/skills/triage/agents/openai.yaml +3 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +378 -0
- package/.claude/skills/ui-ux-pro-max/agents/openai.yaml +5 -0
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.claude/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/.claude/skills/writing-for-agents/SKILL.md +81 -0
- package/.claude/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/.worktreeinclude +3 -0
- package/AGENTS.md +44 -0
- package/README.md +14 -2
- package/VERSION +1 -0
- package/bin/agents.mjs +45 -0
- package/docs/adr/README.md +31 -0
- package/docs/brief-template.md +3 -0
- package/docs/plans/README.md +73 -0
- package/package.json +46 -4
- package/scripts/bootstrap.mjs +188 -0
- package/scripts/check-catalog.mjs +43 -0
- package/scripts/skills-update.mjs +51 -0
- package/skills-lock.json +269 -0
- package/templates/CLAUDE.project.md +41 -0
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
# Performance Diagnostics Reference
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
- Essential pg_stat views
|
|
5
|
+
- Table health diagnostics
|
|
6
|
+
- Index health diagnostics
|
|
7
|
+
- Active query analysis
|
|
8
|
+
- Lock analysis
|
|
9
|
+
- VACUUM and bloat
|
|
10
|
+
- Connection management
|
|
11
|
+
- pg_stat_statements setup and queries
|
|
12
|
+
|
|
13
|
+
## Essential pg_stat Views
|
|
14
|
+
|
|
15
|
+
| View | What it tells you |
|
|
16
|
+
|------|------------------|
|
|
17
|
+
| `pg_stat_user_tables` | Seq scans, index scans, row counts, dead tuples, last vacuum/analyze |
|
|
18
|
+
| `pg_stat_user_indexes` | Index usage counts, tuple reads |
|
|
19
|
+
| `pg_stat_activity` | Currently running queries, wait events, state |
|
|
20
|
+
| `pg_stat_statements` | Top queries by time, calls, rows (extension) |
|
|
21
|
+
| `pg_stat_bgwriter` | Checkpoint frequency, buffer allocation |
|
|
22
|
+
| `pg_stat_io` (PG16+) | I/O statistics by backend type. PG18+ adds byte-level columns and per-backend stats |
|
|
23
|
+
| `pg_locks` | Current locks held and awaited |
|
|
24
|
+
|
|
25
|
+
## Table Health Diagnostics
|
|
26
|
+
|
|
27
|
+
### Tables with Most Sequential Scans
|
|
28
|
+
|
|
29
|
+
```sql
|
|
30
|
+
SELECT
|
|
31
|
+
schemaname,
|
|
32
|
+
relname AS table_name,
|
|
33
|
+
seq_scan,
|
|
34
|
+
seq_tup_read,
|
|
35
|
+
idx_scan,
|
|
36
|
+
pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
|
|
37
|
+
CASE WHEN seq_scan + idx_scan > 0
|
|
38
|
+
THEN round(100.0 * idx_scan / (seq_scan + idx_scan), 1)
|
|
39
|
+
ELSE 0 END AS idx_scan_pct
|
|
40
|
+
FROM pg_stat_user_tables
|
|
41
|
+
WHERE seq_scan > 0
|
|
42
|
+
ORDER BY seq_tup_read DESC
|
|
43
|
+
LIMIT 20;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
High `seq_tup_read` with low `idx_scan_pct` = missing index opportunity.
|
|
47
|
+
|
|
48
|
+
### Tables with High Dead Tuple Ratio
|
|
49
|
+
|
|
50
|
+
```sql
|
|
51
|
+
SELECT
|
|
52
|
+
schemaname,
|
|
53
|
+
relname AS table_name,
|
|
54
|
+
n_live_tup,
|
|
55
|
+
n_dead_tup,
|
|
56
|
+
CASE WHEN n_live_tup > 0
|
|
57
|
+
THEN round(100.0 * n_dead_tup / n_live_tup, 1)
|
|
58
|
+
ELSE 0 END AS dead_pct,
|
|
59
|
+
last_autovacuum,
|
|
60
|
+
last_autoanalyze
|
|
61
|
+
FROM pg_stat_user_tables
|
|
62
|
+
WHERE n_dead_tup > 1000
|
|
63
|
+
ORDER BY n_dead_tup DESC
|
|
64
|
+
LIMIT 20;
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`dead_pct` > 20% = VACUUM is falling behind. Check autovacuum settings.
|
|
68
|
+
|
|
69
|
+
### Relation Storage Breakdown
|
|
70
|
+
|
|
71
|
+
```sql
|
|
72
|
+
SELECT
|
|
73
|
+
schemaname,
|
|
74
|
+
tablename,
|
|
75
|
+
pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS total_size,
|
|
76
|
+
pg_size_pretty(
|
|
77
|
+
pg_total_relation_size(schemaname || '.' || tablename) -
|
|
78
|
+
pg_relation_size(schemaname || '.' || tablename)
|
|
79
|
+
) AS index_and_toast_size
|
|
80
|
+
FROM pg_tables
|
|
81
|
+
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
|
|
82
|
+
ORDER BY pg_total_relation_size(schemaname || '.' || tablename) DESC
|
|
83
|
+
LIMIT 20;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This separates heap size from indexes and TOAST; it is not a bloat estimate. For bloat measurements, use the `pgstattuple` extension (requires superuser or elevated privileges):
|
|
87
|
+
|
|
88
|
+
```sql
|
|
89
|
+
CREATE EXTENSION IF NOT EXISTS pgstattuple;
|
|
90
|
+
SELECT * FROM pgstattuple('orders');
|
|
91
|
+
-- dead_tuple_percent > 20% = significant bloat
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Index Health Diagnostics
|
|
95
|
+
|
|
96
|
+
### Unused Indexes (Wasting Space and Slowing Writes)
|
|
97
|
+
|
|
98
|
+
```sql
|
|
99
|
+
SELECT
|
|
100
|
+
schemaname,
|
|
101
|
+
relname AS table_name,
|
|
102
|
+
indexrelname AS index_name,
|
|
103
|
+
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
|
|
104
|
+
idx_scan AS times_used
|
|
105
|
+
FROM pg_stat_user_indexes
|
|
106
|
+
WHERE idx_scan = 0
|
|
107
|
+
AND indexrelname NOT LIKE '%_pkey' -- exclude primary keys
|
|
108
|
+
ORDER BY pg_relation_size(indexrelid) DESC;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Index Hit Rate (Should Be > 99%)
|
|
112
|
+
|
|
113
|
+
```sql
|
|
114
|
+
SELECT
|
|
115
|
+
sum(idx_blks_hit) AS idx_hit,
|
|
116
|
+
sum(idx_blks_read) AS idx_read,
|
|
117
|
+
CASE WHEN sum(idx_blks_hit + idx_blks_read) > 0
|
|
118
|
+
THEN round(100.0 * sum(idx_blks_hit) / sum(idx_blks_hit + idx_blks_read), 2)
|
|
119
|
+
ELSE 100 END AS hit_rate_pct
|
|
120
|
+
FROM pg_statio_user_indexes;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Table Cache Hit Rate (Should Be > 99%)
|
|
124
|
+
|
|
125
|
+
```sql
|
|
126
|
+
SELECT
|
|
127
|
+
sum(heap_blks_hit) AS heap_hit,
|
|
128
|
+
sum(heap_blks_read) AS heap_read,
|
|
129
|
+
CASE WHEN sum(heap_blks_hit + heap_blks_read) > 0
|
|
130
|
+
THEN round(100.0 * sum(heap_blks_hit) / sum(heap_blks_hit + heap_blks_read), 2)
|
|
131
|
+
ELSE 100 END AS hit_rate_pct
|
|
132
|
+
FROM pg_statio_user_tables;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Hit rate < 99% = consider increasing `shared_buffers` or optimizing queries.
|
|
136
|
+
|
|
137
|
+
### shared_buffers Tuning
|
|
138
|
+
|
|
139
|
+
`shared_buffers` is the primary in-memory page cache. Every page read from disk passes through it, and frequently accessed pages stay cached here.
|
|
140
|
+
|
|
141
|
+
**Sizing rule of thumb**: Start at **25% of total RAM**. Going higher (up to ~40%) can help on read-heavy workloads with large working sets, but beyond that the OS page cache becomes less effective and returns diminish.
|
|
142
|
+
|
|
143
|
+
```sql
|
|
144
|
+
-- Current setting
|
|
145
|
+
SHOW shared_buffers;
|
|
146
|
+
|
|
147
|
+
-- Effective cache (shared_buffers + OS page cache estimate — planner hint only)
|
|
148
|
+
SHOW effective_cache_size;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Diagnostic: Is shared_buffers large enough?**
|
|
152
|
+
|
|
153
|
+
Combine the cache hit rate above with a working set estimate:
|
|
154
|
+
|
|
155
|
+
```sql
|
|
156
|
+
-- Total size of all user tables and indexes
|
|
157
|
+
SELECT
|
|
158
|
+
pg_size_pretty(sum(pg_total_relation_size(relid))) AS total_data_size
|
|
159
|
+
FROM pg_stat_user_tables;
|
|
160
|
+
|
|
161
|
+
-- Compare to shared_buffers
|
|
162
|
+
SELECT
|
|
163
|
+
setting || ' ' || unit AS shared_buffers,
|
|
164
|
+
pg_size_pretty(setting::bigint * 8192) AS shared_buffers_bytes
|
|
165
|
+
FROM pg_settings
|
|
166
|
+
WHERE name = 'shared_buffers';
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
If your hot data (frequently accessed tables + their indexes) significantly exceeds `shared_buffers`, the hit rate drops and you'll see more `shared read` in EXPLAIN output.
|
|
170
|
+
|
|
171
|
+
**When to increase shared_buffers:**
|
|
172
|
+
- Table/index cache hit rate consistently below 99%
|
|
173
|
+
- `shared read` dominates `shared hit` in EXPLAIN plans for hot queries
|
|
174
|
+
- Server has available RAM (check OS isn't swapping)
|
|
175
|
+
|
|
176
|
+
**When NOT to increase shared_buffers:**
|
|
177
|
+
- Low hit rate caused by full table scans (fix with indexes or query changes, not more cache)
|
|
178
|
+
- Server is already memory-constrained (each connection also uses `work_mem`, `maintenance_work_mem`, etc.)
|
|
179
|
+
- Hit rate is already 99%+ (adding more cache won't help)
|
|
180
|
+
|
|
181
|
+
**After changing shared_buffers:**
|
|
182
|
+
- Requires a server restart (`pg_ctl restart`)
|
|
183
|
+
- Update `effective_cache_size` to roughly `shared_buffers + estimated OS page cache` (typically ~75% of total RAM)
|
|
184
|
+
- Monitor hit rates for a representative period after the change
|
|
185
|
+
|
|
186
|
+
**Per-table cache usage** (requires `pg_buffercache` extension):
|
|
187
|
+
|
|
188
|
+
```sql
|
|
189
|
+
CREATE EXTENSION IF NOT EXISTS pg_buffercache;
|
|
190
|
+
|
|
191
|
+
SELECT
|
|
192
|
+
c.relname,
|
|
193
|
+
pg_size_pretty(count(*) * current_setting('block_size')::bigint) AS buffered,
|
|
194
|
+
round(100.0 * count(*) / (SELECT setting::int FROM pg_settings WHERE name = 'shared_buffers'), 1) AS pct_of_cache
|
|
195
|
+
FROM pg_buffercache b
|
|
196
|
+
JOIN pg_class c ON pg_relation_filenode(c.oid) = b.relfilenode
|
|
197
|
+
WHERE b.reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database())
|
|
198
|
+
GROUP BY c.relname
|
|
199
|
+
ORDER BY count(*) DESC
|
|
200
|
+
LIMIT 20;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This shows which tables/indexes are consuming the most shared_buffers — useful for identifying cache hogs or verifying that hot tables are actually cached.
|
|
204
|
+
|
|
205
|
+
### Finding Missing Foreign Key Indexes
|
|
206
|
+
|
|
207
|
+
FK columns without indexes cause slow JOINs and slow CASCADE deletes:
|
|
208
|
+
|
|
209
|
+
```sql
|
|
210
|
+
SELECT
|
|
211
|
+
c.conrelid::regclass AS table_name,
|
|
212
|
+
c.conname AS constraint_name,
|
|
213
|
+
pg_get_constraintdef(c.oid) AS constraint_definition
|
|
214
|
+
FROM pg_constraint c
|
|
215
|
+
WHERE c.contype = 'f'
|
|
216
|
+
AND NOT EXISTS (
|
|
217
|
+
SELECT 1 FROM pg_index i
|
|
218
|
+
WHERE i.indrelid = c.conrelid
|
|
219
|
+
AND i.indisvalid
|
|
220
|
+
AND i.indpred IS NULL
|
|
221
|
+
AND i.indnkeyatts >= cardinality(c.conkey)
|
|
222
|
+
AND (
|
|
223
|
+
SELECT array_agg(key_attnum ORDER BY ordinality)
|
|
224
|
+
FROM unnest(i.indkey::smallint[]) WITH ORDINALITY
|
|
225
|
+
AS keys(key_attnum, ordinality)
|
|
226
|
+
WHERE ordinality <= cardinality(c.conkey)
|
|
227
|
+
) = c.conkey
|
|
228
|
+
);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### pg_stat_io (PG16+)
|
|
232
|
+
|
|
233
|
+
I/O statistics by backend type and context — helps identify I/O-heavy operations:
|
|
234
|
+
|
|
235
|
+
```sql
|
|
236
|
+
SELECT
|
|
237
|
+
backend_type, object, context,
|
|
238
|
+
reads, read_time,
|
|
239
|
+
writes, write_time,
|
|
240
|
+
hits
|
|
241
|
+
FROM pg_stat_io
|
|
242
|
+
WHERE reads > 0 OR writes > 0
|
|
243
|
+
ORDER BY reads + writes DESC
|
|
244
|
+
LIMIT 10;
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Active Query Analysis
|
|
248
|
+
|
|
249
|
+
### Currently Running Queries
|
|
250
|
+
|
|
251
|
+
```sql
|
|
252
|
+
SELECT
|
|
253
|
+
pid,
|
|
254
|
+
now() - query_start AS duration,
|
|
255
|
+
state,
|
|
256
|
+
wait_event_type,
|
|
257
|
+
wait_event,
|
|
258
|
+
left(query, 100) AS query_preview
|
|
259
|
+
FROM pg_stat_activity
|
|
260
|
+
WHERE state != 'idle'
|
|
261
|
+
AND pid != pg_backend_pid()
|
|
262
|
+
ORDER BY duration DESC;
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Long-Running Queries (> 5 minutes)
|
|
266
|
+
|
|
267
|
+
```sql
|
|
268
|
+
SELECT
|
|
269
|
+
pid,
|
|
270
|
+
now() - query_start AS duration,
|
|
271
|
+
usename,
|
|
272
|
+
application_name,
|
|
273
|
+
client_addr,
|
|
274
|
+
left(query, 200) AS query
|
|
275
|
+
FROM pg_stat_activity
|
|
276
|
+
WHERE state = 'active'
|
|
277
|
+
AND now() - query_start > interval '5 minutes'
|
|
278
|
+
AND pid != pg_backend_pid()
|
|
279
|
+
ORDER BY duration DESC;
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Cancel or Terminate a Query
|
|
283
|
+
|
|
284
|
+
```sql
|
|
285
|
+
-- Replace 12345 with a PID selected from pg_stat_activity.
|
|
286
|
+
-- Graceful cancel (sends a cancel signal)
|
|
287
|
+
SELECT pg_cancel_backend(12345);
|
|
288
|
+
|
|
289
|
+
-- Force terminate (kills the connection)
|
|
290
|
+
SELECT pg_terminate_backend(12345);
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Do not target your current session (`pg_backend_pid()`). Prefer cancellation first; terminate only when cancellation does not resolve the problem.
|
|
294
|
+
|
|
295
|
+
## Lock Analysis
|
|
296
|
+
|
|
297
|
+
### Blocked Queries and What's Blocking Them
|
|
298
|
+
|
|
299
|
+
```sql
|
|
300
|
+
SELECT
|
|
301
|
+
blocked.pid AS blocked_pid,
|
|
302
|
+
blocked.query AS blocked_query,
|
|
303
|
+
blocking.pid AS blocking_pid,
|
|
304
|
+
blocking.query AS blocking_query,
|
|
305
|
+
now() - blocked.query_start AS blocked_duration
|
|
306
|
+
FROM pg_stat_activity blocked
|
|
307
|
+
JOIN pg_locks bl ON bl.pid = blocked.pid
|
|
308
|
+
JOIN pg_locks kl ON kl.locktype = bl.locktype
|
|
309
|
+
AND kl.database IS NOT DISTINCT FROM bl.database
|
|
310
|
+
AND kl.relation IS NOT DISTINCT FROM bl.relation
|
|
311
|
+
AND kl.page IS NOT DISTINCT FROM bl.page
|
|
312
|
+
AND kl.tuple IS NOT DISTINCT FROM bl.tuple
|
|
313
|
+
AND kl.virtualxid IS NOT DISTINCT FROM bl.virtualxid
|
|
314
|
+
AND kl.transactionid IS NOT DISTINCT FROM bl.transactionid
|
|
315
|
+
AND kl.classid IS NOT DISTINCT FROM bl.classid
|
|
316
|
+
AND kl.objid IS NOT DISTINCT FROM bl.objid
|
|
317
|
+
AND kl.objsubid IS NOT DISTINCT FROM bl.objsubid
|
|
318
|
+
AND kl.pid != bl.pid
|
|
319
|
+
JOIN pg_stat_activity blocking ON blocking.pid = kl.pid
|
|
320
|
+
WHERE NOT bl.granted AND kl.granted;
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Lock Types Quick Reference
|
|
324
|
+
|
|
325
|
+
| Lock | Acquired by | Conflicts with |
|
|
326
|
+
|------|------------|----------------|
|
|
327
|
+
| `AccessShareLock` | SELECT | AccessExclusiveLock |
|
|
328
|
+
| `RowShareLock` | SELECT FOR UPDATE/SHARE | ExclusiveLock, AccessExclusiveLock |
|
|
329
|
+
| `RowExclusiveLock` | INSERT, UPDATE, DELETE | ShareLock, ShareRowExclusiveLock, ExclusiveLock, AccessExclusiveLock |
|
|
330
|
+
| `ShareLock` | CREATE INDEX (non-concurrent) | RowExclusiveLock and above |
|
|
331
|
+
| `AccessExclusiveLock` | ALTER TABLE, DROP TABLE, VACUUM FULL | Everything |
|
|
332
|
+
|
|
333
|
+
### Advisory Locks
|
|
334
|
+
|
|
335
|
+
For application-level coordination without row locking:
|
|
336
|
+
|
|
337
|
+
```sql
|
|
338
|
+
-- Choose one acquisition method, not both.
|
|
339
|
+
-- Blocking acquisition:
|
|
340
|
+
SELECT pg_advisory_lock(hashtext('my_job_name'));
|
|
341
|
+
|
|
342
|
+
-- Release once for each successful session-level acquisition:
|
|
343
|
+
SELECT pg_advisory_unlock(hashtext('my_job_name'));
|
|
344
|
+
|
|
345
|
+
-- Or use a non-blocking acquisition:
|
|
346
|
+
SELECT pg_try_advisory_lock(hashtext('my_job_name'));
|
|
347
|
+
SELECT pg_advisory_unlock(hashtext('my_job_name'));
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## VACUUM and Bloat
|
|
351
|
+
|
|
352
|
+
### Check Autovacuum Status
|
|
353
|
+
|
|
354
|
+
```sql
|
|
355
|
+
SELECT
|
|
356
|
+
schemaname,
|
|
357
|
+
relname,
|
|
358
|
+
n_dead_tup,
|
|
359
|
+
last_autovacuum,
|
|
360
|
+
last_autoanalyze,
|
|
361
|
+
autovacuum_count,
|
|
362
|
+
autoanalyze_count
|
|
363
|
+
FROM pg_stat_user_tables
|
|
364
|
+
WHERE n_dead_tup > 0
|
|
365
|
+
ORDER BY n_dead_tup DESC
|
|
366
|
+
LIMIT 20;
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### VACUUM Time Tracking (PG18+)
|
|
370
|
+
|
|
371
|
+
PG18 adds cumulative timing columns to `pg_stat_all_tables`:
|
|
372
|
+
|
|
373
|
+
```sql
|
|
374
|
+
SELECT
|
|
375
|
+
schemaname,
|
|
376
|
+
relname,
|
|
377
|
+
total_vacuum_time,
|
|
378
|
+
total_autovacuum_time,
|
|
379
|
+
total_analyze_time,
|
|
380
|
+
total_autoanalyze_time
|
|
381
|
+
FROM pg_stat_user_tables
|
|
382
|
+
WHERE total_autovacuum_time > 0
|
|
383
|
+
ORDER BY total_autovacuum_time DESC
|
|
384
|
+
LIMIT 10;
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Autovacuum Tuning for High-Write Tables
|
|
388
|
+
|
|
389
|
+
```sql
|
|
390
|
+
-- Per-table autovacuum settings for a high-churn table
|
|
391
|
+
ALTER TABLE hot_table SET (
|
|
392
|
+
autovacuum_vacuum_scale_factor = 0.01, -- trigger at 1% dead tuples (default 20%)
|
|
393
|
+
autovacuum_vacuum_cost_delay = 2, -- faster vacuum (default 2ms)
|
|
394
|
+
autovacuum_analyze_scale_factor = 0.005 -- trigger analyze more often
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
-- PG18+: fixed dead-tuple threshold (in addition to percentage-based)
|
|
398
|
+
-- autovacuum_vacuum_max_threshold = 100000000 -- trigger at fixed count regardless of table size
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**PG18 vacuum improvements:**
|
|
402
|
+
- **Eager freezing**: Normal vacuums can freeze some pages opportunistically, reducing later freeze-only vacuum passes. Controlled by `vacuum_max_eager_freeze_failure_rate`.
|
|
403
|
+
- **`autovacuum_worker_slots`**: New GUC specifying max background worker slots; `autovacuum_max_workers` is now adjustable at runtime.
|
|
404
|
+
- **VACUUM/ANALYZE processes inheritance children by default**: Use `VACUUM (ONLY) tablename` for old behavior (parent table only).
|
|
405
|
+
|
|
406
|
+
### Manual VACUUM Operations
|
|
407
|
+
|
|
408
|
+
```sql
|
|
409
|
+
-- Standard VACUUM (reclaims space for reuse, doesn't lock)
|
|
410
|
+
VACUUM orders;
|
|
411
|
+
|
|
412
|
+
-- VACUUM with buffer usage limit (PG16+): limit shared buffer impact
|
|
413
|
+
VACUUM (BUFFER_USAGE_LIMIT '256kB') orders;
|
|
414
|
+
|
|
415
|
+
-- VACUUM with analysis
|
|
416
|
+
VACUUM ANALYZE orders;
|
|
417
|
+
|
|
418
|
+
-- VACUUM FULL (rewrites entire table — LOCKS TABLE, use as last resort)
|
|
419
|
+
VACUUM FULL orders;
|
|
420
|
+
|
|
421
|
+
-- VACUUM VERBOSE (show progress)
|
|
422
|
+
VACUUM VERBOSE orders;
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Monitoring VACUUM Progress
|
|
426
|
+
|
|
427
|
+
```sql
|
|
428
|
+
SELECT
|
|
429
|
+
relid::regclass AS table_name,
|
|
430
|
+
phase,
|
|
431
|
+
heap_blks_total,
|
|
432
|
+
heap_blks_scanned,
|
|
433
|
+
heap_blks_vacuumed,
|
|
434
|
+
CASE WHEN heap_blks_total > 0
|
|
435
|
+
THEN round(100.0 * heap_blks_vacuumed / heap_blks_total, 1)
|
|
436
|
+
ELSE 0 END AS pct_complete
|
|
437
|
+
FROM pg_stat_progress_vacuum;
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Connection Management
|
|
441
|
+
|
|
442
|
+
### Connection Overview
|
|
443
|
+
|
|
444
|
+
```sql
|
|
445
|
+
SELECT
|
|
446
|
+
state,
|
|
447
|
+
count(*) AS connections,
|
|
448
|
+
max(now() - state_change) AS longest_in_state
|
|
449
|
+
FROM pg_stat_activity
|
|
450
|
+
GROUP BY state
|
|
451
|
+
ORDER BY count(*) DESC;
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Idle Connections Holding Resources
|
|
455
|
+
|
|
456
|
+
```sql
|
|
457
|
+
SELECT
|
|
458
|
+
pid,
|
|
459
|
+
usename,
|
|
460
|
+
application_name,
|
|
461
|
+
client_addr,
|
|
462
|
+
now() - state_change AS idle_duration,
|
|
463
|
+
left(query, 100) AS last_query
|
|
464
|
+
FROM pg_stat_activity
|
|
465
|
+
WHERE state = 'idle'
|
|
466
|
+
AND now() - state_change > interval '10 minutes'
|
|
467
|
+
ORDER BY idle_duration DESC;
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Connection Limits
|
|
471
|
+
|
|
472
|
+
```sql
|
|
473
|
+
-- Current vs max connections
|
|
474
|
+
SELECT
|
|
475
|
+
count(*) AS current_connections,
|
|
476
|
+
(SELECT setting::int FROM pg_settings WHERE name = 'max_connections') AS max_connections;
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
If hitting connection limits, use PgBouncer or application-side connection pooling rather than increasing `max_connections`.
|
|
480
|
+
|
|
481
|
+
## pg_stat_statements
|
|
482
|
+
|
|
483
|
+
### Setup
|
|
484
|
+
|
|
485
|
+
```sql
|
|
486
|
+
-- In postgresql.conf:
|
|
487
|
+
-- shared_preload_libraries = 'pg_stat_statements'
|
|
488
|
+
|
|
489
|
+
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Top Queries by Total Time
|
|
493
|
+
|
|
494
|
+
```sql
|
|
495
|
+
SELECT
|
|
496
|
+
left(query, 100) AS query,
|
|
497
|
+
calls,
|
|
498
|
+
round(total_exec_time::numeric, 1) AS total_ms,
|
|
499
|
+
round(mean_exec_time::numeric, 1) AS avg_ms,
|
|
500
|
+
round((100.0 * total_exec_time / nullif(sum(total_exec_time) OVER (), 0))::numeric, 1) AS pct_total,
|
|
501
|
+
rows
|
|
502
|
+
FROM pg_stat_statements
|
|
503
|
+
WHERE dbid = (SELECT oid FROM pg_database WHERE datname = current_database())
|
|
504
|
+
ORDER BY total_exec_time DESC
|
|
505
|
+
LIMIT 20;
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Top Queries by Mean Time (Slowest Individual Executions)
|
|
509
|
+
|
|
510
|
+
```sql
|
|
511
|
+
SELECT
|
|
512
|
+
left(query, 100) AS query,
|
|
513
|
+
calls,
|
|
514
|
+
round(mean_exec_time::numeric, 1) AS avg_ms,
|
|
515
|
+
round(min_exec_time::numeric, 1) AS min_ms,
|
|
516
|
+
round(max_exec_time::numeric, 1) AS max_ms,
|
|
517
|
+
round(stddev_exec_time::numeric, 1) AS stddev_ms
|
|
518
|
+
FROM pg_stat_statements
|
|
519
|
+
WHERE calls >= 10 -- ignore rare queries
|
|
520
|
+
AND dbid = (SELECT oid FROM pg_database WHERE datname = current_database())
|
|
521
|
+
ORDER BY mean_exec_time DESC
|
|
522
|
+
LIMIT 20;
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Parallel Worker Usage (PG18+)
|
|
526
|
+
|
|
527
|
+
PG18 adds parallel worker tracking to `pg_stat_statements`:
|
|
528
|
+
|
|
529
|
+
```sql
|
|
530
|
+
SELECT
|
|
531
|
+
left(query, 80) AS query,
|
|
532
|
+
calls,
|
|
533
|
+
round(mean_exec_time::numeric, 1) AS avg_ms,
|
|
534
|
+
parallel_workers_to_launch,
|
|
535
|
+
parallel_workers_launched
|
|
536
|
+
FROM pg_stat_statements
|
|
537
|
+
WHERE parallel_workers_to_launch > 0
|
|
538
|
+
AND dbid = (SELECT oid FROM pg_database WHERE datname = current_database())
|
|
539
|
+
ORDER BY parallel_workers_to_launch - parallel_workers_launched DESC
|
|
540
|
+
LIMIT 10;
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
A gap between `to_launch` and `launched` indicates insufficient parallel workers.
|
|
544
|
+
|
|
545
|
+
### Reset Statistics
|
|
546
|
+
|
|
547
|
+
```sql
|
|
548
|
+
-- Reset pg_stat_statements (do this after config changes or deployments)
|
|
549
|
+
SELECT pg_stat_statements_reset();
|
|
550
|
+
|
|
551
|
+
-- Reset table/index stats
|
|
552
|
+
SELECT pg_stat_reset();
|
|
553
|
+
```
|