]> begriffs open source - pg_scribe/blob - doc/cli.md
Initial attempt at cli design
[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 operation is idempotent** - it can be safely re-run if initialization fails partway through. Existing slots and extensions will not cause errors.
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 ```
67
68 **What it does:**
69
70 1. Creates the wal2sql extension if it doesn't exist (`CREATE EXTENSION IF NOT EXISTS wal2sql;`)
71    - This automatically installs the DDL event trigger
72 2. Creates a logical replication slot with snapshot export (skips if slot exists)
73 3. Takes synchronized base backup using `pg_dump --snapshot`
74 4. Creates initial `pg_dumpall --globals-only` backup
75 5. Generates metadata file with PostgreSQL version, extensions, encoding
76 6. Prints replica identity warnings for tables without primary keys
77
78 **Example:**
79
80 ```bash
81 pg_scribe --init -d mydb -f /backups/mydb -S mydb_backup
82 ```
83
84 **Output:**
85
86 ```
87 Creating wal2sql extension...
88 Extension 'wal2sql' created (includes DDL event trigger).
89 Creating replication slot 'mydb_backup'...
90 Snapshot exported: 00000003-00000001-1
91 Taking base backup...
92 Base backup saved to: /backups/mydb/base-2024-01-15.sql
93 Capturing globals...
94 Globals saved to: /backups/mydb/globals-2024-01-15.sql
95 Writing metadata...
96
97 Initialization complete!
98
99 Warnings:
100   Table 'public.logs' has no replica identity - UPDATE/DELETE will fail
101   Run: ALTER TABLE public.logs REPLICA IDENTITY FULL;
102 ```
103
104 **Output when re-running (idempotent):**
105
106 ```
107 Checking wal2sql extension...
108 Extension 'wal2sql' already exists.
109 Checking replication slot 'mydb_backup'...
110 Slot 'mydb_backup' already exists, skipping creation.
111 Taking base backup...
112 Base backup saved to: /backups/mydb/base-2024-01-15.sql
113 Capturing globals...
114 Globals saved to: /backups/mydb/globals-2024-01-15.sql
115 Writing metadata...
116
117 Initialization complete!
118 ```
119
120 ---
121
122 ### `--start`
123
124 Start streaming incremental backups continuously from a replication slot.
125
126 **Additional options:**
127
128 ```
129 -f, --file=FILENAME          Output file (use '-' for stdout, required)
130 -S, --slot=SLOTNAME          Replication slot name (default: pg_scribe)
131 -s, --status-interval=SECS   Status update interval in seconds (default: 10)
132 -F, --fsync-interval=SECS    Fsync interval in seconds (default: 10, 0 to disable)
133 -n, --no-loop                Don't retry on connection failure
134 ```
135
136 **What it does:**
137
138 1. Wraps `pg_recvlogical` to stream from the specified replication slot
139 2. Streams decoded changes using the wal2sql plugin
140 3. Writes SQL to output file
141 4. Periodically fsyncs output file for crash safety
142 5. Reports LSN position and lag to stderr
143 6. Responds to SIGHUP by closing and reopening output file (for log rotation)
144
145 **Implementation:** `pg_scribe --start` is a thin wrapper around `pg_recvlogical --start` that:
146 - Invokes `pg_recvlogical` with the `wal2sql` plugin and appropriate options
147 - Forwards signals (SIGHUP, SIGTERM, SIGINT) to the child process
148 - Provides consistent interface with other pg_scribe commands
149
150 **Log rotation support:** File rotation is provided automatically by `pg_recvlogical`:
151
152 1. Rename the current output file
153 2. Send SIGHUP to pg_scribe process (which forwards it to pg_recvlogical)
154 3. pg_recvlogical closes the old file and opens a new one with the same name
155
156 **Example:**
157
158 ```bash
159 # Stream to a file (foreground)
160 pg_scribe --start -d mydb -f /backups/mydb/incremental.sql -S mydb_backup
161
162 # Run as background daemon (redirect stderr to log file)
163 pg_scribe --start -d mydb -f /backups/mydb/incremental.sql -S mydb_backup \
164   2>/var/log/pg_scribe.log &
165
166 # Stream to stdout (for processing with other tools)
167 pg_scribe --start -d mydb -f - -S mydb_backup > /backups/mydb/incremental.sql
168 ```
169
170 **Log rotation script example:**
171
172 ```bash
173 #!/bin/bash
174 # Rotate incremental backup file
175 BACKUP_FILE="/backups/mydb/incremental.sql"
176 mv "$BACKUP_FILE" "$BACKUP_FILE.$(date +%Y%m%d)"
177 killall -SIGHUP pg_scribe
178 ```
179
180 **Output (to stderr):**
181
182 Progress and status messages are written to stderr (following PostgreSQL tool conventions). When running as a background daemon, redirect stderr to a log file using `2>`.
183
184 ```
185 Connecting to replication slot 'mydb_backup'...
186 Streaming to: /backups/mydb/incremental.sql
187 LSN: 0/1A2B3C4D  Lag: 128 bytes
188 LSN: 0/1A2B3C8F  Lag: 0 bytes
189 File rotated (SIGHUP received)
190 Streaming to: /backups/mydb/incremental.sql
191 ...
192 ```
193
194 ---
195
196 ### `--full-backup`
197
198 Take a full backup using `pg_dump` and `pg_dumpall --globals-only`.
199
200 **Additional options:**
201
202 ```
203 -f, --file=DIRECTORY         Backup output directory (required)
204 -Z, --compress=METHOD        Compression method: gzip, lz4, zstd, or none
205                              Can include level (e.g., zstd:9) (default: zstd:9)
206 ```
207
208 **What it does:**
209
210 1. Takes full `pg_dump` backup
211 2. Takes `pg_dumpall --globals-only` backup
212 3. Generates metadata file
213 4. Compresses backups if enabled
214
215 **Example:**
216
217 ```bash
218 pg_scribe --full-backup -d mydb -f /backups/mydb
219
220 # With specific compression
221 pg_scribe --full-backup -d mydb -f /backups/mydb --compress=zstd:9
222 ```
223
224 **Output:**
225
226 ```
227 Taking full backup...
228 Full backup saved to: /backups/mydb/base-2024-01-15.sql.zst
229 Capturing globals...
230 Globals saved to: /backups/mydb/globals-2024-01-15.sql
231 Writing metadata...
232
233 Full backup complete!
234 Size: 1.2 GB (compressed from 5.4 GB)
235 ```
236
237 ---
238
239 ### `--restore`
240
241 Restore database from base backup plus incremental backups.
242
243 **Additional options:**
244
245 ```
246 -f, --file=DIRECTORY         Backup input directory (required)
247 -d, --dbname=DBNAME          Target database name (required)
248 -C, --create                 Create target database
249 -c, --clean                  Drop target database before creating (requires -C)
250 --base-backup=FILENAME       Specific base backup file (default: latest)
251 --recovery-target-time=TIME  Restore until timestamp (format: YYYY-MM-DD HH:MM:SS)
252 --recovery-target-name=NAME  Restore until specific incremental file
253 -j, --jobs=NJOBS             Parallel restore jobs (default: 1)
254 --no-sync-sequences          Skip sequence synchronization
255 ```
256
257 **What it does:**
258
259 1. Locates base backup (latest or specified)
260 2. Finds all incremental backups since base
261 3. Creates target database (if `--create` specified)
262 4. Restores globals (roles, tablespaces)
263 5. Restores base backup
264 6. Applies incremental backups in chronological order
265 7. Synchronizes sequences using `setval()` (unless `--no-sync-sequences`)
266 8. Reports basic statistics
267
268 **Example:**
269
270 ```bash
271 # Restore latest to new database
272 pg_scribe --restore -f /backups/mydb -d mydb_restored --create
273
274 # Restore to specific point in time
275 pg_scribe --restore -f /backups/mydb -d mydb_restored \
276   --recovery-target-time="2024-01-15 14:30:00"
277
278 # Restore specific base backup
279 pg_scribe --restore -f /backups/mydb -d mydb_restored \
280   --base-backup=/backups/mydb/base-2024-01-10.sql
281 ```
282
283 **Output:**
284
285 ```
286 Located base backup: base-2024-01-10.sql (5.4 GB)
287 Found 5 incremental backups to apply
288 Creating database 'mydb_restored'...
289 Restoring globals...
290 Restoring base backup... (5 minutes)
291 Applying incremental backup 1/5: incremental-2024-01-10.sql
292 Applying incremental backup 2/5: incremental-2024-01-11.sql
293 Applying incremental backup 3/5: incremental-2024-01-12.sql
294 Applying incremental backup 4/5: incremental-2024-01-13.sql
295 Applying incremental backup 5/5: incremental-2024-01-14.sql
296 Synchronizing sequences...
297   users_id_seq: set to 12845
298   orders_id_seq: set to 98234
299   products_id_seq: set to 456
300
301 Restore complete!
302 Duration: 8 minutes
303 Rows restored: 1,284,567
304 ```
305
306 ---
307
308 ### `--status`
309
310 Check replication slot health and backup system status.
311
312 **Additional options:**
313
314 ```
315 -S, --slot=SLOTNAME          Replication slot name (default: pg_scribe)
316 -f, --file=DIRECTORY         Backup directory to analyze (optional)
317 --format=FORMAT              Output format: text or json (default: text)
318 ```
319
320 **What it does:**
321
322 1. Queries `pg_replication_slots` for slot health
323 2. Shows replication lag and WAL retention
324 3. Analyzes backup directory if provided
325 4. Reports warnings about potential issues
326
327 **Example:**
328
329 ```bash
330 pg_scribe --status -d mydb -S mydb_backup -f /backups/mydb
331 ```
332
333 **Output:**
334
335 ```
336 Replication Slot: mydb_backup
337   Status:          active
338   Database:        mydb
339   Plugin:          wal2sql
340   Restart LSN:     0/1A2B3C4D
341   Confirmed LSN:   0/1A2B3C8F
342   Replication lag: 128 bytes
343   WAL retention:   2.1 GB
344   Slot age:        2 days 14 hours
345
346 Backup Directory: /backups/mydb
347   Base backups:    5 (oldest: 2024-01-01, newest: 2024-01-15)
348   Incremental:     45 files (12.4 GB total)
349   Last backup:     2024-01-15 14:23:01 (2 minutes ago)
350   Coverage:        Continuous since 2024-01-01
351
352 Status: OK
353 ```
354
355 **Warning example:**
356
357 ```
358 Status: WARNING
359
360 Warnings:
361   Replication lag is 1.2 GB (threshold: 1.0 GB)
362   Slot has been inactive for 6 hours
363   Last incremental backup is 2 hours old
364 ```
365
366 ---
367
368 ## Size and Time Specifications
369
370 Size values accept suffixes:
371 - `K` or `KB` - kilobytes
372 - `M` or `MB` - megabytes
373 - `G` or `GB` - gigabytes
374
375 Time intervals accept suffixes:
376 - `s` - seconds
377 - `m` - minutes
378 - `h` - hours
379 - `d` - days
380
381 **Examples:**
382 - `--compress=zstd:9` (zstd compression, level 9)
383 - `--fsync-interval=10` (10 seconds)
384
385 ---
386
387 ## Exit Status
388
389 ```
390 0   Success
391 1   General error
392 2   Database connection error
393 3   Replication slot error
394 4   Backup/restore error
395 5   Invalid arguments
396 10  Warning conditions (--status only)
397 ```
398
399 ---
400
401 ## Environment Variables
402
403 Standard PostgreSQL environment variables are supported:
404
405 ```
406 PGHOST              Database host
407 PGPORT              Database port
408 PGDATABASE          Database name
409 PGUSER              Database user
410 PGPASSWORD          Database password (not recommended, use .pgpass instead)
411 PG_COLOR            Use color in diagnostics: always, auto, or never
412 ```
413
414 ---
415
416 ## Example Workflows
417
418 ### Initial Setup and Daily Operation
419
420 ```bash
421 # 1. Initialize backup system (idempotent - safe to re-run)
422 pg_scribe --init -d production -f /backups/production -S prod_backup
423
424 # 2. Start streaming backups (run as daemon/service)
425 pg_scribe --start -d production -f /backups/production/incremental.sql \
426   -S prod_backup 2>/var/log/pg_scribe.log &
427
428 # 3. Set up log rotation (logrotate or custom script)
429 cat > /etc/logrotate.d/pg_scribe <<EOF
430 /backups/production/incremental.sql {
431     daily
432     rotate 7
433     dateext
434     postrotate
435         killall -SIGHUP pg_scribe
436     endscript
437 }
438 EOF
439
440 # 4. Schedule daily full backups (cron: 0 2 * * *)
441 pg_scribe --full-backup -d production -f /backups/production
442
443 # 5. Monitor slot health (cron: */15 * * * *)
444 pg_scribe --status -d production -S prod_backup
445 ```
446
447 ### Disaster Recovery
448
449 ```bash
450 # 1. Check available backups
451 pg_scribe --status -f /backups/production
452
453 # 2. Restore to new database
454 pg_scribe --restore -f /backups/production \
455   -d production_restored --create
456
457 # 3. Test restored database
458 psql -d production_restored -c "SELECT COUNT(*) FROM users;"
459
460 # 4. Switch application to restored database (manual step)
461 ```
462
463 ### Point-in-Time Recovery
464
465 ```bash
466 # Restore to specific timestamp
467 pg_scribe --restore -f /backups/production \
468   -d production_pit --create \
469   --recovery-target-time="2024-01-15 09:30:00"
470 ```
471
472 ---
473
474 ## Comparison with PostgreSQL Tools
475
476 pg_scribe follows the same CLI conventions as core PostgreSQL tools:
477
478 | Tool | Action Flags | Connection Options | File Options |
479 |------|-------------|-------------------|--------------|
480 | **pg_recvlogical** | `--create-slot`, `--drop-slot`, `--start` | `-d`, `-h`, `-p`, `-U` | `-f` (output file), `-S` (slot) |
481 | **pg_dump** | (positional dbname) | `-d`, `-h`, `-p`, `-U` | `-f` (output file) |
482 | **pg_basebackup** | (none) | `-d`, `-h`, `-p`, `-U` | `-D` (data directory!) |
483 | **pg_restore** | (none) | `-d`, `-h`, `-p`, `-U` | positional (archive file) |
484 | **pg_scribe** | `--init`, `--start`, `--full-backup`, `--restore`, `--status` | `-d`, `-h`, `-p`, `-U` | `-f` (file/directory), `-S` (slot) |
485
486 **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.
487
488 ---
489
490 ## Implementation Notes
491
492 ### Design Principles
493
494 1. **Consistent with PostgreSQL**: Follow exact same conventions as pg_recvlogical, pg_dump, etc.
495 2. **Safe by default**: Require explicit flags for destructive operations (`-c/--clean`)
496 3. **Idempotent where sensible**: `--init` can be safely re-run
497 4. **Clear output**: Progress to stderr, data to stdout (when using `-f -`)
498 5. **Scriptable**: Support JSON output (`--format=json`), proper exit codes
499
500 ### Technology Choices (POC)
501
502 - **Language**: Bash or Python
503   - Bash: Minimal dependencies, matches PostgreSQL tool style
504   - Python: Better error handling, easier testing
505 - **Dependencies**: Only PostgreSQL client tools (pg_recvlogical, pg_dump, pg_dumpall, psql)
506
507 ### Key Implementation Components
508
509 1. **Connection management**: Use libpq environment variables, .pgpass
510 2. **Error handling**: Validate prerequisites before starting operations
511 3. **File management**:
512    - For `--start`: Invoke `pg_recvlogical` with file specified by `-f`
513    - For `--full-backup`: Write to directory specified by `-f`
514    - For `--restore`: Read from directory specified by `-f`
515 4. **Signal handling**:
516    - SIGTERM/SIGINT for graceful shutdown (forward to child processes)
517    - SIGHUP for file rotation (--start only, forwarded to pg_recvlogical)
518 5. **Output conventions**:
519    - Progress and status messages → stderr
520    - SQL output → file specified by `-f` (or stdout if `-f -`)
521 6. **Process management for --start**:
522    - Spawn `pg_recvlogical` as child process
523    - Forward signals to child process
524    - Monitor child exit status
525    - Wrap child output to provide consistent pg_scribe formatting
526
527 ### Future Enhancements (Beyond POC)
528
529 - Parallel restore with concurrent psql sessions
530 - Retention policy enforcement with automatic cleanup
531 - Backup verification and integrity checks
532 - Remote backup destinations (S3, rsync)
533 - Encryption support (similar to `pg_basebackup --compress`)
534 - Web UI for backup monitoring