1 # pg_scribe Command Line Interface
3 This document describes the command line interface for **pg_scribe**, an incremental SQL backup system for PostgreSQL.
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.
12 # Initialize backup system
13 pg_scribe --init [OPTIONS]
15 # Start streaming incremental backups
16 pg_scribe --start [OPTIONS]
19 pg_scribe --full-backup [OPTIONS]
21 # Restore from backups
22 pg_scribe --restore [OPTIONS]
24 # Check replication slot status
25 pg_scribe --status [OPTIONS]
30 These options apply to all actions:
32 ### Connection Options
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
46 -v, --verbose Enable verbose mode
47 -V, --version Print version and exit
48 -?, --help Show help and exit
53 Exactly one of the following action flags must be specified:
57 Initialize the backup system by creating a replication slot, setting up DDL capture via the wal2sql extension, and taking an initial base backup.
59 **This operation is idempotent** - it can be safely re-run if initialization fails partway through. Existing slots and extensions will not cause errors.
61 **Additional options:**
64 -f, --file=DIRECTORY Backup output directory (required)
65 -S, --slot=SLOTNAME Replication slot name (default: pg_scribe)
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
81 pg_scribe --init -d mydb -f /backups/mydb -S mydb_backup
87 Creating wal2sql extension...
88 Extension 'wal2sql' created (includes DDL event trigger).
89 Creating replication slot 'mydb_backup'...
90 Snapshot exported: 00000003-00000001-1
92 Base backup saved to: /backups/mydb/base-2024-01-15.sql
94 Globals saved to: /backups/mydb/globals-2024-01-15.sql
97 Initialization complete!
100 Table 'public.logs' has no replica identity - UPDATE/DELETE will fail
101 Run: ALTER TABLE public.logs REPLICA IDENTITY FULL;
104 **Output when re-running (idempotent):**
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
114 Globals saved to: /backups/mydb/globals-2024-01-15.sql
117 Initialization complete!
124 Start streaming incremental backups continuously from a replication slot.
126 **Additional options:**
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)
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)
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
149 **Log rotation support:** File rotation is provided automatically by `pg_recvlogical`:
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
158 # Stream to a file (foreground)
159 pg_scribe --start -d mydb -f /backups/mydb/incremental.sql -S mydb_backup
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 &
165 # Stream to stdout (for processing with other tools)
166 pg_scribe --start -d mydb -f - -S mydb_backup > /backups/mydb/incremental.sql
169 **Log rotation script example:**
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
179 **Output (to stderr):**
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>`.
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
197 Take a full backup using `pg_dump` and `pg_dumpall --globals-only`.
199 **Additional options:**
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)
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
217 pg_scribe --full-backup -d mydb -f /backups/mydb
219 # With specific compression
220 pg_scribe --full-backup -d mydb -f /backups/mydb --compress=zstd:9
226 Taking full backup...
227 Full backup saved to: /backups/mydb/base-2024-01-15.sql.zst
229 Globals saved to: /backups/mydb/globals-2024-01-15.sql
232 Full backup complete!
233 Size: 1.2 GB (compressed from 5.4 GB)
240 Restore database from base backup plus incremental backups.
242 **Additional options:**
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
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
266 # Restore latest to new database
267 pg_scribe --restore -f /backups/mydb -d mydb_restored --create
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
277 Located base backup: base-2024-01-10.sql (5.4 GB)
278 Found 5 incremental backups to apply
279 Creating database 'mydb_restored'...
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
294 Rows restored: 1,284,567
301 Check replication slot health and backup system status.
303 **Additional options:**
306 -S, --slot=SLOTNAME Replication slot name (default: pg_scribe)
307 -f, --file=DIRECTORY Backup directory to analyze (optional)
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
320 pg_scribe --status -d mydb -S mydb_backup -f /backups/mydb
326 Replication Slot: mydb_backup
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
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
352 2 Database connection error
353 3 Replication slot error
354 4 Backup/restore error
356 10 Warning conditions (--status only)
361 ## Environment Variables
363 Standard PostgreSQL environment variables are supported:
368 PGDATABASE Database name
370 PGPASSWORD Database password (not recommended, use .pgpass instead)
371 PG_COLOR Use color in diagnostics: always, auto, or never
378 ### Initial Setup and Daily Operation
381 # 1. Initialize backup system (idempotent - safe to re-run)
382 pg_scribe --init -d production -f /backups/production -S prod_backup
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 &
388 # 3. Set up log rotation (logrotate or custom script)
389 cat > /etc/logrotate.d/pg_scribe <<EOF
390 /backups/production/incremental.sql {
395 killall -SIGHUP pg_scribe
400 # 4. Schedule daily full backups (cron: 0 2 * * *)
401 pg_scribe --full-backup -d production -f /backups/production
403 # 5. Monitor slot health (cron: */15 * * * *)
404 pg_scribe --status -d production -S prod_backup
407 ### Disaster Recovery
410 # 1. Check available backups
411 pg_scribe --status -f /backups/production
413 # 2. Restore to new database
414 pg_scribe --restore -f /backups/production \
415 -d production_restored --create
417 # 3. Test restored database
418 psql -d production_restored -c "SELECT COUNT(*) FROM users;"
420 # 4. Switch application to restored database (manual step)
425 ## Comparison with PostgreSQL Tools
427 pg_scribe follows the same CLI conventions as core PostgreSQL tools:
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) |
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.
441 ## Implementation Notes
443 ### Design Principles
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
450 ### Technology Choices (POC)
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)
457 ### Key Implementation Components
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
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.