supabase-postgres.git / summary / log / commit / refs
commit bc12563f245ebe4b7669e08f86f414864fec55ce
Author: Douglas J Hunley <doug.hunley@gmail.com>
Commit: GitHub <noreply@github.com>
Date: Fri Jul 17 15:54:09 2026 +0000
Check for 2 GB free space before PG upgrade (#2271)
* fix(pg_upgrade): check for 2 GiB free space on / before running upgrade
initiate.sh realizes the new pg_upgrade Nix store path onto / before
touching the mounted data disk. If / is already tight on space, Nix
fills the partition mid-upgrade and the failure surfaces deep into the
run instead of at the start. check_free_space in common.sh checks
available space on / via df and aborts immediately, before any
mkdir/nix work, if less than 2 GiB is free.
* fix: fail closed on unparseable df output in check_free_space
df -k wraps onto a second line when the source device name is long, which misaligns the NR==2 awk parse; -P forces single-line POSIX output. Also validate available_kb is numeric before comparing — an empty/non-numeric value from a bad df parse made the bash [ -lt ] test error inside an if condition, which set -e doesn't catch, silently skipping the guard instead of aborting.
* style: match treefmt/shfmt quoting for [[ =~ ]] test
* style(pg_upgrade): use bash arithmetic comparison in check_free_space
[ -lt ] swapped for (( < )) per review — script is bash, not posix sh,
so arithmetic mode reads clearer.
* fix: strip inner spaces from arithmetic conditional in check_free_space
treefmt's shfmt formatting normalizes `(( expr ))` to `((expr))` with no
inner padding; the added check_free_space guard used the padded form,
failing pre-commit-run/treefmt-check CI on all platforms.
* fix(pg_upgrade): re-check free space around nix-store realize
check_free_space only ran once, at the top of initiate_upgrade, but the
tarball extraction, Nix install, and catalog download that follow can eat
into that 2 GiB reserve before nix-store -r (the actual big consumer) runs,
letting a host that passed the entry check still hit ENOSPC mid-realize.
Add a second 2 GiB check immediately before the nix-store -r retry loop to
confirm the reserve is still intact right where it's needed, and a smaller
512 MiB check after the closure is realized, since the remaining apt-get/
locale-gen work needs far less headroom than the Nix realize did.
* fix(pg_upgrade): guard cleanup's umount so status file isn't stuck at running
check_free_space runs before $MOUNT_POINT is ever mounted, so when it fails
the ERR trap's cleanup() hits an umount on an unmounted path. Every other
risky command in cleanup() already has || true; this one didn't, so under
set -e the trap handler aborted before writing UPGRADE_STATUS, leaving
/tmp/pg-upgrade-status stuck at "running" forever on exactly the low-disk
failure path this PR adds. Reproduced and confirmed fixed with a minimal
harness matching the trap structure (removed after verifying).
.../admin_api_scripts/pg_upgrade_scripts/common.sh | 21 +++++++++++++++++++++
.../pg_upgrade_scripts/initiate.sh | 20 +++++++++++++++++++-
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh b/ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh
index a3f6f208..699d7aeb 100755
--- a/ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh
+++ b/ansible/files/admin_api_scripts/pg_upgrade_scripts/common.sh
@@ -44,6 +44,27 @@ function ship_logs {
-d "$BODY"
}
+# Aborts if / has less than required_kb free. Nix realizes the new pg_upgrade
+# store path onto / before the mounted data disk is touched, so a full root
+# partition otherwise fails the upgrade mid-flight instead of at the start.
+function check_free_space {
+ local required_kb=$1
+ local available_kb
+ # -P forces POSIX single-line output; plain `df -k` wraps onto a second
+ # line when the source device name is long, which would misalign NR==2.
+ available_kb=$(df -Pk / | awk 'NR==2 {print $4}')
+
+ if ! [[ $available_kb =~ ^[0-9]+$ ]]; then
+ echo "ERROR: could not determine free space on /; aborting upgrade."
+ return 1
+ fi
+
+ if ((available_kb < required_kb)); then
+ echo "ERROR: only ${available_kb}KB free on / but ${required_kb}KB required; aborting upgrade."
+ return 1
+ fi
+}
+
function retry {
local retries=$1
shift
diff --git a/ansible/files/admin_api_scripts/pg_upgrade_scripts/initiate.sh b/ansible/files/admin_api_scripts/pg_upgrade_scripts/initiate.sh
index df37550a..1b100c9a 100755
--- a/ansible/files/admin_api_scripts/pg_upgrade_scripts/initiate.sh
+++ b/ansible/files/admin_api_scripts/pg_upgrade_scripts/initiate.sh
@@ -156,7 +156,11 @@ EOF
if [ -z "$IS_CI" ] && [ -z "$IS_LOCAL_UPGRADE" ]; then
echo "Unmounting data disk from ${MOUNT_POINT}"
- retry 3 umount $MOUNT_POINT
+ # check_free_space (and other pre-mount failures) can trigger this trap before
+ # $MOUNT_POINT is ever mounted. Without || true, set -e aborts cleanup() right here
+ # under retry's non-zero exit, so UPGRADE_STATUS is never written and the status file
+ # is stuck at "running" forever instead of reporting the real failure.
+ retry 3 umount $MOUNT_POINT || true
fi
echo "$UPGRADE_STATUS" >/tmp/pg-upgrade-status
@@ -211,6 +215,9 @@ EOF
}
function initiate_upgrade {
+ # 2 GiB: enough headroom for the Nix store realize onto / (see check_free_space)
+ check_free_space $((2 * 1024 * 1024))
+
mkdir -p "$MOUNT_POINT"
SHARED_PRELOAD_LIBRARIES=$(grep '^[[:space:]]*shared_preload_libraries' "$POSTGRES_CONFIG_PATH" | sed "s/shared_preload_libraries =\s\{0,1\}'\(.*\)'.*/\1/")
@@ -356,6 +363,13 @@ EXTRA_NIX_CONF
# registered paths are skipped on retry but an in-flight NAR restarts from
# 0%. Failing as a normal command (not exit 1) lets the ERR trap run cleanup
# and record "failed" instead of hanging.
+ #
+ # Re-check free space here: the earlier check at the top of initiate_upgrade
+ # only guarantees headroom at entry, and the tarball extraction / nix install
+ # / catalog download above can already have eaten into that reserve before
+ # the realize (the actual big consumer) starts.
+ check_free_space $((2 * 1024 * 1024))
+
nix_store_ok="false"
for attempt in 1 2 3; do
if timeout -k 10s 120s nix-store -r "$STORE_PATH"; then
@@ -370,6 +384,10 @@ EXTRA_NIX_CONF
done
[ "$nix_store_ok" = "true" ]
+ # Closure is realized, so the multi-GB Nix reserve is no longer needed; just
+ # confirm enough room remains for the lighter apt/locale-gen work still ahead.
+ check_free_space $((512 * 1024))
+
PG_UPGRADE_BIN_DIR="$STORE_PATH"
PGSHARENEW="$PG_UPGRADE_BIN_DIR/share/postgresql"
fi
[parent: 0c68cac1da5e]