diff --git a/scripts/setup-clickhouse.sh b/scripts/setup-clickhouse.sh index 73bb565..c32dec6 100755 --- a/scripts/setup-clickhouse.sh +++ b/scripts/setup-clickhouse.sh @@ -9,11 +9,49 @@ # CLICKHOUSE_CHANNEL=lts bash scripts/setup-clickhouse.sh # # Env: -# CLICKHOUSE_CHANNEL stable (default) or lts — see https://clickhouse.com/docs/knowledgebase/production +# CLICKHOUSE_CHANNEL stable (default) or lts — see https://clickhouse.com/docs/knowledgebase/production +# WAIT_FOR_APT_LOCK_SEC max seconds to wait for apt/dpkg lock (default: 900) # set -euo pipefail 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_for_apt_lock() { + local waited=0 + while true; do + if ! _apt_lock_held; 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 " sudo systemctl status unattended-upgrades" >&2 + echo "Then re-run this script, 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)..." + 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 $? + fi + pgrep -x apt >/dev/null 2>&1 || pgrep -x apt-get >/dev/null 2>&1 || pgrep -x dpkg >/dev/null 2>&1 +} + +apt_get() { + wait_for_apt_lock + sudo apt-get "$@" +} CLICKHOUSE_LIST="/etc/apt/sources.list.d/clickhouse.list" KEYRING="/usr/share/keyrings/clickhouse-keyring.gpg" CHANNEL="${CLICKHOUSE_CHANNEL:-stable}" @@ -25,7 +63,7 @@ for arg in "$@"; do -h|--help) echo "Usage: $0 [--purge-existing]" echo " --purge-existing apt purge existing clickhouse* packages before installing (destructive for old installs)." - echo " Env: CLICKHOUSE_CHANNEL=stable|lts (default: stable)" + echo " Env: CLICKHOUSE_CHANNEL=stable|lts (default: stable), WAIT_FOR_APT_LOCK_SEC (default: 900)" exit 0 ;; *) @@ -57,14 +95,14 @@ if [[ "$PURGE_EXISTING" == "true" ]]; then echo "==> Purging existing ClickHouse packages (--purge-existing)..." sudo systemctl stop clickhouse-server 2>/dev/null || true sudo systemctl stop clickhouse-keeper 2>/dev/null || true - sudo apt-get purge -y 'clickhouse*' || true - sudo apt-get autoremove -y || true + apt_get purge -y 'clickhouse*' || true + apt_get autoremove -y || true fi echo "==> Installing prerequisites..." export DEBIAN_FRONTEND=noninteractive -sudo apt-get update -qq -sudo apt-get install -y apt-transport-https ca-certificates curl gnupg +apt_get update -qq +apt_get install -y apt-transport-https ca-certificates curl gnupg echo "==> Adding ClickHouse APT repo (channel: $CHANNEL)..." sudo mkdir -p /usr/share/keyrings @@ -78,8 +116,8 @@ echo "deb [signed-by=${KEYRING} arch=${ARCH}] https://packages.clickhouse.com/de | sudo tee "$CLICKHOUSE_LIST" >/dev/null echo "==> Installing clickhouse-server and clickhouse-client..." -sudo apt-get update -qq -sudo apt-get install -y clickhouse-server clickhouse-client +apt_get update -qq +apt_get install -y clickhouse-server clickhouse-client echo "==> Enabling and starting clickhouse-server..." sudo systemctl enable --now clickhouse-server