5.7 KiB
5.7 KiB
PostgreSQL Migration Design
Date: 2026-05-25
Scope: Migrate elysia (Drizzle ORM + MySQL) and rust (SeaORM + MySQL) apps to PostgreSQL
Approach: Direct cutover (Approach B)
Connection String: postgresql://asephs:hunterz@100.108.1.124:5432/hub
Current State
Elysia App
- ORM: Drizzle ORM v0.45.2
- Driver: mysql2 v3.20.0
- Schema Location:
apps/elysia/src/db/lib/schema.ts - Tables: users, accounts, sessions, roles, permissions, userRoles, and related junction tables
- Database File:
apps/elysia/src/db/lib/database.ts
Rust App
- ORM: SeaORM v1.1.19
- Feature: sqlx-mysql
- DB Setup:
apps/rust/src/infra/db_setup.rs - Tables: ImageCache (primary table managed by app)
- Config: Environment-based connection string
Apps NOT Migrating
- 9router (uses SQLite runtime, excluded per requirements)
- nextjs (frontend, minimal DB usage)
- leptos, solidjs, rust-auth (frontends, no DB)
Migration Steps
Phase 1: Pre-Migration (Preparation)
- Backup MySQL
mysqldump -u <user> -p <db> > mysql_backup.sql - Convert MySQL dump to PostgreSQL
- Use
pgloaderor manual conversion for schema compatibility - Handle type conversions:
INT→INTEGERVARCHAR(n)→VARCHAR(n)(PostgreSQL compatible)DATETIME→TIMESTAMPAUTO_INCREMENT→SERIALorBIGSERIAL
- Verify indexes and foreign keys convert correctly
- Use
- Test import into target PostgreSQL
psql postgresql://asephs:hunterz@100.108.1.124:5432/hub < converted.sql - Validate data integrity
- Row counts match MySQL
- Indexes exist
- Foreign key constraints enforced
Phase 2: Code Updates
Elysia App
-
Update
apps/elysia/src/db/lib/database.ts- Replace
mysql2import withpostgresdriver - Change Drizzle dialect from
drizzle-orm/mysql2todrizzle-orm/postgres - Update connection string format
- Replace
-
Update
apps/elysia/src/db/lib/schema.ts- Replace
drizzle-orm/mysql-coreimports withdrizzle-orm/postgres-core - Change
mysqlTabletopgTable - Adjust column types if needed (e.g.,
int→integer)
- Replace
-
Update
apps/elysia/package.json- Replace
mysql2withpg(PostgreSQL driver) - Keep
drizzle-ormanddrizzle-kitversions
- Replace
-
Update environment/config
- Change
DATABASE_URLto PostgreSQL connection string
- Change
Rust App
-
Update
apps/rust/Cargo.toml- Replace
sqlx-mysqlfeature withsqlx-postgresin sea-orm dependency - Add
postgresfeature if needed
- Replace
-
Update
apps/rust/src/infra/db_setup.rs- Change
DbBackend::MySqlcheck toDbBackend::Postgres - Adjust SQL syntax for PostgreSQL (e.g., index creation)
- Update error code handling (PostgreSQL uses different error codes)
- Change
-
Update config/environment
- Change
DATABASE_URLto PostgreSQL connection string
- Change
Phase 3: Cutover (Execution)
- Stop all apps
# Stop elysia # Stop rust app - Export MySQL data
mysqldump -u <user> -p <db> > final_backup.sql - Convert and import to PostgreSQL
# Convert dump # Import to PostgreSQL psql postgresql://asephs:hunterz@100.108.1.124:5432/hub < converted.sql - Deploy updated apps
- Deploy elysia with PostgreSQL driver
- Deploy rust with PostgreSQL feature
- Verify connectivity
- Test database queries from both apps
- Check auth flow (users table)
- Verify session management
Phase 4: Validation
- Smoke tests
- User login/logout
- Session creation and retrieval
- Role/permission queries
- ImageCache operations (rust app)
- Data integrity checks
- Row counts match pre-migration
- No orphaned foreign keys
- Indexes performing as expected
- Performance baseline
- Compare query times MySQL vs PostgreSQL
- Monitor connection pool usage
Phase 5: Rollback Plan (if needed)
- Stop apps
- Restore MySQL from backup
mysql -u <user> -p <db> < mysql_backup.sql - Revert connection strings in apps
- Redeploy with MySQL drivers
- Restart apps
Technical Details
Elysia Driver Change
Before:
import { drizzle } from 'drizzle-orm/mysql2'
import { createPool } from 'mysql2/promise'
After:
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
Rust Feature Change
Before:
sea-orm = { version = "1.1.19", features = ["sqlx-mysql", ...] }
After:
sea-orm = { version = "1.1.19", features = ["sqlx-postgres", ...] }
Risk Assessment
| Risk | Mitigation |
|---|---|
| Data loss during migration | Full MySQL backup before cutover; test import first |
| App downtime | Cutover during low-traffic window; rollback plan ready |
| Connection string misconfiguration | Test connection before deploying apps |
| Schema incompatibilities | Pre-test conversion; validate indexes/constraints |
| Performance regression | Baseline MySQL performance; monitor PostgreSQL after cutover |
Success Criteria
- ✅ All data migrated to PostgreSQL (row counts match)
- ✅ Elysia app connects and queries work
- ✅ Rust app connects and queries work
- ✅ Auth flow functional (login/logout)
- ✅ No orphaned foreign keys
- ✅ Indexes present and performant
- ✅ Rollback plan tested and documented
Timeline
- Pre-migration: 30 min (backup, convert, test)
- Code updates: 1-2 hours (driver changes, testing)
- Cutover: 15-30 min (stop apps, migrate data, restart)
- Validation: 30 min (smoke tests, data checks)
- Total: ~3-4 hours (including buffer)