@wazir-dev/cli 1.0.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.
Files changed (629) hide show
  1. package/AGENTS.md +111 -0
  2. package/CHANGELOG.md +14 -0
  3. package/CONTRIBUTING.md +101 -0
  4. package/LICENSE +21 -0
  5. package/README.md +314 -0
  6. package/assets/composition-engine.mmd +34 -0
  7. package/assets/demo-script.sh +17 -0
  8. package/assets/logo-dark.svg +14 -0
  9. package/assets/logo.svg +14 -0
  10. package/assets/pipeline.mmd +39 -0
  11. package/assets/record-demo.sh +51 -0
  12. package/docs/README.md +51 -0
  13. package/docs/adapters/context-mode.md +60 -0
  14. package/docs/concepts/architecture.md +87 -0
  15. package/docs/concepts/artifact-model.md +60 -0
  16. package/docs/concepts/composition-engine.md +36 -0
  17. package/docs/concepts/indexing-and-recall.md +160 -0
  18. package/docs/concepts/observability.md +41 -0
  19. package/docs/concepts/roles-and-workflows.md +59 -0
  20. package/docs/concepts/terminology-policy.md +27 -0
  21. package/docs/getting-started/01-installation.md +78 -0
  22. package/docs/getting-started/02-first-run.md +102 -0
  23. package/docs/getting-started/03-adding-to-project.md +15 -0
  24. package/docs/getting-started/04-host-setup.md +15 -0
  25. package/docs/guides/ci-integration.md +15 -0
  26. package/docs/guides/creating-skills.md +15 -0
  27. package/docs/guides/expertise-module-authoring.md +15 -0
  28. package/docs/guides/hook-development.md +15 -0
  29. package/docs/guides/memory-and-learnings.md +34 -0
  30. package/docs/guides/multi-host-export.md +15 -0
  31. package/docs/guides/troubleshooting.md +101 -0
  32. package/docs/guides/writing-custom-roles.md +15 -0
  33. package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
  34. package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
  35. package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
  36. package/docs/readmes/INDEX.md +99 -0
  37. package/docs/readmes/features/expertise/README.md +171 -0
  38. package/docs/readmes/features/exports/README.md +222 -0
  39. package/docs/readmes/features/hooks/README.md +103 -0
  40. package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
  41. package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
  42. package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
  43. package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
  44. package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
  45. package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
  46. package/docs/readmes/features/hooks/session-start.md +119 -0
  47. package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
  48. package/docs/readmes/features/roles/README.md +157 -0
  49. package/docs/readmes/features/roles/clarifier.md +152 -0
  50. package/docs/readmes/features/roles/content-author.md +190 -0
  51. package/docs/readmes/features/roles/designer.md +193 -0
  52. package/docs/readmes/features/roles/executor.md +184 -0
  53. package/docs/readmes/features/roles/learner.md +210 -0
  54. package/docs/readmes/features/roles/planner.md +182 -0
  55. package/docs/readmes/features/roles/researcher.md +164 -0
  56. package/docs/readmes/features/roles/reviewer.md +184 -0
  57. package/docs/readmes/features/roles/specifier.md +162 -0
  58. package/docs/readmes/features/roles/verifier.md +215 -0
  59. package/docs/readmes/features/schemas/README.md +178 -0
  60. package/docs/readmes/features/skills/README.md +63 -0
  61. package/docs/readmes/features/skills/brainstorming.md +96 -0
  62. package/docs/readmes/features/skills/debugging.md +148 -0
  63. package/docs/readmes/features/skills/design.md +120 -0
  64. package/docs/readmes/features/skills/prepare-next.md +109 -0
  65. package/docs/readmes/features/skills/run-audit.md +159 -0
  66. package/docs/readmes/features/skills/scan-project.md +109 -0
  67. package/docs/readmes/features/skills/self-audit.md +176 -0
  68. package/docs/readmes/features/skills/tdd.md +137 -0
  69. package/docs/readmes/features/skills/using-skills.md +92 -0
  70. package/docs/readmes/features/skills/verification.md +120 -0
  71. package/docs/readmes/features/skills/writing-plans.md +104 -0
  72. package/docs/readmes/features/tooling/README.md +320 -0
  73. package/docs/readmes/features/workflows/README.md +186 -0
  74. package/docs/readmes/features/workflows/author.md +181 -0
  75. package/docs/readmes/features/workflows/clarify.md +154 -0
  76. package/docs/readmes/features/workflows/design-review.md +171 -0
  77. package/docs/readmes/features/workflows/design.md +169 -0
  78. package/docs/readmes/features/workflows/discover.md +162 -0
  79. package/docs/readmes/features/workflows/execute.md +173 -0
  80. package/docs/readmes/features/workflows/learn.md +167 -0
  81. package/docs/readmes/features/workflows/plan-review.md +165 -0
  82. package/docs/readmes/features/workflows/plan.md +170 -0
  83. package/docs/readmes/features/workflows/prepare-next.md +167 -0
  84. package/docs/readmes/features/workflows/review.md +169 -0
  85. package/docs/readmes/features/workflows/run-audit.md +191 -0
  86. package/docs/readmes/features/workflows/spec-challenge.md +159 -0
  87. package/docs/readmes/features/workflows/specify.md +160 -0
  88. package/docs/readmes/features/workflows/verify.md +177 -0
  89. package/docs/readmes/packages/README.md +50 -0
  90. package/docs/readmes/packages/ajv.md +117 -0
  91. package/docs/readmes/packages/context-mode.md +118 -0
  92. package/docs/readmes/packages/gray-matter.md +116 -0
  93. package/docs/readmes/packages/node-test.md +137 -0
  94. package/docs/readmes/packages/yaml.md +112 -0
  95. package/docs/reference/configuration-reference.md +159 -0
  96. package/docs/reference/expertise-index.md +52 -0
  97. package/docs/reference/git-flow.md +43 -0
  98. package/docs/reference/hooks.md +87 -0
  99. package/docs/reference/host-exports.md +50 -0
  100. package/docs/reference/launch-checklist.md +172 -0
  101. package/docs/reference/marketplace-listings.md +76 -0
  102. package/docs/reference/release-process.md +34 -0
  103. package/docs/reference/roles-reference.md +77 -0
  104. package/docs/reference/skills.md +33 -0
  105. package/docs/reference/templates.md +29 -0
  106. package/docs/reference/tooling-cli.md +94 -0
  107. package/docs/truth-claims.yaml +222 -0
  108. package/expertise/PROGRESS.md +63 -0
  109. package/expertise/README.md +18 -0
  110. package/expertise/antipatterns/PROGRESS.md +56 -0
  111. package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
  112. package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
  113. package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
  114. package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
  115. package/expertise/antipatterns/backend/index.md +24 -0
  116. package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
  117. package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
  118. package/expertise/antipatterns/code/async-antipatterns.md +622 -0
  119. package/expertise/antipatterns/code/code-smells.md +1186 -0
  120. package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
  121. package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
  122. package/expertise/antipatterns/code/index.md +27 -0
  123. package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
  124. package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
  125. package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
  126. package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
  127. package/expertise/antipatterns/design/dark-patterns.md +1121 -0
  128. package/expertise/antipatterns/design/index.md +22 -0
  129. package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
  130. package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
  131. package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
  132. package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
  133. package/expertise/antipatterns/frontend/index.md +23 -0
  134. package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
  135. package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
  136. package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
  137. package/expertise/antipatterns/index.md +31 -0
  138. package/expertise/antipatterns/performance/index.md +20 -0
  139. package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
  140. package/expertise/antipatterns/performance/premature-optimization.md +623 -0
  141. package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
  142. package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
  143. package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
  144. package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
  145. package/expertise/antipatterns/process/index.md +23 -0
  146. package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
  147. package/expertise/antipatterns/security/index.md +20 -0
  148. package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
  149. package/expertise/antipatterns/security/security-theater.md +843 -0
  150. package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
  151. package/expertise/architecture/PROGRESS.md +70 -0
  152. package/expertise/architecture/data/caching-architecture.md +671 -0
  153. package/expertise/architecture/data/data-consistency.md +574 -0
  154. package/expertise/architecture/data/data-modeling.md +536 -0
  155. package/expertise/architecture/data/event-streams-and-queues.md +634 -0
  156. package/expertise/architecture/data/index.md +25 -0
  157. package/expertise/architecture/data/search-architecture.md +663 -0
  158. package/expertise/architecture/data/sql-vs-nosql.md +708 -0
  159. package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
  160. package/expertise/architecture/decisions/build-vs-buy.md +616 -0
  161. package/expertise/architecture/decisions/index.md +23 -0
  162. package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
  163. package/expertise/architecture/decisions/technology-selection.md +616 -0
  164. package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
  165. package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
  166. package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
  167. package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
  168. package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
  169. package/expertise/architecture/distributed/index.md +25 -0
  170. package/expertise/architecture/distributed/saga-pattern.md +797 -0
  171. package/expertise/architecture/foundations/architectural-thinking.md +460 -0
  172. package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
  173. package/expertise/architecture/foundations/design-principles-solid.md +649 -0
  174. package/expertise/architecture/foundations/domain-driven-design.md +719 -0
  175. package/expertise/architecture/foundations/index.md +25 -0
  176. package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
  177. package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
  178. package/expertise/architecture/index.md +34 -0
  179. package/expertise/architecture/integration/api-design-graphql.md +638 -0
  180. package/expertise/architecture/integration/api-design-grpc.md +804 -0
  181. package/expertise/architecture/integration/api-design-rest.md +892 -0
  182. package/expertise/architecture/integration/index.md +25 -0
  183. package/expertise/architecture/integration/third-party-integration.md +795 -0
  184. package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
  185. package/expertise/architecture/integration/websockets-realtime.md +791 -0
  186. package/expertise/architecture/mobile-architecture/index.md +22 -0
  187. package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
  188. package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
  189. package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
  190. package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
  191. package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
  192. package/expertise/architecture/patterns/event-driven.md +797 -0
  193. package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
  194. package/expertise/architecture/patterns/index.md +27 -0
  195. package/expertise/architecture/patterns/layered-architecture.md +736 -0
  196. package/expertise/architecture/patterns/microservices.md +753 -0
  197. package/expertise/architecture/patterns/modular-monolith.md +692 -0
  198. package/expertise/architecture/patterns/monolith.md +626 -0
  199. package/expertise/architecture/patterns/plugin-architecture.md +735 -0
  200. package/expertise/architecture/patterns/serverless.md +780 -0
  201. package/expertise/architecture/scaling/database-scaling.md +615 -0
  202. package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
  203. package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
  204. package/expertise/architecture/scaling/index.md +24 -0
  205. package/expertise/architecture/scaling/multi-tenancy.md +800 -0
  206. package/expertise/architecture/scaling/stateless-design.md +787 -0
  207. package/expertise/backend/embedded-firmware.md +625 -0
  208. package/expertise/backend/go.md +853 -0
  209. package/expertise/backend/index.md +24 -0
  210. package/expertise/backend/java-spring.md +448 -0
  211. package/expertise/backend/node-typescript.md +625 -0
  212. package/expertise/backend/python-fastapi.md +724 -0
  213. package/expertise/backend/rust.md +458 -0
  214. package/expertise/backend/solidity.md +711 -0
  215. package/expertise/composition-map.yaml +443 -0
  216. package/expertise/content/foundations/content-modeling.md +395 -0
  217. package/expertise/content/foundations/editorial-standards.md +449 -0
  218. package/expertise/content/foundations/index.md +24 -0
  219. package/expertise/content/foundations/microcopy.md +455 -0
  220. package/expertise/content/foundations/terminology-governance.md +509 -0
  221. package/expertise/content/index.md +34 -0
  222. package/expertise/content/patterns/accessibility-copy.md +518 -0
  223. package/expertise/content/patterns/index.md +24 -0
  224. package/expertise/content/patterns/notification-content.md +433 -0
  225. package/expertise/content/patterns/sample-content.md +486 -0
  226. package/expertise/content/patterns/state-copy.md +439 -0
  227. package/expertise/design/PROGRESS.md +58 -0
  228. package/expertise/design/disciplines/dark-mode-theming.md +577 -0
  229. package/expertise/design/disciplines/design-systems.md +595 -0
  230. package/expertise/design/disciplines/index.md +25 -0
  231. package/expertise/design/disciplines/information-architecture.md +800 -0
  232. package/expertise/design/disciplines/interaction-design.md +788 -0
  233. package/expertise/design/disciplines/responsive-design.md +552 -0
  234. package/expertise/design/disciplines/usability-testing.md +516 -0
  235. package/expertise/design/disciplines/user-research.md +792 -0
  236. package/expertise/design/foundations/accessibility-design.md +796 -0
  237. package/expertise/design/foundations/color-theory.md +797 -0
  238. package/expertise/design/foundations/iconography.md +795 -0
  239. package/expertise/design/foundations/index.md +26 -0
  240. package/expertise/design/foundations/motion-and-animation.md +653 -0
  241. package/expertise/design/foundations/rtl-design.md +585 -0
  242. package/expertise/design/foundations/spacing-and-layout.md +607 -0
  243. package/expertise/design/foundations/typography.md +800 -0
  244. package/expertise/design/foundations/visual-hierarchy.md +761 -0
  245. package/expertise/design/index.md +32 -0
  246. package/expertise/design/patterns/authentication-flows.md +474 -0
  247. package/expertise/design/patterns/content-consumption.md +789 -0
  248. package/expertise/design/patterns/data-display.md +618 -0
  249. package/expertise/design/patterns/e-commerce.md +1494 -0
  250. package/expertise/design/patterns/feedback-and-states.md +642 -0
  251. package/expertise/design/patterns/forms-and-input.md +819 -0
  252. package/expertise/design/patterns/gamification.md +801 -0
  253. package/expertise/design/patterns/index.md +31 -0
  254. package/expertise/design/patterns/microinteractions.md +449 -0
  255. package/expertise/design/patterns/navigation.md +800 -0
  256. package/expertise/design/patterns/notifications.md +705 -0
  257. package/expertise/design/patterns/onboarding.md +700 -0
  258. package/expertise/design/patterns/search-and-filter.md +601 -0
  259. package/expertise/design/patterns/settings-and-preferences.md +768 -0
  260. package/expertise/design/patterns/social-and-community.md +748 -0
  261. package/expertise/design/platforms/desktop-native.md +612 -0
  262. package/expertise/design/platforms/index.md +25 -0
  263. package/expertise/design/platforms/mobile-android.md +825 -0
  264. package/expertise/design/platforms/mobile-cross-platform.md +983 -0
  265. package/expertise/design/platforms/mobile-ios.md +699 -0
  266. package/expertise/design/platforms/tablet.md +794 -0
  267. package/expertise/design/platforms/web-dashboard.md +790 -0
  268. package/expertise/design/platforms/web-responsive.md +550 -0
  269. package/expertise/design/psychology/behavioral-nudges.md +449 -0
  270. package/expertise/design/psychology/cognitive-load.md +1191 -0
  271. package/expertise/design/psychology/error-psychology.md +778 -0
  272. package/expertise/design/psychology/index.md +22 -0
  273. package/expertise/design/psychology/persuasive-design.md +736 -0
  274. package/expertise/design/psychology/user-mental-models.md +623 -0
  275. package/expertise/design/tooling/open-pencil.md +266 -0
  276. package/expertise/frontend/angular.md +1073 -0
  277. package/expertise/frontend/desktop-electron.md +546 -0
  278. package/expertise/frontend/flutter.md +782 -0
  279. package/expertise/frontend/index.md +27 -0
  280. package/expertise/frontend/native-android.md +409 -0
  281. package/expertise/frontend/native-ios.md +490 -0
  282. package/expertise/frontend/react-native.md +1160 -0
  283. package/expertise/frontend/react.md +808 -0
  284. package/expertise/frontend/vue.md +1089 -0
  285. package/expertise/humanize/domain-rules-code.md +79 -0
  286. package/expertise/humanize/domain-rules-content.md +67 -0
  287. package/expertise/humanize/domain-rules-technical-docs.md +56 -0
  288. package/expertise/humanize/index.md +35 -0
  289. package/expertise/humanize/self-audit-checklist.md +87 -0
  290. package/expertise/humanize/sentence-patterns.md +218 -0
  291. package/expertise/humanize/vocabulary-blacklist.md +105 -0
  292. package/expertise/i18n/PROGRESS.md +65 -0
  293. package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
  294. package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
  295. package/expertise/i18n/advanced/complex-scripts.md +30 -0
  296. package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
  297. package/expertise/i18n/advanced/testing-i18n.md +28 -0
  298. package/expertise/i18n/content/content-adaptation.md +23 -0
  299. package/expertise/i18n/content/locale-specific-formatting.md +23 -0
  300. package/expertise/i18n/content/machine-translation-integration.md +28 -0
  301. package/expertise/i18n/content/translation-management.md +29 -0
  302. package/expertise/i18n/foundations/date-time-calendars.md +67 -0
  303. package/expertise/i18n/foundations/i18n-architecture.md +272 -0
  304. package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
  305. package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
  306. package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
  307. package/expertise/i18n/foundations/string-externalization.md +236 -0
  308. package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
  309. package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
  310. package/expertise/i18n/index.md +38 -0
  311. package/expertise/i18n/platform/backend-i18n.md +31 -0
  312. package/expertise/i18n/platform/flutter-i18n.md +148 -0
  313. package/expertise/i18n/platform/native-android-i18n.md +36 -0
  314. package/expertise/i18n/platform/native-ios-i18n.md +36 -0
  315. package/expertise/i18n/platform/react-i18n.md +103 -0
  316. package/expertise/i18n/platform/web-css-i18n.md +81 -0
  317. package/expertise/i18n/rtl/arabic-specific.md +175 -0
  318. package/expertise/i18n/rtl/hebrew-specific.md +149 -0
  319. package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
  320. package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
  321. package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
  322. package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
  323. package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
  324. package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
  325. package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
  326. package/expertise/i18n/rtl/rtl-typography.md +160 -0
  327. package/expertise/index.md +113 -0
  328. package/expertise/index.yaml +216 -0
  329. package/expertise/infrastructure/cloud-aws.md +597 -0
  330. package/expertise/infrastructure/cloud-gcp.md +599 -0
  331. package/expertise/infrastructure/cybersecurity.md +816 -0
  332. package/expertise/infrastructure/database-mongodb.md +447 -0
  333. package/expertise/infrastructure/database-postgres.md +400 -0
  334. package/expertise/infrastructure/devops-cicd.md +787 -0
  335. package/expertise/infrastructure/index.md +27 -0
  336. package/expertise/performance/PROGRESS.md +50 -0
  337. package/expertise/performance/backend/api-latency.md +1204 -0
  338. package/expertise/performance/backend/background-jobs.md +506 -0
  339. package/expertise/performance/backend/connection-pooling.md +1209 -0
  340. package/expertise/performance/backend/database-query-optimization.md +515 -0
  341. package/expertise/performance/backend/index.md +23 -0
  342. package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
  343. package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
  344. package/expertise/performance/foundations/caching-strategies.md +489 -0
  345. package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
  346. package/expertise/performance/foundations/index.md +24 -0
  347. package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
  348. package/expertise/performance/foundations/memory-management.md +964 -0
  349. package/expertise/performance/foundations/performance-budgets.md +1314 -0
  350. package/expertise/performance/index.md +31 -0
  351. package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
  352. package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
  353. package/expertise/performance/infrastructure/index.md +22 -0
  354. package/expertise/performance/infrastructure/load-balancing.md +1081 -0
  355. package/expertise/performance/infrastructure/observability.md +1079 -0
  356. package/expertise/performance/mobile/index.md +23 -0
  357. package/expertise/performance/mobile/mobile-animations.md +544 -0
  358. package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
  359. package/expertise/performance/mobile/mobile-network.md +452 -0
  360. package/expertise/performance/mobile/mobile-rendering.md +599 -0
  361. package/expertise/performance/mobile/mobile-startup-time.md +505 -0
  362. package/expertise/performance/platform-specific/flutter-performance.md +647 -0
  363. package/expertise/performance/platform-specific/index.md +22 -0
  364. package/expertise/performance/platform-specific/node-performance.md +1307 -0
  365. package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
  366. package/expertise/performance/platform-specific/react-performance.md +1403 -0
  367. package/expertise/performance/web/bundle-optimization.md +1239 -0
  368. package/expertise/performance/web/image-and-media.md +636 -0
  369. package/expertise/performance/web/index.md +24 -0
  370. package/expertise/performance/web/network-optimization.md +1133 -0
  371. package/expertise/performance/web/rendering-performance.md +1098 -0
  372. package/expertise/performance/web/ssr-and-hydration.md +918 -0
  373. package/expertise/performance/web/web-vitals.md +1374 -0
  374. package/expertise/quality/accessibility.md +985 -0
  375. package/expertise/quality/evidence-based-verification.md +499 -0
  376. package/expertise/quality/index.md +24 -0
  377. package/expertise/quality/ml-model-audit.md +614 -0
  378. package/expertise/quality/performance.md +600 -0
  379. package/expertise/quality/testing-api.md +891 -0
  380. package/expertise/quality/testing-mobile.md +496 -0
  381. package/expertise/quality/testing-web.md +849 -0
  382. package/expertise/security/PROGRESS.md +54 -0
  383. package/expertise/security/agentic-identity.md +540 -0
  384. package/expertise/security/compliance-frameworks.md +601 -0
  385. package/expertise/security/data/data-encryption.md +364 -0
  386. package/expertise/security/data/data-privacy-gdpr.md +692 -0
  387. package/expertise/security/data/database-security.md +1171 -0
  388. package/expertise/security/data/index.md +22 -0
  389. package/expertise/security/data/pii-handling.md +531 -0
  390. package/expertise/security/foundations/authentication.md +1041 -0
  391. package/expertise/security/foundations/authorization.md +603 -0
  392. package/expertise/security/foundations/cryptography.md +1001 -0
  393. package/expertise/security/foundations/index.md +25 -0
  394. package/expertise/security/foundations/owasp-top-10.md +1354 -0
  395. package/expertise/security/foundations/secrets-management.md +1217 -0
  396. package/expertise/security/foundations/secure-sdlc.md +700 -0
  397. package/expertise/security/foundations/supply-chain-security.md +698 -0
  398. package/expertise/security/index.md +31 -0
  399. package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
  400. package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
  401. package/expertise/security/infrastructure/container-security.md +721 -0
  402. package/expertise/security/infrastructure/incident-response.md +1295 -0
  403. package/expertise/security/infrastructure/index.md +24 -0
  404. package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
  405. package/expertise/security/infrastructure/network-security.md +1337 -0
  406. package/expertise/security/mobile/index.md +23 -0
  407. package/expertise/security/mobile/mobile-android-security.md +1218 -0
  408. package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
  409. package/expertise/security/mobile/mobile-data-storage.md +1265 -0
  410. package/expertise/security/mobile/mobile-ios-security.md +1401 -0
  411. package/expertise/security/mobile/mobile-network-security.md +1520 -0
  412. package/expertise/security/smart-contract-security.md +594 -0
  413. package/expertise/security/testing/index.md +22 -0
  414. package/expertise/security/testing/penetration-testing.md +1258 -0
  415. package/expertise/security/testing/security-code-review.md +1765 -0
  416. package/expertise/security/testing/threat-modeling.md +1074 -0
  417. package/expertise/security/testing/vulnerability-scanning.md +1062 -0
  418. package/expertise/security/web/api-security.md +586 -0
  419. package/expertise/security/web/cors-and-headers.md +433 -0
  420. package/expertise/security/web/csrf.md +562 -0
  421. package/expertise/security/web/file-upload.md +1477 -0
  422. package/expertise/security/web/index.md +25 -0
  423. package/expertise/security/web/injection.md +1375 -0
  424. package/expertise/security/web/session-management.md +1101 -0
  425. package/expertise/security/web/xss.md +1158 -0
  426. package/exports/README.md +17 -0
  427. package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
  428. package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
  429. package/exports/hosts/claude/.claude/agents/designer.md +55 -0
  430. package/exports/hosts/claude/.claude/agents/executor.md +55 -0
  431. package/exports/hosts/claude/.claude/agents/learner.md +51 -0
  432. package/exports/hosts/claude/.claude/agents/planner.md +53 -0
  433. package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
  434. package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
  435. package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
  436. package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
  437. package/exports/hosts/claude/.claude/commands/author.md +42 -0
  438. package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
  439. package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
  440. package/exports/hosts/claude/.claude/commands/design.md +44 -0
  441. package/exports/hosts/claude/.claude/commands/discover.md +37 -0
  442. package/exports/hosts/claude/.claude/commands/execute.md +48 -0
  443. package/exports/hosts/claude/.claude/commands/learn.md +38 -0
  444. package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
  445. package/exports/hosts/claude/.claude/commands/plan.md +39 -0
  446. package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
  447. package/exports/hosts/claude/.claude/commands/review.md +40 -0
  448. package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
  449. package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
  450. package/exports/hosts/claude/.claude/commands/specify.md +38 -0
  451. package/exports/hosts/claude/.claude/commands/verify.md +37 -0
  452. package/exports/hosts/claude/.claude/settings.json +34 -0
  453. package/exports/hosts/claude/CLAUDE.md +19 -0
  454. package/exports/hosts/claude/export.manifest.json +38 -0
  455. package/exports/hosts/claude/host-package.json +67 -0
  456. package/exports/hosts/codex/AGENTS.md +19 -0
  457. package/exports/hosts/codex/export.manifest.json +38 -0
  458. package/exports/hosts/codex/host-package.json +41 -0
  459. package/exports/hosts/cursor/.cursor/hooks.json +16 -0
  460. package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
  461. package/exports/hosts/cursor/export.manifest.json +38 -0
  462. package/exports/hosts/cursor/host-package.json +42 -0
  463. package/exports/hosts/gemini/GEMINI.md +19 -0
  464. package/exports/hosts/gemini/export.manifest.json +38 -0
  465. package/exports/hosts/gemini/host-package.json +41 -0
  466. package/hooks/README.md +18 -0
  467. package/hooks/definitions/loop_cap_guard.yaml +21 -0
  468. package/hooks/definitions/post_tool_capture.yaml +24 -0
  469. package/hooks/definitions/pre_compact_summary.yaml +19 -0
  470. package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
  471. package/hooks/definitions/protected_path_write_guard.yaml +19 -0
  472. package/hooks/definitions/session_start.yaml +19 -0
  473. package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
  474. package/hooks/loop-cap-guard +17 -0
  475. package/hooks/post-tool-lint +36 -0
  476. package/hooks/protected-path-write-guard +17 -0
  477. package/hooks/session-start +41 -0
  478. package/llms-full.txt +2355 -0
  479. package/llms.txt +43 -0
  480. package/package.json +79 -0
  481. package/roles/README.md +20 -0
  482. package/roles/clarifier.md +42 -0
  483. package/roles/content-author.md +63 -0
  484. package/roles/designer.md +55 -0
  485. package/roles/executor.md +55 -0
  486. package/roles/learner.md +51 -0
  487. package/roles/planner.md +53 -0
  488. package/roles/researcher.md +43 -0
  489. package/roles/reviewer.md +54 -0
  490. package/roles/specifier.md +47 -0
  491. package/roles/verifier.md +71 -0
  492. package/schemas/README.md +24 -0
  493. package/schemas/accepted-learning.schema.json +20 -0
  494. package/schemas/author-artifact.schema.json +156 -0
  495. package/schemas/clarification.schema.json +19 -0
  496. package/schemas/design-artifact.schema.json +80 -0
  497. package/schemas/docs-claim.schema.json +18 -0
  498. package/schemas/export-manifest.schema.json +20 -0
  499. package/schemas/hook.schema.json +67 -0
  500. package/schemas/host-export-package.schema.json +18 -0
  501. package/schemas/implementation-plan.schema.json +19 -0
  502. package/schemas/proposed-learning.schema.json +19 -0
  503. package/schemas/research.schema.json +18 -0
  504. package/schemas/review.schema.json +29 -0
  505. package/schemas/run-manifest.schema.json +18 -0
  506. package/schemas/spec-challenge.schema.json +18 -0
  507. package/schemas/spec.schema.json +20 -0
  508. package/schemas/usage.schema.json +102 -0
  509. package/schemas/verification-proof.schema.json +29 -0
  510. package/schemas/wazir-manifest.schema.json +173 -0
  511. package/skills/README.md +40 -0
  512. package/skills/brainstorming/SKILL.md +77 -0
  513. package/skills/debugging/SKILL.md +50 -0
  514. package/skills/design/SKILL.md +61 -0
  515. package/skills/dispatching-parallel-agents/SKILL.md +128 -0
  516. package/skills/executing-plans/SKILL.md +70 -0
  517. package/skills/finishing-a-development-branch/SKILL.md +169 -0
  518. package/skills/humanize/SKILL.md +123 -0
  519. package/skills/init-pipeline/SKILL.md +124 -0
  520. package/skills/prepare-next/SKILL.md +20 -0
  521. package/skills/receiving-code-review/SKILL.md +123 -0
  522. package/skills/requesting-code-review/SKILL.md +105 -0
  523. package/skills/requesting-code-review/code-reviewer.md +108 -0
  524. package/skills/run-audit/SKILL.md +197 -0
  525. package/skills/scan-project/SKILL.md +41 -0
  526. package/skills/self-audit/SKILL.md +153 -0
  527. package/skills/subagent-driven-development/SKILL.md +154 -0
  528. package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
  529. package/skills/subagent-driven-development/implementer-prompt.md +102 -0
  530. package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  531. package/skills/tdd/SKILL.md +23 -0
  532. package/skills/using-git-worktrees/SKILL.md +163 -0
  533. package/skills/using-skills/SKILL.md +95 -0
  534. package/skills/verification/SKILL.md +22 -0
  535. package/skills/wazir/SKILL.md +463 -0
  536. package/skills/writing-plans/SKILL.md +30 -0
  537. package/skills/writing-skills/SKILL.md +157 -0
  538. package/skills/writing-skills/anthropic-best-practices.md +122 -0
  539. package/skills/writing-skills/persuasion-principles.md +50 -0
  540. package/templates/README.md +20 -0
  541. package/templates/artifacts/README.md +10 -0
  542. package/templates/artifacts/accepted-learning.md +19 -0
  543. package/templates/artifacts/accepted-learning.template.json +12 -0
  544. package/templates/artifacts/author.md +74 -0
  545. package/templates/artifacts/author.template.json +19 -0
  546. package/templates/artifacts/clarification.md +21 -0
  547. package/templates/artifacts/clarification.template.json +12 -0
  548. package/templates/artifacts/execute-notes.md +19 -0
  549. package/templates/artifacts/implementation-plan.md +21 -0
  550. package/templates/artifacts/implementation-plan.template.json +11 -0
  551. package/templates/artifacts/learning-proposal.md +19 -0
  552. package/templates/artifacts/next-run-handoff.md +21 -0
  553. package/templates/artifacts/plan-review.md +19 -0
  554. package/templates/artifacts/proposed-learning.template.json +12 -0
  555. package/templates/artifacts/research.md +21 -0
  556. package/templates/artifacts/research.template.json +12 -0
  557. package/templates/artifacts/review-findings.md +19 -0
  558. package/templates/artifacts/review.template.json +11 -0
  559. package/templates/artifacts/run-manifest.template.json +8 -0
  560. package/templates/artifacts/spec-challenge.md +19 -0
  561. package/templates/artifacts/spec-challenge.template.json +11 -0
  562. package/templates/artifacts/spec.md +21 -0
  563. package/templates/artifacts/spec.template.json +12 -0
  564. package/templates/artifacts/verification-proof.md +19 -0
  565. package/templates/artifacts/verification-proof.template.json +11 -0
  566. package/templates/examples/accepted-learning.example.json +14 -0
  567. package/templates/examples/author.example.json +152 -0
  568. package/templates/examples/clarification.example.json +15 -0
  569. package/templates/examples/docs-claim.example.json +8 -0
  570. package/templates/examples/export-manifest.example.json +7 -0
  571. package/templates/examples/host-export-package.example.json +11 -0
  572. package/templates/examples/implementation-plan.example.json +17 -0
  573. package/templates/examples/proposed-learning.example.json +13 -0
  574. package/templates/examples/research.example.json +15 -0
  575. package/templates/examples/research.example.md +6 -0
  576. package/templates/examples/review.example.json +17 -0
  577. package/templates/examples/run-manifest.example.json +9 -0
  578. package/templates/examples/spec-challenge.example.json +14 -0
  579. package/templates/examples/spec.example.json +21 -0
  580. package/templates/examples/verification-proof.example.json +21 -0
  581. package/templates/examples/wazir-manifest.example.yaml +65 -0
  582. package/templates/task-definition-schema.md +99 -0
  583. package/tooling/README.md +20 -0
  584. package/tooling/src/adapters/context-mode.js +50 -0
  585. package/tooling/src/capture/command.js +376 -0
  586. package/tooling/src/capture/store.js +99 -0
  587. package/tooling/src/capture/usage.js +270 -0
  588. package/tooling/src/checks/branches.js +50 -0
  589. package/tooling/src/checks/brand-truth.js +110 -0
  590. package/tooling/src/checks/changelog.js +231 -0
  591. package/tooling/src/checks/command-registry.js +36 -0
  592. package/tooling/src/checks/commits.js +102 -0
  593. package/tooling/src/checks/docs-drift.js +103 -0
  594. package/tooling/src/checks/docs-truth.js +201 -0
  595. package/tooling/src/checks/runtime-surface.js +156 -0
  596. package/tooling/src/cli.js +116 -0
  597. package/tooling/src/command-options.js +56 -0
  598. package/tooling/src/commands/validate.js +320 -0
  599. package/tooling/src/doctor/command.js +91 -0
  600. package/tooling/src/export/command.js +77 -0
  601. package/tooling/src/export/compiler.js +498 -0
  602. package/tooling/src/guards/loop-cap-guard.js +52 -0
  603. package/tooling/src/guards/protected-path-write-guard.js +67 -0
  604. package/tooling/src/index/command.js +152 -0
  605. package/tooling/src/index/storage.js +1061 -0
  606. package/tooling/src/index/summarizers.js +261 -0
  607. package/tooling/src/loaders.js +18 -0
  608. package/tooling/src/project-root.js +22 -0
  609. package/tooling/src/recall/command.js +225 -0
  610. package/tooling/src/schema-validator.js +30 -0
  611. package/tooling/src/state-root.js +40 -0
  612. package/tooling/src/status/command.js +71 -0
  613. package/wazir.manifest.yaml +135 -0
  614. package/workflows/README.md +19 -0
  615. package/workflows/author.md +42 -0
  616. package/workflows/clarify.md +38 -0
  617. package/workflows/design-review.md +46 -0
  618. package/workflows/design.md +44 -0
  619. package/workflows/discover.md +37 -0
  620. package/workflows/execute.md +48 -0
  621. package/workflows/learn.md +38 -0
  622. package/workflows/plan-review.md +42 -0
  623. package/workflows/plan.md +39 -0
  624. package/workflows/prepare-next.md +37 -0
  625. package/workflows/review.md +40 -0
  626. package/workflows/run-audit.md +41 -0
  627. package/workflows/spec-challenge.md +41 -0
  628. package/workflows/specify.md +38 -0
  629. package/workflows/verify.md +37 -0
