Skip to content

Instantly share code, notes, and snippets.

@ashwin47
Created December 22, 2025 08:27
Show Gist options
  • Select an option

  • Save ashwin47/5d1686831935954b45112fa22b291654 to your computer and use it in GitHub Desktop.

Select an option

Save ashwin47/5d1686831935954b45112fa22b291654 to your computer and use it in GitHub Desktop.
litestream pitr issue
#!/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