SiamCafe · Blog
QuestDB Time Series Business Continuity —
บทความ

QuestDB Time Series Business Continuity —

เผยแพร่ 28 พฤษภาคม 2569

QuestDB Business Continuity

QuestDB Time Series Business Continuity BCP RPO RTO Backup Replication Disaster Recovery WAL Snapshot Failover Monitoring

ComponentRPORTOMethodCost
WAL + Snapshot~1 นาที15-30 นาทีLocal Backupต่ำ
Incremental Backup15 นาที30 นาทีrsync + S3ต่ำ-ปานกลาง
Replication (Enterprise)~0 วินาที1-5 นาทีBuilt-in Replicationสูง (License)
Cross-region DR15-60 นาที30-60 นาทีS3 Cross-region + Restoreปานกลาง
Multi-site Active~0 วินาที~0 วินาทีApp-level Dual Writeสูง (2x Resource)

Backup Configuration

# === QuestDB Backup Strategy ===

# #!/bin/bash
# # questdb_backup.sh - Run via cron
# # */15 * * * * /opt/scripts/questdb_backup.sh incremental
# # 0 2 * * * /opt/scripts/questdb_backup.sh full
#
# BACKUP_TYPE=$1
# QUESTDB_DATA="/var/lib/questdb/db"
# BACKUP_DIR="/backup/questdb"
# S3_BUCKET="s3://company-backups/questdb"
# DATE=$(date +%Y%m%d_%H%M%S)
#
# if [ "$BACKUP_TYPE" = "full" ]; then
#   # Full backup with snapshot
#   curl -G http://localhost:9000/exec --data-urlencode "query=SNAPSHOT PREPARE"
#   tar -czf "$BACKUP_DIR/full_$DATE.tar.gz" "$QUESTDB_DATA"
#   curl -G http://localhost:9000/exec --data-urlencode "query=SNAPSHOT COMPLETE"
#   aws s3 cp "$BACKUP_DIR/full_$DATE.tar.gz" "$S3_BUCKET/full/"
# else
#   # Incremental backup (WAL + recent changes)
#   rsync -av --delete "$QUESTDB_DATA/wal/" "$BACKUP_DIR/incremental/wal/"
#   tar -czf "$BACKUP_DIR/incr_$DATE.tar.gz" "$BACKUP_DIR/incremental/"
#   aws s3 cp "$BACKUP_DIR/incr_$DATE.tar.gz" "$S3_BUCKET/incremental/"
# fi
#
# # Cleanup old backups
# find "$BACKUP_DIR" -name "full_*.tar.gz" -mtime +30 -delete
# find "$BACKUP_DIR" -name "incr_*.tar.gz" -mtime +7 -delete
#
# echo "[$BACKUP_TYPE] Backup completed: $DATE"

from dataclasses import dataclass

@dataclass
class BackupSchedule:
    backup_type: str
    frequency: str
    retention: str
    rpo: str
    storage: str

schedules = [
    BackupSchedule("Full Snapshot",
        "ทุกวัน 02:00",
        "30 วัน",
        "24 ชั่วโมง",
        "S3 Standard ~$0.023/GB/month"),
    BackupSchedule("Incremental (WAL)",
        "ทุก 15 นาที",
        "7 วัน",
        "15 นาที",
        "S3 Standard ~$0.023/GB/month"),
    BackupSchedule("WAL Continuous",
        "Real-time (Streaming)",
        "3 วัน",
        "~1 นาที",
        "S3 + Transfer Cost"),
    BackupSchedule("Weekly Archive",
        "ทุกสัปดาห์",
        "12 สัปดาห์",
        "1 สัปดาห์",
        "S3 Glacier ~$0.004/GB/month"),
    BackupSchedule("Monthly Archive",
        "ทุกเดือน",
        "12 เดือน",
        "1 เดือน",
        "S3 Glacier Deep ~$0.00099/GB/month"),
]

print("=== Backup Schedule ===")
for b in schedules:
    print(f"  [{b.backup_type}] Frequency: {b.frequency}")
    print(f"    Retention: {b.retention} | RPO: {b.rpo}")
    print(f"    Storage: {b.storage}")

Replication Setup

# === QuestDB Replication (Open Source) ===

# Option 1: rsync-based Replication
# #!/bin/bash
# # replicate.sh - Run every 5 minutes
# QUESTDB_DATA="/var/lib/questdb/db"
# REPLICA_HOST="replica.internal"
# REPLICA_PATH="/var/lib/questdb/db"
#
# # Sync data to replica (WAL-safe approach)
# rsync -avz --delete \
#   -e "ssh -p 22" \
#   "$QUESTDB_DATA/" \
#   "$REPLICA_HOST:$REPLICA_PATH/"
#
# # Restart replica QuestDB to pick up changes
# ssh $REPLICA_HOST "systemctl restart questdb"

