#!/bin/bash set -euo pipefail PLUGIN_DIR="${PLUGIN_DIR:-$(cd "$(dirname "$0")/.." && pwd)}" DATA_DIR="$PLUGIN_DIR/data" JOB_DIR="$DATA_DIR/jobs/pending" PROCESSING_DIR="$DATA_DIR/jobs/processing" DONE_DIR="$DATA_DIR/jobs/done" WORKER_LOCK_DIR="$DATA_DIR/worker.lock" DB_LOCK_DIR="$DATA_DIR/lock" PID_DIR="$DATA_DIR/pids" CANCEL_DIR="$DATA_DIR/cancel" WORKER_LOCK_STALE_SECONDS="${WORKER_LOCK_STALE_SECONDS:-300}" mkdir -p "$JOB_DIR" "$PROCESSING_DIR" "$DONE_DIR" "$PID_DIR" "$CANCEL_DIR" "$DB_LOCK_DIR" acquire_worker_lock() { if mkdir "$WORKER_LOCK_DIR" 2>/dev/null; then printf '%s' "$$" > "$WORKER_LOCK_DIR/pid" 2>/dev/null || true return 0 fi local existing_pid="" lock_mtime=0 now lock_age=0 if [ -r "$WORKER_LOCK_DIR/pid" ]; then existing_pid="$(cat "$WORKER_LOCK_DIR/pid" 2>/dev/null || true)" fi if [ -n "$existing_pid" ]; then if kill -0 "$existing_pid" 2>/dev/null; then echo "alt-mysql worker: lock held by running pid $existing_pid, exiting" >&2 return 1 fi else lock_mtime="$(stat -c '%Y' "$WORKER_LOCK_DIR" 2>/dev/null || stat -f '%m' "$WORKER_LOCK_DIR" 2>/dev/null || echo 0)" now="$(date +%s)" if [ "$lock_mtime" -gt 0 ]; then lock_age=$((now - lock_mtime)) fi if [ "$lock_age" -lt "$WORKER_LOCK_STALE_SECONDS" ]; then echo "alt-mysql worker: lock present without a live pid but younger than ${WORKER_LOCK_STALE_SECONDS}s (age ${lock_age}s), exiting" >&2 return 1 fi fi echo "alt-mysql worker: reclaiming stale lock (pid=${existing_pid:-none}, age=${lock_age}s)" >&2 rm -rf "$WORKER_LOCK_DIR" if mkdir "$WORKER_LOCK_DIR" 2>/dev/null; then printf '%s' "$$" > "$WORKER_LOCK_DIR/pid" 2>/dev/null || true return 0 fi return 1 } if ! acquire_worker_lock; then exit 0 fi cleanup() { rm -rf "$WORKER_LOCK_DIR" 2>/dev/null || true } trap cleanup EXIT db_lock_path_for_db() { local db_name="$1" local safe safe="$(printf '%s' "$db_name" | tr -c 'A-Za-z0-9_.-' '_')" printf '%s/%s.lock' "$DB_LOCK_DIR" "$safe" } release_db_lock_later() { local lock_path="$1" if [ -z "$lock_path" ]; then return 0 fi ( sleep 60 rm -f "$lock_path" 2>/dev/null || true ) >/dev/null 2>&1 & } while true; do JOB_FILE="$(ls -1 "$JOB_DIR"/*.env 2>/dev/null | head -n 1 || true)" if [ -z "${JOB_FILE:-}" ]; then break fi BASE_NAME="$(basename "$JOB_FILE")" RUN_FILE="$PROCESSING_DIR/$BASE_NAME" mv "$JOB_FILE" "$RUN_FILE" JOB_ID="${BASE_NAME%.env}" PID_FILE="$PID_DIR/${JOB_ID}.pid" JOB_TYPE="restore" DB_NAME="" DB_LOCK_FILE="" # shellcheck disable=SC1090 source "$RUN_FILE" || true JOB_TYPE="${JOB_TYPE:-restore}" DB_NAME="${DB_NAME:-}" DB_LOCK_FILE="${DB_LOCK_FILE:-}" LOCK_PATH="" if [ -n "$DB_LOCK_FILE" ]; then case "$DB_LOCK_FILE" in "$DB_LOCK_DIR"/*.lock) LOCK_PATH="$DB_LOCK_FILE" ;; *) LOCK_PATH="" ;; esac fi if [ -z "$LOCK_PATH" ] && [ -n "$DB_NAME" ]; then LOCK_PATH="$(db_lock_path_for_db "$DB_NAME")" fi RUNNER="$PLUGIN_DIR/scripts/restore_job.sh" if [ "$JOB_TYPE" = "backup" ]; then RUNNER="$PLUGIN_DIR/scripts/backup_job.sh" fi set +e "$RUNNER" "$RUN_FILE" & RUN_PID=$! echo "PID=${RUN_PID}" >> "$RUN_FILE" echo "${RUN_PID}" > "$PID_FILE" wait "$RUN_PID" EXIT_CODE=$? set -e rm -f "$PID_FILE" CANCEL_FLAG="${CANCEL_DIR}/${JOB_ID}.flag" if [ -f "$CANCEL_FLAG" ]; then rm -f "$CANCEL_FLAG" mv "$RUN_FILE" "$DONE_DIR/${JOB_ID}.cancel" elif [ "$EXIT_CODE" -eq 0 ]; then mv "$RUN_FILE" "$DONE_DIR/${JOB_ID}.ok" else mv "$RUN_FILE" "$DONE_DIR/${JOB_ID}.fail" fi release_db_lock_later "$LOCK_PATH" done exit 0