M19: harden performance and recovery
This commit is contained in:
@@ -52,3 +52,24 @@ docker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml logs --tail
|
||||
docker compose -p mobilityops -f compose.yaml -f compose.unraid.yaml exec api alembic current
|
||||
docker logs --tail=200 n8n
|
||||
```
|
||||
|
||||
## Backup and restore
|
||||
|
||||
Create and structurally verify a timestamped PostgreSQL custom-format backup:
|
||||
|
||||
```bash
|
||||
./deploy/unraid/backup-postgres.sh
|
||||
```
|
||||
|
||||
Copy backups off the server according to the host backup policy. A restore is deliberately
|
||||
guarded and creates an additional safety backup before replacing the database:
|
||||
|
||||
```bash
|
||||
./deploy/unraid/restore-postgres.sh \
|
||||
backups/postgres/mobilityops-YYYYMMDDTHHMMSSZ.dump \
|
||||
RESTORE-MOBILITYOPS
|
||||
```
|
||||
|
||||
The restore stops the API, recreates only the configured MobilityOps database, restarts
|
||||
API/web and verifies the active Alembic revision. Test restores in a disposable environment
|
||||
before using a production backup for incident recovery.
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
project="${COMPOSE_PROJECT_NAME:-mobilityops}"
|
||||
destination="${1:-backups/postgres}"
|
||||
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
backup_file="${destination}/mobilityops-${timestamp}.dump"
|
||||
temporary_file="${backup_file}.partial"
|
||||
compose_files="-f compose.yaml -f compose.unraid.yaml"
|
||||
|
||||
mkdir -p "$destination"
|
||||
trap 'rm -f "$temporary_file"' EXIT INT TERM
|
||||
|
||||
docker compose -p "$project" $compose_files exec -T db sh -eu -c \
|
||||
'pg_dump --format=custom --no-owner --no-acl -U "$POSTGRES_USER" "$POSTGRES_DB"' \
|
||||
> "$temporary_file"
|
||||
|
||||
docker compose -p "$project" $compose_files exec -T db pg_restore --list \
|
||||
< "$temporary_file" > /dev/null
|
||||
mv "$temporary_file" "$backup_file"
|
||||
trap - EXIT INT TERM
|
||||
printf '%s\n' "$backup_file"
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
backup_file="${1:-}"
|
||||
confirmation="${2:-}"
|
||||
project="${COMPOSE_PROJECT_NAME:-mobilityops}"
|
||||
compose_files="-f compose.yaml -f compose.unraid.yaml"
|
||||
|
||||
if [ ! -f "$backup_file" ]; then
|
||||
echo "Backup file not found: $backup_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$confirmation" != "RESTORE-MOBILITYOPS" ]; then
|
||||
echo "Refusing destructive restore; pass RESTORE-MOBILITYOPS as the second argument" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker compose -p "$project" $compose_files exec -T db pg_restore --list \
|
||||
< "$backup_file" > /dev/null
|
||||
safety_backup="$(./deploy/unraid/backup-postgres.sh)"
|
||||
echo "Pre-restore safety backup: $safety_backup"
|
||||
|
||||
restart_api() {
|
||||
docker compose -p "$project" $compose_files up -d api web > /dev/null
|
||||
}
|
||||
trap restart_api EXIT INT TERM
|
||||
docker compose -p "$project" $compose_files stop api > /dev/null
|
||||
docker compose -p "$project" $compose_files exec -T db sh -eu -c \
|
||||
'dropdb --force -U "$POSTGRES_USER" "$POSTGRES_DB" && createdb -U "$POSTGRES_USER" "$POSTGRES_DB"'
|
||||
docker compose -p "$project" $compose_files exec -T db sh -eu -c \
|
||||
'pg_restore --no-owner --no-acl -U "$POSTGRES_USER" -d "$POSTGRES_DB"' \
|
||||
< "$backup_file"
|
||||
restart_api
|
||||
trap - EXIT INT TERM
|
||||
docker compose -p "$project" $compose_files exec -T api alembic current
|
||||
echo "Restore completed from $backup_file"
|
||||
Reference in New Issue
Block a user