Polish: daily snapshot rotation #86

Merged
osobh merged 1 commits from polish-snapshot-rotate into main 2026-07-14 20:12:21 +00:00
4 changed files with 102 additions and 0 deletions
Showing only changes of commit 7a3c04bed7 - Show all commits
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# rotate-snapshots.sh — create today's daily snapshot + prune snapshots
# older than N days. Idempotent: safe to run repeatedly on the same day.
#
# Snapshots created by this script are named `daily-YYYY-MM-DD`. Only
# snapshots matching that prefix are candidates for pruning — hand-created
# snapshots (release-anchor, pre-migration, etc.) are never touched.
#
# Env / args:
# CLAWSTOR_BIN path to claw-store binary (default: ~/clawstor-deploy/claw-store)
# CLAWSTOR_CONFIG path to config.toml (default: ~/clawstor-deploy/config.toml)
# RETAIN_DAYS snapshots older than this many days get pruned (default: 30)
# DRY_RUN=1 print what would happen, no side effects
set -euo pipefail
CLAWSTOR_BIN=${CLAWSTOR_BIN:-$HOME/clawstor-deploy/claw-store}
CLAWSTOR_CONFIG=${CLAWSTOR_CONFIG:-$HOME/clawstor-deploy/config.toml}
RETAIN_DAYS=${RETAIN_DAYS:-30}
DRY_RUN=${DRY_RUN:-0}
if [ ! -x "$CLAWSTOR_BIN" ]; then
echo "error: $CLAWSTOR_BIN not executable" >&2
exit 1
fi
CS="$CLAWSTOR_BIN --config $CLAWSTOR_CONFIG"
TODAY=$(date +%Y-%m-%d)
NAME="daily-$TODAY"
echo "── rotate-snapshots ────────────────────────────────"
echo "today: $NAME"
echo "retention: $RETAIN_DAYS days"
# Create today's snapshot. Idempotent: `cluster-snapshot-create` errors
# if the name already exists — we treat that as success.
if [ "$DRY_RUN" = "1" ]; then
echo "dry-run: would create snapshot $NAME"
else
if $CS cluster-snapshot-create --name "$NAME" 2>&1 | tail -6; then
echo "created: $NAME"
else
echo "already exists (idempotent): $NAME"
fi
fi
# Compute cutoff — POSIX date arithmetic (no GNU date extensions needed
# because we do the math on the epoch integer).
NOW_EPOCH=$(date +%s)
CUTOFF_EPOCH=$((NOW_EPOCH - RETAIN_DAYS * 86400))
# Snapshot list format:
# CREATED_AT NAME BLOBS SIZE
# 1784046443 daily-2026-07-14 4 224
# So awk field 1 = epoch, field 2 = name.
PRUNE_LIST=$($CS cluster-snapshot-list 2>/dev/null | awk -v cutoff="$CUTOFF_EPOCH" '
NR > 4 && $2 ~ /^daily-/ && $1 < cutoff { print $2 }
')
echo
if [ -z "$PRUNE_LIST" ]; then
echo "nothing to prune"
else
echo "prune candidates (older than $RETAIN_DAYS days):"
echo "$PRUNE_LIST" | sed 's/^/ /'
if [ "$DRY_RUN" = "1" ]; then
echo "dry-run: no snapshots deleted"
else
echo "$PRUNE_LIST" | while read -r snap; do
$CS cluster-snapshot-delete --name "$snap" 2>&1 | tail -1
done
fi
fi
echo "────────────────────────────────────────────────────"
+1
View File
@@ -11,6 +11,7 @@ Systemd **user** units for clawstor.
| `clawstor-scrub.service` + `clawstor-scrub.timer` | Weekly (Sun 04:00) BLAKE3 verify every chunk against its manifest. Non-zero exit = integrity failure — surfaced by systemd status. |
| `clawstor-gc.service` + `clawstor-gc.timer` | Nightly (03:30) orphan-chunk sweep. Override ExecStart via drop-in to add `--evict-to-gb N` for size-cap fleets. Sequenced before the Sunday scrub so scrub reads a fresh layout. |
| `clawstor-ref-sweep.service` + `clawstor-ref-sweep.timer` | Nightly (03:15) Gitea live-refs poll + stale-fingerprint report. Set `GITEA_TOKEN` via a drop-in for private repos. Report-only (dry-run) — deletion is a follow-on. |
| `clawstor-snapshot-rotate.service` + `clawstor-snapshot-rotate.timer` | Daily (02:00) create `daily-YYYY-MM-DD` snapshot + prune snapshots older than `RETAIN_DAYS` (default 30). Only touches snapshots whose name matches `daily-*` — hand-created ones survive. |
## Install `clawstor-fuse.service`
@@ -0,0 +1,14 @@
[Unit]
Description=Clawstor daily snapshot rotation (create today + prune > N days)
Documentation=https://git.redclaw.dev/clawverse/clawstor
After=clawstor-cluster.service
[Service]
Type=oneshot
Environment=RETAIN_DAYS=30
ExecStart=%h/clawstor-deploy/scripts/rotate-snapshots.sh
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target
@@ -0,0 +1,13 @@
[Unit]
Description=Daily Clawstor snapshot rotation
Documentation=https://git.redclaw.dev/clawverse/clawstor
[Timer]
# 02:00 local — ahead of ref-sweep (03:15) + gc (03:30) so the fresh
# snapshot's pins protect its blobs from tonight's eviction.
OnCalendar=*-*-* 02:00:00
Persistent=true
Unit=clawstor-snapshot-rotate.service
[Install]
WantedBy=timers.target