Problem
Currently, MSR does not validate database connectivity before attempting to run migrations. If the database connection is invalid or unavailable, the migration process fails during execution, which could be:
- After creating backups
- After initializing schema version table
- Partway through migration execution
This wastes time and resources, and makes error diagnosis harder.
Proposed Solution
Add a connection validation method to the IDB interface that allows database handlers to verify connectivity before any migration operations begin.
Design Options to Consider
Option 1: Optional Method (Non-Breaking)
export interface IDB {
[key: string]: unknown;
/**
* Optional: Check if database connection is healthy.
* If not implemented, connection check is skipped.
*/
checkConnection?(): Promise<boolean>;
}
Option 2: Required Method (Breaking Change)
export interface IDB {
[key: string]: unknown;
/**
* Required: Verify database connection is healthy.
* @returns true if connection is valid
* @throws Error if connection fails with details
*/
checkConnection(): Promise<boolean>;
}
Option 3: Separate Interface
export interface IDBWithHealthCheck extends IDB {
checkConnection(): Promise<boolean>;
}
Implementation Examples
PostgreSQL:
class PostgresDB implements IDB {
async checkConnection(): Promise<boolean> {
try {
await this.pool.query('SELECT 1');
return true;
} catch (error) {
this.logger.error('DB connection failed:', error);
return false;
}
}
}
MySQL:
class MySQLDB implements IDB {
async checkConnection(): Promise<boolean> {
try {
await this.connection.ping();
return true;
} catch (error) {
return false;
}
}
}
MongoDB:
class MongoDB implements IDB {
async checkConnection(): Promise<boolean> {
try {
await this.client.db().admin().ping();
return true;
} catch (error) {
return false;
}
}
}
When to Call checkConnection()
The connection check should be called:
- Before migration execution in
MigrationScriptExecutor.migrate()
- Before rollback operations in
MigrationScriptExecutor.rollbackTo()
- Before validation in
MigrationScriptExecutor.validate()
- Optionally before backup creation (if backup uses the same connection)
Error Handling Strategy
Option A: Fail Fast (Recommended)
if (db.checkConnection && !(await db.checkConnection())) {
throw new Error('Database connection check failed. Cannot proceed with migrations.');
}
Option B: Return Result
const connectionValid = db.checkConnection
? await db.checkConnection()
: true; // Assume valid if not implemented
if (!connectionValid) {
this.logger.warn('Connection check failed');
// User decides whether to proceed
}
Option C: Auto-Retry
async function checkConnectionWithRetry(db: IDB, maxRetries = 3): Promise<boolean> {
for (let i = 0; i < maxRetries; i++) {
if (db.checkConnection && await db.checkConnection()) {
return true;
}
await sleep(1000 * (i + 1)); // Exponential backoff
}
return false;
}
Design Questions to Resolve
-
Breaking vs Non-Breaking: Should this be optional or required?
- Optional = no breaking change, but users might not implement it
- Required = forces good practices, but breaks existing implementations
-
Return Type: Boolean vs throw?
- Boolean: Simple, caller handles logic
- Throw: More expressive, includes error details
-
Retry Logic: Should MSR handle retries or leave it to implementers?
-
Timing: Should we check once at start or before each operation?
-
Connection Pooling: How does this interact with connection pools?
- One check for the pool?
- Check each connection?
Recommended Approach
Phase 1 (v0.4.0): Non-breaking optional method
export interface IDB {
[key: string]: unknown;
checkConnection?(): Promise<boolean>;
}
- Add to
MigrationScriptExecutor before major operations
- Log warning if not implemented
- Document as recommended best practice
Phase 2 (v1.0.0): Make required with proper migration guide
export interface IDB {
[key: string]: unknown;
checkConnection(): Promise<boolean>;
}
- Breaking change for major version
- Update all documentation and examples
- Provide migration guide for existing users
Related Work
Implementation Checklist
Benefits
✅ Fail Fast: Detect connection issues before wasting time
✅ Better UX: Clear error messages about connectivity
✅ Safer Operations: Don't start backups with invalid connections
✅ Implementation Flexibility: Each DBMS can implement optimally
✅ Debuggability: Separate connection issues from migration issues
Problem
Currently, MSR does not validate database connectivity before attempting to run migrations. If the database connection is invalid or unavailable, the migration process fails during execution, which could be:
This wastes time and resources, and makes error diagnosis harder.
Proposed Solution
Add a connection validation method to the
IDBinterface that allows database handlers to verify connectivity before any migration operations begin.Design Options to Consider
Option 1: Optional Method (Non-Breaking)
Option 2: Required Method (Breaking Change)
Option 3: Separate Interface
Implementation Examples
PostgreSQL:
MySQL:
MongoDB:
When to Call checkConnection()
The connection check should be called:
MigrationScriptExecutor.migrate()MigrationScriptExecutor.rollbackTo()MigrationScriptExecutor.validate()Error Handling Strategy
Option A: Fail Fast (Recommended)
Option B: Return Result
Option C: Auto-Retry
Design Questions to Resolve
Breaking vs Non-Breaking: Should this be optional or required?
Return Type: Boolean vs throw?
Retry Logic: Should MSR handle retries or leave it to implementers?
Timing: Should we check once at start or before each operation?
Connection Pooling: How does this interact with connection pools?
Recommended Approach
Phase 1 (v0.4.0): Non-breaking optional method
MigrationScriptExecutorbefore major operationsPhase 2 (v1.0.0): Make required with proper migration guide
Related Work
ISqlDBinterface pattern)Implementation Checklist
checkConnection()toIDBinterfaceMigrationScriptExecutorto call connection checkBenefits
✅ Fail Fast: Detect connection issues before wasting time
✅ Better UX: Clear error messages about connectivity
✅ Safer Operations: Don't start backups with invalid connections
✅ Implementation Flexibility: Each DBMS can implement optimally
✅ Debuggability: Separate connection issues from migration issues