Replication
Garak supports primary-secondary replication for high availability and disaster recovery. Secondary nodes stay synchronized with the primary through a combination of cold sync (full snapshots) and hot sync (incremental updates via oplog).
Architecture Overview
Primary Node Secondary Node(s)
| |
| 1. Cold Sync (Initial) |
|---------------------------------->|
| |
| 2. Hot Sync (Continuous) |
|---------------------------------->|
| |
| Writes → Oplog |
| |
| Oplog entries streamed |
|---------------------------------->|
Replication Modes
Cold Sync (Full Snapshot)
Cold sync creates a complete snapshot of the database and transfers it to secondary nodes.
When to use:
- Initial setup of a new secondary
- Secondary is too far behind (> 10 minutes)
- After catastrophic failure requiring full rebuild
- Scheduled full backups
Process:
- Primary node creates a master lock
- Database is tarred while locked
- Lock is released, tar continues in background
- Secondary downloads and extracts tar
- Secondary updates its
latestpointer - Secondary begins hot sync from snapshot time
Performance:
- Creates temporary disk pressure (tar file)
- Brief write lock during snapshot initiation
- Transfer time depends on database size and network
Hot Sync (Incremental Updates)
Hot sync tails the oplog to apply incremental changes.
When to use:
- Continuous replication
- Secondary is caught up or close behind
- Real-time data consistency
Process:
- Secondary sends last known timestamp
- Primary returns oplog entries since that time
- Secondary applies entries in order
- Secondary updates
latestpointer - Process repeats every 1 second
Performance:
- Minimal overhead on primary
- Near real-time replication (1s polling)
- No locks required
Configuration
Primary Node Setup
Set these environment variables on your primary instance:
# Required
DATABASE_LOCATION=/path/to/database
PORT=1234
MASTER_TOKEN=your-secure-master-token
# Optional - for backups
AWS_S3_BACKUP_BUCKET=your-backup-bucket
AWS_REGION=us-east-1
Secondary Node Setup
Set these environment variables on your secondary instance:
# Required
DATABASE_LOCATION=/path/to/database
PORT=1234
MASTER_TOKEN=your-secure-master-token
GARAK_PRIMARY_URL=http://primary-host:1234
# Optional - for backups
AWS_S3_BACKUP_BUCKET=your-backup-bucket
AWS_REGION=us-east-1
The MASTER_TOKEN must be identical on primary and all secondary nodes for authentication.
Secondary Node Behavior
When a secondary node starts:
Check for existing database:
- If no
topfile exists → trigger cold sync - If database exists → check
latesttimestamp
- If no
Check if excessively behind:
- If
latestis > 10 minutes old → trigger cold sync - Backup existing data before cold sync
- Delete old data after backup
- Proceed with cold sync
- If
Start hot sync thread:
- Continuously poll primary every 1 second
- Apply oplog entries incrementally
- Update
latesttimestamp
Monitoring Replication
Check Replication Lag
On the secondary, check the latest file:
# Read the latest timestamp (u64 little-endian)
hexdump -e '8/1 "%02x"' /path/to/database/latest
Compare this timestamp with current time:
const now = Date.now();
const latest = /* read from file */;
const lagMs = now - latest;
console.log(`Replication lag: ${lagMs}ms`);
Check Oplog Size
Monitor the size of the oplog file to ensure it's not growing unbounded:
ls -lh /path/to/database/oplog
Cold Sync API
Initiate Cold Sync
curl -X POST http://primary:1234/replicas/cold_sync \
-H "Authorization: master-token"
Response:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "accepted",
"message": "Cold sync job started. Poll /replicas/cold_sync_status/{job_id} for status."
}
Check Job Status
curl http://primary:1234/replicas/cold_sync_status/{job_id} \
-H "Authorization: master-token"
Response:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "Completed",
"created_at": 1699900000000,
"completed_at": 1699900120000,
"tar_path": "/tmp/snapshot_123456.tar",
"tar_size": 1048576000,
"snapshot_time": 1699900000000
}
Status Values:
"InProgress"- Snapshot still being created"Completed"- Ready to download{"Failed": "error message"}- Job failed
Download Snapshot
curl http://primary:1234/replicas/cold_sync_download/{job_id} \
-H "Authorization: master-token" \
-o snapshot.tar
Response Headers:
Content-Type: application/x-tarx-garak-locktime: {snapshot_timestamp}
Hot Sync API
Request Oplog Entries
curl http://primary:1234/replicas/hot_sync \
-H "Authorization: master-token" \
-H "x-garak-latest: 1699900000000"
Request Headers:
x-garak-latest: Last known timestamp from secondary
Response:
[
{
"Write": {
"silo": "my-silo",
"shard": "shard-1",
"store": "users",
"node_id": 12345,
"data": { "email": "user@example.com", "name": "User" },
"is_new_node": true
}
},
{
"Delete": {
"silo": "my-silo",
"shard": "shard-1",
"store": "users",
"node_id": 67890
}
}
]
Response Headers:
x-garak-latest: Current timestamp on primary
Oplog Format
The oplog is a line-delimited file at {DATABASE_LOCATION}/oplog:
{timestamp};{json_entry}
{timestamp};{json_entry}
...
Example:
1699900000000;{"Write":{"silo":"my-silo","shard":"shard-1","store":"users","node_id":123,"data":{"name":"Alice"},"is_new_node":true}}
1699900001000;{"Delete":{"silo":"my-silo","shard":"shard-1","store":"users","node_id":456}}
Entry Types:
Write:
{
"Write": {
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number,
"data": {...},
"is_new_node": boolean
}
}
Delete:
{
"Delete": {
"silo": "string",
"shard": "string",
"store": "string",
"node_id": number
}
}
Failure Scenarios
Primary Node Failure
Secondary nodes cannot become primaries automatically. Manual intervention required:
- Stop all secondaries to prevent conflicts
- Promote one secondary:
- Remove
GARAK_PRIMARY_URLfrom environment - Restart as new primary
- Remove
- Update other secondaries to point to new primary
- Update application to use new primary URL
Secondary Node Failure
- Secondary restarts automatically
- Checks replication lag
- If < 10 minutes behind: resumes hot sync
- If > 10 minutes behind: triggers cold sync
Network Partition
If secondary loses connection to primary:
- Hot sync requests will fail
- Secondary continues serving read-only traffic (stale data)
- When connection resumes:
- If lag < 10 minutes: resume hot sync
- If lag > 10 minutes: trigger cold sync
Option 2: Trigger cold sync on secondaries
All secondaries can rebuild from fresh snapshot, then old oplog can be truncated.
Backup Integration
Garak includes automatic S3 backup functionality:
# Configure S3 backups
export AWS_S3_BACKUP_BUCKET=my-backup-bucket
export AWS_REGION=us-east-1
Automatic Behaviors:
- Backup created before risky operations (cold sync on existing data)
- Periodic backups via background thread
- Uses AWS SDK with default credential chain
Best Practices
High Availability Setup
- Run at least 2 secondaries for redundancy
- Distribute secondaries across availability zones
- Monitor replication lag with alerting
- Test failover procedures regularly
- Keep oplog size manageable (rotate or trigger cold syncs)
Performance Optimization
- Dedicated network for replication traffic
- SSD storage on all nodes
- Monitor disk I/O during cold sync
- Limit concurrent cold syncs to avoid primary overload
Disaster Recovery
- Regular S3 backups (daily or more frequent)
- Test restore procedures from backups
- Document failover process for team
- Monitor backup job success with alerting
Operational Considerations
- Plan cold sync during low traffic periods
- Verify secondary data after cold sync
- Keep master token secure and rotated
- Monitor secondary disk space (tar files require temporary space)
- Use consistent DATABASE_LOCATION across nodes
Troubleshooting
Secondary Not Catching Up
Symptoms:
- Replication lag increasing
- Hot sync retrieving entries but lag not decreasing
Solutions:
- Check network connectivity between nodes
- Verify master token matches
- Check secondary disk space
- Trigger manual cold sync
Cold Sync Failing
Symptoms:
- Job status shows
Failed - Download returns 500 error
Solutions:
- Check primary disk space for tar file
- Verify master lock is not stuck
- Check primary logs for errors
- Retry with new job
Oplog Growing Rapidly
Symptoms:
- Oplog file size continuously increasing
- Disk space depleting
Solutions:
- Check for write-heavy workload (expected behavior)
- Ensure secondaries are consuming oplog
- Consider oplog rotation during maintenance window
- Add more secondaries to distribute load
Replication Lag Oscillating
Symptoms:
- Lag increases, triggers cold sync
- Completes, but lag increases again quickly
Solutions:
- Secondary may be under-provisioned (CPU/disk)
- Check for slow network between nodes
- Verify no resource contention on secondary
- Consider increasing hot sync frequency (requires code change)