Skip to content

Commit 511e77b

Browse files
etewiahclaude
andcommitted
Add multi-shard support to staging restore script
Update restore_staging_from_production.sh to handle shard databases: New environment variables: - DOKKU_DEMO_SHARD_SERVICE: Dokku service for demo shard - DOKKU_TENANT_SHARD_1_SERVICE: Dokku service for tenant shard 1 - LOCAL_DEMO_SHARD_DB: local demo shard database name - LOCAL_TENANT_SHARD_1_DB: local tenant shard 1 database name New flags: - --primary-only: Skip shard imports - --shards-only: Skip primary database import Changes: - Refactored import logic into reusable import_database() function - Gracefully handle missing shard services (warns instead of failing) - Auto-set PWB_STAGING_*_DATABASE_URL env vars when starting server - Added --if-exists to pg_restore for cleaner first-run behavior - Updated documentation with shard examples Example usage with shards: DOKKU_DEMO_SHARD_SERVICE=pwb-demo-shard \ ./scripts/restore_staging_from_production.sh 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent be36238 commit 511e77b

File tree

1 file changed

+122
-16
lines changed

1 file changed

+122
-16
lines changed

scripts/restore_staging_from_production.sh

Lines changed: 122 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
# Copies the production database (via Dokku) into the local staging database
4+
# Copies the production database(s) (via Dokku) into the local staging database(s)
55
# and then boots the Rails server on port 4444.
66
#
7+
# This script supports multi-shard setups. It will import:
8+
# 1. Primary database (required)
9+
# 2. Demo shard database (optional, if DOKKU_DEMO_SHARD_SERVICE is set)
10+
# 3. Tenant shard 1 database (optional, if DOKKU_TENANT_SHARD_1_SERVICE is set)
11+
#
712
# Required environment variables:
813
# PRODUCTION_SSH_HOST - e.g. root@example.com
9-
# DOKKU_POSTGRES_SERVICE - Dokku Postgres service name to export
14+
# DOKKU_POSTGRES_SERVICE - Dokku Postgres service name for primary database
15+
#
16+
# Optional environment variables (shards):
17+
# DOKKU_DEMO_SHARD_SERVICE - Dokku Postgres service for demo shard
18+
# DOKKU_TENANT_SHARD_1_SERVICE - Dokku Postgres service for tenant shard 1
19+
# LOCAL_DEMO_SHARD_DB - local demo shard database (default: pwb_staging_demo_shard)
20+
# LOCAL_TENANT_SHARD_1_DB - local tenant shard 1 database (default: pwb_staging_shard_1)
1021
#
11-
# Optional environment variables:
22+
# Optional environment variables (general):
1223
# SSH_COMMAND - command used for ssh/rsync (default: ssh)
1324
# RSYNC_RSH - overrides ssh command just for rsync (default: SSH_COMMAND)
1425
# REMOTE_DUMP_PATH - remote path for the dump (default: /tmp/<service>.dump)
@@ -27,12 +38,15 @@ set -euo pipefail
2738
# ./scripts/restore_staging_from_production.sh --import-only
2839
# ./scripts/restore_staging_from_production.sh --server-only
2940
# ./scripts/restore_staging_from_production.sh --skip-import --skip-server
41+
# ./scripts/restore_staging_from_production.sh --primary-only # skip shard imports
3042
#
3143
# Flags:
3244
# --import-only Only refresh the database, do not start the server
3345
# --server-only Only start the server using the existing database
3446
# --skip-import Skip the import step (same as --server-only)
3547
# --skip-server Skip starting the server (same as --import-only)
48+
# --primary-only Only import primary database, skip shards
49+
# --shards-only Only import shard databases, skip primary
3650
# -h|--help Show this help and exit
3751

