Lifecycle & Workflows
Migration script lifecycle and error handling workflows.
Table of contents
Migration Script Lifecycle
High-Level Workflow
This simplified diagram shows the main phases of migration execution:
graph TD
Start[User calls migrate] --> Init[Initialize Schema and Backup]
Init --> Discover[Discover and Filter Scripts]
Discover --> Validate[Validate Pending Scripts]
Validate --> ValidCheck{Valid?}
ValidCheck -->|No| Rollback[Rollback Changes]
ValidCheck -->|Yes| Execute[Execute Scripts]
Execute --> ExecCheck{Success?}
ExecCheck -->|No| Rollback
ExecCheck -->|Yes| Cleanup[Delete Backup]
Cleanup --> Render[Render Results]
Render --> Done[Return Success]
Rollback --> RenderFail[Render Errors]
RenderFail --> Fail[Return Failure]
Detailed Phase Interactions
Initialization and Discovery
sequenceDiagram
participant Executor as MigrationScriptExecutor
participant Backup as BackupService
participant Schema as SchemaVersionService
participant Scanner as MigrationScanner
Executor->>Schema: init()
Schema-->>Executor: Schema table ready
Executor->>Backup: createBackup()
Backup-->>Executor: Backup created
Executor->>Scanner: scan(folder)
Scanner-->>Executor: Pending migrations
Execution Loop
sequenceDiagram
participant Executor as MigrationScriptExecutor
participant Execution as MigrationExecutionService
participant Schema as SchemaVersionService
participant Rollback as RollbackService
loop For each pending migration
Executor->>Execution: execute(script)
Execution-->>Executor: Success or Error
alt Success
Executor->>Schema: add(script)
Schema-->>Executor: Saved
else Error
Executor->>Rollback: rollback()
Rollback-->>Executor: Rolled back
end
end
State Transitions
This state diagram illustrates the various states a migration script can be in throughout its lifecycle, from discovery to completion or failure:
stateDiagram
[*] --> Discovered: File found
Discovered --> Filtered: Check status
Filtered --> Pending: Not yet run
Filtered --> Ignored: Skipped
Filtered --> Migrated: Already run
Pending --> Validated: Pass validation
Validated --> Initialized: Load module
Initialized --> Executing: Run up method
Executing --> Completed: Success
Executing --> Failed: Error
Failed --> RollingBack: Trigger rollback
RollingBack --> RolledBack: Restore state
Completed --> Saved: Save to DB
Saved --> [*]
RolledBack --> [*]
Script Object Evolution
// 1. Discovery (MigrationService)
{
name: "V202311020036_create_users.ts",
filepath: "/path/../version-migration/V202311020036_create_users.ts",
timestamp: 202311020036,
script: undefined // Not loaded yet
}
// 2. Initialization (script.init())
{
name: "V202311020036_create_users.ts",
filepath: "/path/../version-migration/V202311020036_create_users.ts",
timestamp: 202311020036,
script: {
up: async (db, info, handler) => { ... }
}
}
// 3. Execution (MigrationRunner)
{
name: "V202311020036_create_users.ts",
filepath: "/path/../version-migration/V202311020036_create_users.ts",
timestamp: 202311020036,
username: "developer", // ← Added
startedAt: 1699999999000, // ← Added
finishedAt: 1699999999500, // ← Added
result: "Created 3 tables", // ← Added (from up() return)
script: { up: ... }
}
Error Handling Strategy
Fail-Fast Philosophy
MSR stops execution immediately on the first error to prevent cascading failures and maintain database consistency.
Script 1: ✓ Success
Script 2: ✓ Success
Script 3: ✗ FAILS
Script 4: ⊗ Not executed (stopped)
Script 5: ⊗ Not executed (stopped)
Action: Restore from backup, rollback all changes
Error Flow (with Rollback Strategies)
try {
// Conditional backup based on strategy
if (strategy === BACKUP || strategy === BOTH) {
await backup.create()
}
await schema.init()
await runner.execute(scripts) // ← Error here
if (backupPath) {
backup.delete()
}
return { success: true }
} catch (error) {
// Handle rollback based on configured strategy
switch (strategy) {
case BACKUP:
await backup.restore()
break
case DOWN:
await rollbackWithDown(executedScripts)
break
case BOTH:
try {
await rollbackWithDown(executedScripts)
} catch (downError) {
await backup.restore() // Fallback
}
break
case NONE:
logger.warn('No rollback configured')
break
}
if (backupPath) {
backup.delete() // Cleanup
}
return {
success: false,
errors: [error]
}
}
Recovery Process
BACKUP Strategy
- Error Occurs - Migration script throws exception
- Stop Execution - Remaining scripts not executed
- Restore Backup - Database rolled back to pre-migration state
- Delete Backup - Cleanup temporary backup file
- Return Result - Report failure with error details
DOWN Strategy
- Error Occurs - Migration script throws exception
- Stop Execution - Remaining scripts not executed
- Call down() Methods - Execute down() on all attempted migrations in reverse order
- Return Result - Report failure with error details
BOTH Strategy
- Error Occurs - Migration script throws exception
- Stop Execution - Remaining scripts not executed
- Try down() First - Attempt to rollback using down() methods
- Fallback to Backup - If down() fails, restore from backup
- Delete Backup - Cleanup temporary backup file
- Return Result - Report failure with error details
NONE Strategy
- Error Occurs - Migration script throws exception
- Stop Execution - Remaining scripts not executed
- Log Warning - No rollback performed, database may be inconsistent
- Return Result - Report failure with error details