Migrating from v0.4.x to v0.5.0

Guide for upgrading from v0.4.x to v0.5.0 of Migration Script Runner.

Table of Contents

  1. Overview
    1. What’s New in v0.5.0
    2. Migration Effort
  2. Prerequisites
  3. Upgrade Steps
    1. 1. Update Package
    2. 2. Verify Existing Code Works
    3. 3. Run Your Test Suite
    4. 4. Optional: Adopt New Features
  4. No Breaking Changes
  5. New Features (Optional)
    1. Feature 1: Transaction Management
      1. Basic Usage (SQL Databases)
      2. Configure Retry Logic
      3. Implement Transaction Support in Your Handler
    2. Feature 2: Environment Variable Configuration
      1. Quick Start
      2. Use ConfigLoader for Waterfall Loading
      3. Docker/Kubernetes Example
      4. All Available Environment Variables
    3. Feature 3: Transaction Hooks
  6. Testing Your Migration
    1. 1. Test in Development
    2. 2. Verify Transaction Behavior
    3. 3. Test Environment Variables
    4. 4. Production Rollout Checklist
  7. Common Migration Issues
    1. Issue 1: Transaction Deadlocks in Production
    2. Issue 2: Environment Variables Not Loading
    3. Issue 3: Hooks Not Being Called
  8. Rollback Procedure
    1. 1. Reinstall v0.4.x
    2. 2. Remove New Config Properties (if used)
    3. 3. Verify Tests Pass
  9. Getting Help
  10. Summary

Overview

v0.5.0 is a feature release with no breaking changes. All v0.4.x code continues to work without modification. This release adds powerful new features for transaction management and environment variable configuration.

What’s New in v0.5.0

  • Transaction Management - Configurable transactions with automatic retry logic
  • Environment Variables - Complete MSR_* environment variable support
  • Enhanced Hooks - Transaction lifecycle hooks and better hook organization
  • 📚 Improved Documentation - Comprehensive API documentation and reorganized structure

Migration Effort

Estimated Time: 5-15 minutes

Complexity: Low - No code changes required, new features are opt-in


Prerequisites

Before upgrading, ensure:

  • You’re currently on v0.4.x
  • Your tests are passing
  • You have a backup of your database
  • Your migrations follow the standard naming convention (V{timestamp}_{description})

Upgrade Steps

1. Update Package

npm install @migration-script-runner/core@^0.5.0

Or with yarn:

yarn upgrade @migration-script-runner/core@^0.5.0

2. Verify Existing Code Works

Your existing v0.4.x code will continue to work without changes:

// v0.4.x code - still works in v0.5.0
import { MigrationScriptExecutor, Config } from '@migration-script-runner/core';

const config = new Config();
config.folder = './migrations';

const executor = new MigrationScriptExecutor(handler, config);
const result = await executor.up();

3. Run Your Test Suite

npm test

All tests should pass without modifications.

4. Optional: Adopt New Features

v0.5.0 adds optional new features you can adopt at your own pace. See sections below for details.


No Breaking Changes

v0.5.0 maintains 100% backward compatibility with v0.4.x:

Feature v0.4.x Behavior v0.5.0 Behavior
Config class All properties work All properties work + new transaction property (optional)
IMigrationHooks All hooks work All hooks work + new transaction hooks (optional)
Database handlers All interfaces work All interfaces work + new transaction interfaces (optional)
Migration files All file types supported All file types supported (no changes)
API methods All methods unchanged All methods unchanged

You don’t need to change anything - your v0.4.x code works as-is.


New Features (Optional)

Feature 1: Transaction Management

What it does: Provides fine-grained control over how migrations are wrapped in database transactions, with automatic retry logic for transient failures.

Who needs it:

  • Production systems requiring transactional guarantees
  • High-concurrency databases with deadlock risks
  • Systems needing configurable isolation levels
  • NoSQL databases with transaction support

How to adopt:

Basic Usage (SQL Databases)

import { MigrationScriptExecutor, Config, TransactionMode } from '@migration-script-runner/core';

const config = new Config();

// Each migration in its own transaction (default)
config.transaction.mode = TransactionMode.PER_MIGRATION;

// All migrations in single transaction
config.transaction.mode = TransactionMode.PER_BATCH;

// No automatic transactions
config.transaction.mode = TransactionMode.NONE;

const executor = new MigrationScriptExecutor(handler, config);
await executor.up();

Configure Retry Logic

import { IsolationLevel } from '@migration-script-runner/core';

// Handle high-concurrency databases
config.transaction.retries = 10;              // More retries
config.transaction.retryDelay = 200;          // Longer initial delay
config.transaction.retryBackoff = true;       // Exponential backoff

