@luanpdd/kit-mcp 1.30.1 → 1.31.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 (347) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +168 -168
  3. package/gates/agent-no-recursive-dispatch.md +84 -82
  4. package/kit/COMANDOS.md +138 -138
  5. package/kit/README.md +76 -76
  6. package/kit/agents/advisor-researcher.md +107 -106
  7. package/kit/agents/ai-mutation-tester.md +1 -0
  8. package/kit/agents/assumptions-analyzer.md +108 -107
  9. package/kit/agents/audit-log-implementer.md +314 -313
  10. package/kit/agents/auditor-consistencia-isolamento.md +414 -413
  11. package/kit/agents/b2b-saas-architect.md +157 -156
  12. package/kit/agents/burn-rate-forecaster.md +1 -0
  13. package/kit/agents/cascading-failures-auditor.md +299 -298
  14. package/kit/agents/codebase-mapper.md +769 -768
  15. package/kit/agents/crm-pipeline-implementer.md +257 -256
  16. package/kit/agents/debugger.md +814 -813
  17. package/kit/agents/detector-tenant-quente.md +338 -337
  18. package/kit/agents/evolution-go-integrator.md +201 -200
  19. package/kit/agents/example-reviewer.md +22 -21
  20. package/kit/agents/executor.md +565 -564
  21. package/kit/agents/golden-signals-instrumenter.md +1 -0
  22. package/kit/agents/incident-investigator.md +1 -0
  23. package/kit/agents/integration-checker.md +201 -200
  24. package/kit/agents/invite-flow-implementer.md +190 -189
  25. package/kit/agents/legacy-characterizer.md +369 -368
  26. package/kit/agents/lgpd-compliance-auditor.md +296 -295
  27. package/kit/agents/load-shedding-instrumenter.md +1 -0
  28. package/kit/agents/multi-tenant-isolation-auditor.md +254 -253
  29. package/kit/agents/multi-tenant-rls-writer.md +341 -340
  30. package/kit/agents/nyquist-auditor.md +179 -178
  31. package/kit/agents/observability-coverage-auditor.md +316 -315
  32. package/kit/agents/observability-instrumenter.md +1 -0
  33. package/kit/agents/omm-auditor.md +1 -0
  34. package/kit/agents/org-onboarding-implementer.md +224 -223
  35. package/kit/agents/payload-capture-instrumenter.md +274 -273
  36. package/kit/agents/phase-researcher.md +697 -696
  37. package/kit/agents/plan-checker.md +273 -272
  38. package/kit/agents/planner.md +923 -922
  39. package/kit/agents/postmortem-writer.md +1 -0
  40. package/kit/agents/project-researcher.md +653 -652
  41. package/kit/agents/prr-conductor.md +1 -0
  42. package/kit/agents/refactor-safety-auditor.md +405 -404
  43. package/kit/agents/release-pipeline-auditor.md +1 -0
  44. package/kit/agents/research-synthesizer.md +246 -245
  45. package/kit/agents/roadmapper.md +678 -677
  46. package/kit/agents/schema-checker.md +1 -0
  47. package/kit/agents/seam-finder.md +360 -359
  48. package/kit/agents/shotgun-surgery-detector.md +350 -349
  49. package/kit/agents/slo-engineer.md +1 -0
  50. package/kit/agents/storytelling-analyst.md +1 -0
  51. package/kit/agents/supabase-architect.md +1 -0
  52. package/kit/agents/supabase-auth-bootstrapper.md +1 -0
  53. package/kit/agents/supabase-branching-architect.md +563 -562
  54. package/kit/agents/supabase-cicd-pipeline-implementer.md +778 -777
  55. package/kit/agents/supabase-column-privileges-writer.md +400 -399
  56. package/kit/agents/supabase-edge-fn-tester.md +2 -1
  57. package/kit/agents/supabase-edge-fn-writer.md +2 -1
  58. package/kit/agents/supabase-migration-writer.md +386 -385
  59. package/kit/agents/supabase-rbac-implementer.md +393 -392
  60. package/kit/agents/supabase-realtime-implementer.md +364 -363
  61. package/kit/agents/supabase-rls-hardener.md +522 -521
  62. package/kit/agents/supabase-rls-writer.md +324 -323
  63. package/kit/agents/supabase-roles-implementer.md +356 -355
  64. package/kit/agents/supabase-storage-implementer.md +1 -0
  65. package/kit/agents/super-admin-implementer.md +282 -281
  66. package/kit/agents/toil-auditor.md +1 -0
  67. package/kit/agents/ui-auditor.md +438 -437
  68. package/kit/agents/ui-checker.md +303 -302
  69. package/kit/agents/ui-researcher.md +356 -355
  70. package/kit/agents/user-profiler.md +176 -175
  71. package/kit/agents/validador-evolucao-schema.md +336 -335
  72. package/kit/agents/verifier.md +729 -728
  73. package/kit/commands/adicionar-backlog.md +75 -75
  74. package/kit/commands/adicionar-fase.md +42 -42
  75. package/kit/commands/adicionar-tarefa.md +45 -45
  76. package/kit/commands/adicionar-testes.md +41 -41
  77. package/kit/commands/ajuda.md +21 -21
  78. package/kit/commands/atualizar.md +37 -37
  79. package/kit/commands/auditar-cascading.md +111 -111
  80. package/kit/commands/auditar-marco.md +179 -179
  81. package/kit/commands/auditar-observabilidade-cobertura.md +183 -183
  82. package/kit/commands/auditar-refactor.md +219 -219
  83. package/kit/commands/auditar-release.md +109 -109
  84. package/kit/commands/auditar-uat.md +23 -23
  85. package/kit/commands/autonomo.md +40 -40
  86. package/kit/commands/branch-pr.md +24 -24
  87. package/kit/commands/burn-rate-status.md +408 -408
  88. package/kit/commands/capturar-payloads.md +193 -193
  89. package/kit/commands/caracterizar.md +212 -212
  90. package/kit/commands/concluir-marco.md +247 -247
  91. package/kit/commands/configuracoes.md +36 -36
  92. package/kit/commands/dados-distribuidos.md +188 -188
  93. package/kit/commands/definir-perfil.md +10 -10
  94. package/kit/commands/depurar.md +190 -190
  95. package/kit/commands/detectar-duplicacao.md +197 -197
  96. package/kit/commands/discutir-fase.md +131 -131
  97. package/kit/commands/encontrar-seams.md +136 -136
  98. package/kit/commands/entrar-discord.md +17 -17
  99. package/kit/commands/estatisticas.md +18 -18
  100. package/kit/commands/example-greeting.md +33 -33
  101. package/kit/commands/executar-fase.md +58 -58
  102. package/kit/commands/expresso.md +56 -56
  103. package/kit/commands/fase-ui.md +34 -34
  104. package/kit/commands/fazer.md +57 -57
  105. package/kit/commands/fio.md +125 -125
  106. package/kit/commands/fluxos-trabalho.md +64 -64
  107. package/kit/commands/forense.md +176 -176
  108. package/kit/commands/gerenciador.md +38 -38
  109. package/kit/commands/inserir-fase.md +31 -31
  110. package/kit/commands/legacy.md +263 -263
  111. package/kit/commands/limpeza.md +17 -17
  112. package/kit/commands/listar-hipoteses-fase.md +45 -45
  113. package/kit/commands/listar-workspaces.md +18 -18
  114. package/kit/commands/load-shedding.md +117 -117
  115. package/kit/commands/mapear-codebase.md +70 -70
  116. package/kit/commands/multi-tenant.md +163 -163
  117. package/kit/commands/nota.md +33 -33
  118. package/kit/commands/novo-marco.md +43 -43
  119. package/kit/commands/novo-projeto.md +41 -41
  120. package/kit/commands/novo-workspace.md +43 -43
  121. package/kit/commands/pausar-trabalho.md +37 -37
  122. package/kit/commands/perfil-usuario.md +45 -45
  123. package/kit/commands/pesquisar-fase.md +195 -195
  124. package/kit/commands/planejar-fase.md +67 -67
  125. package/kit/commands/planejar-lacunas.md +33 -33
  126. package/kit/commands/plantar-ideia.md +25 -25
  127. package/kit/commands/progresso.md +24 -24
  128. package/kit/commands/proximo.md +30 -30
  129. package/kit/commands/publicar.md +490 -490
  130. package/kit/commands/rapido.md +35 -35
  131. package/kit/commands/reaplicar-patches.md +124 -124
  132. package/kit/commands/refactor-seguro.md +321 -321
  133. package/kit/commands/relatorio-sessao.md +19 -19
  134. package/kit/commands/remover-fase.md +31 -31
  135. package/kit/commands/remover-workspace.md +26 -26
  136. package/kit/commands/resumo-marco.md +50 -50
  137. package/kit/commands/retomar-trabalho.md +40 -40
  138. package/kit/commands/revisar-backlog.md +60 -60
  139. package/kit/commands/revisar-ui.md +32 -32
  140. package/kit/commands/revisar.md +37 -37
  141. package/kit/commands/saude.md +21 -21
  142. package/kit/commands/setup-notion.md +93 -93
  143. package/kit/commands/storytelling.md +179 -179
  144. package/kit/commands/sync-main.md +68 -68
  145. package/kit/commands/validar-fase.md +35 -35
  146. package/kit/commands/verificar-tarefas.md +44 -44
  147. package/kit/commands/verificar-trabalho.md +64 -64
  148. package/kit/file-manifest.json +82 -81
  149. package/kit/framework/bin/lib/commands.cjs +959 -959
  150. package/kit/framework/bin/lib/config.cjs +442 -442
  151. package/kit/framework/bin/lib/core.cjs +1230 -1230
  152. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  153. package/kit/framework/bin/lib/init.cjs +1442 -1442
  154. package/kit/framework/bin/lib/milestone.cjs +252 -252
  155. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  156. package/kit/framework/bin/lib/phase.cjs +888 -888
  157. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  158. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  159. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  160. package/kit/framework/bin/lib/security.cjs +382 -382
  161. package/kit/framework/bin/lib/state.cjs +1031 -1031
  162. package/kit/framework/bin/lib/template.cjs +222 -222
  163. package/kit/framework/bin/lib/uat.cjs +282 -282
  164. package/kit/framework/bin/lib/verify.cjs +888 -888
  165. package/kit/framework/bin/lib/workstream.cjs +491 -491
  166. package/kit/framework/bin/tools.cjs +918 -918
  167. package/kit/framework/commands/workstreams.md +63 -63
  168. package/kit/framework/references/checkpoints.md +778 -778
  169. package/kit/framework/references/continuation-format.md +249 -249
  170. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  171. package/kit/framework/references/git-integration.md +295 -295
  172. package/kit/framework/references/git-planning-commit.md +38 -38
  173. package/kit/framework/references/model-profile-resolution.md +36 -36
  174. package/kit/framework/references/model-profiles.md +139 -139
  175. package/kit/framework/references/phase-argument-parsing.md +61 -61
  176. package/kit/framework/references/planning-config.md +202 -202
  177. package/kit/framework/references/questioning.md +162 -162
  178. package/kit/framework/references/tdd.md +263 -263
  179. package/kit/framework/references/ui-brand.md +160 -160
  180. package/kit/framework/references/user-profiling.md +657 -657
  181. package/kit/framework/references/verification-patterns.md +612 -612
  182. package/kit/framework/references/workstream-flag.md +58 -58
  183. package/kit/framework/templates/DEBUG.md +164 -164
  184. package/kit/framework/templates/UAT.md +265 -265
  185. package/kit/framework/templates/UI-SPEC.md +100 -100
  186. package/kit/framework/templates/VALIDATION.md +76 -76
  187. package/kit/framework/templates/claude-md.md +122 -122
  188. package/kit/framework/templates/codebase/architecture.md +185 -185
  189. package/kit/framework/templates/codebase/concerns.md +205 -205
  190. package/kit/framework/templates/codebase/conventions.md +204 -204
  191. package/kit/framework/templates/codebase/integrations.md +192 -192
  192. package/kit/framework/templates/codebase/stack.md +158 -158
  193. package/kit/framework/templates/codebase/structure.md +199 -199
  194. package/kit/framework/templates/codebase/testing.md +301 -301
  195. package/kit/framework/templates/config.json +44 -44
  196. package/kit/framework/templates/context.md +352 -352
  197. package/kit/framework/templates/continue-here.md +78 -78
  198. package/kit/framework/templates/copilot-instructions.md +7 -7
  199. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  200. package/kit/framework/templates/dev-preferences.md +20 -20
  201. package/kit/framework/templates/discovery.md +146 -146
  202. package/kit/framework/templates/discussion-log.md +63 -63
  203. package/kit/framework/templates/milestone-archive.md +123 -123
  204. package/kit/framework/templates/milestone.md +115 -115
  205. package/kit/framework/templates/phase-prompt.md +610 -610
  206. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  207. package/kit/framework/templates/project.md +186 -186
  208. package/kit/framework/templates/requirements.md +231 -231
  209. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  210. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  211. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  212. package/kit/framework/templates/research-project/STACK.md +120 -120
  213. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  214. package/kit/framework/templates/research.md +419 -419
  215. package/kit/framework/templates/retrospective.md +54 -54
  216. package/kit/framework/templates/roadmap.md +202 -202
  217. package/kit/framework/templates/state.md +176 -176
  218. package/kit/framework/templates/summary-complex.md +59 -59
  219. package/kit/framework/templates/summary-minimal.md +41 -41
  220. package/kit/framework/templates/summary-standard.md +48 -48
  221. package/kit/framework/templates/summary.md +209 -209
  222. package/kit/framework/templates/user-profile.md +146 -146
  223. package/kit/framework/templates/user-setup.md +256 -256
  224. package/kit/framework/templates/verification-report.md +258 -258
  225. package/kit/framework/workflows/add-phase.md +112 -112
  226. package/kit/framework/workflows/add-tests.md +351 -351
  227. package/kit/framework/workflows/add-todo.md +158 -158
  228. package/kit/framework/workflows/audit-milestone.md +340 -340
  229. package/kit/framework/workflows/audit-uat.md +109 -109
  230. package/kit/framework/workflows/autonomous.md +891 -891
  231. package/kit/framework/workflows/check-todos.md +177 -177
  232. package/kit/framework/workflows/cleanup.md +152 -152
  233. package/kit/framework/workflows/complete-milestone.md +696 -696
  234. package/kit/framework/workflows/diagnose-issues.md +231 -231
  235. package/kit/framework/workflows/discovery-phase.md +289 -289
  236. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  237. package/kit/framework/workflows/discuss-phase.md +784 -784
  238. package/kit/framework/workflows/do.md +104 -104
  239. package/kit/framework/workflows/execute-phase.md +838 -838
  240. package/kit/framework/workflows/execute-plan.md +510 -510
  241. package/kit/framework/workflows/fast.md +102 -102
  242. package/kit/framework/workflows/forensics.md +265 -265
  243. package/kit/framework/workflows/health.md +181 -181
  244. package/kit/framework/workflows/help.md +619 -619
  245. package/kit/framework/workflows/insert-phase.md +130 -130
  246. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  247. package/kit/framework/workflows/list-workspaces.md +56 -56
  248. package/kit/framework/workflows/manager.md +362 -362
  249. package/kit/framework/workflows/map-codebase.md +377 -377
  250. package/kit/framework/workflows/milestone-summary.md +223 -223
  251. package/kit/framework/workflows/new-milestone.md +486 -486
  252. package/kit/framework/workflows/new-project.md +1159 -1159
  253. package/kit/framework/workflows/new-workspace.md +237 -237
  254. package/kit/framework/workflows/next.md +97 -97
  255. package/kit/framework/workflows/node-repair.md +92 -92
  256. package/kit/framework/workflows/note.md +156 -156
  257. package/kit/framework/workflows/pause-work.md +176 -176
  258. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  259. package/kit/framework/workflows/plan-phase.md +765 -765
  260. package/kit/framework/workflows/plant-seed.md +169 -169
  261. package/kit/framework/workflows/pr-branch.md +129 -129
  262. package/kit/framework/workflows/profile-user.md +450 -450
  263. package/kit/framework/workflows/progress.md +507 -507
  264. package/kit/framework/workflows/quick.md +757 -757
  265. package/kit/framework/workflows/remove-phase.md +155 -155
  266. package/kit/framework/workflows/remove-workspace.md +90 -90
  267. package/kit/framework/workflows/research-phase.md +82 -82
  268. package/kit/framework/workflows/resume-project.md +326 -326
  269. package/kit/framework/workflows/review.md +228 -228
  270. package/kit/framework/workflows/session-report.md +146 -146
  271. package/kit/framework/workflows/settings.md +283 -283
  272. package/kit/framework/workflows/ship.md +228 -228
  273. package/kit/framework/workflows/stats.md +60 -60
  274. package/kit/framework/workflows/transition.md +671 -671
  275. package/kit/framework/workflows/ui-phase.md +302 -302
  276. package/kit/framework/workflows/ui-review.md +165 -165
  277. package/kit/framework/workflows/update.md +323 -323
  278. package/kit/framework/workflows/validate-phase.md +174 -174
  279. package/kit/framework/workflows/verify-phase.md +252 -252
  280. package/kit/framework/workflows/verify-work.md +637 -637
  281. package/kit/hooks/check-update.js +118 -118
  282. package/kit/hooks/context-monitor.js +163 -163
  283. package/kit/hooks/kit-attribution-reminder.cjs +30 -36
  284. package/kit/hooks/kit-router.cjs +137 -0
  285. package/kit/hooks/prompt-guard.js +103 -103
  286. package/kit/hooks/statusline.js +125 -125
  287. package/kit/hooks/workflow-guard.js +101 -101
  288. package/kit/settings.json +45 -45
  289. package/kit/skills/ai-prompt-characterization/SKILL.md +335 -335
  290. package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -447
  291. package/kit/skills/audit-log-multi-tenant/SKILL.md +340 -340
  292. package/kit/skills/b2b-saas-architecture/SKILL.md +300 -300
  293. package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -385
  294. package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +343 -343
  295. package/kit/skills/escolha-modelo-consistencia/SKILL.md +494 -494
  296. package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -448
  297. package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -322
  298. package/kit/skills/example-skill/SKILL.md +42 -42
  299. package/kit/skills/legacy-api-only-applications/SKILL.md +358 -358
  300. package/kit/skills/legacy-characterization-tests/SKILL.md +330 -330
  301. package/kit/skills/legacy-effect-analysis/SKILL.md +331 -331
  302. package/kit/skills/legacy-extract-class/SKILL.md +203 -203
  303. package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -252
  304. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -460
  305. package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -286
  306. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -434
  307. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -270
  308. package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -340
  309. package/kit/skills/member-invite-flow/SKILL.md +305 -305
  310. package/kit/skills/member-management-react-shadcn/SKILL.md +328 -328
  311. package/kit/skills/multi-tenant-performance-scaling/SKILL.md +316 -316
  312. package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +342 -342
  313. package/kit/skills/org-onboarding-flow/SKILL.md +257 -257
  314. package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -349
  315. package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -271
  316. package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -552
  317. package/kit/skills/pre-refactor-characterization/SKILL.md +421 -421
  318. package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +338 -338
  319. package/kit/skills/streams-eventos-cdc/SKILL.md +711 -711
  320. package/kit/skills/supabase-branching-workflow/SKILL.md +544 -544
  321. package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -880
  322. package/kit/skills/supabase-column-level-security/SKILL.md +426 -426
  323. package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -807
  324. package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -472
  325. package/kit/skills/supabase-edge-functions/SKILL.md +1 -1
  326. package/kit/skills/supabase-edge-functions-auth/SKILL.md +1 -1
  327. package/kit/skills/supabase-edge-functions-limits/SKILL.md +1 -1
  328. package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +1 -1
  329. package/kit/skills/supabase-edge-functions-testing/SKILL.md +1 -1
  330. package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +1 -1
  331. package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
  332. package/kit/skills/supabase-migrations/SKILL.md +297 -297
  333. package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -1053
  334. package/kit/skills/supabase-postgres-roles/SKILL.md +392 -392
  335. package/kit/skills/supabase-realtime/SKILL.md +460 -460
  336. package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -418
  337. package/kit/skills/supabase-rls-policies/SKILL.md +635 -635
  338. package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
  339. package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
  340. package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
  341. package/package.json +1 -1
  342. package/src/core/kit.js +216 -216
  343. package/src/core/reflect.js +247 -247
  344. package/src/core/reverse-sync.js +372 -372
  345. package/src/core/sync.js +437 -418
  346. package/src/core/watch.js +121 -121
  347. package/src/mcp-server/index.js +794 -715
