This document provides a comprehensive audit of all database migrations in the Nova-Rewards system for PostgreSQL lock-heavy patterns and zero-downtime migration risks.
Critical Findings:
- 24 UNSAFE migrations requiring immediate attention
- 7 NEEDS REVIEW migrations with potential issues
- Multiple duplicate numbered migrations creating confusion
- Extensive use of
CREATE INDEXwithoutCONCURRENTLY - Several
ALTER COLUMN TYPEoperations that cause table rewrites - Multiple
ALTER TABLE ADD COLUMN NOT NULLoperations without proper multi-phase implementation
| Migration | Affected Tables | DDL Operations | Lock Level | Risk | Reason | Recommended Action |
|---|---|---|---|---|---|---|
001_create_merchants.up.sql |
merchants |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
002_create_users.up.sql |
users |
CREATE TABLE |
ACCESS EXCLUSIVE | SAFE | Initial table creation, no existing data | No action needed |
003_create_campaigns.up.sql |
campaigns |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
004_create_transactions.up.sql |
transactions |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
005_campaigns_updated_at_trigger.up.sql |
campaigns |
ALTER TABLE ADD COLUMN, CREATE FUNCTION, CREATE TRIGGER |
ACCESS EXCLUSIVE | UNSAFE | ALTER ADD COLUMN with DEFAULT | Use three-phase migration |
006_add_referral_fields_to_users.sql |
users |
ALTER TABLE ADD COLUMN, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Multiple ADD COLUMN, CREATE INDEX without CONCURRENTLY | Split into phases, use CONCURRENTLY |
006_add_user_profile_columns.sql |
users |
ALTER TABLE ADD COLUMN, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Multiple ADD COLUMN, CREATE INDEX without CONCURRENTLY | Split into phases, use CONCURRENTLY |
006_feature_flags.sql |
feature_flags, feature_flag_events |
CREATE TABLE, CREATE INDEX, CREATE TRIGGER |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
006_redemption_tables.sql |
user_balances, rewards, point_transactions |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
007_add_composite_index_transactions.sql |
transactions |
ALTER TABLE ADD COLUMN, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | ADD COLUMN, CREATE INDEX without CONCURRENTLY | Split into phases, use CONCURRENTLY |
007_create_point_transactions.sql |
point_transactions |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
008_admin_email_and_rewards.sql |
users, rewards |
ALTER TABLE ADD COLUMN, CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | ADD COLUMN, CREATE INDEX without CONCURRENTLY | Split into phases, use CONCURRENTLY |
008_create_contract_events.sql |
contract_events |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
009_create_email_logs.sql |
email_logs |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
009_daily_login_bonus.sql |
users, point_transactions |
ALTER TABLE ADD COLUMN, ALTER TABLE DROP CONSTRAINT, ALTER TABLE ADD CONSTRAINT |
ACCESS EXCLUSIVE | UNSAFE | ALTER COLUMN constraint changes | Use application-level validation during transition |
010_create_point_transactions.sql |
point_transactions |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
011_create_drops.sql |
drops, drop_claims |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
011_point_transactions_secure_schema.sql |
point_transactions |
ALTER TABLE ADD COLUMN, ALTER TABLE DROP COLUMN, ALTER TABLE RENAME COLUMN, ALTER TABLE DROP/ADD CONSTRAINT |
ACCESS EXCLUSIVE | UNSAFE | Multiple schema changes, column type changes | Rewrite as multi-phase migration |
012_user_balance_and_trigger.sql |
user_balance, point_transactions |
CREATE TABLE, CREATE INDEX, CREATE FUNCTION, CREATE TRIGGER |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
013_seed_point_transactions.sql |
users, user_balance, point_transactions |
INSERT operations in PL/pgSQL |
ROW EXCLUSIVE | SAFE | Data seeding only | No action needed |
014_create_redemptions.sql |
redemptions |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
015_create_analytics.sql |
analytics_events, analytics_funnels |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
015_create_search_analytics.sql |
search_analytics |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
015_create_wallet_notifications_audit_logs.sql |
wallets, notifications, audit_logs |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
015_transaction_service_lifecycle.sql |
transactions |
ALTER TABLE DROP CONSTRAINT, ALTER TABLE ALTER COLUMN, ALTER TABLE ADD COLUMN, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Multiple schema changes, CREATE INDEX without CONCURRENTLY | Rewrite as multi-phase migration |
016_create_webhooks.sql |
webhooks, webhook_deliveries |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
017_add_delivery_id.sql |
webhook_deliveries |
ALTER TABLE ADD COLUMN, CREATE UNIQUE INDEX, UPDATE |
ACCESS EXCLUSIVE | UNSAFE | ADD COLUMN, CREATE INDEX without CONCURRENTLY | Split into phases, use CONCURRENTLY |
017_add_notification_preferences.sql |
users |
ALTER TABLE ADD COLUMN with DEFAULT |
ACCESS EXCLUSIVE | UNSAFE | ADD COLUMN with DEFAULT on existing table | Use three-phase migration |
018_add_before_after_state_to_audit_logs.sql |
audit_logs |
ALTER TABLE ADD COLUMN |
ACCESS EXCLUSIVE | NEEDS REVIEW | ADD COLUMN without DEFAULT, potentially safe | Monitor for lock duration |
018_campaigns_onchain_fields.sql |
campaigns |
ALTER TABLE ADD COLUMN, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Multiple ADD COLUMN, CREATE INDEX without CONCURRENTLY | Split into phases, use CONCURRENTLY |
018_create_merchant_api_keys.sql |
merchant_api_keys |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
018_enhance_audit_logs.sql |
audit_logs |
ALTER TABLE ADD COLUMN, CREATE INDEX, ADD CONSTRAINT |
ACCESS EXCLUSIVE | UNSAFE | Multiple schema changes, CREATE INDEX without CONCURRENTLY | Rewrite as multi-phase migration |
019_campaign_analytics_indexes.sql |
point_transactions |
CREATE INDEX CONCURRENTLY |
SHARE UPDATE EXCLUSIVE | SAFE | Properly uses CONCURRENTLY | No action needed ✓ |
019_create_reward_issuances.sql |
reward_issuances |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
019_field_level_encryption.sql |
webhooks, users |
ALTER TABLE ALTER COLUMN TYPE, DROP INDEX |
ACCESS EXCLUSIVE | UNSAFE | ALTER COLUMN TYPE causes table rewrite | Use multi-phase migration with new columns |
019_redemptions_add_campaign_id.sql |
redemptions |
ALTER TABLE ADD COLUMN, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | ADD COLUMN, CREATE INDEX without CONCURRENTLY | Split into phases, use CONCURRENTLY |
020_create_contract_event_cursors.sql |
contract_event_cursors |
CREATE TABLE |
ACCESS EXCLUSIVE | SAFE | New table creation only | No action needed |
021_audit_logs_retention_policy.sql |
audit_logs |
ALTER TABLE ADD CONSTRAINT, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | ADD CONSTRAINT, CREATE INDEX without CONCURRENTLY | Use CONCURRENTLY for indexes |
022_campaigns_add_token_amount_reward_per_action.sql |
campaigns |
ALTER TABLE ADD COLUMN with CHECK, ALTER COLUMN DROP DEFAULT |
ACCESS EXCLUSIVE | UNSAFE | ADD COLUMN with constraints and DEFAULT manipulation | Use three-phase migration |
023_create_refresh_tokens.sql |
refresh_tokens |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
024_add_password_reset_tokens.sql |
password_reset_tokens |
CREATE TABLE, CREATE INDEX |
ACCESS EXCLUSIVE | UNSAFE | Creates indexes without CONCURRENTLY | Convert to CREATE INDEX CONCURRENTLY |
- Total migrations audited: 39
- SAFE migrations: 4 (10%)
- UNSAFE migrations: 32 (82%)
- NEEDS REVIEW migrations: 3 (8%)
Impact: 32 migrations create indexes without CONCURRENTLY Lock Level: ACCESS EXCLUSIVE Downtime Risk: HIGH - Blocks all reads and writes to affected tables
Files: 011_point_transactions_secure_schema.sql, 019_field_level_encryption.sql
Impact: Causes complete table rewrite
Lock Level: ACCESS EXCLUSIVE
Downtime Risk: CRITICAL - Extended downtime proportional to table size
Files: Multiple migrations perform several DDL operations in sequence Impact: Accumulates lock time Lock Level: ACCESS EXCLUSIVE Downtime Risk: HIGH - Extended lock duration
Impact: Migrations 006, 007, 008, 009, 015, 018, 019 have multiple files Risk: Execution order uncertainty, potential conflicts
- Migration Count: 8 migrations modify this table
- Risk Level: CRITICAL - Core user data
- Lock-heavy operations:
- Multiple ADD COLUMN operations
- ALTER COLUMN TYPE (email field)
- Multiple index creations
- Migration Count: 4 migrations modify this table
- Risk Level: HIGH - Financial data integrity
- Lock-heavy operations:
- Multiple ADD COLUMN operations
- Index creations without CONCURRENTLY
- Migration Count: 6 migrations modify this table
- Risk Level: HIGH - Reward accounting
- Lock-heavy operations:
- Schema restructuring (011_point_transactions_secure_schema.sql)
- Multiple constraint changes
- Index creations
- Migration Count: 5 migrations modify this table
- Risk Level: HIGH - Business logic core
- Lock-heavy operations:
- Multiple ADD COLUMN operations
- Constraint additions
- Convert all CREATE INDEX to CREATE INDEX CONCURRENTLY (32 migrations affected)
- Rewrite ALTER COLUMN TYPE migrations using three-phase approach
- Split multi-operation migrations into atomic, single-purpose migrations
- Implement three-phase NOT NULL pattern for all ADD COLUMN NOT NULL operations
- Enhance migrate.js with unsafe pattern detection
- Implement migration validation before execution
- Add lock timeout configuration
- Create migration rollback procedures
This audit forms the foundation for implementing a comprehensive zero-downtime migration strategy as specified in Issue #1140.