// Maximum consistency (slower, more deadlocks)
config.transaction.isolation = IsolationLevel.SERIALIZABLE;

Implement Transaction Support in Your Handler

For SQL Databases (PostgreSQL, MySQL, etc.):

import { IDatabaseMigrationHandler, ITransactionalDB } from '@migration-script-runner/core';
import { Pool } from 'pg';

interface IPostgresDB extends ITransactionalDB {
    query<T>(sql: string, params?: unknown[]): Promise<T[]>;
}

class PostgresDB implements IPostgresDB {
    constructor(private pool: Pool) {}

    async beginTransaction(): Promise<void> {
        await this.pool.query('BEGIN');
    }

    async commit(): Promise<void> {
        await this.pool.query('COMMIT');
    }

    async rollback(): Promise<void> {
        await this.pool.query('ROLLBACK');
    }

    async setIsolationLevel(level: string): Promise<void> {
        await this.pool.query(`SET TRANSACTION ISOLATION LEVEL ${level}`);
    }

    async query<T>(sql: string, params?: unknown[]): Promise<T[]> {
        const result = await this.pool.query(sql, params);
        return result.rows as T[];
    }
}

For NoSQL Databases (MongoDB, Firestore, DynamoDB):

import { ICallbackTransactionalDB } from '@migration-script-runner/core';
import { MongoClient, ClientSession } from 'mongodb';

interface IMongoDBConnection extends ICallbackTransactionalDB<ClientSession> {
    client: MongoClient;
}

class MongoDBConnection implements IMongoDBConnection {
    constructor(public client: MongoClient) {}

    async runTransaction<T>(
        callback: (session: ClientSession) => Promise<T>
    ): Promise<T> {
        const session = this.client.startSession();
        try {
            return await session.withTransaction(callback);
        } finally {
            await session.endSession();
        }
    }
}

If you don’t need transactions:

No changes needed! Your existing IDB implementation continues to work. MSR will skip transaction management if your database doesn’t implement transaction interfaces.


Feature 2: Environment Variable Configuration

What it does: Configure MSR entirely through environment variables, following 12-factor app principles.

Who needs it:

  • Docker/Kubernetes deployments
  • CI/CD pipelines
  • Cloud-native applications
  • Multi-environment deployments (dev, staging, production)

How to adopt:

Quick Start

Set environment variables and MSR picks them up automatically:

# Core configuration
export MSR_FOLDER=./database/migrations
export MSR_TABLE_NAME=migration_history
export MSR_DRY_RUN=false

# Validation
export MSR_VALIDATE_BEFORE_RUN=true
export MSR_STRICT_VALIDATION=true

# Transaction management
export MSR_TRANSACTION_MODE=PER_MIGRATION
export MSR_TRANSACTION_ISOLATION=READ_COMMITTED
export MSR_TRANSACTION_RETRIES=5

# Run migrations
node migrate.js

Use ConfigLoader for Waterfall Loading

import { ConfigLoader } from '@migration-script-runner/core';

// Automatic waterfall: env vars → config file → defaults → overrides
const config = ConfigLoader.load({
    // Optional programmatic overrides (highest priority)
    folder: './migrations'
});

const executor = new MigrationScriptExecutor(handler, config);
await executor.up();

Docker/Kubernetes Example

docker-compose.yml:

version: '3.8'
services:
  migrations:
    image: my-app-migrations
    environment:
      MSR_FOLDER: /app/migrations
      MSR_TABLE_NAME: schema_version
      MSR_TRANSACTION_MODE: PER_BATCH
      MSR_TRANSACTION_RETRIES: 10
      MSR_VALIDATE_BEFORE_RUN: "true"
      MSR_STRICT_VALIDATION: "true"

Kubernetes ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: msr-config
data:
  MSR_FOLDER: "/app/migrations"
  MSR_TABLE_NAME: "schema_version"
  MSR_TRANSACTION_MODE: "PER_MIGRATION"
  MSR_TRANSACTION_ISOLATION: "READ_COMMITTED"
  MSR_TRANSACTION_RETRIES: "5"
  MSR_VALIDATE_BEFORE_RUN: "true"

All Available Environment Variables

See Environment Variables API Reference for complete documentation of all 33 environment variables organized by category.


Feature 3: Transaction Hooks

What it does: Monitor and extend transaction lifecycle with new hooks.

Who needs it:

  • Systems requiring transaction metrics
  • Applications needing transaction logging
  • Monitoring and alerting integrations

How to adopt:

import { IMigrationHooks, ITransactionContext } from '@migration-script-runner/core';

class TransactionMonitoringHooks implements IMigrationHooks {
    async beforeTransactionBegin(context: ITransactionContext): Promise<void> {
        console.log(`Starting transaction ${context.transactionId} (${context.mode})`);
    }

    async afterCommit(context: ITransactionContext): Promise<void> {
        const duration = Date.now() - context.startTime;
        console.log(`Transaction ${context.transactionId} committed in ${duration}ms`);

        // Send metrics
        metrics.timing('transaction.duration', duration, {
            mode: context.mode,
            migrations: context.migrations.length,
            attempts: context.attempt
        });
    }

    async onCommitRetry(
        context: ITransactionContext,
        error: Error,
        attempt: number
    ): Promise<void> {
        console.warn(`Transaction ${context.transactionId} retry #${attempt}: ${error.message}`);

        // Alert on multiple retries
        if (attempt >= 3) {
            alerting.warn('High transaction retry count', { context, error, attempt });
        }
    }

    async beforeRollback(context: ITransactionContext, error: Error): Promise<void> {
        console.error(`Rolling back transaction ${context.transactionId}:`, error);
        alerting.error('Transaction rollback', { context, error });
    }
}

const executor = new MigrationScriptExecutor(handler, config, {
    hooks: new TransactionMonitoringHooks()
});

Testing Your Migration

1. Test in Development

# Set environment variables
export MSR_FOLDER=./test-migrations
export MSR_DRY_RUN=true

# Run with new features
npm start

2. Verify Transaction Behavior

Create a test migration that will fail:

// migrations/V202501010001_test_transaction.ts
export default class TestTransaction {
    async up(db: any): Promise<string> {
        await db.query('CREATE TABLE test_tx (id INT)');
        throw new Error('Test rollback');  // Force rollback
    }
}

Run and verify transaction rolls back properly.

3. Test Environment Variables

# Test all core variables work
export MSR_FOLDER=./migrations
export MSR_TABLE_NAME=test_schema
export MSR_VALIDATE_BEFORE_RUN=true

npm start

4. Production Rollout Checklist

  • Tested in development environment
  • Tested in staging with production-like data
  • Verified transaction modes work correctly
  • Confirmed environment variables load properly
  • Validated hooks are called at expected times
  • Checked logs for retry behavior
  • Verified backward compatibility with existing migrations
  • Database backup created before deployment

Common Migration Issues

Issue 1: Transaction Deadlocks in Production

Symptom: Seeing “deadlock detected” errors frequently

Solution:

// Increase retries and use exponential backoff
config.transaction.retries = 10;
config.transaction.retryDelay = 200;
config.transaction.retryBackoff = true;

// OR use per-migration mode instead of per-batch
config.transaction.mode = TransactionMode.PER_MIGRATION;

// OR lower isolation level (if data consistency allows)
config.transaction.isolation = IsolationLevel.READ_COMMITTED;

Issue 2: Environment Variables Not Loading

Symptom: Configuration not applying from environment variables

Solution:

// Ensure you're using ConfigLoader
import { ConfigLoader } from '@migration-script-runner/core';

const config = ConfigLoader.load();  // This loads env vars automatically

// NOT this (manual Config doesn't load env vars automatically)
const config = new Config();  // ❌ Won't load env vars

Issue 3: Hooks Not Being Called

Symptom: Transaction hooks not executing

Solution:

Ensure your database implements transaction interfaces:

// ✅ Implements ITransactionalDB - hooks will be called
class MyDB implements ITransactionalDB {
    async beginTransaction() { /* ... */ }
    async commit() { /* ... */ }
    async rollback() { /* ... */ }
}

// ❌ Only implements IDB - transaction hooks skipped
class MyDB implements IDB {
    // No transaction methods
}

Rollback Procedure

If you need to rollback to v0.4.x:

1. Reinstall v0.4.x

npm install @migration-script-runner/core@^0.4.0

2. Remove New Config Properties (if used)

// Remove v0.5.0 transaction config
delete config.transaction;  // Or just remove the line

3. Verify Tests Pass

npm test

Note: Database migrations executed with v0.5.0 are compatible with v0.4.x. Your migration history will work with both versions.


Getting Help

If you encounter issues during migration:

  1. Check the documentation:
  2. Search existing issues:
  3. Ask for help:

Summary

v0.5.0 is a smooth, non-breaking upgrade that adds powerful new features:

Zero breaking changes - All v0.4.x code works unchanged ✅ Optional adoption - New features are opt-in ✅ 5-15 minute upgrade - Update package and you’re done ✅ Production ready - 100% test coverage, comprehensive code review

Next steps:

  1. Update to v0.5.0: npm install @migration-script-runner/core@^0.5.0
  2. Run tests to verify compatibility
  3. Explore new features at your own pace
  4. Read the CHANGELOG for complete details

Happy migrating! 🚀