300 lines
8.2 KiB
Markdown
300 lines
8.2 KiB
Markdown
# Database Migrations Guide
|
|||
|
|
|
||
|
|
This document describes the database schema management and migration strategy for the CAATSM application.
|
||
|
|
|
||
|
|
## Current Approach
|
||
|
|
|
||
|
|
The application currently uses DDL (Data Definition Language) files for schema management:
|
||
|
|
|
||
|
|
- **Schema file**: `internal/infra/postgres/telegrams.ddl`
|
||
|
|
- **Manual execution**: Schema changes are applied manually using `psql` or similar tools
|
||
|
|
- **Version control**: DDL files are version-controlled in the repository
|
||
|
|
|
||
|
|
### Current Schema Structure
|
||
|
|
|
||
|
|
The application uses TimescaleDB (PostgreSQL extension) with the following key components:
|
||
|
|
|
||
|
|
- **Schema**: `aviation`
|
||
|
|
- **Main table**: `aviation.telegrams` (hypertable for time-series data)
|
||
|
|
- **Raw table**: `aviation.telegrams_raw` (for unparsed/failed messages)
|
||
|
|
- **Indexes**: Multiple indexes on key fields for query performance
|
||
|
|
|
||
|
|
## Recommended Migration Tools
|
||
|
|
|
||
|
|
For production deployments, we recommend using a dedicated migration tool for better schema management:
|
||
|
|
|
||
|
|
### Option 1: golang-migrate (Recommended)
|
||
|
|
|
||
|
|
[golang-migrate](https://github.com/golang-migrate/migrate) is a popular Go-based migration tool with excellent PostgreSQL support.
|
||
|
|
|
||
|
|
**Installation:**
|
||
|
|
```bash
|
||
|
|
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
|
||
|
|
```
|
||
|
|
|
||
|
|
**Setup:**
|
||
|
|
1. Create migrations directory:
|
||
|
|
```bash
|
||
|
|
mkdir -p migrations
|
||
|
|
```
|
||
|
|
|
||
|
|
2. Create initial migration from existing schema:
|
||
|
|
```bash
|
||
|
|
migrate create -ext sql -dir migrations -seq initial_schema
|
||
|
|
```
|
||
|
|
|
||
|
|
3. Copy DDL content to migration files:
|
||
|
|
- `migrations/000001_initial_schema.up.sql` - Create schema
|
||
|
|
- `migrations/000001_initial_schema.down.sql` - Drop schema
|
||
|
|
|
||
|
|
**Usage:**
|
||
|
|
```bash
|
||
|
|
# Apply all migrations
|
||
|
|
migrate -path migrations -database "postgres://user:pass@localhost:5432/aviation?sslmode=disable" up
|
||
|
|
|
||
|
|
# Rollback last migration
|
||
|
|
migrate -path migrations -database "postgres://user:pass@localhost:5432/aviation?sslmode=disable" down 1
|
||
|
|
|
||
|
|
# Check migration version
|
||
|
|
migrate -path migrations -database "postgres://user:pass@localhost:5432/aviation?sslmode=disable" version
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 2: migrate (by golang-migrate, different package)
|
||
|
|
|
||
|
|
Similar to golang-migrate but distributed as a separate package.
|
||
|
|
|
||
|
|
### Option 3: Custom Migration Scripts
|
||
|
|
|
||
|
|
For simple deployments, you can create custom migration scripts that:
|
||
|
|
- Check current schema version
|
||
|
|
- Apply migrations sequentially
|
||
|
|
- Track migration state in a `schema_migrations` table
|
||
|
|
|
||
|
|
## Migration Workflow
|
||
|
|
|
||
|
|
### Development
|
||
|
|
|
||
|
|
1. **Create migration file:**
|
||
|
|
```bash
|
||
|
|
migrate create -ext sql -dir migrations -seq add_new_column
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Write up migration** (`migrations/XXXXXX_add_new_column.up.sql`):
|
||
|
|
```sql
|
||
|
|
ALTER TABLE aviation.telegrams
|
||
|
|
ADD COLUMN new_field TEXT;
|
||
|
|
|
||
|
|
CREATE INDEX idx_telegrams_new_field ON aviation.telegrams (new_field);
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Write down migration** (`migrations/XXXXXX_add_new_column.down.sql`):
|
||
|
|
```sql
|
||
|
|
DROP INDEX IF EXISTS idx_telegrams_new_field;
|
||
|
|
ALTER TABLE aviation.telegrams
|
||
|
|
DROP COLUMN IF EXISTS new_field;
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Test migration:**
|
||
|
|
```bash
|
||
|
|
# Apply
|
||
|
|
migrate -path migrations -database "$DATABASE_URL" up
|
||
|
|
|
||
|
|
# Rollback
|
||
|
|
migrate -path migrations -database "$DATABASE_URL" down 1
|
||
|
|
```
|
||
|
|
|
||
|
|
### Production
|
||
|
|
|
||
|
|
1. **Backup database** before applying migrations:
|
||
|
|
```bash
|
||
|
|
pg_dump -U postgres -d aviation > backup_$(date +%Y%m%d_%H%M%S).sql
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Test migration on staging** environment first
|
||
|
|
|
||
|
|
3. **Apply migration** during maintenance window:
|
||
|
|
```bash
|
||
|
|
migrate -path migrations -database "$DATABASE_URL" up
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Verify migration** success:
|
||
|
|
```bash
|
||
|
|
migrate -path migrations -database "$DATABASE_URL" version
|
||
|
|
```
|
||
|
|
|
||
|
|
5. **Monitor application** for any issues
|
||
|
|
|
||
|
|
## Schema Evolution Best Practices
|
||
|
|
|
||
|
|
### 1. Backward Compatibility
|
||
|
|
|
||
|
|
- **Additive changes** (new columns, indexes) are generally safe
|
||
|
|
- **Removing columns** requires application code changes first
|
||
|
|
- **Changing column types** requires careful planning and data migration
|
||
|
|
|
||
|
|
### 2. TimescaleDB Considerations
|
||
|
|
|
||
|
|
- **Hypertables**: Be careful when modifying hypertable structure
|
||
|
|
- **Retention policies**: Consider impact on existing data
|
||
|
|
- **Compression**: Test compression policies with schema changes
|
||
|
|
|
||
|
|
### 3. Index Management
|
||
|
|
|
||
|
|
- **Create indexes concurrently** in production to avoid locking:
|
||
|
|
```sql
|
||
|
|
CREATE INDEX CONCURRENTLY idx_telegrams_new_field ON aviation.telegrams (new_field);
|
||
|
|
```
|
||
|
|
|
||
|
|
- **Drop unused indexes** to improve write performance
|
||
|
|
|
||
|
|
### 4. Data Migrations
|
||
|
|
|
||
|
|
For data transformations, use separate migration steps:
|
||
|
|
|
||
|
|
1. **Add new column** (nullable)
|
||
|
|
2. **Backfill data** in application or migration script
|
||
|
|
3. **Add constraints** (NOT NULL, etc.) after backfill
|
||
|
|
4. **Remove old column** in separate migration
|
||
|
|
|
||
|
|
### 5. Rollback Procedures
|
||
|
|
|
||
|
|
Always provide rollback migrations:
|
||
|
|
|
||
|
|
- **Test rollback** on staging before production
|
||
|
|
- **Document rollback steps** in migration comments
|
||
|
|
- **Consider data loss** implications of rollbacks
|
||
|
|
|
||
|
|
## Example Migration
|
||
|
|
|
||
|
|
### Adding a New Index
|
||
|
|
|
||
|
|
**Up migration:**
|
||
|
|
```sql
|
||
|
|
-- Add index for querying by category and date
|
||
|
|
CREATE INDEX CONCURRENTLY idx_telegrams_category_date
|
||
|
|
ON aviation.telegrams (category, received_at DESC);
|
||
|
|
```
|
||
|
|
|
||
|
|
**Down migration:**
|
||
|
|
```sql
|
||
|
|
-- Remove index
|
||
|
|
DROP INDEX IF EXISTS idx_telegrams_category_date;
|
||
|
|
```
|
||
|
|
|
||
|
|
### Adding a New Column
|
||
|
|
|
||
|
|
**Up migration:**
|
||
|
|
```sql
|
||
|
|
-- Add processing_metadata column for additional metadata
|
||
|
|
ALTER TABLE aviation.telegrams
|
||
|
|
ADD COLUMN processing_metadata JSONB;
|
||
|
|
|
||
|
|
-- Add index for JSONB queries
|
||
|
|
CREATE INDEX CONCURRENTLY idx_telegrams_processing_metadata_gin
|
||
|
|
ON aviation.telegrams USING GIN (processing_metadata);
|
||
|
|
```
|
||
|
|
|
||
|
|
**Down migration:**
|
||
|
|
```sql
|
||
|
|
-- Remove index and column
|
||
|
|
DROP INDEX IF EXISTS idx_telegrams_processing_metadata_gin;
|
||
|
|
ALTER TABLE aviation.telegrams
|
||
|
|
DROP COLUMN IF EXISTS processing_metadata;
|
||
|
|
```
|
||
|
|
|
||
|
|
## Migration State Management
|
||
|
|
|
||
|
|
### Schema Version Tracking
|
||
|
|
|
||
|
|
Migration tools typically use a `schema_migrations` table to track applied migrations:
|
||
|
|
|
||
|
|
```sql
|
||
|
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||
|
|
version BIGINT NOT NULL PRIMARY KEY,
|
||
|
|
dirty BOOLEAN NOT NULL
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Checking Migration Status
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Check current version
|
||
|
|
migrate -path migrations -database "$DATABASE_URL" version
|
||
|
|
|
||
|
|
# Check for pending migrations
|
||
|
|
migrate -path migrations -database "$DATABASE_URL" up
|
||
|
|
```
|
||
|
|
|
||
|
|
## CI/CD Integration
|
||
|
|
|
||
|
|
### Automated Migration Testing
|
||
|
|
|
||
|
|
Add migration tests to CI pipeline:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# Example GitHub Actions workflow
|
||
|
|
- name: Test migrations
|
||
|
|
run: |
|
||
|
|
# Start test database
|
||
|
|
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=test postgres:15
|
||
|
|
|
||
|
|
# Wait for database
|
||
|
|
sleep 5
|
||
|
|
|
||
|
|
# Apply migrations
|
||
|
|
migrate -path migrations -database "postgres://postgres:test@localhost:5432/test?sslmode=disable" up
|
||
|
|
|
||
|
|
# Verify schema
|
||
|
|
psql "postgres://postgres:test@localhost:5432/test?sslmode=disable" -c "\d aviation.telegrams"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Deployment Automation
|
||
|
|
|
||
|
|
For production deployments, integrate migrations into deployment process:
|
||
|
|
|
||
|
|
1. **Pre-deployment**: Backup database
|
||
|
|
2. **Deployment**: Apply migrations
|
||
|
|
3. **Post-deployment**: Verify migration success
|
||
|
|
4. **Rollback**: If migration fails, rollback application and database
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Migration Failures
|
||
|
|
|
||
|
|
**Common issues:**
|
||
|
|
- **Lock conflicts**: Use `CONCURRENTLY` for index creation
|
||
|
|
- **Timeout errors**: Increase migration timeout for large tables
|
||
|
|
- **Dirty state**: Manually fix `schema_migrations` table if migration fails mid-way
|
||
|
|
|
||
|
|
**Recovery:**
|
||
|
|
```sql
|
||
|
|
-- Check migration state
|
||
|
|
SELECT * FROM schema_migrations;
|
||
|
|
|
||
|
|
-- Fix dirty state (if needed)
|
||
|
|
UPDATE schema_migrations SET dirty = false WHERE version = X;
|
||
|
|
```
|
||
|
|
|
||
|
|
### Performance Considerations
|
||
|
|
|
||
|
|
- **Large tables**: Test migrations on production-sized data
|
||
|
|
- **Downtime**: Plan for maintenance windows for major schema changes
|
||
|
|
- **Replication lag**: Consider impact on read replicas
|
||
|
|
|
||
|
|
## Future Improvements
|
||
|
|
|
||
|
|
Consider implementing:
|
||
|
|
|
||
|
|
1. **Automated migration testing** in CI/CD
|
||
|
|
2. **Migration rollback automation** in deployment pipeline
|
||
|
|
3. **Schema validation** before applying migrations
|
||
|
|
4. **Migration dry-run** mode for testing
|
||
|
|
5. **Migration status monitoring** and alerting
|
||
|
|
|
||
|
|
## References
|
||
|
|
|
||
|
|
- [golang-migrate Documentation](https://github.com/golang-migrate/migrate)
|
||
|
|
- [TimescaleDB Best Practices](https://docs.timescale.com/timescaledb/latest/how-to-guides/migrate-data/)
|
||
|
|
- [PostgreSQL Migration Guide](https://www.postgresql.org/docs/current/ddl-alter.html)
|
||
|
|
|