diff --git a/backup/entrypoint.sh b/backup/entrypoint.sh index d107577..f74eaed 100755 --- a/backup/entrypoint.sh +++ b/backup/entrypoint.sh @@ -1,26 +1,62 @@ #!/bin/sh -# Loops forever: sleeps until the next BACKUP_HOUR:BACKUP_MINUTE UTC, then runs backup_db.sh. -# Defaults: 02:30 UTC nightly. +# Runs backup_db.sh at each time in BACKUP_TIMES_UTC (comma-separated HH:MM list). +# Defaults: 02:30, 08:30, 14:30, 20:30 UTC — four backups per day. +# +# Back-compat: if BACKUP_TIMES_UTC is unset but legacy BACKUP_HOUR/BACKUP_MINUTE are, +# those are honored as a single slot. set -eu -HOUR="${BACKUP_HOUR:-2}" -MINUTE="${BACKUP_MINUTE:-30}" +if [ -n "${BACKUP_TIMES_UTC:-}" ]; then + TIMES="$BACKUP_TIMES_UTC" +elif [ -n "${BACKUP_HOUR:-}" ] || [ -n "${BACKUP_MINUTE:-}" ]; then + TIMES="$(printf '%02d:%02d' "${BACKUP_HOUR:-2}" "${BACKUP_MINUTE:-30}")" +else + TIMES="02:30,08:30,14:30,20:30" +fi + +echo "[$(date -u +%FT%TZ)] backup schedule (UTC): ${TIMES}" if [ "${BACKUP_RUN_ON_START:-0}" = "1" ]; then echo "[$(date -u +%FT%TZ)] BACKUP_RUN_ON_START=1 — running backup immediately" /app/backup_db.sh || echo "[$(date -u +%FT%TZ)] initial backup failed (continuing)" fi +# Compute epoch for "today HH:MM UTC" on both GNU and BSD date. +slot_to_epoch_today() { + HM="$1" + date -u -d "today ${HM}:00" +%s 2>/dev/null \ + || date -u -j -f "%H:%M:%S" "${HM}:00" +%s +} + while true; do NOW_EPOCH=$(date -u +%s) - TARGET=$(date -u -d "today ${HOUR}:${MINUTE}:00" +%s 2>/dev/null \ - || date -u -j -f "%H:%M:%S" "${HOUR}:${MINUTE}:00" +%s) - if [ "$TARGET" -le "$NOW_EPOCH" ]; then - TARGET=$((TARGET + 86400)) + NEXT="" + # Find the smallest TARGET > NOW across all configured slots (rolling to tomorrow if needed). + OLDIFS="$IFS" + IFS=',' + for HM in $TIMES; do + HM="$(echo "$HM" | tr -d ' ')" + [ -z "$HM" ] && continue + T=$(slot_to_epoch_today "$HM") + if [ "$T" -le "$NOW_EPOCH" ]; then + T=$((T + 86400)) + fi + if [ -z "$NEXT" ] || [ "$T" -lt "$NEXT" ]; then + NEXT="$T" + fi + done + IFS="$OLDIFS" + + if [ -z "$NEXT" ]; then + echo "[$(date -u +%FT%TZ)] no valid times in BACKUP_TIMES_UTC='${TIMES}'; sleeping 1h" + sleep 3600 + continue fi - SLEEP=$((TARGET - NOW_EPOCH)) - echo "[$(date -u +%FT%TZ)] next backup in ${SLEEP}s (at $(date -u -d "@${TARGET}" +%FT%TZ 2>/dev/null || date -u -r "${TARGET}" +%FT%TZ))" + + SLEEP=$((NEXT - NOW_EPOCH)) + NEXT_ISO=$(date -u -d "@${NEXT}" +%FT%TZ 2>/dev/null || date -u -r "${NEXT}" +%FT%TZ) + echo "[$(date -u +%FT%TZ)] next backup in ${SLEEP}s (at ${NEXT_ISO})" sleep "$SLEEP" - /app/backup_db.sh || echo "[$(date -u +%FT%TZ)] backup failed (will retry tomorrow)" + /app/backup_db.sh || echo "[$(date -u +%FT%TZ)] backup failed (will retry at next slot)" done diff --git a/docker-compose.yaml b/docker-compose.yaml index 34b5b80..26618b1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -89,9 +89,9 @@ services: condition: service_healthy env_file: .env environment: - # Nightly pg_dump → rustfs. Credentials from .env (RUSTFS_*). - - BACKUP_HOUR=${BACKUP_HOUR:-2} - - BACKUP_MINUTE=${BACKUP_MINUTE:-30} + # pg_dump → rustfs. Credentials from .env (RUSTFS_*). + # BACKUP_TIMES_UTC: comma-separated HH:MM list. Default: 4×/day. + - BACKUP_TIMES_UTC=${BACKUP_TIMES_UTC:-02:30,08:30,14:30,20:30} - BACKUP_KEEP_DAYS=${BACKUP_KEEP_DAYS:-30} - BACKUP_RUN_ON_START=${BACKUP_RUN_ON_START:-0} - RUSTFS_ENDPOINT=${RUSTFS_ENDPOINT}