]> begriffs open source - pg_scribe/blob - doc/cli.md
More little refactors
[pg_scribe] / doc / cli.md
1 # pg_scribe Command Line Interface
2
3 This document describes the command line interface for **pg_scribe**, an incremental SQL backup system for PostgreSQL.
4
5 ## Overview
6
7 pg_scribe uses action flags similar to other PostgreSQL tools like `pg_recvlogical`. The tool performs one primary action per invocation, specified by action flags.
8
9 ## Synopsis
10
11 ```bash
12 # Initialize backup system
13 pg_scribe --init [OPTIONS]
14
15 # Start streaming incremental backups
16 pg_scribe --start [OPTIONS]
17
18 # Take a full backup
19 pg_scribe --full-backup [OPTIONS]
20
21 # Restore from backups
22 pg_scribe --restore [OPTIONS]
23
24 # Check replication slot status
25 pg_scribe --status [OPTIONS]
26 ```
27
28 ## Common Options
29
30 These options apply to all actions:
31
32 ### Connection Options
33
34 ```
35 -d, --dbname=DBNAME          Database name (can be a connection string)
36 -h, --host=HOSTNAME          Database server host (default: localhost)
37 -p, --port=PORT              Database server port (default: 5432)
38 -U, --username=USERNAME      Database user (default: $PGUSER or $USER)
39 -w, --no-password            Never prompt for password
40 -W, --password               Force password prompt
41 ```
42
43 ### General Options
44
45 ```
46 -v, --verbose                Enable verbose mode
47 -V, --version                Print version and exit
48 -?, --help                   Show help and exit
49 ```
50
51 ## Action Flags
52
53 Exactly one of the following action flags must be specified:
54
55 ### `--init`
56
57 Initialize the backup system by creating a replication slot, setting up DDL capture via the wal2sql extension, and taking an initial base backup.
58
59 **This is a one-time initialization operation** - following PostgreSQL conventions (`initdb`, `pg_basebackup`), it requires an empty backup directory and will fail if already initialized. If initialization fails, partial state is automatically cleaned up (replication slot dropped, partial backup files removed).
60
61 **Additional options:**
62
63 ```
64 -f, --file=DIRECTORY         Backup output directory (required)
65 -S, --slot=SLOTNAME          Replication slot name (default: pg_scribe)
66     --force                  Skip validation and force initialization (dangerous!)
67 ```
68
69 **What it does:**
70
71 **Phase 1: Validation** (runs first, can fail)
72
73 1. **CRITICAL Checks** (must pass or initialization fails):
74    - Verify `wal_level = logical`
75    - Verify `max_replication_slots >= 1`
76    - Verify `max_wal_senders >= 1`
77    - Check all tables have adequate replica identity (PRIMARY KEY, USING INDEX, or FULL)
78
79 2. **Coverage Warnings** (non-blocking, informational):
80    - List unlogged tables (will not be backed up)
81    - Check for large objects (not incrementally backed up)
82
83 **Phase 2: Setup** (only runs if validation passes or `--force` used)
84
85 1. Verifies backup directory is empty (or doesn't exist)
86 2. Creates the wal2sql extension if it doesn't exist (`CREATE EXTENSION IF NOT EXISTS wal2sql;`)
87    - This automatically installs the DDL event trigger
88 3. Creates a logical replication slot (fails if slot already exists)
89 4. Takes synchronized base backup using `pg_dump`
90 5. Creates initial `pg_dumpall --globals-only` backup
91 6. Generates metadata file with PostgreSQL version, extensions, encoding
92
93 **Example:**
94
95 ```bash
96 pg_scribe --init -d mydb -f /backups/mydb -S mydb_backup
97 ```
98
99 **Output should convey:**
100
101 - **Validation results** with clear pass/fail status for:
102   - PostgreSQL configuration (wal_level, max_replication_slots, max_wal_senders)
103   - Replica identity for all tables
104   - Coverage warnings (unlogged tables, large objects)
105 - **If validation fails**: List CRITICAL issues with specific fix commands, then exit with error code 5 (unless `--force` used)
106 - **If validation passes** (or `--force`): Progress through setup steps (extension creation, slot creation with snapshot ID, backup paths)
107 - **Final status**: Success message or warning if forced past validation failures
108
109 ---
110
111 ### `--start`
112
113 Start streaming incremental backups continuously from a replication slot.
114
115 **Additional options:**
116
117 ```
118 -f, --file=FILENAME          Output file (use '-' for stdout, required)
119 -S, --slot=SLOTNAME          Replication slot name (default: pg_scribe)
120 -s, --status-interval=SECS   Status update interval in seconds (default: 10)
121 -F, --fsync-interval=SECS    Fsync interval in seconds (default: 10, 0 to disable)
122 ```
123
124 **What it does:**
125
126 1. Validates database connection and replication slot
127 2. Displays configuration (database, slot name, output file, intervals)
128 3. Uses `exec` to replace itself with `pg_recvlogical`, which then:
129    - Streams decoded changes using the wal2sql plugin
130    - Writes SQL to output file
131    - Periodically fsyncs output file for crash safety
132    - Reports LSN position and lag to stderr
133    - Responds to SIGHUP by closing and reopening output file (for log rotation)
134
135 **Implementation:** `pg_scribe --start` is a thin wrapper that validates prerequisites and uses `exec` to become `pg_recvlogical`. This design has several advantages:
136 - **No orphaned processes**: pg_scribe becomes pg_recvlogical (same PID), eliminating parent-child complexity
137 - **Direct signal handling**: SIGHUP, SIGTERM, SIGINT go directly to pg_recvlogical without forwarding
138 - **Simpler code**: No need for signal forwarding, child process tracking, or wait loops
139 - **Reliable cleanup**: Tests and process managers interact with a single process
140
141 **Log rotation support:** File rotation is provided automatically by `pg_recvlogical`:
142
143 1. Rename the current output file
144 2. Send SIGHUP to the pg_scribe/pg_recvlogical process
145 3. pg_recvlogical closes the old file and opens a new one with the same name
146
147 **Example:**
148
149 ```bash
150 # Stream to a file (foreground)
151 pg_scribe --start -d mydb -f /backups/mydb/incremental.sql -S mydb_backup
152
153 # Run as background daemon (redirect stderr to log file)
154 pg_scribe --start -d mydb -f /backups/mydb/incremental.sql -S mydb_backup \
155   2>/var/log/pg_scribe.log &
156
157 # Stream to stdout (for processing with other tools)
158 pg_scribe --start -d mydb -f - -S mydb_backup > /backups/mydb/incremental.sql
159 ```
160
161 **Log rotation:**
162
163 ```bash
164 #!/bin/bash
165 BACKUP_FILE="/backups/mydb/incremental.sql"
166 mv "$BACKUP_FILE" "$BACKUP_FILE.$(date +%Y%m%d)"
167 killall -SIGHUP pg_scribe
168 ```
169
170 **Output (to stderr):** Connection status, output file path, periodic LSN position and lag, file rotation events
171
172 ---
173
174 ### `--full-backup`
175
176 Take a full backup using `pg_dump` and `pg_dumpall --globals-only`.
177
178 **Additional options:**
179
180 ```
181 -f, --file=DIRECTORY         Backup output directory (required)
182 -Z, --compress=METHOD        Compression method: gzip, lz4, zstd, or none
183                              Can include level (e.g., zstd:9) (default: gzip)
184 ```
185
186 **What it does:**
187
188 1. Takes full `pg_dump` backup
189 2. Takes `pg_dumpall --globals-only` backup
190 3. Generates metadata file
191 4. Compresses backups if enabled
192
193 **Example:**
194
195 ```bash
196 pg_scribe --full-backup -d mydb -f /backups/mydb
197 pg_scribe --full-backup -d mydb -f /backups/mydb --compress=zstd:9
198 ```
199
200 **Output should convey:** Progress for pg_dump and pg_dumpall steps, file paths, backup size (compressed and uncompressed if applicable)
201
202 ---
203
204 ### `--restore`
205
206 Restore database from base backup plus incremental backups.
207
208 **Additional options:**
209
210 ```
211 -f, --file=DIRECTORY         Backup input directory (required)
212 -d, --dbname=DBNAME          Target database name (required)
213 -C, --create                 Create target database
214 --base-backup=FILENAME       Specific base backup file (default: latest)
215 --no-sync-sequences          Skip sequence synchronization
216 ```
217
218 **What it does:**
219
220 1. Locates base backup (latest or specified)
221 2. Finds all incremental backups since base
222 3. Creates target database (if `--create` specified)
223 4. Restores globals (roles, tablespaces)
224 5. Restores base backup
225 6. Applies incremental backups in chronological order
226 7. Synchronizes sequences using `setval()` (unless `--no-sync-sequences`)
227 8. Reports basic statistics
228
229 **Example:**
230
231 ```bash
232 # Restore latest to new database
233 pg_scribe --restore -f /backups/mydb -d mydb_restored --create
234
235 # Restore specific base backup
236 pg_scribe --restore -f /backups/mydb -d mydb_restored \
237   --base-backup=/backups/mydb/base-2024-01-10.sql
238 ```
239
240 **Output should convey:** Base backup identified, number of incremental backups to apply, progress through each restore phase (globals, base, incrementals with counts), sequence synchronization details, final statistics (duration, row counts)
241
242 ---
243
244 ### `--status`
245
246 Check replication slot health and backup system status.
247
248 **Additional options:**
249
250 ```
251 -S, --slot=SLOTNAME          Replication slot name (default: pg_scribe)
252 -f, --file=DIRECTORY         Backup directory to analyze (optional)
253 ```
254
255 **What it does:**
256
257 1. Queries `pg_replication_slots` for slot health
258 2. Shows replication lag and WAL retention
259 3. Analyzes backup directory if provided
260 4. Reports warnings about potential issues
261
262 **Example:**
263
264 ```bash
265 pg_scribe --status -d mydb -S mydb_backup -f /backups/mydb
266 ```
267
268 **Output should convey:** Replication slot details (active status, LSN positions, lag, WAL retention, age), backup directory analysis (base backup count and dates, incremental file count and size, last backup timestamp, coverage continuity), overall health status with warnings if applicable
269
270 ---
271
272 ## Exit Status
273
274 ```
275 0   Success
276 1   General error
277 2   Database connection error
278 3   Replication slot error
279 4   Backup/restore error
280 5   Invalid arguments or validation failure (--init without --force)
281 10  Warning conditions (--status only)
282 ```
283
284 ---
285
286 ## Environment Variables
287
288 Standard PostgreSQL environment variables are supported:
289
290 ```
291 PGHOST              Database host
292 PGPORT              Database port
293 PGDATABASE          Database name
294 PGUSER              Database user
295 PGPASSWORD          Database password (not recommended, use .pgpass instead)
296 PG_COLOR            Use color in diagnostics: always, auto, or never
297 ```
298
299 ---
300
301 ## Example Workflows
302
303 ### Initial Setup and Daily Operation
304
305 ```bash
306 # 1. Initialize backup system (one-time setup, requires empty directory)
307 pg_scribe --init -d production -f /backups/production -S prod_backup
308
309 # 2. Start streaming backups (run as daemon/service)
310 pg_scribe --start -d production -f /backups/production/incremental.sql \
311   -S prod_backup 2>/var/log/pg_scribe.log &
312
313 # 3. Set up log rotation (logrotate or custom script)
314 cat > /etc/logrotate.d/pg_scribe <<EOF
315 /backups/production/incremental.sql {
316     daily
317     rotate 7
318     dateext
319     postrotate
320         killall -SIGHUP pg_scribe
321     endscript
322 }
323 EOF
324
325 # 4. Schedule daily full backups (cron: 0 2 * * *)
326 pg_scribe --full-backup -d production -f /backups/production
327
328 # 5. Monitor slot health (cron: */15 * * * *)
329 pg_scribe --status -d production -S prod_backup
330 ```
331
332 ### Disaster Recovery
333
334 ```bash
335 # 1. Check available backups
336 pg_scribe --status -f /backups/production
337
338 # 2. Restore to new database
339 pg_scribe --restore -f /backups/production \
340   -d production_restored --create
341
342 # 3. Test restored database
343 psql -d production_restored -c "SELECT COUNT(*) FROM users;"
344
345 # 4. Switch application to restored database (manual step)
346 ```
347
348 ---
349
350 ## Comparison with PostgreSQL Tools
351
352 pg_scribe follows the same CLI conventions as core PostgreSQL tools:
353
354 | Tool | Action Flags | Connection Options | File Options |
355 |------|-------------|-------------------|--------------|
356 | **pg_recvlogical** | `--create-slot`, `--drop-slot`, `--start` | `-d`, `-h`, `-p`, `-U` | `-f` (output file), `-S` (slot) |
357 | **pg_dump** | (positional dbname) | `-d`, `-h`, `-p`, `-U` | `-f` (output file) |
358 | **pg_basebackup** | (none) | `-d`, `-h`, `-p`, `-U` | `-D` (data directory!) |
359 | **pg_restore** | (none) | `-d`, `-h`, `-p`, `-U` | positional (archive file) |
360 | **pg_scribe** | `--init`, `--start`, `--full-backup`, `--restore`, `--status` | `-d`, `-h`, `-p`, `-U` | `-f` (file/directory), `-S` (slot) |
361
362 **Note:** `pg_basebackup` uses `-D/--pgdata` because it creates an actual PostgreSQL data directory cluster. `pg_scribe` uses `-f/--file` like `pg_dump` and `pg_recvlogical` because it creates backup files.
363
364 ---
365
366 ## Implementation Notes
367
368 ### Design Principles
369
370 1. **Consistent with PostgreSQL**: Follow exact same conventions as `pg_recvlogical`, `pg_dump`, `initdb`, `pg_basebackup`
371 2. **Fail-fast initialization**: `--init` requires empty directory, cleans up on failure (like `initdb` and `pg_basebackup`)
372 3. **Clear output**: Progress to stderr, data to stdout (when using `-f -`)
373 4. **Scriptable**: Clear text output format, proper exit codes
374
375 ### Technology Choices (POC)
376
377 - **Language**: Bash or Python
378   - Bash: Minimal dependencies, matches PostgreSQL tool style
379   - Python: Better error handling, easier testing
380 - **Dependencies**: Only PostgreSQL client tools (pg_recvlogical, pg_dump, pg_dumpall, psql)
381
382 ### Key Implementation Components
383
384 1. **Connection management**: Use libpq environment variables, .pgpass
385 2. **Error handling**: Validate prerequisites before starting operations
386 3. **File management**:
387    - For `--start`: Invoke `pg_recvlogical` with file specified by `-f`
388    - For `--full-backup`: Write to directory specified by `-f`
389    - For `--restore`: Read from directory specified by `-f`
390 4. **Signal handling**:
391    - SIGTERM/SIGINT for graceful shutdown (handled directly by pg_recvlogical after exec)
392    - SIGHUP for file rotation (--start only, handled directly by pg_recvlogical after exec)
393 5. **Output conventions**:
394    - Progress and status messages → stderr
395    - SQL output → file specified by `-f` (or stdout if `-f -`)
396 6. **Process management for --start**:
397    - Validate prerequisites (connection, replication slot)
398    - Display configuration to stderr
399    - Use `exec` to replace pg_scribe with `pg_recvlogical`
400    - Benefits: No orphaned processes, direct signal handling, simpler code, same PID
401
402 **Extensibility Note**: This POC design uses a modular, action-based CLI that can accommodate additional features and options in future versions without breaking compatibility.