- Add MigrationLock module using PostgreSQL advisory locks - Prevent concurrent migrations across multiple nodes - Update Release.migrate to use distributed lock when cluster enabled - Disable auto-migrations in cluster mode to avoid race conditions - Add init container configuration for Kubernetes deployments - Document migration strategies in k8s/README-migrations.md This ensures only one node runs database migrations at a time, preventing conflicts and race conditions in distributed deployments. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2.2 KiB
2.2 KiB
Database Migration Strategy for Kubernetes
This document describes how database migrations are handled in the Kubernetes deployment to ensure only one node runs migrations at a time.
Overview
In a distributed environment with multiple pods, we need to ensure that database migrations only run once, not multiple times concurrently. We use two strategies:
- PostgreSQL Advisory Locks - For runtime migration coordination
- Init Containers - For deployment-time migrations (recommended)
Implementation
1. PostgreSQL Advisory Locks
The Aprsme.MigrationLock module uses PostgreSQL advisory locks to ensure only one node can run migrations at a time:
- Uses
pg_try_advisory_lock()to acquire a non-blocking lock - Other nodes wait for the lock to be released
- Automatically releases the lock after migrations complete
2. Init Container (Recommended)
The StatefulSet can be configured with an init container that runs migrations before the main pods start:
# Apply the init container patch
kubectl patch statefulset aprs -n aprs --patch-file k8s/statefulset-init-container-patch.yaml
This approach:
- Runs migrations sequentially before any pods start
- Prevents race conditions
- Makes migration failures visible in pod events
3. Auto-Migration Disabled in Cluster Mode
When CLUSTER_ENABLED=true, automatic migrations on startup are disabled to prevent race conditions.
Manual Migration
To run migrations manually:
# Run on the first pod
kubectl exec -it aprs-0 -n aprs -- /app/bin/migrate
# Or create a one-off job
kubectl run migrate-job --rm -it --image=ghcr.io/aprsme/aprs.me:latest \
--env="DATABASE_URL=$DATABASE_URL" \
--env="SECRET_KEY_BASE=$SECRET_KEY_BASE" \
--env="MIX_ENV=prod" \
--env="SKIP_DB_CREATE=true" \
--restart=Never \
-- /app/bin/migrate
Configuration
Environment variables:
CLUSTER_ENABLED=true- Enables distributed lockingSKIP_DB_CREATE=true- Skips database creation (for PgBouncer)
Best Practices
- Use Init Containers for production deployments
- Test migrations in a staging environment first
- Monitor migration logs during deployments
- Have rollback plan ready with
mix ecto.rollback