3852
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@@ -47,7 +61,14 @@ BACKUP_DIR="${BACKUP_DIR:-$ROOT_DIR/db_imports}"
4761
if [[ "$BACKUP_DIR" != /* ]]; then
4862
BACKUP_DIR="$ROOT_DIR/${BACKUP_DIR#./}"
4963
fi
64+
65+
# Primary database
5066
LOCAL_DB_NAME="${LOCAL_STAGING_DB:-pwb_staging}"
67+
68+
# Shard databases
69+
LOCAL_DEMO_SHARD_DB="${LOCAL_DEMO_SHARD_DB:-pwb_staging_demo_shard}"
70+
LOCAL_TENANT_SHARD_1_DB="${LOCAL_TENANT_SHARD_1_DB:-pwb_staging_shard_1}"
71+
5172
LOCAL_DB_HOST="${LOCAL_DB_HOST:-localhost}"
5273
LOCAL_DB_PORT="${LOCAL_DB_PORT:-5432}"
5374
LOCAL_DB_USER="${LOCAL_DB_USER:-}"
@@ -57,6 +78,8 @@ RAILS_PORT="${RAILS_PORT:-4444}"
5778

5879
ACTION_IMPORT=true
5980
ACTION_SERVER=true
81+
IMPORT_PRIMARY=true
82+
IMPORT_SHARDS=true
6083

6184
log() {
6285
printf '[%s] %s\n' "$(date +'%Y-%m-%d %H:%M:%S')" "$*"
@@ -71,7 +94,18 @@ Options:
7194
--server-only Run server only (skip import)
7295
--skip-import Alias for --server-only
7396
--skip-server Alias for --import-only
97+
--primary-only Only import primary database, skip shards
98+
--shards-only Only import shard databases, skip primary
7499
-h, --help Show this help and exit
100+
101+
Shard Environment Variables:
102+
DOKKU_DEMO_SHARD_SERVICE Dokku service name for demo shard
103+
DOKKU_TENANT_SHARD_1_SERVICE Dokku service name for tenant shard 1
104+
105+
Example with shards:
106+
DOKKU_DEMO_SHARD_SERVICE=pwb-demo-shard \
107+
DOKKU_TENANT_SHARD_1_SERVICE=pwb-shard-1 \
108+
./scripts/restore_staging_from_production.sh
75109
EOF
76110
}
77111

@@ -121,21 +155,27 @@ SQL
121155
fi
122156
}
123157

124-
perform_import() {
125-
: "${PRODUCTION_SSH_HOST:?Set PRODUCTION_SSH_HOST (e.g. root@example.com)}"
126-
: "${DOKKU_POSTGRES_SERVICE:?Set DOKKU_POSTGRES_SERVICE (dokku postgres:export service name)}"
158+
# Import a single database from Dokku
159+
# Arguments: $1 = dokku service name, $2 = local database name, $3 = label (for logging)
160+
import_database() {
161+
local dokku_service="$1"
162+
local local_db="$2"
163+
local label="${3:-$dokku_service}"
164+
165+
log "=== Importing $label ==="
127166

128167
mkdir -p "$BACKUP_DIR"
129168

130169
local timestamp
131170
timestamp="$(date +%Y%m%d-%H%M%S)"
132-
local remote_dump_path
133-
remote_dump_path="${REMOTE_DUMP_PATH:-/tmp/${DOKKU_POSTGRES_SERVICE}.dump}"
134-
local local_dump_file
135-
local_dump_file="${BACKUP_DIR}/${timestamp}-${DOKKU_POSTGRES_SERVICE}.dump"
171+
local remote_dump_path="/tmp/${dokku_service}.dump"
172+
local local_dump_file="${BACKUP_DIR}/${timestamp}-${dokku_service}.dump"
136173

137-
log "Exporting $DOKKU_POSTGRES_SERVICE from $PRODUCTION_SSH_HOST"
138-
$SSH_COMMAND "$PRODUCTION_SSH_HOST" "dokku postgres:export $DOKKU_POSTGRES_SERVICE > $remote_dump_path"
174+
log "Exporting $dokku_service from $PRODUCTION_SSH_HOST"
175+
if ! $SSH_COMMAND "$PRODUCTION_SSH_HOST" "dokku postgres:export $dokku_service > $remote_dump_path" 2>/dev/null; then
176+
log "WARNING: Failed to export $dokku_service - service may not exist"
177+
return 1
178+
fi
139179

140180
log "Downloading dump to $local_dump_file"
141181
RSYNC_CMD=(rsync -az)
@@ -148,14 +188,61 @@ perform_import() {
148188
$SSH_COMMAND "$PRODUCTION_SSH_HOST" "rm -f $remote_dump_path"
149189

150190
ensure_role_exists "$LOCAL_DB_USER"
151-
ensure_database_exists "$LOCAL_DB_NAME"
191+
ensure_database_exists "$local_db"
152192

153-
log "Restoring into local database $LOCAL_DB_NAME"
154-
PG_RESTORE_CMD=(pg_restore --clean --no-owner --host "$LOCAL_DB_HOST" --port "$LOCAL_DB_PORT" --dbname "$LOCAL_DB_NAME")
193+
log "Restoring into local database $local_db"
194+
PG_RESTORE_CMD=(pg_restore --clean --no-owner --host "$LOCAL_DB_HOST" --port "$LOCAL_DB_PORT" --dbname "$local_db")
155195
if [[ -n "$LOCAL_DB_USER" ]]; then
156196
PG_RESTORE_CMD+=(--username "$LOCAL_DB_USER")
157197
fi
158-
"${PG_RESTORE_CMD[@]}" "$local_dump_file"
198+
# Use --if-exists to avoid errors on first restore when tables don't exist
199+
"${PG_RESTORE_CMD[@]}" --if-exists "$local_dump_file" || {
200+
log "WARNING: pg_restore had some errors (this is often normal for --clean on first run)"
201+
}
202+
203+
log "Successfully imported $label into $local_db"
204+
return 0
205+
}
206+
207+
perform_import() {
208+
local imported_primary=false
209+
local imported_shards=0
210+
211+
# Import primary database
212+
if [[ "$IMPORT_PRIMARY" == true ]]; then
213+
if import_database "$DOKKU_POSTGRES_SERVICE" "$LOCAL_DB_NAME" "primary database"; then
214+
imported_primary=true
215+
fi
216+
else
217+
log "Skipping primary database import"
218+
fi
219+
220+
# Import shard databases
221+
if [[ "$IMPORT_SHARDS" == true ]]; then
222+
# Demo shard
223+
if [[ -n "${DOKKU_DEMO_SHARD_SERVICE:-}" ]]; then
224+
if import_database "$DOKKU_DEMO_SHARD_SERVICE" "$LOCAL_DEMO_SHARD_DB" "demo shard"; then
225+
((imported_shards++)) || true
226+
fi
227+
else
228+
log "Skipping demo shard (DOKKU_DEMO_SHARD_SERVICE not set)"
229+
fi
230+
231+
# Tenant shard 1
232+
if [[ -n "${DOKKU_TENANT_SHARD_1_SERVICE:-}" ]]; then
233+
if import_database "$DOKKU_TENANT_SHARD_1_SERVICE" "$LOCAL_TENANT_SHARD_1_DB" "tenant shard 1"; then
234+
((imported_shards++)) || true
235+
fi
236+
else
237+
log "Skipping tenant shard 1 (DOKKU_TENANT_SHARD_1_SERVICE not set)"
238+
fi
239+
else
240+
log "Skipping shard imports"
241+
fi
242+
243+
log "=== Import Summary ==="
244+
log "Primary database: $([ "$imported_primary" == true ] && echo "imported" || echo "skipped")"
245+
log "Shards imported: $imported_shards"
159246
}
160247

161248
start_server() {
@@ -168,7 +255,20 @@ start_server() {
168255
fi
169256
fi
170257

258+
# Set up shard database URLs for staging environment
259+
# These match the database names used in config/database.yml for staging
260+
if [[ -n "${DOKKU_DEMO_SHARD_SERVICE:-}" ]] || [[ -f "$BACKUP_DIR"/*-"${DOKKU_DEMO_SHARD_SERVICE:-demo}"*.dump ]] 2>/dev/null; then
261+
export PWB_STAGING_DEMO_SHARD_DATABASE_URL="postgresql://${LOCAL_DB_USER:-}@${LOCAL_DB_HOST}:${LOCAL_DB_PORT}/${LOCAL_DEMO_SHARD_DB}"
262+
log "Demo shard URL: $PWB_STAGING_DEMO_SHARD_DATABASE_URL"
263+
fi
264+
265+
if [[ -n "${DOKKU_TENANT_SHARD_1_SERVICE:-}" ]] || [[ -f "$BACKUP_DIR"/*-"${DOKKU_TENANT_SHARD_1_SERVICE:-shard}"*.dump ]] 2>/dev/null; then
266+
export PWB_STAGING_TENANT_SHARD_1_DATABASE_URL="postgresql://${LOCAL_DB_USER:-}@${LOCAL_DB_HOST}:${LOCAL_DB_PORT}/${LOCAL_TENANT_SHARD_1_DB}"
267+
log "Tenant shard 1 URL: $PWB_STAGING_TENANT_SHARD_1_DATABASE_URL"
268+
fi
269+
171270
log "Starting Rails server on port $RAILS_PORT (Ctrl+C to stop)"
271+
log "Shard environment variables set for staging"
172272
RAILS_ENV=staging bundle exec rails server -p "$RAILS_PORT"
173273
}
174274

@@ -180,6 +280,12 @@ while [[ $# -gt 0 ]]; do
180280
--server-only|--skip-import)
181281
ACTION_IMPORT=false
182282
;;
283+
--primary-only)
284+
IMPORT_SHARDS=false
285+
;;
286+
--shards-only)
287+
IMPORT_PRIMARY=false
288+
;;
183289
-h|--help)
184290
usage
185291
exit 0

0 commit comments

Comments
 (0)