Files
computer-playbook/roles/web-app-openproject/files/shell/await_migrations.sh
kevinveenbirkenbachandClaude Opus 5.5 c66b79ce82 fix(web-app-openproject): probe migrations without creating tables
Swarm Nextcloud#3 on main 748189e890 (job 117182318941) wedged the
OpenProject schema: the convergence check gave up after 74 attempts
while the seeder kept exiting 1, first with
`relation "ar_internal_metadata" already exists` from its structure
load, then with `relation "announcements" already exists` on every
`db:migrate`.

The seeder (`docker/prod/seeder`) loads `db/structure.sql` through
psql, statement by statement, when `\dt` finds no relation. In swarm,
`depends_on` is ignored, so web and worker start alongside it and poll
`await_migrations.sh`. That gate called
`rails db:abort_if_pending_migrations`, which runs
`migration_context.open`, i.e. `Migrator.new`, whose constructor
executes `schema_migration.create_table` and
`internal_metadata.create_table` (Rails 8.0.4,
activerecord/lib/active_record/migration.rb:1430-1431). The seeder
saw an empty database at 05:33:20; by 05:33:38 the gate had created
`ar_internal_metadata`, the structure load aborted at line 255 with
everything alphabetically before it already committed, and every
later attempt took the migrate path into tables that already existed.

Probe with `rails db:migrate:status` instead. It aborts while
`schema_migrations` is missing and otherwise only reads the recorded
versions (database_tasks.rb:303, migration.rb:1318); the gate treats
any `down` row as pending.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 10:17:39 +02:00

44 lines
1.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Wait for the seeder's migrations, then exec the app entrypoint.
#
# Swarm ignores depends_on, so web and worker would start while the seeder is
# still migrating and die on ActiveRecord::PendingMigrationError.
#
# The wait is bounded on purpose. The probe fails both for pending migrations
# and for an unreachable database, so an unbounded loop would spin forever
# whenever the database is down. Past the deadline the entrypoint starts regardless,
# which is exactly today's behaviour: the worst case stays the crash loop that
# already exists, never a service that refuses to come up.
#
# Argv:
# $1 deadline_seconds -- how long to wait before starting anyway
# $2 poll_seconds -- sleep between probes
# $3.. entrypoint -- the upstream start command to exec
set -euo pipefail
deadline_seconds="${1:?deadline_seconds required}"
poll_seconds="${2:?poll_seconds required}"
shift 2
[ "$#" -gt 0 ] || {
echo "await_migrations: entrypoint required" >&2
exit 2
}
schema_is_current() {
local status
status=$(bundle exec rails db:migrate:status 2>/dev/null) || return 1
! grep -q -E '^[[:space:]]*down[[:space:]]' <<<"$status"
}
deadline=$(($(date +%s) + deadline_seconds))
while [ "$(date +%s)" -lt "$deadline" ]; do
if schema_is_current; then
echo "await_migrations: schema is current, starting $1"
exec "$@"
fi
sleep "$poll_seconds"
done
echo "await_migrations: deadline of ${deadline_seconds}s reached, starting $1 regardless" >&2
exec "$@"