@@ -0,0 +1,1366 @@
1
+ # PostgreSQL Performance Expertise Module
2
+
3
+ > Comprehensive guide to PostgreSQL performance tuning, covering configuration,
4
+ > indexing, query optimization, connection pooling, vacuum management, partitioning,
5
+ > monitoring, and anti-pattern avoidance. Every recommendation is backed by numbers
6
+ > from real benchmarks and production experience.
7
+
8
+ ---
9
+
10
+ ## Table of Contents
11
+
12
+ 1. [Configuration Tuning](#1-configuration-tuning)
13
+ 2. [Index Strategies](#2-index-strategies)
14
+ 3. [Query Optimization with EXPLAIN](#3-query-optimization-with-explain)
15
+ 4. [Connection Pooling](#4-connection-pooling)
16
+ 5. [VACUUM and Autovacuum Tuning](#5-vacuum-and-autovacuum-tuning)
17
+ 6. [Table Partitioning Strategies](#6-table-partitioning-strategies)
18
+ 7. [Common Bottlenecks](#7-common-bottlenecks)
19
+ 8. [Anti-Patterns](#8-anti-patterns)
20
+ 9. [Monitoring with pg_stat_statements and pg_stat_user_tables](#9-monitoring)
21
+ 10. [Before/After Query Examples](#10-beforeafter-query-examples)
22
+ 11. [Quick Reference Checklist](#11-quick-reference-checklist)
23
+ 12. [Sources](#12-sources)
24
+
25
+ ---
26
+
27
+ ## 1. Configuration Tuning
28
+
29
+ PostgreSQL ships with conservative defaults designed to run on minimal hardware. Production
30
+ deployments require tuning to match available resources. The parameters below have the
31
+ highest impact on throughput and latency.
32
+
33
+ ### 1.1 shared_buffers
34
+
35
+ **What it does:** Controls the amount of memory PostgreSQL dedicates to caching data pages
36
+ in its own buffer pool, separate from the OS page cache.
37
+
38
+ **Recommended values:**
39
+
40
+ | System RAM | shared_buffers | Notes |
41
+ |------------|---------------|-------|
42
+ | 4 GB | 1 GB (25%) | Minimum production server |
43
+ | 16 GB | 4-5 GB (25-33%) | Standard web application |
44
+ | 64 GB | 16-20 GB (25-33%) | Medium OLTP workload |
45
+ | 256 GB | 64-80 GB (25-33%) | Large analytics/OLTP |
46
+
47
+ - The general guideline is 25-33% of total system RAM. Setting it higher than 40% rarely
48
+ helps because PostgreSQL also relies on the OS page cache for double-buffering.
49
+ - Increasing shared_buffers requires a corresponding increase in max_wal_size to spread
50
+ checkpoint writes over a longer period, avoiding I/O spikes.
51
+ - Requires a server restart to change.
52
+
53
+ ```
54
+ # postgresql.conf - 64 GB RAM server
55
+ shared_buffers = '16GB'
56
+ ```
57
+
58
+ ### 1.2 work_mem
59
+
60
+ **What it does:** Sets the maximum memory each query operation (sort, hash join, hash
61
+ aggregate) can use before spilling to temporary disk files. A single complex query can
62
+ allocate work_mem multiple times (once per sort/hash node).
63
+
64
+ **Formula:** `(Total RAM - shared_buffers) / (16 x CPU cores)`
65
+
66
+ | System RAM | CPU Cores | shared_buffers | work_mem |
67
+ |------------|-----------|---------------|----------|
68
+ | 16 GB | 4 | 4 GB | 192 MB |
69
+ | 64 GB | 16 | 16 GB | 187 MB |
70
+ | 256 GB | 32 | 64 GB | 375 MB |
71
+
72
+ - OLTP workloads: Keep lower (4-64 MB) since many concurrent connections each allocate
73
+ this amount. With 200 connections at 64 MB work_mem, worst case is 12.8 GB consumed
74
+ just by sort buffers.
75
+ - OLAP/reporting workloads: Can go higher (256 MB-1 GB) with fewer concurrent queries.
76
+ - Monitor temp file usage via `pg_stat_database.temp_bytes` -- if temp files are
77
+ generated frequently, increase work_mem.
78
+
79
+ ```
80
+ # OLTP server with 200 max connections
81
+ work_mem = '32MB'
82
+
83
+ # OLAP server with 20 max connections
84
+ work_mem = '512MB'
85
+ ```
86
+
87
+ ### 1.3 effective_cache_size
88
+
89
+ **What it does:** Tells the query planner how much memory is available for disk caching
90
+ across both shared_buffers and the OS page cache. Does not allocate memory; only
91
+ influences cost estimates for index scans vs sequential scans.
92
+
93
+ **Recommended values:**
94
+ - Conservative: 50% of total RAM
95
+ - Typical: 75% of total RAM (most common production setting)
96
+
97
+ A 64 GB server should use `effective_cache_size = '48GB'`. Setting this too low causes
98
+ the planner to prefer sequential scans over index scans because it assumes data is
99
+ unlikely to be cached, artificially inflating the estimated cost of random I/O.
100
+
101
+ ```
102
+ # 64 GB RAM server
103
+ effective_cache_size = '48GB'
104
+ ```
105
+
106
+ ### 1.4 random_page_cost
107
+
108
+ **What it does:** Tells the planner the relative cost of a non-sequential (random) disk
109
+ page fetch compared to a sequential one. Default is 4.0 (random I/O is 4x more
110
+ expensive than sequential).
111
+
112
+ **SSD tuning is critical:** On SSDs, random reads are nearly as fast as sequential reads.
113
+
114
+ | Storage Type | random_page_cost | Rationale |
115
+ |-------------|-----------------|-----------|
116
+ | HDD (7200 RPM) | 4.0 (default) | Random seeks are ~10ms vs ~0.1ms sequential |
117
+ | SSD (SATA) | 1.5-2.0 | Random latency ~0.1ms, sequential ~0.05ms |
118
+ | NVMe SSD | 1.1-1.5 | Near-uniform access latency |
119
+ | RAM disk | 1.0 | No mechanical seek penalty |
120
+
121
+ Leaving this at 4.0 on SSD storage causes PostgreSQL to avoid index scans in favor of
122
+ sequential scans, often resulting in 10-100x slower queries on large tables.
123
+
124
+ ```
125
+ # NVMe SSD storage
126
+ random_page_cost = 1.1
127
+ seq_page_cost = 1.0
128
+ ```
129
+
130
+ ### 1.5 WAL and Checkpoint Configuration
131
+
132
+ | Parameter | Default | Recommended | Why |
133
+ |-----------|---------|-------------|-----|
134
+ | wal_buffers | 64 KB (auto-tuned) | 16-64 MB | Buffers WAL records before disk flush; 16 MB minimum for write-heavy loads |
135
+ | max_wal_size | 1 GB | 2-4 GB | Reduces checkpoint frequency; more WAL between checkpoints |
136
+ | min_wal_size | 80 MB | 512 MB-1 GB | Keeps WAL files around to avoid recreation overhead |
137
+ | checkpoint_completion_target | 0.9 | 0.9 | Spreads checkpoint I/O over 90% of the interval; reduces spikes |
138
+ | checkpoint_timeout | 5 min | 10-15 min | Less frequent checkpoints; reduces write amplification |
139
+
140
+ ```
141
+ # Write-heavy OLTP workload
142
+ wal_buffers = '64MB'
143
+ max_wal_size = '4GB'
144
+ min_wal_size = '1GB'
145
+ checkpoint_completion_target = 0.9
146
+ checkpoint_timeout = '15min'
147
+ ```
148
+
149
+ ### 1.6 Parallelism Settings
150
+
151
+ | Parameter | Default | Recommended | Effect |
152
+ |-----------|---------|-------------|--------|
153
+ | max_parallel_workers_per_gather | 2 | 2-4 | Workers per parallel query node |
154
+ | max_parallel_workers | 8 | CPU cores / 2 | Total parallel workers system-wide |
155
+ | max_worker_processes | 8 | CPU cores | Total background workers |
156
+ | parallel_tuple_cost | 0.1 | 0.01-0.1 | Lower = planner uses parallelism more |
157
+ | min_parallel_table_scan_size | 8 MB | 8 MB | Minimum table size for parallel seq scan |
158
+
159
+ Parallelism is most effective for OLAP queries scanning large tables. For OLTP with many
160
+ short queries, keep defaults or reduce parallel workers to avoid contention.
161
+
162
+ ---
163
+
164
+ ## 2. Index Strategies
165
+
166
+ Indexes are the single most impactful tool for query performance. PostgreSQL supports
167
+ seven index types, each optimized for different access patterns.
168
+
169
+ ### 2.1 B-tree Indexes (Default)
170
+
171
+ **Best for:** Equality (=), range (<, >, BETWEEN), sorting (ORDER BY), and uniqueness.
172
+
173
+ B-tree is the default and handles ~90% of indexing needs. Supports the operators
174
+ `<`, `<=`, `=`, `>=`, `>`, `BETWEEN`, `IN`, `IS NULL`, and `IS NOT NULL`.
175
+
176
+ ```sql
177
+ -- Standard B-tree index
178
+ CREATE INDEX idx_orders_created_at ON orders (created_at);
179
+
180
+ -- Multi-column B-tree (leftmost prefix rule applies)
181
+ CREATE INDEX idx_orders_user_status ON orders (user_id, status);
182
+ -- This index serves: WHERE user_id = X
183
+ -- WHERE user_id = X AND status = Y
184
+ -- But NOT: WHERE status = Y (alone)
185
+ ```
186
+
187
+ **Size impact:** A B-tree index on a 100M row table with an integer column is typically
188
+ 200-400 MB. On a UUID column, expect 2-4 GB.
189
+
190
+ ### 2.2 GIN Indexes (Generalized Inverted Index)
191
+
192
+ **Best for:** JSONB containment (@>), array overlap (&&), full-text search (@@),
193
+ trigram similarity (%).
194
+
195
+ GIN creates an entry for each element within a composite value, making lookups
196
+ extremely fast for "contains" queries. Write overhead is 3-10x higher than B-tree
197
+ because each INSERT may generate multiple index entries.
198
+
199
+ ```sql
200
+ -- JSONB containment queries
201
+ CREATE INDEX idx_events_data ON events USING gin (data);
202
+ -- Enables: WHERE data @> '{"type": "purchase"}'
203
+
204
+ -- Full-text search
205
+ CREATE INDEX idx_articles_search ON articles USING gin (to_tsvector('english', body));
206
+ -- Enables: WHERE to_tsvector('english', body) @@ to_tsquery('postgresql & performance')
207
+
208
+ -- Trigram pattern matching (requires pg_trgm extension)
209
+ CREATE EXTENSION IF NOT EXISTS pg_trgm;
210
+ CREATE INDEX idx_users_name_trgm ON users USING gin (name gin_trgm_ops);
211
+ -- Enables: WHERE name LIKE '%john%' (leading wildcard!)
212
+ -- Enables: WHERE name ILIKE '%john%'
213
+ ```
214
+
215
+ **Performance note:** GIN indexes are 3-5x slower to build than B-tree but provide
216
+ sub-millisecond lookups for containment queries that would otherwise require full
217
+ table scans.
218
+
219
+ ### 2.3 GiST Indexes (Generalized Search Tree)
220
+
221
+ **Best for:** Geometric data, range types, nearest-neighbor searches, full-text search
222
+ (alternative to GIN with faster updates but slower reads).
223
+
224
+ ```sql
225
+ -- Geometric proximity queries
226
+ CREATE INDEX idx_locations_point ON locations USING gist (coordinates);
227
+ -- Enables: WHERE coordinates <-> point(40.7128, -74.0060) < 0.01
228
+
229
+ -- Range overlap queries
230
+ CREATE INDEX idx_reservations_period ON reservations USING gist (
231
+ tsrange(check_in, check_out)
232
+ );
233
+ -- Enables: WHERE tsrange(check_in, check_out) && tsrange('2025-01-01', '2025-01-07')
234
+ ```
235
+
236
+ **GiST vs GIN for full-text search:** GIN is 2-3x faster for reads but 3x slower for
237
+ updates. Use GIN for read-heavy workloads; GiST for write-heavy ones.
238
+
239
+ ### 2.4 BRIN Indexes (Block Range Indexes)
240
+
241
+ **Best for:** Very large tables (100M+ rows) where data is physically ordered by the
242
+ indexed column. Common for time-series, append-only logs, and IoT data.
243
+
244
+ BRIN stores min/max summaries per block range (default 128 pages = 1 MB) instead of
245
+ indexing every row. This makes BRIN indexes 100-1000x smaller than equivalent B-tree
246
+ indexes.
247
+
248
+ ```sql
249
+ -- Time-series data (rows inserted in chronological order)
250
+ CREATE INDEX idx_sensor_readings_ts ON sensor_readings USING brin (recorded_at);
251
+ -- Size: ~100 KB for a 10 GB table (vs ~200 MB for B-tree)
252
+ ```
253
+
254
+ | Metric | B-tree (10 GB table) | BRIN (10 GB table) |
255
+ |--------|---------------------|-------------------|
256
+ | Index size | ~200 MB | ~100 KB |
257
+ | Build time | ~60 seconds | ~2 seconds |
258
+ | Point query speed | <1 ms | 5-50 ms |
259
+ | Range scan speed | <10 ms | 10-100 ms |
260
+
261
+ **Caveat:** BRIN is only effective when physical row order correlates with the indexed
262
+ column. Run `SELECT correlation FROM pg_stats WHERE tablename = 'your_table'` -- a
263
+ correlation near 1.0 or -1.0 indicates BRIN suitability.
264
+
265
+ ### 2.5 Partial Indexes
266
+
267
+ **What:** Indexes that cover only a subset of rows, defined by a WHERE clause. Smaller,
268
+ faster to maintain, and more cache-friendly than full indexes.
269
+
270
+ ```sql
271
+ -- Only index active users (5% of 10M rows = 500K entries)
272
+ CREATE INDEX idx_users_active_email ON users (email)
273
+ WHERE is_active = true;
274
+ -- Size: ~20 MB instead of ~400 MB for a full index
275
+
276
+ -- Only index unprocessed orders
277
+ CREATE INDEX idx_orders_pending ON orders (created_at)
278
+ WHERE status = 'pending';
279
+ -- If 2% of orders are pending, this index is 50x smaller than a full index
280
+ ```
281
+
282
+ **Query must match the partial index predicate** for the planner to use it:
283
+
284
+ ```sql
285
+ -- Uses the partial index:
286
+ SELECT * FROM users WHERE email = 'x@y.com' AND is_active = true;
287
+
288
+ -- Does NOT use the partial index:
289
+ SELECT * FROM users WHERE email = 'x@y.com';
290
+ ```
291
+
292
+ ### 2.6 Covering Indexes (INCLUDE)
293
+
294
+ **What:** B-tree indexes that store additional columns in the leaf pages, enabling
295
+ index-only scans without touching the heap (table data).
296
+
297
+ ```sql
298
+ -- Covering index: the query never needs to read the table
299
+ CREATE INDEX idx_orders_user_covering ON orders (user_id)
300
+ INCLUDE (total_amount, status);
301
+
302
+ -- This query becomes an index-only scan:
303
+ SELECT total_amount, status FROM orders WHERE user_id = 42;
304
+ ```
305
+
306
+ Index-only scans avoid heap fetches entirely, which can improve performance by 2-10x
307
+ for queries that only need the included columns. The visibility map must be up to date
308
+ (run VACUUM) for index-only scans to be effective.
309
+
310
+ ### 2.7 Expression Indexes
311
+
312
+ **What:** Indexes on the result of an expression or function, not just raw column values.
313
+
314
+ ```sql
315
+ -- Case-insensitive email lookup
316
+ CREATE INDEX idx_users_email_lower ON users (lower(email));
317
+ -- Enables: WHERE lower(email) = 'user@example.com'
318
+
319
+ -- Extract year from timestamp
320
+ CREATE INDEX idx_orders_year ON orders (extract(year FROM created_at));
321
+ -- Enables: WHERE extract(year FROM created_at) = 2025
322
+
323
+ -- JSONB field extraction
324
+ CREATE INDEX idx_events_type ON events ((data->>'type'));
325
+ -- Enables: WHERE data->>'type' = 'click'
326
+ ```
327
+
328
+ **The query must use the exact same expression** as the index definition for the planner
329
+ to recognize it.
330
+
331
+ ---
332
+
333
+ ## 3. Query Optimization with EXPLAIN
334
+
335
+ EXPLAIN is the most important diagnostic tool in PostgreSQL. Always use it with ANALYZE
336
+ and BUFFERS to get actual execution statistics.
337
+
338
+ ### 3.1 EXPLAIN Variants
339
+
340
+ ```sql
341
+ -- Basic: shows the planner's estimated plan (no execution)
342
+ EXPLAIN SELECT ...;
343
+
344
+ -- With execution: runs the query and shows actual times
345
+ EXPLAIN ANALYZE SELECT ...;
346
+
347
+ -- With buffer statistics: shows cache hits vs disk reads
348
+ EXPLAIN (ANALYZE, BUFFERS) SELECT ...;
349
+
350
+ -- Machine-readable format for tooling
351
+ EXPLAIN (ANALYZE, BUFFERS, FORMAT YAML) SELECT ...;
352
+ EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT ...;
353
+
354
+ -- PostgreSQL 18+: ANALYZE automatically includes BUFFERS
355
+ EXPLAIN ANALYZE SELECT ...; -- includes buffer stats in PG 18+
356
+ ```
357
+
358
+ ### 3.2 Reading EXPLAIN Output
359
+
360
+ Key fields to examine:
361
+
362
+ | Field | Meaning | Warning Sign |
363
+ |-------|---------|-------------|
364
+ | `Seq Scan` | Full table scan | On tables > 10K rows with a WHERE clause |
365
+ | `actual time=X..Y` | Startup..total time in ms | Total > 100ms for OLTP |
366
+ | `rows=N` | Actual rows returned by node | Large mismatch with `rows` estimate |
367
+ | `Buffers: shared hit=X` | Pages read from shared_buffers | Good: high hit ratio |
368
+ | `Buffers: shared read=Y` | Pages read from disk | Bad if Y >> X |
369
+ | `Buffers: temp read/written` | Spilled to disk temp files | work_mem too low |
370
+ | `Sort Method: external merge` | Sort spilled to disk | Increase work_mem |
371
+ | `Hash Batch` | Hash join spilled to disk | Increase work_mem |
372
+
373
+ ### 3.3 Planner Estimate Mismatches
374
+
375
+ When the planner's estimated rows differ from actual rows by 10x or more, the chosen
376
+ plan is likely suboptimal. Common causes:
377
+
378
+ 1. **Stale statistics** -- Run `ANALYZE tablename` to refresh.
379
+ 2. **Correlated columns** -- The planner assumes column independence. Use extended
380
+ statistics: `CREATE STATISTICS stat_name (dependencies) ON col1, col2 FROM table;`
381
+ 3. **Skewed data** -- Default histogram has 100 buckets. Increase with
382
+ `ALTER TABLE t ALTER COLUMN c SET STATISTICS 1000;`
383
+
384
+ ### 3.4 Common Plan Node Improvements
385
+
386
+ | Slow Node | Likely Fix | Expected Improvement |
387
+ |-----------|-----------|---------------------|
388
+ | Seq Scan on large table | Add appropriate index | 10-1000x |
389
+ | Nested Loop with inner Seq Scan | Add index on join column | 100-10000x |
390
+ | Sort (external merge Disk) | Increase work_mem | 2-5x |
391
+ | Hash Join (multiple batches) | Increase work_mem | 2-5x |
392
+ | Bitmap Heap Scan (lossy) | Increase work_mem | 1.5-3x |
393
+ | Index Scan with Filter (many rows filtered) | Use partial or covering index | 2-10x |
394
+
395
+ ---
396
+
397
+ ## 4. Connection Pooling
398
+
399
+ PostgreSQL uses a process-per-connection model. Each connection forks a backend process
400
+ consuming ~10 MB of memory minimum. Beyond 200 active connections, performance degrades
401
+ sharply due to OS context switching, lock contention, and cache thrashing.
402
+
403
+ ### 4.1 The Connection Problem
404
+
405
+ **Optimal active connections:** `(CPU cores * 2) + effective_spindle_count`
406
+
407
+ For a 16-core server with SSDs, the optimal number is approximately 33-40 active
408
+ connections. Having 500 connections with 40 active at any time means 460 idle connections
409
+ each consuming memory and causing context-switching overhead.
410
+
411
+ **Measured impact of idle connections:**
412
+ - 50 idle connections: ~1-2% throughput loss
413
+ - 200 idle connections: ~10-15% throughput loss
414
+ - 500 idle connections: ~25-35% throughput loss
415
+ - 1000+ idle connections: throughput can drop by 50%+ due to process table pressure
416
+
417
+ ### 4.2 PgBouncer
418
+
419
+ The most widely deployed PostgreSQL connection pooler. Single-threaded, lightweight
420
+ (~2 KB per connection), and battle-tested.
421
+
422
+ **Pooling modes:**
423
+
424
+ | Mode | Description | Use Case |
425
+ |------|-------------|----------|
426
+ | Session | Client holds a server connection for its entire session | Legacy apps needing session state |
427
+ | Transaction | Client gets a server connection only during a transaction | Most web applications (recommended) |
428
+ | Statement | Client gets a connection per statement | Simple read-only queries only |
429
+
430
+ **Benchmark results (pgbench, 16-core server):**
431
+
432
+ | Metric | Direct PG (200 conn) | PgBouncer Transaction (200 conn, 40 pool) |
433
+ |--------|---------------------|------------------------------------------|
434
+ | TPS | ~8,000 | ~50,000 |
435
+ | Avg latency | 25 ms | 4 ms |
436
+ | p99 latency | 180 ms | 15 ms |
437
+ | Memory usage | 2 GB (backends) | 400 MB |
438
+
439
+ ```ini
440
+ # pgbouncer.ini
441
+ [databases]
442
+ mydb = host=127.0.0.1 port=5432 dbname=mydb
443
+
444
+ [pgbouncer]
445
+ listen_port = 6432
446
+ listen_addr = 0.0.0.0
447
+ auth_type = md5
448
+ auth_file = /etc/pgbouncer/userlist.txt
449
+ pool_mode = transaction
450
+ max_client_conn = 1000
451
+ default_pool_size = 40
452
+ min_pool_size = 10
453
+ reserve_pool_size = 5
454
+ reserve_pool_timeout = 3
455
+ server_idle_timeout = 300
456
+ ```
457
+
458
+ **Limitation:** Single-threaded. At very high concurrency (>5000 connections), the single
459
+ event loop becomes a bottleneck. PgBouncer peaks at ~44,000 TPS and degrades to
460
+ 25,000-30,000 TPS beyond 75 concurrent active connections in benchmarks.
461
+
462
+ ### 4.3 PgCat
463
+
464
+ Multi-threaded Rust-based pooler developed by PostgresML/Supabase. Scales better under
465
+ high concurrency.
466
+
467
+ **Benchmark comparison with PgBouncer (1000 concurrent clients):**
468
+
469
+ | Metric | PgBouncer | PgCat |
470
+ |--------|-----------|-------|
471
+ | Peak TPS | 44,096 | 59,000 |
472
+ | Latency at 100 clients | Lower by ~50us | Slightly higher |
473
+ | Latency at 500 clients | Higher | Lower |
474
+ | CPU usage | Single core maxed | Distributed across cores |
475
+ | Features | Mature, stable | Query routing, load balancing, sharding |
476
+
477
+ **When to choose PgCat over PgBouncer:**
478
+ - More than 5000 client connections
479
+ - Need for query routing across read replicas
480
+ - Need for built-in load balancing
481
+ - Multi-tenant architectures requiring query-level routing
482
+
483
+ ### 4.4 Application-Level Pooling
484
+
485
+ Most application frameworks provide connection pools that sit between the application
486
+ and PgBouncer/PostgreSQL:
487
+
488
+ - **HikariCP (Java):** Set `maximumPoolSize` to CPU cores * 2. Default idle timeout
489
+ of 600 seconds works well.
490
+ - **SQLAlchemy (Python):** `pool_size=20, max_overflow=10, pool_pre_ping=True`
491
+ - **node-postgres (Node.js):** `max: 20, idleTimeoutMillis: 30000`
492
+
493
+ Layer pooling: Application pool (20-50) -> PgBouncer (40-100 server connections) ->
494
+ PostgreSQL (max_connections = 150).
495
+
496
+ ---
497
+
498
+ ## 5. VACUUM and Autovacuum Tuning
499
+
500
+ PostgreSQL's MVCC implementation creates dead tuples on every UPDATE and DELETE. Without
501
+ VACUUM, tables and indexes bloat, causing sequential scans to read increasingly more
502
+ dead data.
503
+
504
+ ### 5.1 How MVCC Creates Bloat
505
+
506
+ 1. An UPDATE creates a new tuple version; the old one is marked dead.
507
+ 2. A DELETE marks the existing tuple as dead.
508
+ 3. Dead tuples remain until VACUUM removes them.
509
+ 4. Dead tuples are only removable when no active transaction can see them.
510
+
511
+ **Impact of bloat:** A table with 50% dead tuples requires 2x the I/O for sequential
512
+ scans, 2x the buffer cache, and 2x the backup storage.
513
+
514
+ ### 5.2 Autovacuum Default Behavior
515
+
516
+ Autovacuum triggers when:
517
+ `dead_tuples > autovacuum_vacuum_threshold + (autovacuum_vacuum_scale_factor * table_rows)`
518
+
519
+ Default: `50 + (0.20 * table_rows)` -- VACUUM starts when 20% of rows are dead.
520
+
521
+ **Problem for large tables:** A 100M row table won't be vacuumed until 20,000,050 dead
522
+ tuples accumulate. This is far too late -- the table will be severely bloated.
523
+
524
+ ### 5.3 Recommended Autovacuum Settings
525
+
526
+ **Global settings (postgresql.conf):**
527
+
528
+ ```
529
+ # Reduce scale factor so VACUUM triggers sooner
530
+ autovacuum_vacuum_scale_factor = 0.05 # 5% instead of 20%
531
+ autovacuum_analyze_scale_factor = 0.02 # 2% instead of 10%
532
+
533
+ # Make VACUUM faster by increasing I/O budget
534
+ autovacuum_vacuum_cost_limit = 1000 # default: 200
535
+ autovacuum_vacuum_cost_delay = 2ms # default: 2ms (PG 12+)
536
+
537
+ # Allow more concurrent autovacuum workers
538
+ autovacuum_max_workers = 5 # default: 3
539
+
540
+ # Run more frequently
541
+ autovacuum_naptime = 15s # default: 1min
542
+ ```
543
+
544
+ **Per-table overrides for high-churn tables:**
545
+
546
+ ```sql
547
+ -- Orders table: 50M rows, 100K updates/hour
548
+ ALTER TABLE orders SET (
549
+ autovacuum_vacuum_scale_factor = 0.01, -- 1% = 500K dead tuples
550
+ autovacuum_vacuum_threshold = 1000,
551
+ autovacuum_analyze_scale_factor = 0.005,
552
+ autovacuum_vacuum_cost_limit = 2000
553
+ );
554
+
555
+ -- Sessions table: 10M rows, constant insert/delete
556
+ ALTER TABLE sessions SET (
557
+ autovacuum_vacuum_scale_factor = 0.02,
558
+ autovacuum_vacuum_threshold = 500,
559
+ autovacuum_vacuum_cost_limit = 3000
560
+ );
561
+ ```
562
+
563
+ ### 5.4 Monitoring VACUUM Health
564
+
565
+ ```sql
566
+ -- Check dead tuple counts and last vacuum time per table
567
+ SELECT
568
+ schemaname, relname,
569
+ n_live_tup,
570
+ n_dead_tup,
571
+ round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 1) AS dead_pct,
572
+ last_vacuum,
573
+ last_autovacuum,
574
+ last_analyze,
575
+ last_autoanalyze
576
+ FROM pg_stat_user_tables
577
+ WHERE n_dead_tup > 10000
578
+ ORDER BY n_dead_tup DESC;
579
+
580
+ -- Check for long-running transactions blocking VACUUM
581
+ SELECT pid, age(backend_xid) AS xid_age,
582
+ now() - xact_start AS duration,
583
+ query
584
+ FROM pg_stat_activity
585
+ WHERE backend_xid IS NOT NULL
586
+ ORDER BY age(backend_xid) DESC
587
+ LIMIT 5;
588
+ ```
589
+
590
+ ### 5.5 The Long-Running Transaction Trap
591
+
592
+ Autovacuum cannot reclaim dead tuples that are still visible to any open transaction.
593
+ A single forgotten `BEGIN` without a `COMMIT` can hold back VACUUM across the entire
594
+ database, causing unbounded bloat.
595
+
596
+ **Prevention:**
597
+ - Set `idle_in_transaction_session_timeout = '5min'` to kill abandoned transactions
598
+ - Set `statement_timeout = '30s'` for web applications
599
+ - Monitor `pg_stat_activity` for long-running `idle in transaction` sessions
600
+
601
+ ### 5.6 Visibility Map and Index-Only Scans
602
+
603
+ VACUUM maintains a visibility map that tracks which heap pages contain only tuples
604
+ visible to all transactions. This enables:
605
+
606
+ 1. **Index-only scans:** If a page is marked "all-visible," the executor skips the heap
607
+ fetch, reading only from the index. This can improve query performance by 2-10x.
608
+ 2. **Faster future VACUUMs:** Pages already marked all-visible are skipped, making
609
+ subsequent VACUUM runs much faster on stable data.
610
+
611
+ ---
612
+
613
+ ## 6. Table Partitioning Strategies
614
+
615
+ Partitioning splits a large logical table into smaller physical tables. PostgreSQL 10+
616
+ supports declarative partitioning with range, list, and hash strategies.
617
+
618
+ ### 6.1 When to Partition
619
+
620
+ Partition when:
621
+ - Table exceeds 50-100 GB or 100M+ rows
622
+ - Queries naturally filter on the partition key (date ranges, tenant IDs)
623
+ - Maintenance operations (VACUUM, REINDEX) on the full table are too slow
624
+ - Data lifecycle requires dropping old data (detach partition vs DELETE)
625
+
626
+ Do NOT partition when:
627
+ - Table is under 10 GB and queries are well-indexed
628
+ - Queries do not include the partition key in WHERE clauses
629
+ - You would create more than 1000 partitions (planner overhead)
630
+ - Individual partitions would have fewer than 10,000 rows
631
+
632
+ ### 6.2 Range Partitioning (Time-Series)
633
+
634
+ The most common strategy. Partitions by continuous intervals, typically dates.
635
+
636
+ ```sql
637
+ CREATE TABLE events (
638
+ id bigint GENERATED ALWAYS AS IDENTITY,
639
+ event_type text NOT NULL,
640
+ payload jsonb,
641
+ created_at timestamptz NOT NULL DEFAULT now()
642
+ ) PARTITION BY RANGE (created_at);
643
+
644
+ -- Monthly partitions
645
+ CREATE TABLE events_2025_01 PARTITION OF events
646
+ FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
647
+ CREATE TABLE events_2025_02 PARTITION OF events
648
+ FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');
649
+ -- ... additional months
650
+
651
+ -- Indexes are defined on the parent; PostgreSQL creates them on each partition
652
+ CREATE INDEX idx_events_type ON events (event_type);
653
+ CREATE INDEX idx_events_created ON events (created_at);
654
+ ```
655
+
656
+ **Performance benefit:** A query with `WHERE created_at BETWEEN '2025-01-15' AND
657
+ '2025-01-20'` only scans the January partition, skipping all others (partition pruning).
658
+ On a table with 24 monthly partitions, this means scanning ~4% of the data.
659
+
660
+ ### 6.3 List Partitioning (Categorical)
661
+
662
+ Partitions by discrete values. Common for multi-tenant or status-based partitioning.
663
+
664
+ ```sql
665
+ CREATE TABLE orders (
666
+ id bigint GENERATED ALWAYS AS IDENTITY,
667
+ tenant_id int NOT NULL,
668
+ total numeric(12,2),
669
+ created_at timestamptz NOT NULL
670
+ ) PARTITION BY LIST (tenant_id);
671
+
672
+ CREATE TABLE orders_tenant_1 PARTITION OF orders FOR VALUES IN (1);
673
+ CREATE TABLE orders_tenant_2 PARTITION OF orders FOR VALUES IN (2);
674
+ CREATE TABLE orders_tenant_3 PARTITION OF orders FOR VALUES IN (3);
675
+ -- Default partition for new tenants
676
+ CREATE TABLE orders_default PARTITION OF orders DEFAULT;
677
+ ```
678
+
679
+ ### 6.4 Hash Partitioning (Uniform Distribution)
680
+
681
+ Distributes rows evenly across a fixed number of partitions using a hash function.
682
+ Best for uniform access patterns without natural range boundaries.
683
+
684
+ ```sql
685
+ CREATE TABLE user_activities (
686
+ id bigint GENERATED ALWAYS AS IDENTITY,
687
+ user_id bigint NOT NULL,
688
+ action text,
689
+ created_at timestamptz NOT NULL
690
+ ) PARTITION BY HASH (user_id);
691
+
692
+ CREATE TABLE user_activities_0 PARTITION OF user_activities
693
+ FOR VALUES WITH (MODULUS 8, REMAINDER 0);
694
+ CREATE TABLE user_activities_1 PARTITION OF user_activities
695
+ FOR VALUES WITH (MODULUS 8, REMAINDER 1);
696
+ -- ... partitions 2-7
697
+ ```
698
+
699
+ ### 6.5 Partition Maintenance
700
+
701
+ ```sql
702
+ -- Drop old data by detaching and dropping the partition (instant vs DELETE)
703
+ ALTER TABLE events DETACH PARTITION events_2023_01;
704
+ DROP TABLE events_2023_01; -- instant; no VACUUM needed
705
+
706
+ -- Add future partitions (automate with pg_partman or cron)
707
+ CREATE TABLE events_2026_01 PARTITION OF events
708
+ FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
709
+
710
+ -- VACUUM/ANALYZE individual partitions independently
711
+ VACUUM ANALYZE events_2025_01;
712
+ ```
713
+
714
+ **Guidelines:** Keep 50-200 partitions maximum. Each partition should hold at least
715
+ 10,000-100,000 rows. The planner must evaluate all partitions during planning, so
716
+ excessive partitions add planning overhead (~0.5-1 ms per 100 partitions).
717
+
718
+ ---
719
+
720
+ ## 7. Common Bottlenecks
721
+
722
+ ### 7.1 Sequential Scans on Large Tables
723
+
724
+ **Symptom:** `Seq Scan on large_table` in EXPLAIN with `rows=50000000`.
725
+
726
+ **Cause:** Missing index, or planner chooses sequential scan because:
727
+ - `random_page_cost` is too high for your storage (SSD but set to 4.0)
728
+ - `effective_cache_size` is too low (planner assumes cache misses)
729
+ - Table statistics are stale (run ANALYZE)
730
+
731
+ **Fix:** Add an appropriate index. If the index exists but isn't used, check the above
732
+ configuration parameters.
733
+
734
+ ```sql
735
+ -- Detect tables with high sequential scan counts
736
+ SELECT schemaname, relname,
737
+ seq_scan, seq_tup_read,
738
+ idx_scan, idx_tup_fetch,
739
+ CASE WHEN seq_scan > 0
740
+ THEN round(seq_tup_read::numeric / seq_scan)
741
+ ELSE 0 END AS avg_rows_per_seq_scan
742
+ FROM pg_stat_user_tables
743
+ WHERE seq_scan > 100
744
+ AND seq_tup_read > 100000
745
+ ORDER BY seq_tup_read DESC
746
+ LIMIT 20;
747
+ ```
748
+
749
+ ### 7.2 Missing Indexes
750
+
751
+ **Symptom:** Queries with WHERE clauses on non-indexed columns show sequential scans.
752
+
753
+ ```sql
754
+ -- Find tables that might benefit from indexes
755
+ -- High seq_scan count + low or zero idx_scan = likely missing index
756
+ SELECT relname, seq_scan, idx_scan,
757
+ seq_tup_read, idx_tup_fetch,
758
+ n_live_tup
759
+ FROM pg_stat_user_tables
760
+ WHERE seq_scan > idx_scan
761
+ AND n_live_tup > 10000
762
+ ORDER BY seq_tup_read DESC;
763
+ ```
764
+
765
+ ### 7.3 Bloated Tables and Indexes
766
+
767
+ **Symptom:** Table size much larger than expected for row count. Slow sequential scans
768
+ even with appropriate WHERE clauses.
769
+
770
+ ```sql
771
+ -- Estimate table bloat using pgstattuple extension
772
+ CREATE EXTENSION IF NOT EXISTS pgstattuple;
773
+ SELECT * FROM pgstattuple('orders');
774
+ -- Look at dead_tuple_percent; > 20% indicates significant bloat
775
+
776
+ -- Estimate index bloat
777
+ SELECT * FROM pgstatindex('idx_orders_user_id');
778
+ -- Look at avg_leaf_density; < 50% indicates index bloat needing REINDEX
779
+ ```
780
+
781
+ **Fix:**
782
+ - For moderate bloat: Let autovacuum catch up (tune settings per Section 5)
783
+ - For severe bloat: `VACUUM FULL tablename` (takes an exclusive lock) or use
784
+ `pg_repack` for online table compaction without locking
785
+
786
+ ### 7.4 Lock Contention
787
+
788
+ **Symptom:** Queries blocked waiting for locks. Throughput drops. `wait_event_type =
789
+ 'Lock'` in pg_stat_activity.
790
+
791
+ PostgreSQL uses 16 fast-path lock slots per backend. When these are exhausted, the
792
+ system falls back to the shared lock table, and throughput can drop by up to 34%.
793
+
794
+ ```sql
795
+ -- Find blocked queries
796
+ SELECT blocked.pid AS blocked_pid,
797
+ blocked.query AS blocked_query,
798
+ blocking.pid AS blocking_pid,
799
+ blocking.query AS blocking_query,
800
+ now() - blocked.query_start AS blocked_duration
801
+ FROM pg_stat_activity blocked
802
+ JOIN pg_locks bl ON bl.pid = blocked.pid
803
+ JOIN pg_locks kl ON kl.locktype = bl.locktype
804
+ AND kl.database IS NOT DISTINCT FROM bl.database
805
+ AND kl.relation IS NOT DISTINCT FROM bl.relation
806
+ AND kl.page IS NOT DISTINCT FROM bl.page
807
+ AND kl.tuple IS NOT DISTINCT FROM bl.tuple
808
+ AND kl.transactionid IS NOT DISTINCT FROM bl.transactionid
809
+ AND kl.pid != bl.pid
810
+ AND kl.granted
811
+ JOIN pg_stat_activity blocking ON blocking.pid = kl.pid
812
+ WHERE NOT bl.granted;
813
+ ```
814
+
815
+ **Prevention:**
816
+ - Keep transactions short (< 100ms for OLTP)
817
+ - Avoid long-running DDL during peak hours
818
+ - Use `CONCURRENTLY` for index creation: `CREATE INDEX CONCURRENTLY ...`
819
+ - Lock timeout: `SET lock_timeout = '5s';`
820
+
821
+ ### 7.5 Connection Exhaustion
822
+
823
+ **Symptom:** `FATAL: too many connections for role "..."` or `FATAL: sorry, too many
824
+ clients already`.
825
+
826
+ Each PostgreSQL backend process consumes ~10 MB minimum. 500 connections = 5 GB of
827
+ memory just for backend processes, before any query execution memory.
828
+
829
+ **Fix:** Deploy PgBouncer in transaction pooling mode (see Section 4). Set
830
+ `max_connections` to a reasonable ceiling (100-200 for most workloads) and let the
831
+ pooler handle thousands of application connections.
832
+
833
+ ---
834
+
835
+ ## 8. Anti-Patterns
836
+
837
+ ### 8.1 Over-Indexing
838
+
839
+ **Problem:** Every index on a table must be updated on each INSERT, UPDATE, and DELETE.
840
+ Five indexes on a table means 5 additional write operations per row modification.
841
+
842
+ **Measured impact:**
843
+
844
+ | Indexes on table | INSERT throughput (relative) | Storage overhead |
845
+ |-----------------|---------------------------|-----------------|
846
+ | 0 | 1.0x (baseline) | 0% |
847
+ | 1 | 0.85x | +15-25% |
848
+ | 3 | 0.65x | +40-60% |
849
+ | 5 | 0.50x | +70-100% |
850
+ | 10 | 0.30x | +150-200% |
851
+
852
+ **Detection:**
853
+
854
+ ```sql
855
+ -- Find unused indexes (candidates for removal)
856
+ SELECT schemaname, relname, indexrelname,
857
+ idx_scan, idx_tup_read, idx_tup_fetch,
858
+ pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
859
+ FROM pg_stat_user_indexes
860
+ WHERE idx_scan = 0
861
+ AND schemaname = 'public'
862
+ ORDER BY pg_relation_size(indexrelid) DESC;
863
+
864
+ -- Find duplicate/redundant indexes
865
+ SELECT a.indexrelid::regclass AS index_a,
866
+ b.indexrelid::regclass AS index_b,
867
+ pg_size_pretty(pg_relation_size(a.indexrelid)) AS size_a,
868
+ pg_size_pretty(pg_relation_size(b.indexrelid)) AS size_b
869
+ FROM pg_index a
870
+ JOIN pg_index b ON a.indrelid = b.indrelid
871
+ AND a.indexrelid != b.indexrelid
872
+ AND a.indkey::text LIKE b.indkey::text || '%'
873
+ WHERE a.indisvalid AND b.indisvalid;
874
+ ```
875
+
876
+ **Rule of thumb:** Audit indexes monthly. Remove any index with 0 scans over 30+ days
877
+ (after confirming it isn't needed for unique constraints or infrequent batch jobs).
878
+
879
+ ### 8.2 SELECT *
880
+
881
+ **Problem:** Fetches all columns, preventing index-only scans and transferring unnecessary
882
+ data over the network.
883
+
884
+ ```sql
885
+ -- Anti-pattern: fetches all 30 columns when only 3 are needed
886
+ SELECT * FROM orders WHERE user_id = 42;
887
+
888
+ -- Correct: enables index-only scan if covering index exists
889
+ SELECT id, total_amount, status FROM orders WHERE user_id = 42;
890
+ ```
891
+
892
+ **Impact:** On a table with 30 columns averaging 200 bytes per row, `SELECT *` transfers
893
+ 6 KB per row. Selecting 3 columns transfers ~60 bytes per row -- a 100x reduction in
894
+ network I/O for large result sets.
895
+
896
+ ### 8.3 LIKE '%pattern%' Without Trigram Index
897
+
898
+ **Problem:** Leading wildcard patterns (`LIKE '%pattern%'`) cannot use standard B-tree
899
+ indexes, forcing a sequential scan on every row.
900
+
901
+ ```sql
902
+ -- Anti-pattern: always a Seq Scan without pg_trgm
903
+ SELECT * FROM products WHERE name LIKE '%wireless%';
904
+ -- Seq Scan on products: 12,000 ms on 5M rows
905
+
906
+ -- Solution: GIN trigram index
907
+ CREATE EXTENSION IF NOT EXISTS pg_trgm;
908
+ CREATE INDEX idx_products_name_trgm ON products USING gin (name gin_trgm_ops);
909
+ -- Now: Bitmap Index Scan: 3 ms on 5M rows
910
+ ```
911
+
912
+ ### 8.4 NOT IN vs NOT EXISTS
913
+
914
+ **Problem:** `NOT IN` has dangerous NULL semantics and often produces worse query plans.
915
+
916
+ ```sql
917
+ -- Anti-pattern: NOT IN with a subquery
918
+ -- If ANY row in excluded_ids has a NULL value, the entire NOT IN returns
919
+ -- no rows (three-valued logic). Also produces a hashed SubPlan.
920
+ SELECT * FROM orders
921
+ WHERE user_id NOT IN (SELECT user_id FROM blocked_users);
922
+
923
+ -- Correct: NOT EXISTS handles NULLs correctly and often uses Anti Join
924
+ SELECT * FROM orders o
925
+ WHERE NOT EXISTS (
926
+ SELECT 1 FROM blocked_users b WHERE b.user_id = o.user_id
927
+ );
928
+ ```
929
+
930
+ **Performance difference:** On tables with 1M orders and 10K blocked_users, NOT EXISTS
931
+ with an indexed join column runs in ~50ms. NOT IN can take 500ms-5s depending on the
932
+ subquery size because the planner may materialize and hash the entire subquery result.
933
+
934
+ ### 8.5 Missing LIMIT on Exploratory Queries
935
+
936
+ ```sql
937
+ -- Anti-pattern: fetches and transfers all 50M rows to the client
938
+ SELECT * FROM events WHERE event_type = 'page_view';
939
+
940
+ -- Correct: limit to needed rows
941
+ SELECT id, created_at FROM events
942
+ WHERE event_type = 'page_view'
943
+ ORDER BY created_at DESC
944
+ LIMIT 100;
945
+ ```
946
+
947
+ ### 8.6 Implicit Type Casting in WHERE Clauses
948
+
949
+ ```sql
950
+ -- Anti-pattern: index on user_id (integer) is not used because of text comparison
951
+ SELECT * FROM orders WHERE user_id = '42'; -- implicit cast; may skip index
952
+
953
+ -- Correct: match the column type
954
+ SELECT * FROM orders WHERE user_id = 42;
955
+ ```
956
+
957
+ ### 8.7 Using OFFSET for Pagination on Large Tables
958
+
959
+ ```sql
960
+ -- Anti-pattern: OFFSET 1000000 still scans and discards 1M rows
961
+ SELECT * FROM events ORDER BY id LIMIT 20 OFFSET 1000000;
962
+ -- Execution time grows linearly with OFFSET value
963
+
964
+ -- Correct: keyset/cursor pagination
965
+ SELECT * FROM events
966
+ WHERE id > 1000000 -- last seen id
967
+ ORDER BY id
968
+ LIMIT 20;
969
+ -- Constant execution time regardless of page depth
970
+ ```
971
+
972
+ ---
973
+
974
+ ## 9. Monitoring
975
+
976
+ ### 9.1 pg_stat_statements
977
+
978
+ The single most important extension for query performance monitoring. It normalizes
979
+ queries by replacing literal values with `$1`, `$2`, etc., and tracks cumulative
980
+ execution statistics.
981
+
982
+ **Setup:**
983
+
984
+ ```sql
985
+ -- Add to postgresql.conf
986
+ -- shared_preload_libraries = 'pg_stat_statements'
987
+ -- pg_stat_statements.track = all
988
+
989
+ CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
990
+ ```
991
+
992
+ **Top queries by total execution time:**
993
+
994
+ ```sql
995
+ SELECT
996
+ substring(query, 1, 80) AS short_query,
997
+ calls,
998
+ round(total_exec_time::numeric, 2) AS total_ms,
999
+ round(mean_exec_time::numeric, 2) AS avg_ms,
1000
+ round((100.0 * total_exec_time / sum(total_exec_time) OVER ()), 2) AS pct_total,
1001
+ rows
1002
+ FROM pg_stat_statements
1003
+ ORDER BY total_exec_time DESC
1004
+ LIMIT 20;
1005
+ ```
1006
+
1007
+ **Key insight:** A moderately slow query (50ms) called 10,000 times per hour (total:
1008
+ 500 seconds/hour) is a higher priority to optimize than a slow query (5s) called twice
1009
+ per day (total: 10 seconds/day). Always prioritize by `total_exec_time`, not `mean_exec_time`.
1010
+
1011
+ **Queries with the most I/O:**
1012
+
1013
+ ```sql
1014
+ SELECT
1015
+ substring(query, 1, 80) AS short_query,
1016
+ calls,
1017
+ shared_blks_hit,
1018
+ shared_blks_read,
1019
+ round(100.0 * shared_blks_hit /
1020
+ NULLIF(shared_blks_hit + shared_blks_read, 0), 2) AS cache_hit_pct,
1021
+ temp_blks_read + temp_blks_written AS temp_blks
1022
+ FROM pg_stat_statements
1023
+ WHERE shared_blks_read > 1000
1024
+ ORDER BY shared_blks_read DESC
1025
+ LIMIT 20;
1026
+ ```
1027
+
1028
+ **Target:** Cache hit ratio above 99% for OLTP workloads. Below 95% indicates
1029
+ shared_buffers may be too small or the working set has grown beyond available memory.
1030
+
1031
+ ### 9.2 pg_stat_user_tables
1032
+
1033
+ Provides aggregate I/O and maintenance statistics per table.
1034
+
1035
+ ```sql
1036
+ SELECT
1037
+ relname,
1038
+ seq_scan,
1039
+ seq_tup_read,
1040
+ idx_scan,
1041
+ idx_tup_fetch,
1042
+ n_live_tup,
1043
+ n_dead_tup,
1044
+ round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 1) AS dead_pct,
1045
+ last_autovacuum,
1046
+ last_autoanalyze
1047
+ FROM pg_stat_user_tables
1048
+ ORDER BY n_dead_tup DESC
1049
+ LIMIT 20;
1050
+ ```
1051
+
1052
+ **What to look for:**
1053
+ - `seq_scan` >> `idx_scan`: Missing indexes on frequently queried tables
1054
+ - `dead_pct` > 10%: Autovacuum cannot keep up; tune per-table settings
1055
+ - `last_autovacuum` is NULL or very old: Autovacuum may not be running
1056
+
1057
+ ### 9.3 pg_stat_user_indexes
1058
+
1059
+ ```sql
1060
+ -- Index usage statistics: find unused indexes
1061
+ SELECT
1062
+ schemaname || '.' || relname AS table,
1063
+ indexrelname AS index,
1064
+ idx_scan AS scans,
1065
+ pg_size_pretty(pg_relation_size(indexrelid)) AS size
1066
+ FROM pg_stat_user_indexes
1067
+ WHERE idx_scan < 10
1068
+ ORDER BY pg_relation_size(indexrelid) DESC
1069
+ LIMIT 20;
1070
+ ```
1071
+
1072
+ ### 9.4 Real-Time Activity Monitoring
1073
+
1074
+ ```sql
1075
+ -- Active queries right now
1076
+ SELECT pid, now() - query_start AS duration,
1077
+ state, wait_event_type, wait_event,
1078
+ substring(query, 1, 100) AS query
1079
+ FROM pg_stat_activity
1080
+ WHERE state != 'idle'
1081
+ AND pid != pg_backend_pid()
1082
+ ORDER BY duration DESC;
1083
+
1084
+ -- Overall cache hit ratio
1085
+ SELECT
1086
+ sum(blks_hit) AS cache_hits,
1087
+ sum(blks_read) AS disk_reads,
1088
+ round(100.0 * sum(blks_hit) / NULLIF(sum(blks_hit) + sum(blks_read), 0), 2)
1089
+ AS cache_hit_ratio
1090
+ FROM pg_stat_database;
1091
+ ```
1092
+
1093
+ ---
1094
+
1095
+ ## 10. Before/After Query Examples
1096
+
1097
+ ### Example 1: Missing Index on Foreign Key
1098
+
1099
+ **Before (Seq Scan, 2.3 seconds):**
1100
+
1101
+ ```sql
1102
+ EXPLAIN (ANALYZE, BUFFERS)
1103
+ SELECT o.id, o.total, o.created_at
1104
+ FROM orders o
1105
+ WHERE o.user_id = 12345;
1106
+ ```
1107
+
1108
+ ```
1109
+ Seq Scan on orders (cost=0.00..458932.00 rows=42 width=28)
1110
+ (actual time=1847.234..2291.561 rows=38 loops=1)
1111
+ Filter: (user_id = 12345)
1112
+ Rows Removed by Filter: 9999962
1113
+ Buffers: shared hit=12034 read=196498
1114
+ Planning Time: 0.089 ms
1115
+ Execution Time: 2291.612 ms
1116
+ ```
1117
+
1118
+ **After (Index Scan, 0.1 ms):**
1119
+
1120
+ ```sql
1121
+ CREATE INDEX idx_orders_user_id ON orders (user_id);
1122
+
1123
+ EXPLAIN (ANALYZE, BUFFERS)
1124
+ SELECT o.id, o.total, o.created_at
1125
+ FROM orders o
1126
+ WHERE o.user_id = 12345;
1127
+ ```
1128
+
1129
+ ```
1130
+ Index Scan using idx_orders_user_id on orders
1131
+ (cost=0.43..164.50 rows=42 width=28)
1132
+ (actual time=0.031..0.089 rows=38 loops=1)
1133
+ Index Cond: (user_id = 12345)
1134
+ Buffers: shared hit=42
1135
+ Planning Time: 0.104 ms
1136
+ Execution Time: 0.112 ms
1137
+ ```
1138
+
1139
+ **Improvement:** 2291ms to 0.1ms (20,000x faster). Buffer reads dropped from 208,532
1140
+ to 42 (4,960x less I/O).
1141
+
1142
+ ### Example 2: Covering Index Enabling Index-Only Scan
1143
+
1144
+ **Before (Index Scan + Heap Fetch, 45 ms):**
1145
+
1146
+ ```sql
1147
+ EXPLAIN (ANALYZE, BUFFERS)
1148
+ SELECT status, total_amount
1149
+ FROM orders
1150
+ WHERE user_id = 42;
1151
+ ```
1152
+
1153
+ ```
1154
+ Index Scan using idx_orders_user_id on orders
1155
+ (cost=0.43..1250.33 rows=310 width=18)
1156
+ (actual time=0.028..44.912 rows=305 loops=1)
1157
+ Index Cond: (user_id = 42)
1158
+ Buffers: shared hit=285 read=127
1159
+ Planning Time: 0.095 ms
1160
+ Execution Time: 45.001 ms
1161
+ ```
1162
+
1163
+ **After (Index-Only Scan, 0.3 ms):**
1164
+
1165
+ ```sql
1166
+ CREATE INDEX idx_orders_user_covering ON orders (user_id)
1167
+ INCLUDE (status, total_amount);
1168
+ VACUUM orders; -- update visibility map
1169
+
1170
+ EXPLAIN (ANALYZE, BUFFERS)
1171
+ SELECT status, total_amount
1172
+ FROM orders
1173
+ WHERE user_id = 42;
1174
+ ```
1175
+
1176
+ ```
1177
+ Index Only Scan using idx_orders_user_covering on orders
1178
+ (cost=0.43..12.80 rows=310 width=18)
1179
+ (actual time=0.024..0.298 rows=305 loops=1)
1180
+ Index Cond: (user_id = 42)
1181
+ Heap Fetches: 0
1182
+ Buffers: shared hit=5
1183
+ Planning Time: 0.088 ms
1184
+ Execution Time: 0.341 ms
1185
+ ```
1186
+
1187
+ **Improvement:** 45ms to 0.3ms (150x faster). Buffer accesses dropped from 412 to 5
1188
+ (82x less I/O). Zero heap fetches because all data comes from the index.
1189
+
1190
+ ### Example 3: Trigram Index for LIKE '%pattern%'
1191
+
1192
+ **Before (Seq Scan, 8.4 seconds):**
1193
+
1194
+ ```sql
1195
+ EXPLAIN (ANALYZE, BUFFERS)
1196
+ SELECT id, name, email
1197
+ FROM customers
1198
+ WHERE name ILIKE '%garcia%';
1199
+ ```
1200
+
1201
+ ```
1202
+ Seq Scan on customers (cost=0.00..385421.00 rows=523 width=68)
1203
+ (actual time=234.112..8412.445 rows=487 loops=1)
1204
+ Filter: (name ~~* '%garcia%')
1205
+ Rows Removed by Filter: 4999513
1206
+ Buffers: shared hit=8432 read=148221
1207
+ Planning Time: 0.067 ms
1208
+ Execution Time: 8412.534 ms
1209
+ ```
1210
+
1211
+ **After (Bitmap Index Scan, 2.8 ms):**
1212
+
1213
+ ```sql
1214
+ CREATE EXTENSION IF NOT EXISTS pg_trgm;
1215
+ CREATE INDEX idx_customers_name_trgm ON customers USING gin (name gin_trgm_ops);
1216
+
1217
+ EXPLAIN (ANALYZE, BUFFERS)
1218
+ SELECT id, name, email
1219
+ FROM customers
1220
+ WHERE name ILIKE '%garcia%';
1221
+ ```
1222
+
1223
+ ```
1224
+ Bitmap Heap Scan on customers (cost=52.08..2104.33 rows=523 width=68)
1225
+ (actual time=1.245..2.801 rows=487 loops=1)
1226
+ Recheck Cond: (name ~~* '%garcia%')
1227
+ Rows Removed by Index Recheck: 12
1228
+ Heap Blocks: exact=478
1229
+ Buffers: shared hit=492
1230
+ -> Bitmap Index Scan on idx_customers_name_trgm
1231
+ (cost=0.00..51.95 rows=523 width=0)
1232
+ (actual time=1.102..1.103 rows=499 loops=1)
1233
+ Buffers: shared hit=14
1234
+ Planning Time: 0.234 ms
1235
+ Execution Time: 2.856 ms
1236
+ ```
1237
+
1238
+ **Improvement:** 8412ms to 2.8ms (3,000x faster). Buffer accesses dropped from 156,653
1239
+ to 492 (318x less I/O).
1240
+
1241
+ ### Example 4: Partition Pruning on Time-Series Table
1242
+
1243
+ **Before (Scan all data, 3.2 seconds):**
1244
+
1245
+ ```sql
1246
+ -- Unpartitioned 500M row table
1247
+ EXPLAIN (ANALYZE, BUFFERS)
1248
+ SELECT count(*), event_type
1249
+ FROM events
1250
+ WHERE created_at BETWEEN '2025-01-15' AND '2025-01-20'
1251
+ GROUP BY event_type;
1252
+ ```
1253
+
1254
+ ```
1255
+ HashAggregate (cost=12458932.00..12458942.00 rows=10 width=40)
1256
+ (actual time=3201.445..3201.501 rows=8 loops=1)
1257
+ Group Key: event_type
1258
+ Buffers: shared hit=45023 read=1245889
1259
+ -> Seq Scan on events (cost=0.00..12358932.00 rows=20000000 width=32)
1260
+ (actual time=0.045..2845.112 rows=2741823 loops=1)
1261
+ Filter: (created_at >= '...' AND created_at <= '...')
1262
+ Rows Removed by Filter: 497258177
1263
+ Buffers: shared hit=45023 read=1245889
1264
+ Planning Time: 0.112 ms
1265
+ Execution Time: 3201.589 ms
1266
+ ```
1267
+
1268
+ **After (Partition pruning, 285 ms):**
1269
+
1270
+ ```sql
1271
+ -- Same query on partitioned table (monthly partitions)
1272
+ EXPLAIN (ANALYZE, BUFFERS)
1273
+ SELECT count(*), event_type
1274
+ FROM events
1275
+ WHERE created_at BETWEEN '2025-01-15' AND '2025-01-20'
1276
+ GROUP BY event_type;
1277
+ ```
1278
+
1279
+ ```
1280
+ HashAggregate (cost=548932.00..548942.00 rows=10 width=40)
1281
+ (actual time=285.112..285.168 rows=8 loops=1)
1282
+ Group Key: event_type
1283
+ Buffers: shared hit=42018 read=52344
1284
+ -> Append (cost=0.43..498932.00 rows=2741823 width=32)
1285
+ (actual time=0.034..198.445 rows=2741823 loops=1)
1286
+ Subplans Removed: 23 <-- 23 of 24 partitions pruned!
1287
+ -> Index Scan using events_2025_01_created_at_idx on events_2025_01
1288
+ (cost=0.43..498932.00 rows=2741823 width=32)
1289
+ (actual time=0.033..198.401 rows=2741823 loops=1)
1290
+ Index Cond: (created_at >= '...' AND created_at <= '...')
1291
+ Buffers: shared hit=42018 read=52344
1292
+ Planning Time: 1.245 ms
1293
+ Execution Time: 285.234 ms
1294
+ ```
1295
+
1296
+ **Improvement:** 3201ms to 285ms (11x faster). The planner pruned 23 of 24 partitions,
1297
+ scanning only January data. Buffer reads dropped from 1,290,912 to 94,362 (13x less I/O).
1298
+
1299
+ ---
1300
+
1301
+ ## 11. Quick Reference Checklist
1302
+
1303
+ ### Initial Setup (Day 1)
1304
+
1305
+ - [ ] Set `shared_buffers` to 25-33% of RAM
1306
+ - [ ] Set `effective_cache_size` to 75% of RAM
1307
+ - [ ] Set `work_mem` using formula: `(RAM - shared_buffers) / (16 * CPU cores)`
1308
+ - [ ] Set `random_page_cost` to 1.1 for NVMe, 1.5 for SSD, 4.0 for HDD
1309
+ - [ ] Set `max_wal_size` to 2-4 GB
1310
+ - [ ] Set `checkpoint_completion_target` to 0.9
1311
+ - [ ] Enable `pg_stat_statements` in shared_preload_libraries
1312
+ - [ ] Deploy PgBouncer in transaction pooling mode
1313
+ - [ ] Set `idle_in_transaction_session_timeout` to 5 minutes
1314
+
1315
+ ### Weekly Audit
1316
+
1317
+ - [ ] Review top 20 queries by total_exec_time in pg_stat_statements
1318
+ - [ ] Check for tables with > 10% dead tuples in pg_stat_user_tables
1319
+ - [ ] Identify unused indexes (idx_scan = 0) for potential removal
1320
+ - [ ] Check cache hit ratio (target > 99% for OLTP)
1321
+ - [ ] Review sequential scan counts on large tables
1322
+
1323
+ ### Monthly Audit
1324
+
1325
+ - [ ] Run REINDEX CONCURRENTLY on indexes with > 30% bloat
1326
+ - [ ] Review and remove confirmed unused indexes
1327
+ - [ ] Check for duplicate/redundant indexes
1328
+ - [ ] Evaluate partitioning for tables that grew past 50 GB
1329
+ - [ ] Reset pg_stat_statements: `SELECT pg_stat_statements_reset();`
1330
+ - [ ] Review and tune per-table autovacuum settings for high-churn tables
1331
+
1332
+ ---
1333
+
1334
+ ## 12. Sources
1335
+
1336
+ - [PostgreSQL Official Documentation: Resource Consumption](https://www.postgresql.org/docs/current/runtime-config-resource.html)
1337
+ - [PostgreSQL Wiki: Tuning Your PostgreSQL Server](https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server)
1338
+ - [PostgreSQL Performance Tuning Best Practices 2025 - Mydbops](https://www.mydbops.com/blog/postgresql-parameter-tuning-best-practices)
1339
+ - [Crunchy Data: Optimize PostgreSQL Server Performance](https://www.crunchydata.com/blog/optimize-postgresql-server-performance)
1340
+ - [EDB: How to Tune PostgreSQL Memory](https://www.enterprisedb.com/postgres-tutorials/how-tune-postgresql-memory)
1341
+ - [OneUpTime: PostgreSQL shared_buffers and work_mem Tuning](https://oneuptime.com/blog/post/2026-01-25-postgresql-shared-buffers-work-mem-tuning/view)
1342
+ - [DBA Dataverse: PostgreSQL Configuration Parameters](https://dbadataverse.com/tech/postgresql/2025/02/postgresql-configuration-parameters-best-practices-for-performance-tuning)
1343
+ - [PostgreSQL Official Documentation: Index Types](https://www.postgresql.org/docs/current/indexes-types.html)
1344
+ - [Mydbops: PostgreSQL Index Best Practices](https://www.mydbops.com/blog/postgresql-indexing-best-practices-guide)
1345
+ - [Percona: A Practical Guide to PostgreSQL Indexes](https://www.percona.com/blog/a-practical-guide-to-postgresql-indexes/)
1346
+ - [Percona: How PostgreSQL Indexes Can Negatively Impact Performance](https://www.percona.com/blog/postgresql-indexes-can-hurt-you-negative-effects-and-the-costs-involved/)
1347
+ - [pganalyze: EXPLAIN ANALYZE BUFFERS and Nested Loops](https://pganalyze.com/blog/5mins-explain-analyze-buffers-nested-loops)
1348
+ - [PostgresAI: EXPLAIN ANALYZE Needs BUFFERS](https://postgres.ai/blog/20220106-explain-analyze-needs-buffers-to-improve-the-postgres-query-optimization-process)
1349
+ - [pgMustard: Using BUFFERS for Query Optimization](https://www.pgmustard.com/blog/using-postgres-buffers-for-query-optimization)
1350
+ - [Neon: PostgreSQL 18 Enhanced EXPLAIN with Automatic Buffers](https://neon.com/postgresql/postgresql-18/enhanced-explain)
1351
+ - [Onidel: PgBouncer vs Pgcat vs Odyssey on VPS in 2025](https://onidel.com/blog/postgresql-proxy-comparison-2025)
1352
+ - [Tembo: Benchmarking PostgreSQL Connection Poolers](https://legacy.tembo.io/blog/postgres-connection-poolers/)
1353
+ - [Percona: PgBouncer for PostgreSQL](https://www.percona.com/blog/pgbouncer-for-postgresql-how-connection-pooling-solves-enterprise-slowdowns/)
1354
+ - [pganalyze: PgCat vs PgBouncer](https://pganalyze.com/blog/5mins-postgres-pgcat-vs-pgbouncer)
1355
+ - [AWS: Understanding Autovacuum in RDS for PostgreSQL](https://aws.amazon.com/blogs/database/understanding-autovacuum-in-amazon-rds-for-postgresql-environments/)
1356
+ - [EDB: Autovacuum Tuning Basics](https://www.enterprisedb.com/blog/autovacuum-tuning-basics)
1357
+ - [Cybertec: Tuning Autovacuum for PostgreSQL](https://www.cybertec-postgresql.com/en/tuning-autovacuum-postgresql/)
1358
+ - [PostgreSQL Official Documentation: Table Partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html)
1359
+ - [EDB: How to Use Table Partitioning to Scale PostgreSQL](https://www.enterprisedb.com/postgres-tutorials/how-use-table-partitioning-scale-postgresql)
1360
+ - [AWS: Diagnose and Mitigate Lock Manager Contention](https://aws.amazon.com/blogs/database/improve-postgresql-performance-diagnose-and-mitigate-lock-manager-contention/)
1361
+ - [PostgreSQL Wiki: Number of Database Connections](https://wiki.postgresql.org/wiki/Number_Of_Database_Connections)
1362
+ - [Last9: PostgreSQL Performance Tuning](https://last9.io/blog/postgresql-performance/)
1363
+ - [Supabase: pg_stat_statements Documentation](https://supabase.com/docs/guides/database/extensions/pg_stat_statements)
1364
+ - [Tiger Data: Identify Performance Bottlenecks with pg_stat_statements](https://www.tigerdata.com/blog/using-pg-stat-statements-to-optimize-queries)
1365
+ - [PostgresAI: Why Keep Your Index Set Lean](https://postgres.ai/blog/20251110-postgres-marathon-2-013-why-keep-your-index-set-lean)
1366
+ - [QuerySharp: How to Optimize LIKE Queries in PostgreSQL](https://querysharp.com/blog/how-to-optimize-like-queries-postgresql)