@evertonferreira96/agents 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/api.md +45 -0
- package/.claude/agents/app.md +41 -0
- package/.claude/agents/architect.md +49 -0
- package/.claude/agents/infra.md +27 -0
- package/.claude/agents/product.md +58 -0
- package/.claude/agents/quick.md +33 -0
- package/.claude/agents/security.md +39 -0
- package/.claude/agents/ui.md +54 -0
- package/.claude/agents/verifier.md +56 -0
- package/.claude/agents/web.md +41 -0
- package/.claude/hooks/config-update-check.mjs +14 -0
- package/.claude/hooks/context-budget.mjs +51 -0
- package/.claude/hooks/graphify-update.sh +16 -0
- package/.claude/hooks/guard-bash.sh +64 -0
- package/.claude/hooks/orchestrator-guard.sh +49 -0
- package/.claude/hooks/post-compact.sh +26 -0
- package/.claude/hooks/readonly-guard.sh +54 -0
- package/.claude/hooks/stop-verify.sh +52 -0
- package/.claude/rules/api.md +21 -0
- package/.claude/rules/db.md +9 -0
- package/.claude/rules/delivery-diagram.md +16 -0
- package/.claude/rules/git-workflow.md +34 -0
- package/.claude/rules/graphify.md +3 -0
- package/.claude/rules/mobile.md +22 -0
- package/.claude/rules/tickets.md +9 -0
- package/.claude/rules/ui.md +14 -0
- package/.claude/rules/web.md +27 -0
- package/.claude/settings.json +114 -0
- package/.claude/skills/animate/RECIPES.md +324 -0
- package/.claude/skills/animate/SKILL.md +208 -0
- package/.claude/skills/animate/agents/openai.yaml +5 -0
- package/.claude/skills/animate-expo/RECIPES.md +385 -0
- package/.claude/skills/animate-expo/SKILL.md +264 -0
- package/.claude/skills/animate-expo/agents/openai.yaml +5 -0
- package/.claude/skills/animation-performance/SKILL.md +223 -0
- package/.claude/skills/animation-performance/agents/openai.yaml +5 -0
- package/.claude/skills/apple-design/SKILL.md +291 -0
- package/.claude/skills/apple-design/agents/openai.yaml +5 -0
- package/.claude/skills/arch-api/SKILL.md +291 -0
- package/.claude/skills/arch-api/agents/openai.yaml +3 -0
- package/.claude/skills/arch-api/references/common.md +174 -0
- package/.claude/skills/arch-api/references/contract.md +289 -0
- package/.claude/skills/arch-api/references/domain.md +301 -0
- package/.claude/skills/arch-api/references/endpoints.md +184 -0
- package/.claude/skills/arch-api/references/review.md +228 -0
- package/.claude/skills/arch-api/references/security.md +151 -0
- package/.claude/skills/arch-app/SKILL.md +278 -0
- package/.claude/skills/arch-app/agents/openai.yaml +3 -0
- package/.claude/skills/arch-app/references/navigation-session.md +257 -0
- package/.claude/skills/arch-app/references/review.md +155 -0
- package/.claude/skills/arch-app/references/screens.md +214 -0
- package/.claude/skills/arch-app/references/services.md +301 -0
- package/.claude/skills/arch-app/references/ui-mobile.md +124 -0
- package/.claude/skills/arch-db/SKILL.md +181 -0
- package/.claude/skills/arch-db/agents/openai.yaml +3 -0
- package/.claude/skills/arch-db/references/indexes-queries.md +251 -0
- package/.claude/skills/arch-db/references/migrations.md +191 -0
- package/.claude/skills/arch-db/references/modeling.md +214 -0
- package/.claude/skills/arch-db/references/mysql.md +262 -0
- package/.claude/skills/arch-db/references/pooling-ops.md +112 -0
- package/.claude/skills/arch-db/references/review.md +143 -0
- package/.claude/skills/arch-db/references/transactions-locks.md +141 -0
- package/.claude/skills/arch-web/SKILL.md +307 -0
- package/.claude/skills/arch-web/agents/openai.yaml +3 -0
- package/.claude/skills/arch-web/references/auth-session-env.md +165 -0
- package/.claude/skills/arch-web/references/blocks.md +198 -0
- package/.claude/skills/arch-web/references/cache.md +124 -0
- package/.claude/skills/arch-web/references/review.md +142 -0
- package/.claude/skills/arch-web/references/services.md +297 -0
- package/.claude/skills/arch-web/references/tests.md +159 -0
- package/.claude/skills/arch-web/references/ui-forms.md +143 -0
- package/.claude/skills/break-ui/CATALOG.md +135 -0
- package/.claude/skills/break-ui/SKILL.md +176 -0
- package/.claude/skills/break-ui/agents/openai.yaml +5 -0
- package/.claude/skills/brief/SKILL.md +91 -0
- package/.claude/skills/brief/agents/openai.yaml +3 -0
- package/.claude/skills/diagnosing-bugs/SKILL.md +138 -0
- package/.claude/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/.claude/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/.claude/skills/emil-design-eng/SKILL.md +675 -0
- package/.claude/skills/emil-design-eng/agents/openai.yaml +5 -0
- package/.claude/skills/find-animation-opportunities/SKILL.md +141 -0
- package/.claude/skills/find-animation-opportunities/agents/openai.yaml +5 -0
- package/.claude/skills/graphify/.graphify_version +1 -0
- package/.claude/skills/graphify/SKILL.md +742 -0
- package/.claude/skills/graphify/agents/openai.yaml +3 -0
- package/.claude/skills/graphify/references/add-watch.md +56 -0
- package/.claude/skills/graphify/references/exports.md +91 -0
- package/.claude/skills/graphify/references/extraction-spec.md +70 -0
- package/.claude/skills/graphify/references/github-and-merge.md +46 -0
- package/.claude/skills/graphify/references/hooks.md +33 -0
- package/.claude/skills/graphify/references/query.md +317 -0
- package/.claude/skills/graphify/references/transcribe.md +52 -0
- package/.claude/skills/graphify/references/update.md +221 -0
- package/.claude/skills/grill-me/SKILL.md +7 -0
- package/.claude/skills/grill-me/agents/openai.yaml +5 -0
- package/.claude/skills/grill-with-docs/SKILL.md +64 -0
- package/.claude/skills/grill-with-docs/agents/openai.yaml +5 -0
- package/.claude/skills/grilling/SKILL.md +30 -0
- package/.claude/skills/grilling/agents/openai.yaml +3 -0
- package/.claude/skills/handoff/SKILL.md +28 -0
- package/.claude/skills/handoff/agents/openai.yaml +5 -0
- package/.claude/skills/impeccable/SKILL.md +89 -0
- package/.claude/skills/impeccable/agents/openai.yaml +5 -0
- package/.claude/skills/impeccable/reference/adapt.md +318 -0
- package/.claude/skills/impeccable/reference/adapt.native.md +58 -0
- package/.claude/skills/impeccable/reference/android.md +46 -0
- package/.claude/skills/impeccable/reference/animate.md +89 -0
- package/.claude/skills/impeccable/reference/audit.md +137 -0
- package/.claude/skills/impeccable/reference/audit.native.md +139 -0
- package/.claude/skills/impeccable/reference/bolder.md +33 -0
- package/.claude/skills/impeccable/reference/clarify.md +94 -0
- package/.claude/skills/impeccable/reference/colorize.md +86 -0
- package/.claude/skills/impeccable/reference/component-review.md +63 -0
- package/.claude/skills/impeccable/reference/craft-floor.md +44 -0
- package/.claude/skills/impeccable/reference/craft.md +5 -0
- package/.claude/skills/impeccable/reference/critique.md +809 -0
- package/.claude/skills/impeccable/reference/degraded/asset-producer.md +42 -0
- package/.claude/skills/impeccable/reference/degraded/documenter.md +24 -0
- package/.claude/skills/impeccable/reference/degraded/finish-reviewer.md +38 -0
- package/.claude/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/.claude/skills/impeccable/reference/delight.md +70 -0
- package/.claude/skills/impeccable/reference/distill.md +111 -0
- package/.claude/skills/impeccable/reference/doctor.md +54 -0
- package/.claude/skills/impeccable/reference/document.md +416 -0
- package/.claude/skills/impeccable/reference/extract.md +69 -0
- package/.claude/skills/impeccable/reference/generate.md +101 -0
- package/.claude/skills/impeccable/reference/harden.md +345 -0
- package/.claude/skills/impeccable/reference/hooks.md +113 -0
- package/.claude/skills/impeccable/reference/init.md +131 -0
- package/.claude/skills/impeccable/reference/ios.md +51 -0
- package/.claude/skills/impeccable/reference/layout.md +84 -0
- package/.claude/skills/impeccable/reference/live-setup.md +104 -0
- package/.claude/skills/impeccable/reference/live.md +325 -0
- package/.claude/skills/impeccable/reference/mode-operate.md +21 -0
- package/.claude/skills/impeccable/reference/mode-persuade.md +19 -0
- package/.claude/skills/impeccable/reference/mode-read.md +21 -0
- package/.claude/skills/impeccable/reference/new-work.md +154 -0
- package/.claude/skills/impeccable/reference/onboard.md +234 -0
- package/.claude/skills/impeccable/reference/operate.md +61 -0
- package/.claude/skills/impeccable/reference/optimize.md +258 -0
- package/.claude/skills/impeccable/reference/overdrive.md +127 -0
- package/.claude/skills/impeccable/reference/polish.md +105 -0
- package/.claude/skills/impeccable/reference/quieter.md +99 -0
- package/.claude/skills/impeccable/reference/region-map.md +26 -0
- package/.claude/skills/impeccable/reference/routing.md +24 -0
- package/.claude/skills/impeccable/reference/shape.md +59 -0
- package/.claude/skills/impeccable/reference/typeset.md +80 -0
- package/.claude/skills/impeccable/reference/visualize.md +47 -0
- package/.claude/skills/impeccable/scripts/VERSION +1 -0
- package/.claude/skills/impeccable/scripts/command-metadata.json +98 -0
- package/.claude/skills/impeccable/scripts/data/font-index-failures.json +121 -0
- package/.claude/skills/impeccable/scripts/data/font-index.json +1 -0
- package/.claude/skills/impeccable/scripts/impeccable +206 -0
- package/.claude/skills/impeccable/scripts/impeccable.cmd +214 -0
- package/.claude/skills/impeccable/scripts/live-browser-dom.js +281 -0
- package/.claude/skills/impeccable/scripts/live-browser-ignores.js +242 -0
- package/.claude/skills/impeccable/scripts/live-browser-session.js +148 -0
- package/.claude/skills/impeccable/scripts/live-browser.js +13532 -0
- package/.claude/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/.claude/skills/improve-animations/AUDIT.md +115 -0
- package/.claude/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
- package/.claude/skills/improve-animations/SKILL.md +110 -0
- package/.claude/skills/improve-animations/agents/openai.yaml +5 -0
- package/.claude/skills/mobile-native/SKILL.md +311 -0
- package/.claude/skills/mobile-native/agents/openai.yaml +3 -0
- package/.claude/skills/orchestrate/SKILL.md +181 -0
- package/.claude/skills/orchestrate/agents/openai.yaml +3 -0
- package/.claude/skills/pick-ui-library/SKILL.md +85 -0
- package/.claude/skills/pick-ui-library/agents/openai.yaml +5 -0
- package/.claude/skills/ponytail/SKILL.md +52 -0
- package/.claude/skills/ponytail/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-audit/SKILL.md +103 -0
- package/.claude/skills/ponytail-audit/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-debt/SKILL.md +44 -0
- package/.claude/skills/ponytail-debt/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-review/SKILL.md +101 -0
- package/.claude/skills/ponytail-review/agents/openai.yaml +3 -0
- package/.claude/skills/postgres-best-practices/SKILL.md +42 -0
- package/.claude/skills/postgres-best-practices/agents/openai.yaml +3 -0
- package/.claude/skills/postgres-best-practices/references/backup-restore.md +372 -0
- package/.claude/skills/postgres-best-practices/references/bulk-loading.md +315 -0
- package/.claude/skills/postgres-best-practices/references/connection-pooling.md +310 -0
- package/.claude/skills/postgres-best-practices/references/hot-standby.md +221 -0
- package/.claude/skills/postgres-best-practices/references/indexing.md +318 -0
- package/.claude/skills/postgres-best-practices/references/logical-replication.md +610 -0
- package/.claude/skills/postgres-best-practices/references/major-version-upgrades.md +334 -0
- package/.claude/skills/postgres-best-practices/references/performance-diagnostics.md +553 -0
- package/.claude/skills/postgres-best-practices/references/query-optimization.md +294 -0
- package/.claude/skills/postgres-best-practices/references/query-patterns.md +566 -0
- package/.claude/skills/postgres-best-practices/references/schema-design.md +362 -0
- package/.claude/skills/postgres-best-practices/references/security-roles.md +489 -0
- package/.claude/skills/postgres-best-practices/references/transaction-isolation.md +344 -0
- package/.claude/skills/project-docs/SKILL.md +330 -0
- package/.claude/skills/project-docs/agents/openai.yaml +3 -0
- package/.claude/skills/prototype/PICKER.md +197 -0
- package/.claude/skills/prototype/SKILL.md +98 -0
- package/.claude/skills/prototype/agents/openai.yaml +5 -0
- package/.claude/skills/retro/SKILL.md +43 -0
- package/.claude/skills/retro/agents/openai.yaml +5 -0
- package/.claude/skills/review/SKILL.md +74 -0
- package/.claude/skills/review/agents/openai.yaml +3 -0
- package/.claude/skills/review/references/smells.md +23 -0
- package/.claude/skills/review-animations/SKILL.md +120 -0
- package/.claude/skills/review-animations/STANDARDS.md +187 -0
- package/.claude/skills/review-animations/agents/openai.yaml +5 -0
- package/.claude/skills/tdd/SKILL.md +38 -0
- package/.claude/skills/tdd/agents/openai.yaml +3 -0
- package/.claude/skills/tdd/mocking.md +59 -0
- package/.claude/skills/tdd/tests.md +77 -0
- package/.claude/skills/triage/SKILL.md +58 -0
- package/.claude/skills/triage/agents/openai.yaml +3 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +378 -0
- package/.claude/skills/ui-ux-pro-max/agents/openai.yaml +5 -0
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.claude/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/.claude/skills/writing-for-agents/SKILL.md +81 -0
- package/.claude/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/.worktreeinclude +3 -0
- package/AGENTS.md +44 -0
- package/README.md +14 -2
- package/VERSION +1 -0
- package/bin/agents.mjs +45 -0
- package/docs/adr/README.md +31 -0
- package/docs/brief-template.md +3 -0
- package/docs/plans/README.md +73 -0
- package/package.json +46 -4
- package/scripts/bootstrap.mjs +188 -0
- package/scripts/check-catalog.mjs +43 -0
- package/scripts/skills-update.mjs +51 -0
- package/skills-lock.json +269 -0
- package/templates/CLAUDE.project.md +41 -0
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
# Logical Replication & Migrations Reference
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
- Overview and prerequisites
|
|
5
|
+
- Publisher setup
|
|
6
|
+
- Subscriber setup
|
|
7
|
+
- Managing publications (add/remove tables)
|
|
8
|
+
- Managing subscriptions
|
|
9
|
+
- Monitoring replication progress
|
|
10
|
+
- Schema changes during replication
|
|
11
|
+
- Live migration patterns
|
|
12
|
+
- Troubleshooting
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Logical replication streams row-level changes (INSERT, UPDATE, DELETE) from a **publisher** to one or more **subscribers** using the publish/subscribe model. Unlike physical replication, it:
|
|
17
|
+
|
|
18
|
+
- Replicates specific tables, not the entire cluster
|
|
19
|
+
- Allows different indexes, security policies, or schemas on the subscriber
|
|
20
|
+
- Works across different PG major versions (useful for upgrades)
|
|
21
|
+
- Allows the subscriber to be writable (for other tables)
|
|
22
|
+
|
|
23
|
+
### Prerequisites
|
|
24
|
+
|
|
25
|
+
**On the publisher:**
|
|
26
|
+
- `wal_level = logical` (requires restart if changing)
|
|
27
|
+
- Sufficient `max_replication_slots` (one per subscription)
|
|
28
|
+
- Sufficient `max_wal_senders` (one per subscription + headroom)
|
|
29
|
+
- Tables must have a replica identity (primary key by default)
|
|
30
|
+
- The replication role needs `REPLICATION` privilege, plus `USAGE` on the schema and `SELECT` on replicated tables
|
|
31
|
+
|
|
32
|
+
**On the subscriber:**
|
|
33
|
+
- Target tables must already exist with compatible schema
|
|
34
|
+
- Sufficient `max_logical_replication_workers`
|
|
35
|
+
- Sufficient `max_worker_processes`
|
|
36
|
+
- The role needs `pg_create_subscription` membership (PG16+)
|
|
37
|
+
|
|
38
|
+
Check current settings:
|
|
39
|
+
|
|
40
|
+
```sql
|
|
41
|
+
SHOW wal_level; -- must be 'logical'
|
|
42
|
+
SHOW max_replication_slots; -- must have available slots
|
|
43
|
+
SHOW max_wal_senders; -- must have available senders
|
|
44
|
+
|
|
45
|
+
-- Check how many slots are already in use
|
|
46
|
+
SELECT count(*) AS used_slots FROM pg_replication_slots;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**How many slots does a subscription need?**
|
|
50
|
+
|
|
51
|
+
During **initial sync**, Postgres creates one temporary replication slot per table being copied in parallel, plus one permanent slot for the subscription itself. For example, a subscription syncing 10 tables with `max_sync_workers_per_subscription = 2` (default) uses up to **3 slots** at peak: 1 permanent + 2 temporary for parallel table copy.
|
|
52
|
+
|
|
53
|
+
Once initial sync completes and the subscription enters **streaming (CDC) mode**, only the **1 permanent slot** per subscription is used. The temporary per-table slots are dropped.
|
|
54
|
+
|
|
55
|
+
**Rule of thumb**: `max_replication_slots` >= (number of subscriptions) + (max_sync_workers_per_subscription) + headroom for physical replication slots. The default of 10 is sufficient for most setups.
|
|
56
|
+
|
|
57
|
+
## Publisher Setup
|
|
58
|
+
|
|
59
|
+
### Create a Publication
|
|
60
|
+
|
|
61
|
+
```sql
|
|
62
|
+
-- Publish specific tables
|
|
63
|
+
CREATE PUBLICATION my_pub FOR TABLE orders, customers, products;
|
|
64
|
+
|
|
65
|
+
-- Publish all tables in a schema (PG15+)
|
|
66
|
+
CREATE PUBLICATION my_pub FOR TABLES IN SCHEMA public;
|
|
67
|
+
|
|
68
|
+
-- Publish all tables in the database
|
|
69
|
+
-- WARNING: FOR ALL TABLES prevents later ADD/DROP TABLE modifications.
|
|
70
|
+
-- Prefer listing tables explicitly if you may need to change the set later.
|
|
71
|
+
CREATE PUBLICATION my_pub FOR ALL TABLES;
|
|
72
|
+
|
|
73
|
+
-- Publish only specific operations
|
|
74
|
+
CREATE PUBLICATION inserts_only FOR TABLE events
|
|
75
|
+
WITH (publish = 'insert');
|
|
76
|
+
|
|
77
|
+
-- Publish with row filter (PG15+)
|
|
78
|
+
CREATE PUBLICATION active_orders FOR TABLE orders
|
|
79
|
+
WHERE (status = 'active');
|
|
80
|
+
|
|
81
|
+
-- Publish specific columns only (PG15+)
|
|
82
|
+
CREATE PUBLICATION partial_customers FOR TABLE customers (id, name, email);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Replica Identity
|
|
86
|
+
|
|
87
|
+
Logical replication needs a way to identify rows for UPDATE and DELETE. By default, it uses the primary key.
|
|
88
|
+
|
|
89
|
+
```sql
|
|
90
|
+
-- Check current replica identity
|
|
91
|
+
SELECT relname, relreplident
|
|
92
|
+
FROM pg_class
|
|
93
|
+
WHERE relname IN ('orders', 'customers');
|
|
94
|
+
-- 'd' = default (primary key), 'f' = full, 'n' = nothing, 'i' = index
|
|
95
|
+
|
|
96
|
+
-- If a table has no primary key, use FULL (sends entire old row)
|
|
97
|
+
ALTER TABLE legacy_table REPLICA IDENTITY FULL;
|
|
98
|
+
|
|
99
|
+
-- Or use a unique index
|
|
100
|
+
CREATE UNIQUE INDEX idx_legacy_key ON legacy_table(external_id);
|
|
101
|
+
ALTER TABLE legacy_table REPLICA IDENTITY USING INDEX idx_legacy_key;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Without a replica identity, UPDATE and DELETE will fail on the publisher** for that table.
|
|
105
|
+
|
|
106
|
+
### Grant Permissions to the Replication Role
|
|
107
|
+
|
|
108
|
+
The replication role needs schema access and SELECT on the published tables:
|
|
109
|
+
|
|
110
|
+
```sql
|
|
111
|
+
GRANT USAGE ON SCHEMA public TO repl_user;
|
|
112
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO repl_user;
|
|
113
|
+
|
|
114
|
+
-- Also grant for future tables
|
|
115
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO repl_user;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Decoder Plugins
|
|
119
|
+
|
|
120
|
+
Postgres supports logical decoding output plugins, including:
|
|
121
|
+
- **`pgoutput`** (default): built into Postgres, used by native logical replication
|
|
122
|
+
- **`wal2json`**: an optional third-party plugin that converts WAL to JSON format for CDC integrations
|
|
123
|
+
|
|
124
|
+
The decoder is specified when creating a replication slot manually:
|
|
125
|
+
|
|
126
|
+
```sql
|
|
127
|
+
SELECT pg_create_logical_replication_slot('my_slot', 'pgoutput');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
After installing `wal2json` on the database server:
|
|
131
|
+
|
|
132
|
+
```sql
|
|
133
|
+
SELECT pg_create_logical_replication_slot('my_slot', 'wal2json');
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
When using `CREATE SUBSCRIPTION`, the default `pgoutput` plugin is used automatically.
|
|
137
|
+
|
|
138
|
+
### List Publications
|
|
139
|
+
|
|
140
|
+
```sql
|
|
141
|
+
SELECT * FROM pg_publication;
|
|
142
|
+
|
|
143
|
+
-- See which tables are in a publication
|
|
144
|
+
SELECT pubname, schemaname, tablename
|
|
145
|
+
FROM pg_publication_tables
|
|
146
|
+
ORDER BY pubname, tablename;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Subscriber Setup
|
|
150
|
+
|
|
151
|
+
### Create a Subscription
|
|
152
|
+
|
|
153
|
+
```sql
|
|
154
|
+
-- Basic subscription (triggers initial data copy)
|
|
155
|
+
CREATE SUBSCRIPTION my_sub
|
|
156
|
+
CONNECTION 'host=publisher_host dbname=source_db user=repl_user password=...'
|
|
157
|
+
PUBLICATION my_pub;
|
|
158
|
+
|
|
159
|
+
-- Subscribe without initial data copy (tables already have data)
|
|
160
|
+
CREATE SUBSCRIPTION my_sub
|
|
161
|
+
CONNECTION 'host=publisher_host dbname=source_db user=repl_user password=...'
|
|
162
|
+
PUBLICATION my_pub
|
|
163
|
+
WITH (copy_data = false);
|
|
164
|
+
|
|
165
|
+
-- Subscribe to multiple publications
|
|
166
|
+
CREATE SUBSCRIPTION my_sub
|
|
167
|
+
CONNECTION 'host=publisher_host dbname=source_db user=repl_user password=...'
|
|
168
|
+
PUBLICATION pub_orders, pub_customers;
|
|
169
|
+
|
|
170
|
+
-- Create disabled (activate later)
|
|
171
|
+
CREATE SUBSCRIPTION my_sub
|
|
172
|
+
CONNECTION 'host=publisher_host dbname=source_db user=repl_user password=...'
|
|
173
|
+
PUBLICATION my_pub
|
|
174
|
+
WITH (enabled = false);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### List Subscriptions
|
|
178
|
+
|
|
179
|
+
```sql
|
|
180
|
+
SELECT * FROM pg_subscription;
|
|
181
|
+
|
|
182
|
+
-- Subscription status and worker info
|
|
183
|
+
SELECT subname, pid, relid::regclass, received_lsn, latest_end_lsn,
|
|
184
|
+
latest_end_time
|
|
185
|
+
FROM pg_stat_subscription;
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Managing Publications
|
|
189
|
+
|
|
190
|
+
### Add Tables
|
|
191
|
+
|
|
192
|
+
```sql
|
|
193
|
+
ALTER PUBLICATION my_pub ADD TABLE new_table;
|
|
194
|
+
|
|
195
|
+
-- Add with row filter (PG15+)
|
|
196
|
+
ALTER PUBLICATION my_pub ADD TABLE audit_log WHERE (created_at > '2024-01-01');
|
|
197
|
+
|
|
198
|
+
-- Add multiple tables at once
|
|
199
|
+
ALTER PUBLICATION my_pub ADD TABLE table_a, table_b, table_c;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Remove Tables
|
|
203
|
+
|
|
204
|
+
```sql
|
|
205
|
+
ALTER PUBLICATION my_pub DROP TABLE old_table;
|
|
206
|
+
|
|
207
|
+
-- Remove multiple
|
|
208
|
+
ALTER PUBLICATION my_pub DROP TABLE table_a, table_b;
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Replace Entire Table List
|
|
212
|
+
|
|
213
|
+
```sql
|
|
214
|
+
ALTER PUBLICATION my_pub SET TABLE orders, customers, products, shipments;
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Change Published Operations
|
|
218
|
+
|
|
219
|
+
```sql
|
|
220
|
+
-- Only publish inserts and updates (no deletes)
|
|
221
|
+
ALTER PUBLICATION my_pub SET (publish = 'insert, update');
|
|
222
|
+
|
|
223
|
+
-- Restore all operations
|
|
224
|
+
ALTER PUBLICATION my_pub SET (publish = 'insert, update, delete, truncate');
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### After Adding Tables — Refresh the Subscriber
|
|
228
|
+
|
|
229
|
+
After adding tables to a publication, the subscriber must refresh:
|
|
230
|
+
|
|
231
|
+
```sql
|
|
232
|
+
-- On the subscriber: pick up new tables and copy initial data
|
|
233
|
+
ALTER SUBSCRIPTION my_sub REFRESH PUBLICATION;
|
|
234
|
+
|
|
235
|
+
-- Refresh without copying data for new tables
|
|
236
|
+
ALTER SUBSCRIPTION my_sub REFRESH PUBLICATION WITH (copy_data = false);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Drop a Publication
|
|
240
|
+
|
|
241
|
+
```sql
|
|
242
|
+
DROP PUBLICATION my_pub;
|
|
243
|
+
-- or
|
|
244
|
+
DROP PUBLICATION IF EXISTS my_pub;
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Managing Subscriptions
|
|
248
|
+
|
|
249
|
+
### Enable / Disable
|
|
250
|
+
|
|
251
|
+
```sql
|
|
252
|
+
-- Pause replication
|
|
253
|
+
ALTER SUBSCRIPTION my_sub DISABLE;
|
|
254
|
+
|
|
255
|
+
-- Resume replication
|
|
256
|
+
ALTER SUBSCRIPTION my_sub ENABLE;
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Change Connection
|
|
260
|
+
|
|
261
|
+
```sql
|
|
262
|
+
ALTER SUBSCRIPTION my_sub CONNECTION 'host=new_host dbname=source_db user=repl_user password=...';
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Change Publications
|
|
266
|
+
|
|
267
|
+
```sql
|
|
268
|
+
-- Switch to different publications
|
|
269
|
+
ALTER SUBSCRIPTION my_sub SET PUBLICATION new_pub;
|
|
270
|
+
|
|
271
|
+
-- Add a publication
|
|
272
|
+
ALTER SUBSCRIPTION my_sub ADD PUBLICATION extra_pub;
|
|
273
|
+
|
|
274
|
+
-- Remove a publication
|
|
275
|
+
ALTER SUBSCRIPTION my_sub DROP PUBLICATION old_pub;
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Drop a Subscription
|
|
279
|
+
|
|
280
|
+
```sql
|
|
281
|
+
-- This also drops the replication slot on the publisher
|
|
282
|
+
DROP SUBSCRIPTION my_sub;
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
If the publisher is unreachable, disable first then drop:
|
|
286
|
+
|
|
287
|
+
```sql
|
|
288
|
+
ALTER SUBSCRIPTION my_sub DISABLE;
|
|
289
|
+
ALTER SUBSCRIPTION my_sub SET (slot_name = NONE);
|
|
290
|
+
DROP SUBSCRIPTION my_sub;
|
|
291
|
+
-- Then manually drop the orphaned slot on the publisher when it's back:
|
|
292
|
+
-- SELECT pg_drop_replication_slot('my_sub');
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Monitoring Replication Progress
|
|
296
|
+
|
|
297
|
+
### On the Publisher: Replication Slots and Lag
|
|
298
|
+
|
|
299
|
+
```sql
|
|
300
|
+
-- Replication slot status
|
|
301
|
+
SELECT
|
|
302
|
+
slot_name,
|
|
303
|
+
plugin,
|
|
304
|
+
slot_type,
|
|
305
|
+
active,
|
|
306
|
+
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS replication_lag
|
|
307
|
+
FROM pg_replication_slots
|
|
308
|
+
WHERE slot_type = 'logical';
|
|
309
|
+
|
|
310
|
+
-- Active WAL senders
|
|
311
|
+
SELECT
|
|
312
|
+
pid,
|
|
313
|
+
application_name,
|
|
314
|
+
client_addr,
|
|
315
|
+
state,
|
|
316
|
+
sent_lsn,
|
|
317
|
+
write_lsn,
|
|
318
|
+
flush_lsn,
|
|
319
|
+
replay_lsn,
|
|
320
|
+
pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS lag
|
|
321
|
+
FROM pg_stat_replication;
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### On the Subscriber: Subscription Status
|
|
325
|
+
|
|
326
|
+
```sql
|
|
327
|
+
-- Overall subscription status
|
|
328
|
+
SELECT
|
|
329
|
+
subname,
|
|
330
|
+
pid,
|
|
331
|
+
received_lsn,
|
|
332
|
+
latest_end_lsn,
|
|
333
|
+
latest_end_time,
|
|
334
|
+
last_msg_send_time,
|
|
335
|
+
last_msg_receipt_time
|
|
336
|
+
FROM pg_stat_subscription
|
|
337
|
+
WHERE subname IS NOT NULL;
|
|
338
|
+
|
|
339
|
+
-- Per-table sync state (initial copy progress)
|
|
340
|
+
SELECT
|
|
341
|
+
s.subname AS subscription,
|
|
342
|
+
sr.srrelid::regclass AS table_name,
|
|
343
|
+
sr.srsubstate AS state,
|
|
344
|
+
CASE sr.srsubstate
|
|
345
|
+
WHEN 'i' THEN 'initialize'
|
|
346
|
+
WHEN 'd' THEN 'data is being copied'
|
|
347
|
+
WHEN 'f' THEN 'finished table copy'
|
|
348
|
+
WHEN 's' THEN 'synchronized'
|
|
349
|
+
WHEN 'r' THEN 'ready (normal replication)'
|
|
350
|
+
ELSE 'unknown'
|
|
351
|
+
END AS state_meaning,
|
|
352
|
+
sr.srsublsn AS lsn
|
|
353
|
+
FROM pg_catalog.pg_subscription_rel AS sr
|
|
354
|
+
JOIN pg_catalog.pg_subscription AS s
|
|
355
|
+
ON s.oid = sr.srsubid
|
|
356
|
+
ORDER BY s.subname, sr.srrelid::regclass::text;
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**State codes for `pg_subscription_rel.srsubstate`:**
|
|
360
|
+
|
|
361
|
+
| Code | Meaning |
|
|
362
|
+
|------|---------|
|
|
363
|
+
| `i` | Initializing |
|
|
364
|
+
| `d` | Copying data (initial sync) |
|
|
365
|
+
| `f` | Finished table copy, waiting for sync |
|
|
366
|
+
| `s` | Synced with publisher |
|
|
367
|
+
| `r` | Ready (streaming) |
|
|
368
|
+
|
|
369
|
+
These codes describe each table's initialization state. `s` means the table synchronized during initialization; it does not prove that current replication lag is zero.
|
|
370
|
+
|
|
371
|
+
### Replication Lag Monitoring Query
|
|
372
|
+
|
|
373
|
+
Run on the subscriber to check how far behind it is:
|
|
374
|
+
|
|
375
|
+
```sql
|
|
376
|
+
-- Lag in bytes and time
|
|
377
|
+
SELECT
|
|
378
|
+
s.subname,
|
|
379
|
+
st.received_lsn,
|
|
380
|
+
st.latest_end_lsn,
|
|
381
|
+
st.latest_end_time,
|
|
382
|
+
now() - st.latest_end_time AS time_lag
|
|
383
|
+
FROM pg_subscription s
|
|
384
|
+
JOIN pg_stat_subscription st ON st.subid = s.oid
|
|
385
|
+
WHERE st.pid IS NOT NULL
|
|
386
|
+
AND st.relid IS NULL
|
|
387
|
+
-- PG17+ can also expose parallel apply workers; keep only the leader.
|
|
388
|
+
AND coalesce(to_jsonb(st)->>'worker_type', 'apply') = 'apply';
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Schema Changes During Replication
|
|
392
|
+
|
|
393
|
+
Logical replication does **NOT** replicate DDL. Schema changes must be applied manually on both sides.
|
|
394
|
+
|
|
395
|
+
### Safe Pattern for Adding a Column
|
|
396
|
+
|
|
397
|
+
```sql
|
|
398
|
+
-- 1. Add column on SUBSCRIBER first (nullable, no default)
|
|
399
|
+
ALTER TABLE orders ADD COLUMN priority int;
|
|
400
|
+
|
|
401
|
+
-- 2. Add column on PUBLISHER
|
|
402
|
+
ALTER TABLE orders ADD COLUMN priority int;
|
|
403
|
+
|
|
404
|
+
-- 3. New rows will now include the column
|
|
405
|
+
-- Existing replicated rows will have NULL for the new column
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**Add on subscriber first** to avoid errors when the publisher starts sending the new column before the subscriber schema is updated.
|
|
409
|
+
|
|
410
|
+
### Safe Pattern for Dropping a Column
|
|
411
|
+
|
|
412
|
+
```sql
|
|
413
|
+
-- 1. Remove column from PUBLICATION (PG15+, if using column lists)
|
|
414
|
+
ALTER PUBLICATION my_pub SET TABLE orders (id, customer_id, total, created_at);
|
|
415
|
+
|
|
416
|
+
-- 2. Drop column on PUBLISHER
|
|
417
|
+
ALTER TABLE orders DROP COLUMN old_column;
|
|
418
|
+
|
|
419
|
+
-- 3. Drop column on SUBSCRIBER
|
|
420
|
+
ALTER TABLE orders DROP COLUMN old_column;
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Adding a New Table to Replication
|
|
424
|
+
|
|
425
|
+
```sql
|
|
426
|
+
-- 1. Create table on SUBSCRIBER with matching schema
|
|
427
|
+
CREATE TABLE shipments (...);
|
|
428
|
+
|
|
429
|
+
-- 2. Add table to publication on PUBLISHER
|
|
430
|
+
ALTER PUBLICATION my_pub ADD TABLE shipments;
|
|
431
|
+
|
|
432
|
+
-- 3. Refresh on SUBSCRIBER (copies existing data)
|
|
433
|
+
ALTER SUBSCRIPTION my_sub REFRESH PUBLICATION;
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Live Migration Patterns
|
|
437
|
+
|
|
438
|
+
### Migration to a New Database (Minimal Downtime)
|
|
439
|
+
|
|
440
|
+
1. **Set up target**: Create schema and constraints on the new database. **Defer index creation** until after initial data copy completes — this significantly speeds up the initial sync.
|
|
441
|
+
2. **Start replication**: Create publication on source, subscription on target
|
|
442
|
+
3. **Wait for sync**: Monitor until all tables reach `r` (ready) state
|
|
443
|
+
4. **Verify**: Compare row counts, spot-check data
|
|
444
|
+
5. **Cutover**:
|
|
445
|
+
- Stop writes to source (set `default_transaction_read_only = on` or revoke write access)
|
|
446
|
+
- Wait for final lag to drain to zero
|
|
447
|
+
- Verify sequences: advance sequences on target to match source
|
|
448
|
+
- Switch application connection strings
|
|
449
|
+
6. **Cleanup**: Drop subscription, drop publication, drop replication slot
|
|
450
|
+
|
|
451
|
+
### Sequence Synchronization
|
|
452
|
+
|
|
453
|
+
Logical replication does **NOT** replicate sequences. Before cutover, sync them:
|
|
454
|
+
|
|
455
|
+
```sql
|
|
456
|
+
-- On SOURCE: get current sequence values
|
|
457
|
+
SELECT schemaname, sequencename, last_value
|
|
458
|
+
FROM pg_sequences
|
|
459
|
+
WHERE schemaname = 'public';
|
|
460
|
+
|
|
461
|
+
-- On TARGET: substitute the value obtained from the source.
|
|
462
|
+
-- Add a buffer only if writes can still reach the source during cutover.
|
|
463
|
+
SELECT pg_catalog.setval(
|
|
464
|
+
'public.orders_id_seq'::regclass,
|
|
465
|
+
<source_last_value> + 1000,
|
|
466
|
+
true
|
|
467
|
+
);
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Row Count Verification
|
|
471
|
+
|
|
472
|
+
```sql
|
|
473
|
+
-- Run on both source and target, compare results
|
|
474
|
+
SELECT
|
|
475
|
+
schemaname,
|
|
476
|
+
relname,
|
|
477
|
+
n_live_tup AS approx_row_count
|
|
478
|
+
FROM pg_stat_user_tables
|
|
479
|
+
WHERE schemaname = 'public'
|
|
480
|
+
ORDER BY relname;
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
For exact counts (slower):
|
|
484
|
+
|
|
485
|
+
```sql
|
|
486
|
+
CREATE TEMP TABLE exact_row_counts (
|
|
487
|
+
schemaname name NOT NULL,
|
|
488
|
+
tablename name NOT NULL,
|
|
489
|
+
exact_count bigint NOT NULL
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
DO $$
|
|
493
|
+
DECLARE r record;
|
|
494
|
+
BEGIN
|
|
495
|
+
FOR r IN
|
|
496
|
+
SELECT schemaname, tablename
|
|
497
|
+
FROM pg_catalog.pg_tables
|
|
498
|
+
WHERE schemaname = 'public'
|
|
499
|
+
ORDER BY tablename
|
|
500
|
+
LOOP
|
|
501
|
+
EXECUTE format(
|
|
502
|
+
'INSERT INTO pg_temp.exact_row_counts
|
|
503
|
+
SELECT %L::name, %L::name, count(*)
|
|
504
|
+
FROM %I.%I',
|
|
505
|
+
r.schemaname, r.tablename,
|
|
506
|
+
r.schemaname, r.tablename
|
|
507
|
+
);
|
|
508
|
+
END LOOP;
|
|
509
|
+
END $$;
|
|
510
|
+
|
|
511
|
+
SELECT
|
|
512
|
+
format('%I.%I', schemaname, tablename) AS table_name,
|
|
513
|
+
exact_count
|
|
514
|
+
FROM pg_temp.exact_row_counts
|
|
515
|
+
ORDER BY schemaname, tablename;
|
|
516
|
+
|
|
517
|
+
DROP TABLE pg_temp.exact_row_counts;
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Run the exact-count query on both source and target only after stopping writes and allowing replication lag to drain. It performs a full scan of every selected table.
|
|
521
|
+
|
|
522
|
+
## Troubleshooting
|
|
523
|
+
|
|
524
|
+
### Replication Slot Growing / WAL Accumulation
|
|
525
|
+
|
|
526
|
+
If a subscriber falls behind or is disconnected, WAL accumulates on the publisher:
|
|
527
|
+
|
|
528
|
+
```sql
|
|
529
|
+
-- Check slot lag on publisher
|
|
530
|
+
SELECT slot_name, active,
|
|
531
|
+
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
|
|
532
|
+
FROM pg_replication_slots;
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Fix**: Reconnect the subscriber, or drop the slot if the subscription is no longer needed:
|
|
536
|
+
|
|
537
|
+
```sql
|
|
538
|
+
-- Drop an inactive slot (publisher)
|
|
539
|
+
SELECT pg_drop_replication_slot('orphaned_slot_name');
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Initial Sync Stuck or Slow
|
|
543
|
+
|
|
544
|
+
```sql
|
|
545
|
+
-- Check which tables are still copying
|
|
546
|
+
SELECT srrelid::regclass AS table_name, srsubstate AS state
|
|
547
|
+
FROM pg_subscription_rel
|
|
548
|
+
WHERE srsubstate != 'r';
|
|
549
|
+
-- 'd' = still copying data, 'i' = initializing
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
Initial sync speed depends on table size and network. For very large tables, consider:
|
|
553
|
+
- Dump/restore the data first, then create subscription with `copy_data = false`
|
|
554
|
+
- Increase `max_logical_replication_workers` if many tables need initial sync in parallel
|
|
555
|
+
|
|
556
|
+
### Conflict Errors
|
|
557
|
+
|
|
558
|
+
If the subscriber has conflicting data (e.g., duplicate key):
|
|
559
|
+
|
|
560
|
+
```sql
|
|
561
|
+
-- PG15+: cumulative subscription apply-error counters
|
|
562
|
+
SELECT * FROM pg_stat_subscription_stats;
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
This view reports counters rather than detailed error messages. Check PostgreSQL server logs for the specific conflict; on PG14, logs are the primary source because `pg_stat_subscription_stats` is unavailable.
|
|
566
|
+
|
|
567
|
+
**PG16+**: Skip a conflicting transaction:
|
|
568
|
+
|
|
569
|
+
```sql
|
|
570
|
+
ALTER SUBSCRIPTION my_sub SKIP (lsn = '0/12345678');
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
**Pre-PG16**: Delete the conflicting row on the subscriber, then replication will proceed.
|
|
574
|
+
|
|
575
|
+
### "Publisher Does Not Exist" After Refresh
|
|
576
|
+
|
|
577
|
+
If you renamed or recreated a publication:
|
|
578
|
+
|
|
579
|
+
```sql
|
|
580
|
+
-- Subscriber needs to be pointed to the new publication name
|
|
581
|
+
ALTER SUBSCRIPTION my_sub SET PUBLICATION new_pub_name;
|
|
582
|
+
ALTER SUBSCRIPTION my_sub REFRESH PUBLICATION;
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Inactive Slot Timeout (PG18+)
|
|
586
|
+
|
|
587
|
+
PG18 adds `idle_replication_slot_timeout` to automatically invalidate inactive replication slots, preventing unbounded WAL accumulation:
|
|
588
|
+
|
|
589
|
+
```sql
|
|
590
|
+
SHOW idle_replication_slot_timeout; -- auto-invalidates stale slots
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Generated Column Replication (PG18+)
|
|
594
|
+
|
|
595
|
+
Logical replication can now replicate generated column values via the `publish_generated_columns` publication option:
|
|
596
|
+
|
|
597
|
+
```sql
|
|
598
|
+
CREATE PUBLICATION my_pub FOR TABLE orders
|
|
599
|
+
WITH (publish_generated_columns = true);
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### Check if wal_level is Logical
|
|
603
|
+
|
|
604
|
+
```sql
|
|
605
|
+
SHOW wal_level;
|
|
606
|
+
-- If not 'logical', it requires a restart to change
|
|
607
|
+
-- In postgresql.conf: wal_level = logical
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
On managed platforms, `wal_level` may be controlled by the platform's settings UI or API rather than `postgresql.conf`.
|