# Option 2: Application-level Dual Write
# import questdb.ingress as qi
#
# primary = qi.Sender("primary.internal", 9009)
# replica = qi.Sender("replica.internal", 9009)
#
# def write_metric(table, columns, timestamp):
#     for sender in [primary, replica]:
#         try:
#             sender.row(table, columns=columns, at=timestamp)
#         except Exception as e:
#             log.error(f"Write failed: {sender.host}: {e}")

@dataclass
class ReplicationOption:
    method: str
    rpo: str
    rto: str
    complexity: str
    limitation: str

options = [
    ReplicationOption("QuestDB Enterprise Replication",
        "~0 วินาที (Streaming)",
        "1-5 นาที (Auto-failover)",
        "ต่ำ (Built-in)",
        "ต้อง Enterprise License"),
    ReplicationOption("rsync Periodic Sync",
        "5-15 นาที",
        "10-30 นาที (Manual)",
        "ต่ำ",
        "ต้อง Restart Replica อาจสูญข้อมูลระหว่าง Sync"),
    ReplicationOption("Application Dual Write",
        "~0 วินาที",
        "~0 วินาที (Active-Active)",
        "ปานกลาง",
        "Application ต้อง Handle Failure ข้อมูลอาจไม่ตรงกัน"),
    ReplicationOption("Storage Replication (EBS/ZFS)",
        "ขึ้นกับ Sync Interval",
        "15-30 นาที",
        "ปานกลาง",
        "ขึ้นกับ Cloud Provider Storage Layer"),
    ReplicationOption("Kafka + Dual Consumer",
        "~0 วินาที",
        "1-5 นาที",
        "สูง",
        "ต้องมี Kafka Infrastructure เพิ่ม Complexity"),
]

print("=== Replication Options ===")
for o in options:
    print(f"  [{o.method}]")
    print(f"    RPO: {o.rpo} | RTO: {o.rto}")
    print(f"    Complexity: {o.complexity}")
    print(f"    Limitation: {o.limitation}")

DR Testing

# === Disaster Recovery Testing ===

@dataclass
class DRTest:
    test: str
    scenario: str
    frequency: str
    success_criteria: str
    runbook_step: str

dr_tests = [
    DRTest("Full Restore Test",
        "Restore QuestDB จาก Full Backup ล่าสุด",
        "ทุกเดือน",
        "Restore ภายใน 30 นาที Query ทำงานปกติ Data ครบ",
        "1.Stop QuestDB 2.Restore 3.Start 4.Verify"),
    DRTest("Incremental Restore",
        "Restore Full + Apply Incremental Backups",
        "ทุก 2 สัปดาห์",
        "Data ครบถึง 15 นาทีก่อน Failure",
        "1.Restore Full 2.Apply WAL 3.Verify Timeline"),
    DRTest("Failover to Replica",
        "จำลอง Primary ล่ม Failover ไป Replica",
        "ทุกไตรมาส",
        "Failover ภายใน 5 นาที Application เชื่อมต่อ Replica",
        "1.Stop Primary 2.Promote Replica 3.Update DNS"),
    DRTest("Cross-region DR",
        "จำลอง Region ล่มทั้ง Region",
        "ทุก 6 เดือน",
        "DR Site ทำงานภายใน 60 นาที",
        "1.Detect 2.DNS Failover 3.Restore DR 4.Verify"),
    DRTest("Data Integrity Check",
        "ตรวจสอบข้อมูล Backup ถูกต้องครบถ้วน",
        "ทุกสัปดาห์ (Automated)",
        "Row Count ตรง Checksum ตรง No Corruption",
        "1.Restore to Test 2.Count Rows 3.Compare Checksums"),
]

print("=== DR Tests ===")
for d in dr_tests:
    print(f"\n  [{d.test}] Frequency: {d.frequency}")
    print(f"    Scenario: {d.scenario}")
    print(f"    Success: {d.success_criteria}")
    print(f"    Steps: {d.runbook_step}")

เคล็ดลับ

  • SNAPSHOT: ใช้ SNAPSHOT PREPARE/COMPLETE สำหรับ Consistent Backup
  • S3: Upload Backup ไป S3 ป้องกัน Disk Failure
  • Test: ทดสอบ Restore ทุกเดือน ไม่ใช่แค่ Backup
  • Monitor: ตั้ง Alert เมื่อ Backup Fail Replica Lag สูง
  • Runbook: เขียน Runbook ชัดเจน วัด RTO จริงเทียบ Target

Business Continuity คืออะไร

BCP แผนรองรับระบบล่ม RPO สูญข้อมูลกี่นาที RTO กลับมากี่นาที WAL Snapshot Replication Failover DR Site Drill Runbook