diff --git a/templates/backup-cronjob.yaml b/templates/backup-cronjob.yaml index d347b02..f8389c3 100644 --- a/templates/backup-cronjob.yaml +++ b/templates/backup-cronjob.yaml @@ -149,15 +149,30 @@ spec: echo "(filestore empty; skipping archive)" fi echo ">>> rotating: keep last $RETAIN snapshots under ${S3_PREFIX}/" - # Group keys by timestamp prefix (everything before - # the first dot after the date) and prune the oldest - # groups. Both .sql.gz and .filestore.tar.gz share - # the same timestamp prefix, so groups stay paired. + # Rotation must scope to the TOP-LEVEL dated backup + # files only — never touch checkpoints/, exports/, + # or blobs/ subdirectories. The previous version + # listed everything recursively under ${S3_PREFIX}/ + # and grep'd for *.sql.gz, which matched + # checkpoints//db.sql.gz too — those sort + # ahead of the bare dated keys in reverse-alpha + # ('c' > '2'), so legitimate top-level backups + # routinely fell out of the keep-window AND + # checkpoint safety copies got deleted as a side + # effect. Use --delimiter / so list-objects-v2 + # returns ONLY direct children of the prefix; the + # subdirectory keys come back as CommonPrefixes + # (which we discard). + # + # Both .sql.gz and .filestore.tar.gz share the + # same timestamp stem, so prune-by-stem keeps + # pairs aligned. aws --endpoint-url "$S3_ENDPOINT" s3api list-objects-v2 \ --bucket "$S3_BUCKET" --prefix "${S3_PREFIX}/" \ + --delimiter / \ --query 'Contents[].Key' --output text 2>/dev/null \ | tr '\t' '\n' \ - | grep -E '\.sql\.gz$' \ + | grep -E "^${S3_PREFIX}/[0-9]{8}T[0-9]{6}Z\.sql\.gz$" \ | sort -r | tail -n +$((RETAIN + 1)) \ | while read OLDSQL; do [ -n "$OLDSQL" ] || continue