@@ -1,385 +1,385 @@
1
- ---
2
- name: consistencia-leitura-replica
3
- description: Use ao usar Supabase read replicas via Supavisor (porta 6543) ou ao combinar Realtime broadcast + leitura DB…
4
- ---
5
-
6
- # Consistência Leitura Réplica — Supabase + Supavisor + Realtime
7
-
8
- ## Quando usar
9
-
10
- LLM carrega esta skill ao usar Supabase Pro+ com **read replicas** ou ao combinar Realtime broadcast com leitura subsequente do DB. Trigger phrases:
11
-
12
- - "Supabase read replica", "réplica de leitura"
13
- - "porta 6543 vs 5432", "Supavisor session vs transaction"
14
- - "read-after-write", "leitura após escrita inconsistente"
15
- - "monotonic reads", "leituras não-monotônicas"
16
- - "consistent prefix reads", "prefixo causal violado"
17
- - "replication lag Supabase", "atraso de replicação"
18
- - "broadcast Realtime + SELECT stale"
19
- - "pg_last_wal_replay_lsn", "WAL position detection"
20
-
21
- Esta skill aplica **DDIA Ch 5 "Problems With Replication Lag"** ao stack Supabase. Cross-referenciada por `supabase-realtime` (v1.8) ao bundlear broadcast + leitura, e por `multi-tenant-performance-scaling` (v1.21) ao escalar Postgres em Pro+.
22
-
23
- Termos canônicos (`read-after-write consistency`, `monotonic reads`, `consistent prefix reads`, `replication lag`, `leader-follower replication`) definidos em [`_shared-dados-distribuidos/glossary.md`](../_shared-dados-distribuidos/glossary.md) seções (a) e (b) — esta skill **não duplica**, apenas linka.
24
-
25
- ## Regras absolutas
26
-
27
- **REGRA #1 (read-after-write own data):** Para leituras do **próprio dado do usuário** dentro de janela de **5s após write**, rotear para **porta 5432 (líder)** — não 6543. Sem isso, usuário cria post → GET retorna 404 → percepção de bug. Justify: DDIA p. 156 "always read the user's own profile from the leader".
28
-
29
- **REGRA #2 (sticky session monotonic):** Para usuários ativos lendo de réplicas, escolher réplica via `hash(user_id) mod N` — **não round-robin**. Round-robin viola monotonic reads (DDIA p. 158). Mitigação obrigatória: fallback para líder se réplica down.
30
-
31
- **REGRA #3 (broadcast trust payload):** Após receber Realtime broadcast com `payload.record`, **NÃO** fazer SELECT subsequente. Confiar no payload — server é a fonte canônica do evento. SELECT pode atingir réplica que ainda não replicou (lag típico 50-500ms).
32
-
33
- **REGRA #4 (causal partition):** Writes causalmente relacionados (pergunta + resposta em chat, parent + child em árvore) **DEVEM** ir para a mesma partição lógica. Em Supabase: usar mesmo `org_id` ou `conversation_id` como partition key. DDIA p. 159 "any writes which are causally related to each other are written to the same partition".
34
-
35
- **REGRA #5 (LSN wait com timeout):** Quando usar `pg_last_wal_replay_lsn() >= captured_lsn`, **SEMPRE** com timeout (3-5s). Sem timeout, query trava se réplica falhou. Após timeout, fallback para líder.
36
-
37
- ## Patterns canônicos
38
-
39
- ### Problema 1: Read-after-write inconsistente (DDIA Ch 5, p. 156)
40
-
41
- **Cenário canônico:** usuário cria post via form submit → tela mostra "criado com sucesso" → usuário clica "ver post" → request vai para réplica → réplica ainda não replicou → 404. Da perspectiva do usuário: "perdi meu dado".
42
-
43
- ```
44
- User 1234 ──INSERT─→ Leader (5432) ┐
45
- │ │ replication lag (50-500ms)
46
- └──WAL──────────────→ Follower (6543, replica)
47
-
48
- User 1234 ─SELECT────────────────────────────→ ❌ 404
49
- (vai para replica via Supavisor)
50
- ```
51
-
52
- **Solução A — leitura no líder após write do mesmo usuário:**
53
-
54
- ```typescript
55
- // PT-BR: client mantém timestamp do último write em memória
56
- class SupabaseRouter {
57
- private lastWriteAt: Map<string, number> = new Map() // userId → timestamp ms
58
- private readonly STICKY_WINDOW_MS = 5000 // 5s leitura no líder
59
-
60
- async write(userId: string, table: string, payload: unknown) {
61
- const result = await this.leaderClient.from(table).insert(payload)
62
- this.lastWriteAt.set(userId, Date.now())
63
- return result
64
- }
65
-
66
- async read(userId: string, table: string, filter: object) {
67
- const lastWrite = this.lastWriteAt.get(userId) ?? 0
68
- const elapsedMs = Date.now() - lastWrite
69
-
70
- // PT-BR: dentro da janela 5s, ler do líder (porta 5432)
71
- if (elapsedMs < this.STICKY_WINDOW_MS) {
72
- return this.leaderClient.from(table).select().match(filter)
73
- }
74
- // PT-BR: fora da janela, OK ler de replica via pooler 6543
75
- return this.replicaClient.from(table).select().match(filter)
76
- }
77
- }
78
- ```
79
-
80
- **Trade-off:** dentro da janela, perde benefício do read scaling. DDIA recomenda janela curta (1-5s) — cobre 99% dos casos UX sem sobrecarregar líder.
81
-
82
- ### Problema 2: Leituras não-monotônicas (DDIA Ch 5, p. 158)
83
-
84
- **Cenário canônico:** usuário 2345 abre lista de comentários — primeira leitura vai para réplica 1 (lag 100ms) → vê comentário X. Segundo refresh vai para réplica 2 (lag 800ms) → comentário X **desapareceu**. Da perspectiva do usuário: "dado voltou no tempo".
85
-
86
- ```
87
- User 2345 ─SELECT(1)────→ Replica 1 (lag 100ms) → 1 result ✅
88
- User 2345 ─SELECT(2)────→ Replica 2 (lag 800ms) → 0 results ❌ (parecia ter sumido)
89
- ```
90
-
91
- **Solução B — sticky session por `user_id` em routing:**
92
-
93
- ```typescript
94
- // PT-BR: hash determinístico do user_id → escolhe replica fixa para esse user
95
- import { createHash } from 'node:crypto'
96
-
97
- function pickReplica(userId: string, replicas: ReadonlyArray<SupabaseClient>): SupabaseClient {
98
- const hash = createHash('sha256').update(userId).digest()
99
- const idx = hash.readUInt32BE(0) % replicas.length
100
- return replicas[idx]
101
- }
102
-
103
- // PT-BR: usage com fallback para líder se replica falhar
104
- async function readWithStickyReplica(userId: string, query: () => Promise<Result>) {
105
- const replica = pickReplica(userId, REPLICAS)
106
- try {
107
- return await query.call(replica)
108
- } catch (err) {
109
- if (isReplicaDownError(err)) {
110
- // PT-BR: fallback obrigatório — REGRA #2
111
- return await query.call(LEADER)
112
- }
113
- throw err
114
- }
115
- }
116
- ```
117
-
118
- **Pitfall:** réplica fica down → todos os usuários alocados a ela ficam sem leitura. Mitigação: detectar via health check + reroute para líder até réplica voltar.
119
-
120
- ### Problema 3: Prefixo causal violado (DDIA Ch 5, p. 159)
121
-
122
- **Cenário canônico chat:** Mr Poons pergunta "How far into the future can you see, Mrs Cake?" → write vai para partição A. Mrs Cake responde "About ten seconds usually, Mr Poons." → write vai para partição B. Observador lê de B (lag baixo) e A (lag alto): **vê resposta antes da pergunta**.
123
-
124
- ```
125
- Partição A (lag 800ms): "How far into the future..."
126
- Partição B (lag 100ms): "About ten seconds..."
127
-
128
- Observer lê B → vê resposta ✅
129
- Observer lê A → vê pergunta ✅
130
- Observer ordem percebida: resposta → pergunta ❌
131
- ```
132
-
133
- **Solução parcial — particionamento por chave causal:**
134
-
135
- ```sql
136
- -- PT-BR: ambas msgs (pergunta + resposta) particionadas por conversation_id
137
- -- garante mesma partição lógica = mesma ordem WAL
138
- create table public.messages (
139
- id uuid primary key default gen_random_uuid(),
140
- conversation_id uuid not null,
141
- author_id uuid not null,
142
- body text not null,
143
- created_at timestamptz not null default now()
144
- ) partition by hash (conversation_id);
145
-
146
- -- PT-BR: indexes ajudam ordering
147
- create index messages_conv_created_idx
148
- on public.messages (conversation_id, created_at);
149
- ```
150
-
151
- **Limitação:** garante consistent prefix dentro de uma partição. Cross-partition (ex: usuário em duas conversações simultâneas), DDIA p. 159 conclui "in general, ensuring consistent prefix reads requires snapshot isolation". Em Supabase prático: **manter conversação em uma tabela com chave causal explícita**.
152
-
153
- ### Solução C — Detecção stale via `pg_last_wal_replay_lsn()`
154
-
155
- Quando precisa de garantia "este read viu meu write" sem rotear ao líder:
156
-
157
- ```sql
158
- -- PT-BR: capturar LSN no líder após write (chamada do app via RPC)
159
- create or replace function public.get_current_lsn()
160
- returns text
161
- language sql
162
- security invoker
163
- set search_path = ''
164
- as $$
165
- -- PT-BR: pg_current_wal_lsn() retorna posição atual do WAL no líder
166
- select pg_current_wal_lsn()::text;
167
- $$;
168
-
169
- -- PT-BR: na replica, esperar até replay alcançar o LSN capturado
170
- create or replace function public.wait_for_lsn(target_lsn text, timeout_ms int default 5000)
171
- returns boolean
172
- language plpgsql
173
- security invoker
174
- set search_path = ''
175
- as $$
176
- declare
177
- start_at timestamptz := clock_timestamp();
178
- elapsed_ms int;
179
- begin
180
- loop
181
- -- PT-BR: pg_last_wal_replay_lsn() na replica = última posição replayed
182
- if pg_last_wal_replay_lsn() >= target_lsn::pg_lsn then
183
- return true;
184
- end if;
185
-
186
- elapsed_ms := extract(milliseconds from (clock_timestamp() - start_at))::int;
187
- if elapsed_ms >= timeout_ms then
188
- return false; -- PT-BR: REGRA #5 — timeout sem bloquear infinito
189
- end if;
190
-
191
- perform pg_sleep(0.05); -- PT-BR: 50ms entre polls
192
- end loop;
193
- end;
194
- $$;
195
- ```
196
-
197
- **Uso típico no client:**
198
-
199
- ```typescript
200
- // PT-BR: 1) write no líder, captura LSN
201
- const { data: lsn } = await leaderClient.rpc('get_current_lsn')
202
- await leaderClient.from('orders').insert(order)
203
-
204
- // PT-BR: 2) read no líder, espera replay
205
- const { data: ready } = await replicaClient.rpc('wait_for_lsn', {
206
- target_lsn: lsn,
207
- timeout_ms: 3000,
208
- })
209
-
210
- if (ready) {
211
- // PT-BR: replica caught up, leitura é safe
212
- return replicaClient.from('orders').select().eq('id', order.id)
213
- }
214
- // PT-BR: timeout — fallback para líder (REGRA #5)
215
- return leaderClient.from('orders').select().eq('id', order.id)
216
- ```
217
-
218
- ### Supavisor read replica routing
219
-
220
- | Porta | Modo | Use case | Connection string |
221
- |---|---|---|---|
222
- | **6543** | Transaction (default Pro+) | Apps com pooler já configurado, edge runtimes, serverless | `postgresql://postgres.[ref]:pwd@aws-0-region.pooler.supabase.com:6543/postgres` |
223
- | **5432** | Session (líder) | Reads críticas (read-after-write), writes, prepared statements, advisory locks | `postgresql://postgres.[ref]:pwd@aws-0-region.pooler.supabase.com:5432/postgres` |
224
- | `pooler.read.*` | Réplica routing | Read-heavy workloads em Pro+ com replicas habilitadas | (futuro Supabase feature — placeholder hoje) |
225
-
226
- **Decisão por tipo de query:**
227
-
228
- ```
229
- SELECT do próprio dado dentro 5s do write? → 5432 (líder, REGRA #1)
230
- SELECT cross-user, sem janela sticky? → 6543 (replica via Supavisor)
231
- INSERT / UPDATE / DELETE? → 5432 (sempre líder)
232
- SELECT FOR UPDATE / advisory lock? → 5432 (transaction precisa session mode)
233
- ```
234
-
235
- Cross-ref ATIVO: [`multi-tenant-performance-scaling/SKILL.md`](../multi-tenant-performance-scaling/SKILL.md) (v1.21) cobre Supavisor REGRA #1 sob lente de connection pooling — esta skill cobre a mesma porta sob lente de consistência.
236
-
237
- ### Realtime broadcast + leitura DB — padrão "ler o próprio broadcast"
238
-
239
- **Cenário canônico:** client A faz INSERT em `orders` → server emite Realtime broadcast `new_order` no canal `org:orders:org_42` → client B recebe broadcast → client B faz SELECT para refresh da lista. **Pode receber dado stale** (replica não replicou ainda).
240
-
241
- **Sequência do bug:**
242
-
243
- ```
244
- t=0ms Client A INSERT → Leader
245
- t=10ms Server emite broadcast → todos clients no canal recebem
246
- t=15ms Client B recebe broadcast → triggers re-fetch
247
- t=20ms Client B SELECT → Replica (lag ainda 80ms)
248
- t=20ms Replica retorna lista SEM o novo order ❌
249
- t=80ms Replica finalmente replicou (mas client B já desenhou stale)
250
- ```
251
-
252
- **Padrão correto — confiar no payload broadcast:**
253
-
254
- ```typescript
255
- // PT-BR: client confia no payload, NÃO faz SELECT subsequente
256
- import { createClient } from '@supabase/supabase-js'
257
-
258
- const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
259
-
260
- const channel = supabase
261
- .channel('org:orders:org_42', { config: { private: true } })
262
- .on('broadcast', { event: 'new_order' }, ({ payload }) => {
263
- // PT-BR: REGRA #3 — confie no payload, NÃO faça SELECT
264
- setOrders((prev) => [...prev, payload.record])
265
- })
266
- .subscribe()
267
-
268
- // PT-BR: cleanup obrigatório — pattern de supabase-realtime v1.8
269
- return () => {
270
- supabase.removeChannel(channel)
271
- }
272
- ```
273
-
274
- **Server side — emitir payload completo no broadcast:**
275
-
276
- ```typescript
277
- // PT-BR: Edge Function que cria order e broadcast com record completo
278
- Deno.serve(async (req) => {
279
- const supabase = createClient(SUPABASE_URL, SERVICE_ROLE_KEY)
280
- const order = await req.json()
281
-
282
- // PT-BR: 1) INSERT no líder
283
- const { data: created } = await supabase
284
- .from('orders')
285
- .insert(order)
286
- .select()
287
- .single()
288
-
289
- // PT-BR: 2) broadcast com record completo — clients confiam neste payload
290
- await supabase
291
- .channel(`org:orders:${order.org_id}`)
292
- .send({
293
- type: 'broadcast',
294
- event: 'new_order',
295
- payload: { record: created }, // PT-BR: payload canônico
296
- })
297
-
298
- return new Response(JSON.stringify(created), { status: 201 })
299
- })
300
- ```
301
-
302
- **Cross-ref ATIVO:** [`supabase-realtime/SKILL.md`](../supabase-realtime/SKILL.md) (v1.8) define padrão de canal (`scope:entity:id`, `private:true`, `removeChannel` cleanup). Esta skill estende com o padrão `payload.record` específico para evitar replica lag bug.
303
-
304
- ## Anti-patterns
305
-
306
- ### Anti-pattern 1: Round-robin entre réplicas para o mesmo usuário
307
-
308
- **Errado:**
309
- ```typescript
310
- // PT-BR: pegar replica aleatoriamente cada read
311
- const replica = REPLICAS[Math.floor(Math.random() * REPLICAS.length)]
312
- return replica.from('messages').select()
313
- ```
314
-
315
- **Por quê:** viola monotonic reads (DDIA p. 158). User vê mensagem X em uma leitura, depois X some na próxima (replica 2 ainda não replicou). Leituras "voltam no tempo".
316
-
317
- **Certo:** sticky session por `hash(user_id) mod N` (Solução B acima).
318
-
319
- ### Anti-pattern 2: Re-fetch após broadcast
320
-
321
- **Errado:**
322
- ```typescript
323
- .on('broadcast', { event: 'new_order' }, async () => {
324
- // PT-BR: re-fetch que pode atingir replica stale
325
- const { data } = await supabase.from('orders').select() // ❌
326
- setOrders(data)
327
- })
328
- ```
329
-
330
- **Por quê:** broadcast chega em 10-15ms, mas replication lag tipicamente 50-500ms. SELECT no callback **garantido** vai chegar antes da replica replicar. Bug "intermittent missing data".
331
-
332
- **Certo:** confiar no `payload.record` enviado pelo server (REGRA #3 + Solução padrão "ler o próprio broadcast").
333
-
334
- ### Anti-pattern 3: `pg_last_wal_replay_lsn()` sem timeout
335
-
336
- **Errado:**
337
- ```sql
338
- -- PT-BR: loop infinito se replica falhou
339
- do $$
340
- begin
341
- loop
342
- if pg_last_wal_replay_lsn() >= captured_lsn::pg_lsn then exit; end if;
343
- perform pg_sleep(0.05);
344
- end loop;
345
- end$$;
346
- ```
347
-
348
- **Por quê:** se replica desconectou do WAL stream (network partition, disk full), `pg_last_wal_replay_lsn()` nunca alcança o LSN do líder. Query trava indefinidamente, esgota connection pool.
349
-
350
- **Certo:** timeout 3-5s + fallback explícito para líder (REGRA #5 + função `wait_for_lsn` acima).
351
-
352
- ### Anti-pattern 4: Cross-partition para conversação causal
353
-
354
- **Errado:**
355
- ```sql
356
- -- PT-BR: messages particionadas por created_at (range temporal)
357
- create table public.messages (...) partition by range (created_at);
358
- ```
359
-
360
- **Por quê:** pergunta e resposta em uma conversação podem cair em partições diferentes (se virada de mês entre as duas). Viola consistent prefix reads — observador vê resposta antes da pergunta.
361
-
362
- **Certo:** particionar por `conversation_id` (HASH), garante que toda a conversação fica na mesma partição = mesma ordem WAL = consistent prefix.
363
-
364
- ### Anti-pattern 5: Porta 6543 para `SELECT FOR UPDATE`
365
-
366
- **Errado:**
367
- ```typescript
368
- // PT-BR: tentando lock pessimista via Supavisor transaction mode
369
- const { data } = await client6543.rpc('lock_order', { id })
370
- ```
371
-
372
- **Por quê:** Supavisor 6543 (transaction mode) não preserva sessão entre statements — `SELECT FOR UPDATE` libera o lock na próxima query. Lock vira no-op silencioso.
373
-
374
- **Certo:** porta 5432 (session mode) para qualquer operação que precisa estado de sessão (locks, prepared statements, `SET LOCAL`, advisory locks).
375
-
376
- ## Ver também
377
-
378
- - [`_shared-dados-distribuidos/glossary.md`](../_shared-dados-distribuidos/glossary.md) — termos canônicos `read-after-write consistency`, `monotonic reads`, `consistent prefix reads`, `replication lag`, `leader-follower replication` (Phase 117)
379
- - [`supabase-realtime/SKILL.md`](../supabase-realtime/SKILL.md) — broadcast com `private:true`, naming `scope:entity:id`, cleanup `removeChannel` (v1.8)
380
- - [`multi-tenant-performance-scaling/SKILL.md`](../multi-tenant-performance-scaling/SKILL.md) — Supavisor connection string canônica, REGRA #1 porta 6543 (v1.21)
381
- - [`supabase-database-functions/SKILL.md`](../supabase-database-functions/SKILL.md) — padrões PG functions (security invoker, search_path) usados em `get_current_lsn` e `wait_for_lsn`
382
- - [Designing Data-Intensive Applications, Martin Kleppmann (O'Reilly 2017)](https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/) — Ch 5 "Problems With Replication Lag" (p. 155-160)
383
- - [PostgreSQL Documentation — pg_last_wal_replay_lsn](https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-RECOVERY-INFO)
384
- - [Supabase Read Replicas](https://supabase.com/docs/guides/platform/read-replicas)
385
- - [Supabase Supavisor 1M Connections](https://supabase.com/blog/supavisor-1-million)
1
+ ---
2
+ name: consistencia-leitura-replica
3
+ description: Use ao usar Supabase read replicas via Supavisor (porta 6543) ou ao combinar Realtime broadcast + leitura DB…
4
+ ---
5
+
6
+ # Consistência Leitura Réplica — Supabase + Supavisor + Realtime
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill ao usar Supabase Pro+ com **read replicas** ou ao combinar Realtime broadcast com leitura subsequente do DB. Trigger phrases:
11
+
12
+ - "Supabase read replica", "réplica de leitura"
13
+ - "porta 6543 vs 5432", "Supavisor session vs transaction"
14
+ - "read-after-write", "leitura após escrita inconsistente"
15
+ - "monotonic reads", "leituras não-monotônicas"
16
+ - "consistent prefix reads", "prefixo causal violado"
17
+ - "replication lag Supabase", "atraso de replicação"
18
+ - "broadcast Realtime + SELECT stale"
19
+ - "pg_last_wal_replay_lsn", "WAL position detection"
20
+
21
+ Esta skill aplica **DDIA Ch 5 "Problems With Replication Lag"** ao stack Supabase. Cross-referenciada por `supabase-realtime` (v1.8) ao bundlear broadcast + leitura, e por `multi-tenant-performance-scaling` (v1.21) ao escalar Postgres em Pro+.
22
+
23
+ Termos canônicos (`read-after-write consistency`, `monotonic reads`, `consistent prefix reads`, `replication lag`, `leader-follower replication`) definidos em [`_shared-dados-distribuidos/glossary.md`](../_shared-dados-distribuidos/glossary.md) seções (a) e (b) — esta skill **não duplica**, apenas linka.
24
+
25
+ ## Regras absolutas
26
+
27
+ **REGRA #1 (read-after-write own data):** Para leituras do **próprio dado do usuário** dentro de janela de **5s após write**, rotear para **porta 5432 (líder)** — não 6543. Sem isso, usuário cria post → GET retorna 404 → percepção de bug. Justify: DDIA p. 156 "always read the user's own profile from the leader".
28
+
29
+ **REGRA #2 (sticky session monotonic):** Para usuários ativos lendo de réplicas, escolher réplica via `hash(user_id) mod N` — **não round-robin**. Round-robin viola monotonic reads (DDIA p. 158). Mitigação obrigatória: fallback para líder se réplica down.
30
+
31
+ **REGRA #3 (broadcast trust payload):** Após receber Realtime broadcast com `payload.record`, **NÃO** fazer SELECT subsequente. Confiar no payload — server é a fonte canônica do evento. SELECT pode atingir réplica que ainda não replicou (lag típico 50-500ms).
32
+
33
+ **REGRA #4 (causal partition):** Writes causalmente relacionados (pergunta + resposta em chat, parent + child em árvore) **DEVEM** ir para a mesma partição lógica. Em Supabase: usar mesmo `org_id` ou `conversation_id` como partition key. DDIA p. 159 "any writes which are causally related to each other are written to the same partition".
34
+
35
+ **REGRA #5 (LSN wait com timeout):** Quando usar `pg_last_wal_replay_lsn() >= captured_lsn`, **SEMPRE** com timeout (3-5s). Sem timeout, query trava se réplica falhou. Após timeout, fallback para líder.
36
+
37
+ ## Patterns canônicos
38
+
39
+ ### Problema 1: Read-after-write inconsistente (DDIA Ch 5, p. 156)
40
+
41
+ **Cenário canônico:** usuário cria post via form submit → tela mostra "criado com sucesso" → usuário clica "ver post" → request vai para réplica → réplica ainda não replicou → 404. Da perspectiva do usuário: "perdi meu dado".
42
+
43
+ ```
44
+ User 1234 ──INSERT─→ Leader (5432) ┐
45
+ │ │ replication lag (50-500ms)
46
+ └──WAL──────────────→ Follower (6543, replica)
47
+
48
+ User 1234 ─SELECT────────────────────────────→ ❌ 404
49
+ (vai para replica via Supavisor)
50
+ ```
51
+
52
+ **Solução A — leitura no líder após write do mesmo usuário:**
53
+
54
+ ```typescript
55
+ // PT-BR: client mantém timestamp do último write em memória
56
+ class SupabaseRouter {
57
+ private lastWriteAt: Map<string, number> = new Map() // userId → timestamp ms
58
+ private readonly STICKY_WINDOW_MS = 5000 // 5s leitura no líder
59
+
60
+ async write(userId: string, table: string, payload: unknown) {
61
+ const result = await this.leaderClient.from(table).insert(payload)
62
+ this.lastWriteAt.set(userId, Date.now())
63
+ return result
64
+ }
65
+
66
+ async read(userId: string, table: string, filter: object) {
67
+ const lastWrite = this.lastWriteAt.get(userId) ?? 0
68
+ const elapsedMs = Date.now() - lastWrite
69
+
70
+ // PT-BR: dentro da janela 5s, ler do líder (porta 5432)
71
+ if (elapsedMs < this.STICKY_WINDOW_MS) {
72
+ return this.leaderClient.from(table).select().match(filter)
73
+ }
74
+ // PT-BR: fora da janela, OK ler de replica via pooler 6543
75
+ return this.replicaClient.from(table).select().match(filter)
76
+ }
77
+ }
78
+ ```
79
+
80
+ **Trade-off:** dentro da janela, perde benefício do read scaling. DDIA recomenda janela curta (1-5s) — cobre 99% dos casos UX sem sobrecarregar líder.
81
+
82
+ ### Problema 2: Leituras não-monotônicas (DDIA Ch 5, p. 158)
83
+
84
+ **Cenário canônico:** usuário 2345 abre lista de comentários — primeira leitura vai para réplica 1 (lag 100ms) → vê comentário X. Segundo refresh vai para réplica 2 (lag 800ms) → comentário X **desapareceu**. Da perspectiva do usuário: "dado voltou no tempo".
85
+
86
+ ```
87
+ User 2345 ─SELECT(1)────→ Replica 1 (lag 100ms) → 1 result ✅
88
+ User 2345 ─SELECT(2)────→ Replica 2 (lag 800ms) → 0 results ❌ (parecia ter sumido)
89
+ ```
90
+
91
+ **Solução B — sticky session por `user_id` em routing:**
92
+
93
+ ```typescript
94
+ // PT-BR: hash determinístico do user_id → escolhe replica fixa para esse user
95
+ import { createHash } from 'node:crypto'
96
+
97
+ function pickReplica(userId: string, replicas: ReadonlyArray<SupabaseClient>): SupabaseClient {
98
+ const hash = createHash('sha256').update(userId).digest()
99
+ const idx = hash.readUInt32BE(0) % replicas.length
100
+ return replicas[idx]
101
+ }
102
+
103
+ // PT-BR: usage com fallback para líder se replica falhar
104
+ async function readWithStickyReplica(userId: string, query: () => Promise<Result>) {
105
+ const replica = pickReplica(userId, REPLICAS)
106
+ try {
107
+ return await query.call(replica)
108
+ } catch (err) {
109
+ if (isReplicaDownError(err)) {
110
+ // PT-BR: fallback obrigatório — REGRA #2
111
+ return await query.call(LEADER)
112
+ }
113
+ throw err
114
+ }
115
+ }
116
+ ```
117
+
118
+ **Pitfall:** réplica fica down → todos os usuários alocados a ela ficam sem leitura. Mitigação: detectar via health check + reroute para líder até réplica voltar.
119
+
120
+ ### Problema 3: Prefixo causal violado (DDIA Ch 5, p. 159)
121
+
122
+ **Cenário canônico chat:** Mr Poons pergunta "How far into the future can you see, Mrs Cake?" → write vai para partição A. Mrs Cake responde "About ten seconds usually, Mr Poons." → write vai para partição B. Observador lê de B (lag baixo) e A (lag alto): **vê resposta antes da pergunta**.
123
+
124
+ ```
125
+ Partição A (lag 800ms): "How far into the future..."
126
+ Partição B (lag 100ms): "About ten seconds..."
127
+
128
+ Observer lê B → vê resposta ✅
129
+ Observer lê A → vê pergunta ✅
130
+ Observer ordem percebida: resposta → pergunta ❌
131
+ ```
132
+
133
+ **Solução parcial — particionamento por chave causal:**
134
+
135
+ ```sql
136
+ -- PT-BR: ambas msgs (pergunta + resposta) particionadas por conversation_id
137
+ -- garante mesma partição lógica = mesma ordem WAL
138
+ create table public.messages (
139
+ id uuid primary key default gen_random_uuid(),
140
+ conversation_id uuid not null,
141
+ author_id uuid not null,
142
+ body text not null,
143
+ created_at timestamptz not null default now()
144
+ ) partition by hash (conversation_id);
145
+
146
+ -- PT-BR: indexes ajudam ordering
147
+ create index messages_conv_created_idx
148
+ on public.messages (conversation_id, created_at);
149
+ ```
150
+
151
+ **Limitação:** garante consistent prefix dentro de uma partição. Cross-partition (ex: usuário em duas conversações simultâneas), DDIA p. 159 conclui "in general, ensuring consistent prefix reads requires snapshot isolation". Em Supabase prático: **manter conversação em uma tabela com chave causal explícita**.
152
+
153
+ ### Solução C — Detecção stale via `pg_last_wal_replay_lsn()`
154
+
155
+ Quando precisa de garantia "este read viu meu write" sem rotear ao líder:
156
+
157
+ ```sql
158
+ -- PT-BR: capturar LSN no líder após write (chamada do app via RPC)
159
+ create or replace function public.get_current_lsn()
160
+ returns text
161
+ language sql
162
+ security invoker
163
+ set search_path = ''
164
+ as $$
165
+ -- PT-BR: pg_current_wal_lsn() retorna posição atual do WAL no líder
166
+ select pg_current_wal_lsn()::text;
167
+ $$;
168
+
169
+ -- PT-BR: na replica, esperar até replay alcançar o LSN capturado
170
+ create or replace function public.wait_for_lsn(target_lsn text, timeout_ms int default 5000)
171
+ returns boolean
172
+ language plpgsql
173
+ security invoker
174
+ set search_path = ''
175
+ as $$
176
+ declare
177
+ start_at timestamptz := clock_timestamp();
178
+ elapsed_ms int;
179
+ begin
180
+ loop
181
+ -- PT-BR: pg_last_wal_replay_lsn() na replica = última posição replayed
182
+ if pg_last_wal_replay_lsn() >= target_lsn::pg_lsn then
183
+ return true;
184
+ end if;
185
+
186
+ elapsed_ms := extract(milliseconds from (clock_timestamp() - start_at))::int;
187
+ if elapsed_ms >= timeout_ms then
188
+ return false; -- PT-BR: REGRA #5 — timeout sem bloquear infinito
189
+ end if;
190
+
191
+ perform pg_sleep(0.05); -- PT-BR: 50ms entre polls
192
+ end loop;
193
+ end;
194
+ $$;
195
+ ```
196
+
197
+ **Uso típico no client:**
198
+
199
+ ```typescript
200
+ // PT-BR: 1) write no líder, captura LSN
201
+ const { data: lsn } = await leaderClient.rpc('get_current_lsn')
202
+ await leaderClient.from('orders').insert(order)
203
+
204
+ // PT-BR: 2) read no líder, espera replay
205
+ const { data: ready } = await replicaClient.rpc('wait_for_lsn', {
206
+ target_lsn: lsn,
207
+ timeout_ms: 3000,
208
+ })
209
+
210
+ if (ready) {
211
+ // PT-BR: replica caught up, leitura é safe
212
+ return replicaClient.from('orders').select().eq('id', order.id)
213
+ }
214
+ // PT-BR: timeout — fallback para líder (REGRA #5)
215
+ return leaderClient.from('orders').select().eq('id', order.id)
216
+ ```
217
+
218
+ ### Supavisor read replica routing
219
+
220
+ | Porta | Modo | Use case | Connection string |
221
+ |---|---|---|---|
222
+ | **6543** | Transaction (default Pro+) | Apps com pooler já configurado, edge runtimes, serverless | `postgresql://postgres.[ref]:pwd@aws-0-region.pooler.supabase.com:6543/postgres` |
223
+ | **5432** | Session (líder) | Reads críticas (read-after-write), writes, prepared statements, advisory locks | `postgresql://postgres.[ref]:pwd@aws-0-region.pooler.supabase.com:5432/postgres` |
224
+ | `pooler.read.*` | Réplica routing | Read-heavy workloads em Pro+ com replicas habilitadas | (futuro Supabase feature — placeholder hoje) |
225
+
226
+ **Decisão por tipo de query:**
227
+
228
+ ```
229
+ SELECT do próprio dado dentro 5s do write? → 5432 (líder, REGRA #1)
230
+ SELECT cross-user, sem janela sticky? → 6543 (replica via Supavisor)
231
+ INSERT / UPDATE / DELETE? → 5432 (sempre líder)
232
+ SELECT FOR UPDATE / advisory lock? → 5432 (transaction precisa session mode)
233
+ ```
234
+
235
+ Cross-ref ATIVO: [`multi-tenant-performance-scaling/SKILL.md`](../multi-tenant-performance-scaling/SKILL.md) (v1.21) cobre Supavisor REGRA #1 sob lente de connection pooling — esta skill cobre a mesma porta sob lente de consistência.
236
+
237
+ ### Realtime broadcast + leitura DB — padrão "ler o próprio broadcast"
238
+
239
+ **Cenário canônico:** client A faz INSERT em `orders` → server emite Realtime broadcast `new_order` no canal `org:orders:org_42` → client B recebe broadcast → client B faz SELECT para refresh da lista. **Pode receber dado stale** (replica não replicou ainda).
240
+
241
+ **Sequência do bug:**
242
+
243
+ ```
244
+ t=0ms Client A INSERT → Leader
245
+ t=10ms Server emite broadcast → todos clients no canal recebem
246
+ t=15ms Client B recebe broadcast → triggers re-fetch
247
+ t=20ms Client B SELECT → Replica (lag ainda 80ms)
248
+ t=20ms Replica retorna lista SEM o novo order ❌
249
+ t=80ms Replica finalmente replicou (mas client B já desenhou stale)
250
+ ```
251
+
252
+ **Padrão correto — confiar no payload broadcast:**
253
+
254
+ ```typescript
255
+ // PT-BR: client confia no payload, NÃO faz SELECT subsequente
256
+ import { createClient } from '@supabase/supabase-js'
257
+
258
+ const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
259
+
260
+ const channel = supabase
261
+ .channel('org:orders:org_42', { config: { private: true } })
262
+ .on('broadcast', { event: 'new_order' }, ({ payload }) => {
263
+ // PT-BR: REGRA #3 — confie no payload, NÃO faça SELECT
264
+ setOrders((prev) => [...prev, payload.record])
265
+ })
266
+ .subscribe()
267
+
268
+ // PT-BR: cleanup obrigatório — pattern de supabase-realtime v1.8
269
+ return () => {
270
+ supabase.removeChannel(channel)
271
+ }
272
+ ```
273
+
274
+ **Server side — emitir payload completo no broadcast:**
275
+
276
+ ```typescript
277
+ // PT-BR: Edge Function que cria order e broadcast com record completo
278
+ Deno.serve(async (req) => {
279
+ const supabase = createClient(SUPABASE_URL, SERVICE_ROLE_KEY)
280
+ const order = await req.json()
281
+
282
+ // PT-BR: 1) INSERT no líder
283
+ const { data: created } = await supabase
284
+ .from('orders')
285
+ .insert(order)
286
+ .select()
287
+ .single()
288
+
289
+ // PT-BR: 2) broadcast com record completo — clients confiam neste payload
290
+ await supabase
291
+ .channel(`org:orders:${order.org_id}`)
292
+ .send({
293
+ type: 'broadcast',
294
+ event: 'new_order',
295
+ payload: { record: created }, // PT-BR: payload canônico
296
+ })
297
+
298
+ return new Response(JSON.stringify(created), { status: 201 })
299
+ })
300
+ ```
301
+
302
+ **Cross-ref ATIVO:** [`supabase-realtime/SKILL.md`](../supabase-realtime/SKILL.md) (v1.8) define padrão de canal (`scope:entity:id`, `private:true`, `removeChannel` cleanup). Esta skill estende com o padrão `payload.record` específico para evitar replica lag bug.
303
+
304
+ ## Anti-patterns
305
+
306
+ ### Anti-pattern 1: Round-robin entre réplicas para o mesmo usuário
307
+
308
+ **Errado:**
309
+ ```typescript
310
+ // PT-BR: pegar replica aleatoriamente cada read
311
+ const replica = REPLICAS[Math.floor(Math.random() * REPLICAS.length)]
312
+ return replica.from('messages').select()
313
+ ```
314
+
315
+ **Por quê:** viola monotonic reads (DDIA p. 158). User vê mensagem X em uma leitura, depois X some na próxima (replica 2 ainda não replicou). Leituras "voltam no tempo".
316
+
317
+ **Certo:** sticky session por `hash(user_id) mod N` (Solução B acima).
318
+
319
+ ### Anti-pattern 2: Re-fetch após broadcast
320
+
321
+ **Errado:**
322
+ ```typescript
323
+ .on('broadcast', { event: 'new_order' }, async () => {
324
+ // PT-BR: re-fetch que pode atingir replica stale
325
+ const { data } = await supabase.from('orders').select() // ❌
326
+ setOrders(data)
327
+ })
328
+ ```
329
+
330
+ **Por quê:** broadcast chega em 10-15ms, mas replication lag tipicamente 50-500ms. SELECT no callback **garantido** vai chegar antes da replica replicar. Bug "intermittent missing data".
331
+
332
+ **Certo:** confiar no `payload.record` enviado pelo server (REGRA #3 + Solução padrão "ler o próprio broadcast").
333
+
334
+ ### Anti-pattern 3: `pg_last_wal_replay_lsn()` sem timeout
335
+
336
+ **Errado:**
337
+ ```sql
338
+ -- PT-BR: loop infinito se replica falhou
339
+ do $$
340
+ begin
341
+ loop
342
+ if pg_last_wal_replay_lsn() >= captured_lsn::pg_lsn then exit; end if;
343
+ perform pg_sleep(0.05);
344
+ end loop;
345
+ end$$;
346
+ ```
347
+
348
+ **Por quê:** se replica desconectou do WAL stream (network partition, disk full), `pg_last_wal_replay_lsn()` nunca alcança o LSN do líder. Query trava indefinidamente, esgota connection pool.
349
+
350
+ **Certo:** timeout 3-5s + fallback explícito para líder (REGRA #5 + função `wait_for_lsn` acima).
351
+
352
+ ### Anti-pattern 4: Cross-partition para conversação causal
353
+
354
+ **Errado:**
355
+ ```sql
356
+ -- PT-BR: messages particionadas por created_at (range temporal)
357
+ create table public.messages (...) partition by range (created_at);
358
+ ```
359
+
360
+ **Por quê:** pergunta e resposta em uma conversação podem cair em partições diferentes (se virada de mês entre as duas). Viola consistent prefix reads — observador vê resposta antes da pergunta.
361
+
362
+ **Certo:** particionar por `conversation_id` (HASH), garante que toda a conversação fica na mesma partição = mesma ordem WAL = consistent prefix.
363
+
364
+ ### Anti-pattern 5: Porta 6543 para `SELECT FOR UPDATE`
365
+
366
+ **Errado:**
367
+ ```typescript
368
+ // PT-BR: tentando lock pessimista via Supavisor transaction mode
369
+ const { data } = await client6543.rpc('lock_order', { id })
370
+ ```
371
+
372
+ **Por quê:** Supavisor 6543 (transaction mode) não preserva sessão entre statements — `SELECT FOR UPDATE` libera o lock na próxima query. Lock vira no-op silencioso.
373
+
374
+ **Certo:** porta 5432 (session mode) para qualquer operação que precisa estado de sessão (locks, prepared statements, `SET LOCAL`, advisory locks).
375
+
376
+ ## Ver também
377
+
378
+ - [`_shared-dados-distribuidos/glossary.md`](../_shared-dados-distribuidos/glossary.md) — termos canônicos `read-after-write consistency`, `monotonic reads`, `consistent prefix reads`, `replication lag`, `leader-follower replication` (Phase 117)
379
+ - [`supabase-realtime/SKILL.md`](../supabase-realtime/SKILL.md) — broadcast com `private:true`, naming `scope:entity:id`, cleanup `removeChannel` (v1.8)
380
+ - [`multi-tenant-performance-scaling/SKILL.md`](../multi-tenant-performance-scaling/SKILL.md) — Supavisor connection string canônica, REGRA #1 porta 6543 (v1.21)
381
+ - [`supabase-database-functions/SKILL.md`](../supabase-database-functions/SKILL.md) — padrões PG functions (security invoker, search_path) usados em `get_current_lsn` e `wait_for_lsn`
382
+ - [Designing Data-Intensive Applications, Martin Kleppmann (O'Reilly 2017)](https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/) — Ch 5 "Problems With Replication Lag" (p. 155-160)
383
+ - [PostgreSQL Documentation — pg_last_wal_replay_lsn](https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-RECOVERY-INFO)
384
+ - [Supabase Read Replicas](https://supabase.com/docs/guides/platform/read-replicas)
385
+ - [Supabase Supavisor 1M Connections](https://supabase.com/blog/supavisor-1-million)