Files
zeekay a485f34a14 rebrand: complete litestream -> replicate rename
- All source files, configs, docs, CI renamed
- Binary: replicate (not litestream)
- Config: replicate.yml (litestream.yml as fallback)
- Env: REPLICATE_* (LITESTREAM_* as fallback)
- Socket: /var/run/replicate.sock
- Image: ghcr.io/hanzoai/replicate
- Module: github.com/hanzoai/replicate
- Package name: replicate (Go), replicate-vfs (npm/pip/gem)
- VFS: registered as "replicate", pragmas replicate_*
- C exports: ReplicateVFSRegister, GoReplicate*
- Windows service: Replicate
- Systemd unit: replicate.service
- Upstream attribution preserved (README, upgrade test scripts)
2026-04-09 18:03:37 -07:00

207 lines
6.4 KiB
Bash
Executable File

#!/bin/bash
# Replicate v0.5.0 Critical Bug Reproduction Script
#
# This script demonstrates a CRITICAL data loss bug where restore fails
# after Replicate is interrupted and a checkpoint occurs during downtime.
#
# Requirements:
# - replicate binary (built from current main branch)
# - replicate-test binary (from PR #748 or build with: go build -o bin/replicate-test ./cmd/replicate-test)
# - SQLite3 command line tool
#
# Expected behavior: Database should restore successfully
# Actual behavior: Restore fails with "nonsequential page numbers" error
set -e
echo "============================================"
echo "Replicate v0.5.0 Critical Bug Reproduction"
echo "============================================"
echo ""
echo "This demonstrates a data loss scenario where restore fails after:"
echo "1. Replicate is killed (simulating crash)"
echo "2. Writes continue and a checkpoint occurs"
echo "3. Replicate is restarted"
echo ""
# Configuration
DB="/tmp/critical-bug-test.db"
REPLICA="/tmp/critical-bug-replica"
# Clean up any previous test
echo "[SETUP] Cleaning up previous test files..."
rm -f "$DB"*
rm -rf "$REPLICA"
# ALWAYS use local build for testing
REPLICATE="./bin/replicate"
if [ ! -f "$REPLICATE" ]; then
echo "ERROR: Local replicate build not found at $REPLICATE"
echo "Please build first: go build -o bin/replicate ./cmd/replicate"
exit 1
fi
echo "Using local build: $REPLICATE"
# Check for replicate-test binary
if [ -f "./bin/replicate-test" ]; then
REPLICATE_TEST="./bin/replicate-test"
echo "Using local replicate-test: $REPLICATE_TEST"
else
echo "ERROR: replicate-test not found. Please build with:"
echo " go build -o bin/replicate-test ./cmd/replicate-test"
echo ""
echo "Or get it from: https://github.com/benbjohnson/replicate/pull/748"
exit 1
fi
# Show versions
echo "Versions:"
$REPLICATE version
echo ""
# Step 1: Create and populate initial database
echo ""
echo "[STEP 1] Creating test database (50MB)..."
$REPLICATE_TEST populate -db "$DB" -target-size 50MB -table-count 2
INITIAL_SIZE=$(ls -lh "$DB" 2>/dev/null | awk '{print $5}')
echo "✓ Database created: $INITIAL_SIZE"
# Step 2: Start Replicate replication
echo ""
echo "[STEP 2] Starting Replicate replication..."
./bin/replicate replicate "$DB" "file://$REPLICA" > /tmp/replicate.log 2>&1 &
REPLICATE_PID=$!
sleep 3
if ! kill -0 $REPLICATE_PID 2>/dev/null; then
echo "ERROR: Replicate failed to start. Check /tmp/replicate.log"
cat /tmp/replicate.log
exit 1
fi
echo "✓ Replicate running (PID: $REPLICATE_PID)"
# Step 3: Start continuous writes
echo ""
echo "[STEP 3] Starting continuous writes..."
./bin/replicate-test load -db "$DB" -write-rate 100 -duration 2m -pattern constant > /tmp/writes.log 2>&1 &
WRITE_PID=$!
echo "✓ Write load started (PID: $WRITE_PID)"
# Step 4: Let it run normally for 20 seconds
echo ""
echo "[STEP 4] Running normally for 20 seconds..."
sleep 20
# Get row count before interruption
ROWS_BEFORE=$(sqlite3 "$DB" "SELECT COUNT(*) FROM load_test;" 2>/dev/null || echo "0")
echo "✓ Rows written before interruption: $ROWS_BEFORE"
# Step 5: Kill Replicate (simulate crash)
echo ""
echo "[STEP 5] Killing Replicate (simulating crash)..."
kill -9 $REPLICATE_PID 2>/dev/null || true
echo "✓ Replicate killed"
# Step 6: Let writes continue for 15 seconds without Replicate
echo ""
echo "[STEP 6] Continuing writes for 15 seconds (Replicate is down)..."
sleep 15
# Step 7: Execute non-PASSIVE checkpoint
echo ""
echo "[STEP 7] Executing FULL checkpoint while Replicate is down..."
CHECKPOINT_RESULT=$(sqlite3 "$DB" "PRAGMA wal_checkpoint(FULL);" 2>&1)
echo "✓ Checkpoint result: $CHECKPOINT_RESULT"
ROWS_AFTER_CHECKPOINT=$(sqlite3 "$DB" "SELECT COUNT(*) FROM load_test;")
echo "✓ Rows after checkpoint: $ROWS_AFTER_CHECKPOINT"
# Step 8: Resume Replicate
echo ""
echo "[STEP 8] Resuming Replicate..."
./bin/replicate replicate "$DB" "file://$REPLICA" >> /tmp/replicate.log 2>&1 &
NEW_REPLICATE_PID=$!
sleep 3
if ! kill -0 $NEW_REPLICATE_PID 2>/dev/null; then
echo "WARNING: Replicate failed to restart"
fi
echo "✓ Replicate restarted (PID: $NEW_REPLICATE_PID)"
# Step 9: Let Replicate catch up
echo ""
echo "[STEP 9] Letting Replicate catch up for 20 seconds..."
sleep 20
# Stop writes
kill $WRITE_PID 2>/dev/null || true
echo "✓ Writes stopped"
# Wait for final sync
sleep 5
# Get final row count
FINAL_COUNT=$(sqlite3 "$DB" "SELECT COUNT(*) FROM load_test;")
echo "✓ Final row count in source database: $FINAL_COUNT"
# Kill Replicate
kill $NEW_REPLICATE_PID 2>/dev/null || true
# Step 10: Attempt to restore (THIS IS WHERE THE BUG OCCURS)
echo ""
echo "[STEP 10] Attempting to restore database..."
echo "=========================================="
echo ""
rm -f /tmp/restored.db
if ./bin/replicate restore -o /tmp/restored.db "file://$REPLICA" 2>&1 | tee /tmp/restore-output.log; then
echo ""
echo "✓ SUCCESS: Restore completed successfully"
# Verify restored database
RESTORED_COUNT=$(sqlite3 /tmp/restored.db "SELECT COUNT(*) FROM load_test;" 2>/dev/null || echo "0")
INTEGRITY=$(sqlite3 /tmp/restored.db "PRAGMA integrity_check;" 2>/dev/null || echo "FAILED")
echo " - Restored row count: $RESTORED_COUNT"
echo " - Integrity check: $INTEGRITY"
if [ "$RESTORED_COUNT" -eq "$FINAL_COUNT" ]; then
echo " - Data integrity: ✓ VERIFIED (no data loss)"
else
LOSS=$((FINAL_COUNT - RESTORED_COUNT))
echo " - Data integrity: ✗ FAILED (lost $LOSS rows)"
fi
else
echo ""
echo "✗ CRITICAL BUG REPRODUCED: Restore failed!"
echo ""
echo "Error output:"
echo "-------------"
cat /tmp/restore-output.log
echo ""
echo "This is the critical bug. The database cannot be restored after"
echo "Replicate was interrupted and a checkpoint occurred during downtime."
echo ""
echo "Original database stats:"
echo " - Rows before interruption: $ROWS_BEFORE"
echo " - Rows after checkpoint: $ROWS_AFTER_CHECKPOINT"
echo " - Final rows: $FINAL_COUNT"
echo " - DATA IS UNRECOVERABLE"
fi
echo ""
echo "=========================================="
echo "Test artifacts saved in:"
echo " - Source database: $DB"
echo " - Replica files: $REPLICA/"
echo " - Replicate log: /tmp/replicate.log"
echo " - Restore output: /tmp/restore-output.log"
echo ""
# Clean up processes
pkill -f replicate-test 2>/dev/null || true
pkill -f "replicate replicate" 2>/dev/null || true
echo "Test complete."