Migrating from v0.7.x to v0.8.0
Guide for upgrading from v0.7.x to v0.8.0 of Migration Script Runner.
Table of Contents
- Overview
- Prerequisites
- Upgrade Steps
- New Features (Optional Adoption)
- Bug Fixes
- Backwards Compatibility
- Common Migration Scenarios
- Testing Your Migration
- Rollback Plan
- Getting Help
- References
- Summary
Overview
v0.8.0 adds production-ready features for preventing concurrent migrations and improving adapter type safety. This release has no breaking changes - it’s fully backwards compatible with v0.7.x.
What’s New in v0.8.0
- 🔒 Migration Locking - Prevent concurrent migrations with database-level locking mechanism
- 🛡️ Lock Ownership Verification - Two-phase locking prevents race conditions
- 🖥️ Lock CLI Commands -
lock:statusandlock:releasecommands for lock management - 🔧 Handler Generic Type - Type-safe handler access in adapters (no more casting!)
- 🐛 Down Migration Fix - Fixed TypeError when rolling back migrations
- 📦 npm Provenance - Enhanced supply chain security with build provenance
Migration Effort
Estimated Time: 5-10 minutes (optional feature adoption)
Complexity: Low
- No breaking changes - fully backwards compatible
- Locking is an optional feature you can enable when ready
- Handler generic type is optional for better type safety
Prerequisites
Before upgrading, ensure:
- You’re currently on v0.7.x
- Your tests are passing
- You have TypeScript 4.5+ (for best generic inference)
Upgrade Steps
1. Update Package
npm install @migration-script-runner/core@^0.8.0
Or with yarn:
yarn upgrade @migration-script-runner/core@^0.8.0
2. No Code Changes Required
✅ Your existing code continues to work without modifications.
All v0.8.0 features are backwards compatible:
- Locking is disabled by default
- Handler generic type has a sensible default
- Bug fixes are transparent
New Features (Optional Adoption)
Feature 1: Migration Locking
What it does: Prevents multiple migration processes from running simultaneously.
Why you need it:
- Prevents race conditions in production
- Avoids corrupted migration state
- Essential for multi-instance deployments
1.1 Implement ILockingService
Create a locking service for your database:
import { ILockingService, ILockStatus, IDB } from '@migration-script-runner/core';
class MyDatabaseLockingService implements ILockingService<IMyDB> {
constructor(private db: IMyDB) {}
async acquireLock(executorId: string, timeout: number): Promise<boolean> {
// Database-specific lock acquisition
// Example for SQL databases:
const result = await this.db.query(
`INSERT INTO migration_locks (executor_id, locked_at, expires_at)
VALUES ($1, NOW(), NOW() + INTERVAL '${timeout}ms')
ON CONFLICT (id) DO NOTHING
RETURNING id`,
[executorId]
);
return result.rows.length > 0;
}
async verifyLockOwnership(executorId: string): Promise<boolean> {
const result = await this.db.query(
'SELECT executor_id FROM migration_locks WHERE id = 1',
[]
);
return result.rows[0]?.executor_id === executorId;
}
async releaseLock(executorId: string): Promise<void> {
await this.db.query(
'DELETE FROM migration_locks WHERE executor_id = $1',
[executorId]
);
}
async forceReleaseLock(): Promise<void> {
await this.db.query('DELETE FROM migration_locks WHERE id = 1', []);
}
async checkAndReleaseExpiredLock(): Promise<void> {
await this.db.query(
'DELETE FROM migration_locks WHERE expires_at < NOW()',
[]
);
}
async getLockStatus(): Promise<ILockStatus> {
const result = await this.db.query(
`SELECT executor_id, locked_at, expires_at
FROM migration_locks WHERE id = 1`,
[]
);
if (result.rows.length === 0) {
return {
isLocked: false,
lockedBy: null,
lockedAt: null,
expiresAt: null
};
}
const row = result.rows[0];
return {
isLocked: true,
lockedBy: row.executor_id,
lockedAt: new Date(row.locked_at),
expiresAt: new Date(row.expires_at),
processId: row.executor_id.split('-')[1] // Extract PID from executor ID
};
}
}
1.2 Add Locking Service to Handler
import { IDatabaseMigrationHandler } from '@migration-script-runner/core';
class MyDatabaseHandler implements IDatabaseMigrationHandler<IMyDB> {
// ... existing properties ...
// Add locking service (NEW in v0.8.0)
lockingService?: ILockingService<IMyDB>;
constructor(db: IMyDB) {
this.db = db;
this.schemaVersion = new MySchemaVersion(db);
this.backup = new MyBackupService(db);
// Enable locking
this.lockingService = new MyDatabaseLockingService(db);
}
// ... existing methods ...
}
1.3 Configure Locking (Optional)
Default settings work for most cases, but you can customize:
import { Config } from '@migration-script-runner/core';
const config = new Config();
// Locking configuration (all optional - these are defaults)
config.locking.enabled = true; // Enable/disable locking
config.locking.timeout = 600000; // 10 minutes in milliseconds
config.locking.retryAttempts = 0; // Fail immediately (no retries)
config.locking.retryDelay = 1000; // 1 second between retries
1.4 Use Lock CLI Commands
# Check current lock status
msr lock:status
# Force-release a stuck lock (requires --force flag for safety)
msr lock:release --force
# Disable locking for a single run
msr migrate --no-lock
Example output:
$ msr lock:status
Lock Status: LOCKED
Locked by: server-prod-12345-a1b2c3d4-e5f6-7890-abcd-ef1234567890
Locked at: 2025-12-18T10:00:00.000Z
Expires at: 2025-12-18T10:10:00.000Z
Process ID: 12345
Another migration is currently running.
If you believe this is a stale lock, use: msr lock:release --force
Feature 2: Handler Generic Type Parameter
What it does: Provides type-safe access to handler properties in adapters.
Why you need it: Eliminates casting and bracket notation workarounds in adapter code.
BEFORE (v0.7.x):
import { MigrationScriptExecutor } from '@migration-script-runner/core';
class MyAdapter extends MigrationScriptExecutor<IMyDB> {
// Workaround: need to cast handler to access custom properties
private getHandler(): MyHandler {
return this['handler'] as MyHandler; // ❌ Bracket notation + casting
}
getConnectionInfo() {
const handler = this.getHandler();
return {
host: handler.config.host,
port: handler.config.port
};
}
}
AFTER (v0.8.0):
import { MigrationScriptExecutor } from '@migration-script-runner/core';
// Specify handler type as second generic parameter
class MyAdapter extends MigrationScriptExecutor<IMyDB, MyHandler> {
getConnectionInfo() {
// ✅ Type-safe access - no casting needed!
return {
host: this.handler.config.host,
port: this.handler.config.port
};
}
useCustomMethod() {
// ✅ IDE autocomplete works!
this.handler.customMethod();
}
}
Benefits:
- ✅ Full IDE autocomplete support
- ✅ Compile-time type checking
- ✅ No casting or workarounds needed
- ✅ Cleaner, more maintainable code
Bug Fixes
Down Migration TypeError Fixed
Issue: Calling executor.down(targetVersion) threw TypeError: s.init is not a function.
Fix: Migrations from database are now properly matched with filesystem scripts before rollback.
Impact: Down migrations now work correctly. No code changes needed - fix is automatic.
Backwards Compatibility
✅ 100% backwards compatible with v0.7.x
- All existing code works without changes
- Locking is opt-in (disabled by default)
- Handler generic type has sensible default
- No deprecated features
Common Migration Scenarios
Scenario 1: Basic Upgrade (No New Features)
npm install @migration-script-runner/core@^0.8.0
# Done! Everything continues to work.
Scenario 2: Enable Locking for Production
- Implement
ILockingServicefor your database - Add
lockingServiceto your handler - (Optional) Configure locking timeout/retries
- Deploy to production
Estimated time: 30-45 minutes
Scenario 3: Improve Adapter Type Safety
- Add second generic parameter to your adapter class
- Remove casting workarounds
- Enjoy improved IDE support
Estimated time: 5-10 minutes
Testing Your Migration
1. Run Tests
npm test
Ensure all tests pass after upgrading.
2. Test Lock Commands (if enabled)
# Check lock status
msr lock:status
# Try concurrent migrations (should fail)
msr migrate & # Start first migration
msr migrate # Second should fail with lock error
3. Test Down Migrations
# Run migrations
msr migrate
# Roll back to previous version
msr down 202501010001
# Should work without errors
Rollback Plan
If you encounter issues, rollback is simple:
npm install @migration-script-runner/core@^0.7.0
Since there are no breaking changes, rolling back is safe and won’t require code changes.
Getting Help
If you encounter issues during migration:
- Check GitHub Issues
- Review Locking Documentation
- See Lock Commands Guide
- Open a new issue with
v0.8.0label
References
- Locking Configuration
- Lock CLI Commands
- Production Deployment Guide
- CLI Adapter Development
- Changelog
Summary
v0.8.0 is a production-ready release that adds essential features for multi-instance deployments while maintaining full backwards compatibility.
Key Takeaways:
- ✅ No breaking changes - upgrade is safe
- 🔒 Enable locking to prevent concurrent migrations
- 🔧 Use handler generic type for better type safety
- 🐛 Down migrations now work correctly
- 📦 npm provenance for supply chain security
Recommended Actions:
- Upgrade immediately (backwards compatible)
- Enable locking for production environments
- Consider handler generic type for adapters
- Test down migrations if you use them
Happy migrating! 🚀