]> begriffs open source - pg_scribe/blob - doc/cli.md
Reduce scope
[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 ```
134
135 **What it does:**
136
137 1. Wraps `pg_recvlogical` to stream from the specified replication slot
138 2. Streams decoded changes using the wal2sql plugin
139 3. Writes SQL to output file
140 4. Periodically fsyncs output file for crash safety
141 5. Reports LSN position and lag to stderr
142 6. Responds to SIGHUP by closing and reopening output file (for log rotation)
143
144 **Implementation:** `pg_scribe --start` is a thin wrapper around `pg_recvlogical --start` that:
145 - Invokes `pg_recvlogical` with the `wal2sql` plugin and appropriate options
146 - Forwards signals (SIGHUP, SIGTERM, SIGINT) to the child process
147 - Provides consistent interface with other pg_scribe commands
148
149 **Log rotation support:** File rotation is provided automatically by `pg_recvlogical`:
150
151 1. Rename the current output file
152 2. Send SIGHUP to pg_scribe process (which forwards it to pg_recvlogical)
153 3. pg_recvlogical closes the old file and opens a new one with the same name
154
155 **Example:**
156
157 ```bash
158 # Stream to a file (foreground)
159 pg_scribe --start -d mydb -f /backups/mydb/incremental.sql -S mydb_backup
160
161 # Run as background daemon (redirect stderr to log file)
162 pg_scribe --start -d mydb -f /backups/mydb/incremental.sql -S mydb_backup \
163   2>/var/log/pg_scribe.log &
164
165 # Stream to stdout (for processing with other tools)
166 pg_scribe --start -d mydb -f - -S mydb_backup > /backups/mydb/incremental.sql
167 ```
168
169 **Log rotation script example:**
170
171 ```bash
172 #!/bin/bash
173 # Rotate incremental backup file
174 BACKUP_FILE="/backups/mydb/incremental.sql"
175 mv "$BACKUP_FILE" "$BACKUP_FILE.$(date +%Y%m%d)"
176 killall -SIGHUP pg_scribe
177 ```
178
179 **Output (to stderr):**
180
181 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>`.
182
183 ```
184 Connecting to replication slot 'mydb_backup'...
185 Streaming to: /backups/mydb/incremental.sql
186 LSN: 0/1A2B3C4D  Lag: 128 bytes
187 LSN: 0/1A2B3C8F  Lag: 0 bytes
188 File rotated (SIGHUP received)
189 Streaming to: /backups/mydb/incremental.sql
190 ...
191 ```
192
193 ---
194
195 ### `--full-backup`
196
197 Take a full backup using `pg_dump` and `pg_dumpall --globals-only`.
198
199 **Additional options:**
200
201 ```
202 -f, --file=DIRECTORY         Backup output directory (required)
203 -Z, --compress=METHOD        Compression method: gzip, lz4, zstd, or none
204                              Can include level (e.g., zstd:9) (default: zstd:9)
205 ```
206
207 **What it does:**
208
209 1. Takes full `pg_dump` backup
210 2. Takes `pg_dumpall --globals-only` backup
211 3. Generates metadata file
212 4. Compresses backups if enabled
213
214 **Example:**
215
216 ```bash
217 pg_scribe --full-backup -d mydb -f /backups/mydb
218
219 # With specific compression
220 pg_scribe --full-backup -d mydb -f /backups/mydb --compress=zstd:9
221 ```
222
223 **Output:**
224
225 ```
226 Taking full backup...
227 Full backup saved to: /backups/mydb/base-2024-01-15.sql.zst
228 Capturing globals...
229 Globals saved to: /backups/mydb/globals-2024-01-15.sql
230 Writing metadata...
231
232 Full backup complete!
233 Size: 1.2 GB (compressed from 5.4 GB)
234 ```
235
236 ---
237
238 ### `--restore`
239
240 Restore database from base backup plus incremental backups.
241
242 **Additional options:**
243
244 ```
245 -f, --file=DIRECTORY         Backup input directory (required)
246 -d, --dbname=DBNAME          Target database name (required)
247 -C, --create                 Create target database
248 --base-backup=FILENAME       Specific base backup file (default: latest)
249 --no-sync-sequences          Skip sequence synchronization
250 ```
251
252 **What it does:**
253
254 1. Locates base backup (latest or specified)
255 2. Finds all incremental backups since base
256 3. Creates target database (if `--create` specified)
257 4. Restores globals (roles, tablespaces)
258 5. Restores base backup
259 6. Applies incremental backups in chronological order
260 7. Synchronizes sequences using `setval()` (unless `--no-sync-sequences`)
261 8. Reports basic statistics
262
263 **Example:**
264
265 ```bash
266 # Restore latest to new database
267 pg_scribe --restore -f /backups/mydb -d mydb_restored --create
268
269 # Restore specific base backup
270 pg_scribe --restore -f /backups/mydb -d mydb_restored \
271   --base-backup=/backups/mydb/base-2024-01-10.sql
272 ```
273
274 **Output:**
275
276 ```
277 Located base backup: base-2024-01-10.sql (5.4 GB)
278 Found 5 incremental backups to apply
279 Creating database 'mydb_restored'...
280 Restoring globals...
281 Restoring base backup... (5 minutes)
282 Applying incremental backup 1/5: incremental-2024-01-10.sql
283 Applying incremental backup 2/5: incremental-2024-01-11.sql
284 Applying incremental backup 3/5: incremental-2024-01-12.sql
285 Applying incremental backup 4/5: incremental-2024-01-13.sql
286 Applying incremental backup 5/5: incremental-2024-01-14.sql
287 Synchronizing sequences...
288   users_id_seq: set to 12845
289   orders_id_seq: set to 98234
290   products_id_seq: set to 456
291
292 Restore complete!
293 Duration: 8 minutes
294 Rows restored: 1,284,567
295 ```
296
297 ---
298
299 ### `--status`
300
301 Check replication slot health and backup system status.
302
303 **Additional options:**
304
305 ```
306 -S, --slot=SLOTNAME          Replication slot name (default: pg_scribe)
307 -f, --file=DIRECTORY         Backup directory to analyze (optional)
308 ```
309
310 **What it does:**
311
312 1. Queries `pg_replication_slots` for slot health
313 2. Shows replication lag and WAL retention
314 3. Analyzes backup directory if provided
315 4. Reports warnings about potential issues
316
317 **Example:**
318
319 ```bash
320 pg_scribe --status -d mydb -S mydb_backup -f /backups/mydb
321 ```
322
323 **Output:**
324
325 ```
326 Replication Slot: mydb_backup
327   Status:          active
328   Database:        mydb
329   Plugin:          wal2sql
330   Restart LSN:     0/1A2B3C4D
331   Confirmed LSN:   0/1A2B3C8F
332   Replication lag: 128 bytes
333   WAL retention:   2.1 GB
334   Slot age:        2 days 14 hours
335
336 Backup Directory: /backups/mydb
337   Base backups:    5 (oldest: 2024-01-01, newest: 2024-01-15)
338   Incremental:     45 files (12.4 GB total)
339   Last backup:     2024-01-15 14:23:01 (2 minutes ago)
340   Coverage:        Continuous since 2024-01-01
341
342 Status: OK
343 ```
344
345 ---
346
347 ## Exit Status
348
349 ```
350 0   Success
351 1   General error
352 2   Database connection error
353 3   Replication slot error
354 4   Backup/restore error
355 5   Invalid arguments
356 10  Warning conditions (--status only)
357 ```
358
359 ---
360
361 ## Environment Variables
362
363 Standard PostgreSQL environment variables are supported:
364
365 ```
366 PGHOST              Database host
367 PGPORT              Database port
368 PGDATABASE          Database name
369 PGUSER              Database user
370 PGPASSWORD          Database password (not recommended, use .pgpass instead)
371 PG_COLOR            Use color in diagnostics: always, auto, or never
372 ```
373
374 ---
375
376 ## Example Workflows
377
378 ### Initial Setup and Daily Operation
379
380 ```bash
381 # 1. Initialize backup system (idempotent - safe to re-run)
382 pg_scribe --init -d production -f /backups/production -S prod_backup
383
384 # 2. Start streaming backups (run as daemon/service)
385 pg_scribe --start -d production -f /backups/production/incremental.sql \
386   -S prod_backup 2>/var/log/pg_scribe.log &
387
388 # 3. Set up log rotation (logrotate or custom script)
389 cat > /etc/logrotate.d/pg_scribe <<EOF
390 /backups/production/incremental.sql {
391     daily
392     rotate 7
393     dateext
394     postrotate
395         killall -SIGHUP pg_scribe
396     endscript
397 }
398 EOF
399
400 # 4. Schedule daily full backups (cron: 0 2 * * *)
401 pg_scribe --full-backup -d production -f /backups/production
402
403 # 5. Monitor slot health (cron: */15 * * * *)
404 pg_scribe --status -d production -S prod_backup
405 ```
406
407 ### Disaster Recovery
408
409 ```bash
410 # 1. Check available backups
411 pg_scribe --status -f /backups/production
412
413 # 2. Restore to new database
414 pg_scribe --restore -f /backups/production \
415   -d production_restored --create
416
417 # 3. Test restored database
418 psql -d production_restored -c "SELECT COUNT(*) FROM users;"
419
420 # 4. Switch application to restored database (manual step)
421 ```
422
423 ---
424
425 ## Comparison with PostgreSQL Tools
426
427 pg_scribe follows the same CLI conventions as core PostgreSQL tools:
428
429 | Tool | Action Flags | Connection Options | File Options |
430 |------|-------------|-------------------|--------------|
431 | **pg_recvlogical** | `--create-slot`, `--drop-slot`, `--start` | `-d`, `-h`, `-p`, `-U` | `-f` (output file), `-S` (slot) |
432 | **pg_dump** | (positional dbname) | `-d`, `-h`, `-p`, `-U` | `-f` (output file) |
433 | **pg_basebackup** | (none) | `-d`, `-h`, `-p`, `-U` | `-D` (data directory!) |
434 | **pg_restore** | (none) | `-d`, `-h`, `-p`, `-U` | positional (archive file) |
435 | **pg_scribe** | `--init`, `--start`, `--full-backup`, `--restore`, `--status` | `-d`, `-h`, `-p`, `-U` | `-f` (file/directory), `-S` (slot) |
436
437 **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.
438
439 ---
440
441 ## Implementation Notes
442
443 ### Design Principles
444
445 1. **Consistent with PostgreSQL**: Follow exact same conventions as pg_recvlogical, pg_dump, etc.
446 2. **Idempotent where sensible**: `--init` can be safely re-run
447 3. **Clear output**: Progress to stderr, data to stdout (when using `-f -`)
448 4. **Scriptable**: Clear text output format, proper exit codes
449
450 ### Technology Choices (POC)
451
452 - **Language**: Bash or Python
453   - Bash: Minimal dependencies, matches PostgreSQL tool style
454   - Python: Better error handling, easier testing
455 - **Dependencies**: Only PostgreSQL client tools (pg_recvlogical, pg_dump, pg_dumpall, psql)
456
457 ### Key Implementation Components
458
459 1. **Connection management**: Use libpq environment variables, .pgpass
460 2. **Error handling**: Validate prerequisites before starting operations
461 3. **File management**:
462    - For `--start`: Invoke `pg_recvlogical` with file specified by `-f`
463    - For `--full-backup`: Write to directory specified by `-f`
464    - For `--restore`: Read from directory specified by `-f`
465 4. **Signal handling**:
466    - SIGTERM/SIGINT for graceful shutdown (forward to child processes)
467    - SIGHUP for file rotation (--start only, forwarded to pg_recvlogical)
468 5. **Output conventions**:
469    - Progress and status messages → stderr
470    - SQL output → file specified by `-f` (or stdout if `-f -`)
471 6. **Process management for --start**:
472    - Spawn `pg_recvlogical` as child process
473    - Forward signals to child process
474    - Monitor child exit status
475    - Wrap child output to provide consistent pg_scribe formatting
476
477 **Extensibility Note**: This POC design uses a modular, action-based CLI that can accommodate additional features and options in future versions without breaking compatibility.