#!/bin/bash
# Shared bounded depot cleanup. Requires GNU find; callers own logging/traps.
# Report one reaped find process. Counts remain useful on a partial failure.
function _sdp_cache_cleanup_report () {
local CountFile=$1 Depot=$2 Start=$3 Result=$4 Removed
Removed=$(wc -c < "$CountFile") || { echo "ERROR: Cannot count removals for $Depot." >&2; return 1; }
Removed=${Removed//[[:space:]]/}
SDP_CACHE_REMOVED=$((SDP_CACHE_REMOVED + Removed))
if [[ "$Result" == 0 ]]; then
printf '%s: removed %s file(s) in %s seconds.\n' "$Depot" "$Removed" "$((SECONDS - Start))"
else
printf 'ERROR: %s: cleanup failed/interrupted after removing %s file(s) in %s seconds.\n' "$Depot" "$Removed" "$((SECONDS - Start))" >&2
return 1
fi
}
function sdp_cache_cleanup_dirs () {
local Age=$1 CrossDevice=$2 Limit=${SDP_CACHE_CLEAN_WORKERS:-4}
shift 2
local Depot Worker Status=0 Index=0 CountDir Result CancelSignal=
local SavedTerm SavedInt SavedHup SignalNumber=0 Position ThisPID=${BASHPID:-$$}
local -a Workers=() WorkerIndexes=() StartTimes=() FindArgs=() Depots=("$@")
SDP_CACHE_REMOVED=0
SDP_CACHE_DEPOTS=$#
# Validate the decimal string before arithmetic so oversized input cannot
# wrap around to an apparently valid worker count.
while [[ ${#Limit} -gt 1 && $Limit == 0* ]]; do Limit=${Limit#0}; done
if [[ ! "$Limit" =~ ^[0-9]{1,3}$ ]] || (( 10#$Limit < 1 || 10#$Limit > 256 )); then
echo "SDP_CACHE_CLEAN_WORKERS must be between 1 and 256." >&2
return 1
fi
Limit=$((10#$Limit))
CountDir=$(mktemp -d "${P4TMP:-${TMPDIR:-/tmp}}/cache-clean.XXXXXX") || return 1
[[ "$CrossDevice" == no ]] && FindArgs+=(-xdev)
SavedTerm=$(trap -p TERM); SavedInt=$(trap -p INT); SavedHup=$(trap -p HUP)
# A signal only marks cancellation here. In particular, a signal between
# starting find and recording $! cannot leave an untracked child behind.
# Cleanup then restores and replays the caller's original signal handler.
trap 'CancelSignal=TERM' TERM
trap 'CancelSignal=INT' INT
trap 'CancelSignal=HUP' HUP
echo "Cleaning $# depot(s) with at most $Limit workers."
for Depot in "$@"; do
[[ -z "$CancelSignal" ]] || break
if (( ${#Workers[@]} >= Limit )); then
Result=0
wait "${Workers[0]}" || Result=$?
[[ -z "$CancelSignal" ]] || break
Position=${WorkerIndexes[0]}
_sdp_cache_cleanup_report "$CountDir/$Position" "${Depots[Position]}" "${StartTimes[Position]}" "$Result" || Status=1
Workers=("${Workers[@]:1}"); WorkerIndexes=("${WorkerIndexes[@]:1}")
fi
[[ -z "$CancelSignal" ]] || break
StartTimes[Index]=$SECONDS
# Track actual find PIDs, not wrapper subshells: cancellation must stop
# the process deleting files and wait for it before returning to callers.
# One byte per deletion handles filenames containing newlines correctly.
find "$Depot/" "${FindArgs[@]}" -atime "+$Age" -type f -delete -printf '.' > "$CountDir/$Index" &
Workers+=("$!"); WorkerIndexes+=("$Index")
Index=$((Index + 1))
done
while (( ${#Workers[@]} )) && [[ -z "$CancelSignal" ]]; do
Result=0
wait "${Workers[0]}" || Result=$?
[[ -z "$CancelSignal" ]] || break
Position=${WorkerIndexes[0]}
_sdp_cache_cleanup_report "$CountDir/$Position" "${Depots[Position]}" "${StartTimes[Position]}" "$Result" || Status=1
Workers=("${Workers[@]:1}"); WorkerIndexes=("${WorkerIndexes[@]:1}")
done
if [[ -n "$CancelSignal" ]]; then
echo "ERROR: Cache cleanup interrupted by $CancelSignal; stopping and reaping find workers." >&2
# Background commands can inherit ignored SIGINT. TERM is intentional for
# all cancellation signals; KILL bounds cleanup if a child ignores TERM.
for Worker in "${Workers[@]}"; do kill -TERM "$Worker" 2>/dev/null || :; done
if (( ${#Workers[@]} )); then sleep 0.1; fi
for Worker in "${Workers[@]}"; do kill -KILL "$Worker" 2>/dev/null || :; done
for ((Index=0; Index<${#Workers[@]}; Index++)); do
wait "${Workers[Index]}" 2>/dev/null || :
Position=${WorkerIndexes[Index]}
_sdp_cache_cleanup_report "$CountDir/$Position" "${Depots[Position]}" "${StartTimes[Position]}" 1 || :
done
Status=1
# Caller termination traps may exit before the normal summary path.
if declare -F sdp_add_field >/dev/null; then
sdp_add_field depot_count "$SDP_CACHE_DEPOTS"
sdp_add_field files_removed "$SDP_CACHE_REMOVED"
fi
if declare -F sdp_set_error >/dev/null; then sdp_set_error "Cache cleanup interrupted by $CancelSignal"; fi
fi
rm -rf -- "$CountDir" || Status=1
trap - TERM INT HUP
# These strings are obtained only from Bash's own trap -p output.
[[ -z "$SavedTerm" ]] || eval "$SavedTerm"
[[ -z "$SavedInt" ]] || eval "$SavedInt"
[[ -z "$SavedHup" ]] || eval "$SavedHup"
if [[ -n "$CancelSignal" ]]; then
case "$CancelSignal" in TERM) SignalNumber=15;; INT) SignalNumber=2;; HUP) SignalNumber=1;; esac
kill -s "$CancelSignal" "$ThisPID"
# If the caller deliberately ignores/handles the signal without exiting,
# it still receives a nonzero result from this cancelled cleanup pass.
return "$((128 + SignalNumber))"
fi
return "$Status"
}
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 34004 | Russell C. Jackson (Rusty) |
Improve SDP backup/recovery, Maintenance, logging and cloud operation. Add owned checkpoint locks, recovery-aware retention, preserved database switches, bounded cleanup and coordinated AWS/Azure snapshots. Fix service-user password initialization, edge staging, standby promotion state parsing, proxy cache setup, typed depot verification and historical-server configuration/error handling. Remove unused OTP programs; keep Windows removed, server.locks cleanup and the journal-count condition unchanged. Add no global shell strict modes. Update operational documentation and add reproducible mkdirs.sh/mkdirs.cfg container assurance. Validate Ubuntu 26.04 and UBI 8.10/9.8/10.2 across both Python runtimes: 44 stages; 428 offline tests per runtime/OS, zero offline skips; 19 deployed-archive comparisons per combination. Three six-node topology runs pass all 30 recovery/failure scenarios. P4D 2023.1 through 2026.1 on UBI 8/9 pass all 14 cells and 98 scenarios. Static, documentation and provenance checks pass. Containers do not certify every fleet kernel, patch build or integration. Tested source manifest SHA-256: e1a23e17b8ced8c1148d9a250589977c6f6e98606bf2a81dc3ff5d7c909e3695 Deployed comparison archive SHA-256: 1bd23217356ee3808e040d39aea5be9519bda7c8f2b4924656a70aff8ad7293a |