diff --git a/scripts/setup-clickhouse.sh b/scripts/setup-clickhouse.sh index c32dec6..6f0f38b 100755 --- a/scripts/setup-clickhouse.sh +++ b/scripts/setup-clickhouse.sh @@ -5,7 +5,7 @@ # # Usage (from repo root or any path): # bash scripts/setup-clickhouse.sh -# bash scripts/setup-clickhouse.sh --purge-existing # remove old clickhouse* packages first +# bash scripts/setup-clickhouse.sh --purge-existing # one line; do not split after "--" # CLICKHOUSE_CHANNEL=lts bash scripts/setup-clickhouse.sh # # Env: @@ -18,34 +18,51 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" WAIT_FOR_APT_LOCK_SEC="${WAIT_FOR_APT_LOCK_SEC:-900}" APT_LOCK_SLEEP_SEC="${APT_LOCK_SLEEP_SEC:-5}" -# Wait until unattended-upgrades / another apt releases dpkg locks (avoids E: Could not get lock ... lock-frontend). +# Wait until dpkg/apt can run (avoids E: Could not get lock ... lock-frontend). +# Uses non-blocking flock(1) on the same lock files apt/dpkg use — not fuser on lists/archives +# locks, which can look "busy" for a long time and cause endless waits. wait_for_apt_lock() { local waited=0 + local last_holder_line=-1 while true; do - if ! _apt_lock_held; then + if _dpkg_locks_available; then return 0 fi if (( waited >= WAIT_FOR_APT_LOCK_SEC )); then echo "Error: timed out after ${WAIT_FOR_APT_LOCK_SEC}s waiting for apt/dpkg lock." >&2 - echo "Another process is using apt (often PID 1 child: unattended-upgrades). Check:" >&2 - echo " sudo ps aux | egrep 'apt|dpkg' | grep -v egrep" >&2 + echo "Check what holds the lock:" >&2 + echo " sudo fuser -v /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock 2>&1 | head -20" >&2 + echo " sudo ps aux | egrep '[a]pt|[d]pkg'" >&2 echo " sudo systemctl status unattended-upgrades" >&2 - echo "Then re-run this script, or set WAIT_FOR_APT_LOCK_SEC=3600 to wait longer." >&2 + echo "Then re-run, or set WAIT_FOR_APT_LOCK_SEC=3600 to wait longer." >&2 exit 1 fi echo "==> Waiting for apt/dpkg lock to clear (${waited}s / ${WAIT_FOR_APT_LOCK_SEC}s max)..." + if (( waited >= 60 && waited != last_holder_line && waited % 60 == 0 )); then + last_holder_line=$waited + if command -v fuser >/dev/null 2>&1; then + echo " (lock holders: $(sudo fuser /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock 2>/dev/null | tr -s ' ' || true))" >&2 + fi + fi sleep "$APT_LOCK_SLEEP_SEC" waited=$((waited + APT_LOCK_SLEEP_SEC)) done } -_apt_lock_held() { - if command -v fuser >/dev/null 2>&1; then - sudo fuser /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock \ - /var/cache/apt/archives/lock /var/lib/apt/lists/lock >/dev/null 2>&1 - return $? +# True if we can take an exclusive flock on dpkg locks right now (same mechanism apt uses). +_dpkg_locks_available() { + if ! command -v flock >/dev/null 2>&1; then + echo "Error: flock(1) not found (install util-linux)." >&2 + exit 1 fi - pgrep -x apt >/dev/null 2>&1 || pgrep -x apt-get >/dev/null 2>&1 || pgrep -x dpkg >/dev/null 2>&1 + local f + for f in /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock; do + [[ -e "$f" ]] || continue + if ! sudo flock -n "$f" true 2>/dev/null; then + return 1 + fi + done + return 0 } apt_get() {