bbc1afb5e1
The mkdir-based worker lock had no staleness detection at all, unlike BackupQueueService::withQueueLock()'s 120s override for its own queue lock. If the root cron-driven worker was ever killed ungracefully (OOM, kill -9, reboot mid-job), the lock directory was left behind forever - every subsequent cron tick would silently exit without processing any job, so backups/restores would queue forever with no visible error. Track the owning PID inside the lock directory and reclaim the lock when that PID is no longer running, or when the lock has no PID file and is older than WORKER_LOCK_STALE_SECONDS (default 300s). Log to stderr on both the "held by a live worker" and "reclaiming stale lock" paths instead of exiting silently either way. cleanup() now uses rm -rf since the lock directory holds a pid file, not just an empty marker.
144 lines
3.6 KiB
Bash
144 lines
3.6 KiB
Bash
#!/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
|