File size: 6,106 Bytes
0f33135 14fbdb2 0f33135 c1cdb5b 14fbdb2 c1cdb5b 14fbdb2 e18b997 14fbdb2 e18b997 14fbdb2 7ecc783 e18b997 14fbdb2 19a98be 14fbdb2 c1cdb5b 14fbdb2 c1cdb5b 14fbdb2 c1cdb5b 14fbdb2 c1cdb5b e18b997 14fbdb2 e18b997 c1cdb5b 14fbdb2 c1cdb5b 14fbdb2 c1cdb5b 19a98be 14fbdb2 c1cdb5b 19a98be 14fbdb2 c1cdb5b 0f33135 14fbdb2 e18b997 14fbdb2 e18b997 0f33135 7ecc783 0f33135 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | #!/bin/sh
set -eu
PGDATA=/tmp/sub2api-postgresql
LEGACY_PGDATA=/data/postgresql
BACKUP_DIR=/data/postgresql-backup
BACKUP_FILE="$BACKUP_DIR/latest.dump"
REDIS_DATA_DIR=/tmp/sub2api-redis
APP_DATA_DIR=/data/sub2api
APP_PID=""
REDIS_PID=""
BACKUP_PID=""
MIGRATED=0
NEW_CLUSTER=0
PG_DIRS="
pg_commit_ts
pg_dynshmem
pg_logical
pg_logical/mappings
pg_logical/snapshots
pg_multixact
pg_multixact/members
pg_multixact/offsets
pg_notify
pg_replslot
pg_serial
pg_snapshots
pg_stat
pg_stat_tmp
pg_subtrans
pg_tblspc
pg_twophase
pg_wal
pg_wal/archive_status
pg_wal/summaries
"
ensure_pg_dirs() {
for rel in $PG_DIRS; do
dir="$PGDATA/$rel"
if [ ! -d "$dir" ]; then
mkdir -p "$dir"
chown postgres:postgres "$dir"
chmod 700 "$dir"
fi
rm -f "$dir/.hf-keep"
done
}
start_postgres() {
su-exec postgres pg_ctl -D "$PGDATA" -o "-c listen_addresses=127.0.0.1 -c port=5432 -c unix_socket_directories=/tmp" -w start
}
start_redis() {
su-exec redis redis-server \
--bind 127.0.0.1 \
--port 6379 \
--dir "$REDIS_DATA_DIR" \
--save 60 1 \
--appendonly yes \
--appendfsync everysec &
REDIS_PID=$!
}
backup_postgres() {
if ! su-exec postgres pg_ctl -D "$PGDATA" status >/dev/null 2>&1; then
return 1
fi
mkdir -p "$BACKUP_DIR"
local_dump="/tmp/sub2api-backup.$.dump"
bucket_dump="$BACKUP_DIR/.latest.$$.dump"
rm -f "$local_dump" "$bucket_dump"
if ! su-exec postgres pg_dump -h /tmp -U "$DATABASE_USER" -Fc -d "$DATABASE_DBNAME" -f "$local_dump"; then
rm -f "$local_dump"
return 1
fi
chmod 600 "$local_dump"
if ! cp "$local_dump" "$bucket_dump"; then
rm -f "$local_dump" "$bucket_dump"
return 1
fi
if ! mv -f "$bucket_dump" "$BACKUP_FILE"; then
rm -f "$local_dump" "$bucket_dump"
return 1
fi
rm -f "$local_dump"
echo "PostgreSQL backup saved to persistent storage"
}
cleanup() {
trap - EXIT INT TERM
if [ -n "$APP_PID" ] && kill -0 "$APP_PID" 2>/dev/null; then
kill -TERM "$APP_PID" 2>/dev/null || true
wait "$APP_PID" 2>/dev/null || true
fi
if [ -n "$BACKUP_PID" ] && kill -0 "$BACKUP_PID" 2>/dev/null; then
kill -TERM "$BACKUP_PID" 2>/dev/null || true
wait "$BACKUP_PID" 2>/dev/null || true
fi
backup_postgres >/dev/null 2>&1 || true
if [ -n "$REDIS_PID" ] && kill -0 "$REDIS_PID" 2>/dev/null; then
kill -TERM "$REDIS_PID" 2>/dev/null || true
wait "$REDIS_PID" 2>/dev/null || true
fi
su-exec postgres pg_ctl -D "$PGDATA" -m fast -w stop >/dev/null 2>&1 || true
}
trap cleanup EXIT INT TERM
if [ -z "$DATABASE_PASSWORD" ]; then
echo "DATABASE_PASSWORD is required" >&2
exit 1
fi
mkdir -p "$BACKUP_DIR" "$REDIS_DATA_DIR" "$APP_DATA_DIR"
chown -R redis:redis "$REDIS_DATA_DIR"
chown -R sub2api:sub2api "$APP_DATA_DIR"
# Prefer the logical backup after the first successful migration. The original
# bucket-backed cluster remains untouched as a one-time recovery source.
if [ ! -s "$PGDATA/PG_VERSION" ] && [ ! -s "$BACKUP_FILE" ] && [ -s "$LEGACY_PGDATA/PG_VERSION" ]; then
echo "Migrating legacy PostgreSQL data to local runtime storage"
copy_attempt=0
while :; do
rm -rf "$PGDATA"
mkdir -p "$PGDATA"
if cp -a "$LEGACY_PGDATA/." "$PGDATA/"; then
break
fi
copy_attempt=$((copy_attempt + 1))
if [ "$copy_attempt" -ge 3 ]; then
echo "Legacy PostgreSQL data copy failed" >&2
exit 1
fi
sleep 5
done
rm -f "$PGDATA/postmaster.pid" "$PGDATA/backup_label" "$PGDATA/tablespace_map"
MIGRATED=1
fi
if [ ! -s "$PGDATA/PG_VERSION" ]; then
NEW_CLUSTER=1
mkdir -p "$PGDATA"
chown postgres:postgres "$PGDATA"
chmod 700 "$PGDATA"
PASSWORD_FILE=$(mktemp)
chmod 600 "$PASSWORD_FILE"
printf '%s\n' "$DATABASE_PASSWORD" > "$PASSWORD_FILE"
chown postgres:postgres "$PASSWORD_FILE"
su-exec postgres initdb -D "$PGDATA" -U "$DATABASE_USER" --pwfile="$PASSWORD_FILE" --auth-local=trust --auth-host=scram-sha-256
rm -f "$PASSWORD_FILE"
fi
ensure_pg_dirs
chown -R postgres:postgres "$PGDATA"
chmod 700 "$PGDATA"
rm -f "$PGDATA/postmaster.pid"
start_postgres
if ! su-exec postgres psql -h /tmp -U "$DATABASE_USER" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sub2api'" | grep -q 1; then
su-exec postgres createdb -h /tmp -U "$DATABASE_USER" -O "$DATABASE_USER" "$DATABASE_DBNAME"
fi
if [ "$NEW_CLUSTER" -eq 1 ] && [ "$MIGRATED" -eq 0 ] && [ -s "$BACKUP_FILE" ]; then
echo "Restoring PostgreSQL from persistent logical backup"
restore_dump=/tmp/sub2api-restore.dump
rm -f "$restore_dump"
if ! cp "$BACKUP_FILE" "$restore_dump"; then
echo "Persistent PostgreSQL backup could not be read" >&2
exit 1
fi
if ! su-exec postgres pg_restore -l "$restore_dump" >/dev/null 2>&1; then
echo "Persistent PostgreSQL backup is invalid" >&2
exit 1
fi
su-exec postgres pg_restore -h /tmp -U "$DATABASE_USER" \
--clean --if-exists --no-owner --exit-on-error \
-d "$DATABASE_DBNAME" "$restore_dump"
rm -f "$restore_dump"
echo "PostgreSQL restore completed"
fi
if ! backup_postgres; then
echo "Initial PostgreSQL backup failed" >&2
exit 1
fi
start_redis
attempt=0
until redis-cli -h 127.0.0.1 -p 6379 ping 2>/dev/null | grep -q PONG; do
if ! kill -0 "$REDIS_PID" 2>/dev/null; then
echo "Redis failed to start" >&2
exit 1
fi
attempt=$((attempt + 1))
if [ "$attempt" -ge 30 ]; then
echo "Redis readiness check timed out" >&2
exit 1
fi
sleep 1
done
su-exec sub2api /app/sub2api &
APP_PID=$!
(
sleep 15
while kill -0 "$APP_PID" 2>/dev/null; do
backup_postgres || echo "PostgreSQL periodic backup failed" >&2
sleep 30
done
) &
BACKUP_PID=$!
while kill -0 "$APP_PID" 2>/dev/null; do
if ! su-exec postgres pg_ctl -D "$PGDATA" status >/dev/null 2>&1; then
echo "PostgreSQL stopped unexpectedly; attempting local recovery" >&2
rm -f "$PGDATA/postmaster.pid"
if ! start_postgres; then
echo "PostgreSQL recovery failed; stopping Sub2API" >&2
kill -TERM "$APP_PID" 2>/dev/null || true
wait "$APP_PID" 2>/dev/null || true
exit 1
fi
fi
sleep 2
done
wait "$APP_PID"
|