Created
December 22, 2025 08:27
-
-
Save ashwin47/5d1686831935954b45112fa22b291654 to your computer and use it in GitHub Desktop.
litestream pitr issue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # litestream-pitr-repro.sh | |
| # Reproduces: `restore -timestamp` hangs indefinitely (regression in v0.5.1) | |
| # | |
| # The bug triggers with longer backup history (100+ LTX files). | |
| # This script creates ~60 LTX files which may or may not trigger the bug. | |
| # For reliable reproduction, use a database with longer history. | |
| # | |
| # Usage: | |
| # export LITESTREAM_ACCESS_KEY_ID="your-access-key" | |
| # export LITESTREAM_SECRET_ACCESS_KEY="your-secret-key" | |
| # export S3_ENDPOINT="https://your-endpoint.com" | |
| # export S3_BUCKET="your-bucket" | |
| # ./litestream-pitr-repro.sh | |
| # | |
| # Requirements: Docker | |
| set -e | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| NC='\033[0m' | |
| if [ -z "$LITESTREAM_ACCESS_KEY_ID" ] || [ -z "$LITESTREAM_SECRET_ACCESS_KEY" ] || [ -z "$S3_ENDPOINT" ] || [ -z "$S3_BUCKET" ]; then | |
| echo "Usage:" | |
| echo " export LITESTREAM_ACCESS_KEY_ID='...'" | |
| echo " export LITESTREAM_SECRET_ACCESS_KEY='...'" | |
| echo " export S3_ENDPOINT='https://...'" | |
| echo " export S3_BUCKET='...'" | |
| echo " ./litestream-pitr-repro.sh" | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}=== Litestream PITR Bug Reproduction ===${NC}" | |
| echo "" | |
| echo "Note: Bug triggers with longer backup history (100+ LTX files)." | |
| echo "This script creates ~60 LTX files. For reliable reproduction," | |
| echo "run for longer or use an existing database with more history." | |
| echo "" | |
| TEST_DIR=$(mktemp -d) | |
| S3_PATH="litestream-pitr-repro-$$" | |
| echo "Test directory: $TEST_DIR" | |
| echo "S3 path: $S3_BUCKET/$S3_PATH" | |
| cd "$TEST_DIR" | |
| cat > litestream.yml << EOF | |
| dbs: | |
| - path: /data/test.db | |
| replicas: | |
| - type: s3 | |
| bucket: ${S3_BUCKET} | |
| path: ${S3_PATH} | |
| endpoint: "${S3_ENDPOINT}" | |
| force-path-style: true | |
| sync-interval: 1s | |
| EOF | |
| cat > Dockerfile << 'EOF' | |
| ARG VERSION=latest | |
| FROM litestream/litestream:${VERSION} | |
| RUN apk add --no-cache sqlite | |
| WORKDIR /data | |
| ENTRYPOINT [] | |
| EOF | |
| docker build -t litestream-test --build-arg VERSION=0.5.5 -q . > /dev/null | |
| echo -e "${GREEN}Step 1: Creating database and starting replication${NC}" | |
| cat > setup.sh << 'SCRIPT' | |
| #!/bin/sh | |
| sqlite3 /data/test.db << SQL | |
| PRAGMA journal_mode=WAL; | |
| CREATE TABLE events (id INTEGER PRIMARY KEY, name TEXT, ts TEXT); | |
| SQL | |
| litestream replicate -config /data/litestream.yml & | |
| PID=$! | |
| sleep 3 | |
| # Create many LTX files (60+ writes with delays) | |
| echo "Creating backup history (this takes ~2 minutes)..." | |
| for i in $(seq 1 60); do | |
| sqlite3 /data/test.db "INSERT INTO events (name, ts) VALUES ('event-$i', datetime('now'));" | |
| sleep 2 | |
| if [ $((i % 10)) -eq 0 ]; then | |
| echo " $i writes completed..." | |
| fi | |
| done | |
| kill $PID 2>/dev/null || true | |
| sleep 2 | |
| SCRIPT | |
| chmod +x setup.sh | |
| docker run --rm \ | |
| -e LITESTREAM_ACCESS_KEY_ID \ | |
| -e LITESTREAM_SECRET_ACCESS_KEY \ | |
| -v "$TEST_DIR:/data" \ | |
| litestream-test \ | |
| /bin/sh /data/setup.sh 2>&1 | grep -v "^time=" | |
| echo "" | |
| echo -e "${GREEN}Step 2: Checking LTX file count${NC}" | |
| LTX_COUNT=$(docker run --rm \ | |
| -e LITESTREAM_ACCESS_KEY_ID \ | |
| -e LITESTREAM_SECRET_ACCESS_KEY \ | |
| -v "$TEST_DIR:/data" \ | |
| litestream-test \ | |
| litestream ltx -config /data/litestream.yml /data/test.db 2>&1 | grep -v "^time=" | wc -l) | |
| echo "LTX files: $LTX_COUNT" | |
| PITR_TS=$(docker run --rm \ | |
| -e LITESTREAM_ACCESS_KEY_ID \ | |
| -e LITESTREAM_SECRET_ACCESS_KEY \ | |
| -v "$TEST_DIR:/data" \ | |
| litestream-test \ | |
| litestream ltx -config /data/litestream.yml /data/test.db 2>&1 | \ | |
| grep -v "^time=" | tail -10 | head -1 | awk '{print $4}') | |
| echo "PITR timestamp: $PITR_TS" | |
| rm -f "$TEST_DIR/test.db"* | |
| echo "" | |
| echo -e "${GREEN}Step 3: Testing latest restore${NC}" | |
| if docker run --rm \ | |
| -e LITESTREAM_ACCESS_KEY_ID \ | |
| -e LITESTREAM_SECRET_ACCESS_KEY \ | |
| -v "$TEST_DIR:/data" \ | |
| litestream-test \ | |
| litestream restore -config /data/litestream.yml -o /data/restored.db /data/test.db 2>&1 | grep -v "^time="; then | |
| ROWS=$(sqlite3 "$TEST_DIR/restored.db" "SELECT COUNT(*) FROM events;") | |
| echo "SUCCESS: $ROWS rows restored" | |
| else | |
| echo "FAILED" | |
| fi | |
| echo "" | |
| echo -e "${YELLOW}Step 4: Testing PITR restore (20s timeout)${NC}" | |
| echo "Timestamp: $PITR_TS" | |
| rm -f "$TEST_DIR/restored.db" | |
| docker run --name pitr-test -d \ | |
| -e LITESTREAM_ACCESS_KEY_ID \ | |
| -e LITESTREAM_SECRET_ACCESS_KEY \ | |
| -v "$TEST_DIR:/data" \ | |
| litestream-test \ | |
| litestream restore -config /data/litestream.yml -timestamp "$PITR_TS" -o /data/restored.db /data/test.db > /dev/null 2>&1 | |
| sleep 20 | |
| if docker ps -q -f name=pitr-test | grep -q .; then | |
| echo -e "${RED}HUNG after 20s - BUG CONFIRMED${NC}" | |
| docker kill pitr-test > /dev/null 2>&1 || true | |
| else | |
| EXIT=$(docker inspect pitr-test --format='{{.State.ExitCode}}' 2>/dev/null || echo "?") | |
| if [ -f "$TEST_DIR/restored.db" ]; then | |
| ROWS=$(sqlite3 "$TEST_DIR/restored.db" "SELECT COUNT(*) FROM events;" 2>/dev/null || echo "0") | |
| echo "SUCCESS (exit $EXIT, $ROWS rows) - bug may not trigger with short history" | |
| else | |
| echo "FAILED (exit $EXIT)" | |
| fi | |
| fi | |
| docker rm pitr-test > /dev/null 2>&1 || true | |
| echo "" | |
| echo -e "${GREEN}========================================${NC}" | |
| echo "Cleanup:" | |
| echo " rm -rf $TEST_DIR" | |
| echo " docker rmi litestream-test" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment