#!/usr/bin/env bash # # split-databases.sh — Migrate from single appdb to per-domain databases. # # Prerequisites: # - All apps stopped (5-min maintenance window) # - init-databases.sql already run (CREATE DATABASE db_*) # - Run from a host that can reach the Postgres container # # Usage: # PGHOST=db PGUSER=postgres PGPASSWORD=change-me bash split-databases.sh # set -euo pipefail SOURCE_DB="${SOURCE_DB:-appdb}" # ── Table → database mapping ─────────────────────────────────────────────── declare -A DB_TABLES DB_TABLES[db_account]=" users magic_links oauth_codes oauth_grants ghost_labels user_labels ghost_newsletters user_newsletters ghost_tiers ghost_subscriptions kv " DB_TABLES[db_blog]=" authors tags posts post_authors post_tags post_likes menu_items menu_nodes container_relations page_configs " DB_TABLES[db_market]=" products product_images product_sections product_labels product_stickers product_attributes product_nutrition product_allergens product_likes product_logs market_places nav_tops nav_subs listings listing_items link_errors link_externals subcategory_redirects cart_items orders order_items " # db_cart merged into db_market — cart and market share the same bounded context # (commerce). Cart needs direct read access to products/market_places. DB_TABLES[db_events]=" calendars calendar_slots calendar_entries calendar_entry_posts ticket_types tickets " DB_TABLES[db_federation]=" ap_anchors ap_actor_profiles ap_activities ap_followers ap_inbox_items ap_remote_actors ap_following ap_remote_posts ap_local_posts ap_interactions ap_notifications ap_delivery_log ipfs_pins " # ── Migrate each domain ──────────────────────────────────────────────────── for target_db in db_account db_blog db_market db_events db_federation; do tables="${DB_TABLES[$target_db]}" table_list="" for t in $tables; do table_list="$table_list --table=$t" done echo "=== Migrating $target_db ===" echo " Tables: $(echo $tables | tr '\n' ' ')" # Dump schema + data for these tables from the source DB pg_dump "$SOURCE_DB" $table_list --no-owner --no-privileges \ | psql -q "$target_db" echo " Done." done # ── Stamp Alembic head in each domain DB ────────────────────────────────── echo "" echo "=== Stamping Alembic head in each DB ===" for target_db in db_account db_blog db_market db_events db_federation; do # Create alembic_version table and stamp current head psql -q "$target_db" <<'SQL' CREATE TABLE IF NOT EXISTS alembic_version ( version_num VARCHAR(32) NOT NULL, CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num) ); DELETE FROM alembic_version; INSERT INTO alembic_version (version_num) VALUES ('w3u1q9r0s1'); SQL echo " $target_db stamped at w3u1q9r0s1" done echo "" echo "=== Migration complete ===" echo "" echo "Next steps:" echo " 1. Update docker-compose.yml — set per-app DATABASE_URL to the new DBs" echo " 2. Remove schema_sql config (no longer needed)" echo " 3. Redeploy all services" echo "" echo "Per-app DATABASE_URL values:" echo " blog: postgresql+asyncpg://postgres:change-me@db:5432/db_blog" echo " market: postgresql+asyncpg://postgres:change-me@db:5432/db_market" echo " cart: postgresql+asyncpg://postgres:change-me@db:5432/db_market (shared with market)" echo " events: postgresql+asyncpg://postgres:change-me@db:5432/db_events" echo " federation: postgresql+asyncpg://postgres:change-me@db:5432/db_federation" echo " account: postgresql+asyncpg://postgres:change-me@db:5432/db_account" echo "" echo " DATABASE_URL_ACCOUNT: postgresql+asyncpg://postgres:change-me@db:5432/db_account" echo " DATABASE_URL_FEDERATION: postgresql+asyncpg://postgres:change-me@db:5432/db_federation"