Database Migrations

Last updated: October 9, 2026

This guide covers Sophie Bot's database migration system using Beanie ODM.

Overview

Sophie uses Beanie's built-in migration system to manage database schema evolution. Migrations are versioned, reversible Python scripts that transform data between schema versions.

How It Works

  • Migration files are stored in
    sophie_bot/db/migrations/
  • Each migration has a timestamp prefix:
    YYYYMMDD_HHMMSS_description.py
  • Migrations define
    Forward
    and
    Backward
    classes for apply/rollback
  • Applied migrations are tracked in the
    migration_states
    collection
  • Migrations run automatically on startup for beta instance
Database initialization also repairs raw ObjectId
filters.chat
values left by the already-applied
20260125_210117_convert_filters_chat_id_to_link
migration. The repair runs even when migrations are disabled, preserves each referenced ID, and leaves existing DBRefs unchanged. Repeated or concurrent startup runs are safe. Fresh migrations write DBRefs directly.

Creating a Migration

Quick Start

Loading...
This creates a file like:
sophie_bot/db/migrations/20240125_120000_add_user_preferences.py

Manual Creation

  • Create file:
    sophie_bot/db/migrations/20240125_120000_add_user_preferences.py
  • Define old and new model structures if needed
  • Implement Forward and Backward migration classes
  • Test migration locally
  • Add tests to
    tests/test_migrations.py

Migration Examples

Example 1: Add New Field

Loading...

Example 2: Rename Field

Loading...

Example 3: Convert Data Type

Loading...

Example 4: Bulk Migration with Free Fall

Loading...

Migration Types

Iterative Migration

When to use:
  • Most document transformations
  • When you need to transform data field-by-field
  • Simple field additions, renames, or type conversions
Pros:
  • Beanie handles document loading and saving
  • Memory efficient (processes one document at a time)
  • Can be resumed if interrupted
  • Good for large collections
Cons:
  • Limited control over database session
  • Can be slower for bulk operations
  • Hard to track progress

Free Fall Migration

When to use:
  • Bulk operations (rebuilding indexes, collections)
  • When you need full database control
  • Complex transformations requiring multiple collections
  • Performance-critical operations
Pros:
  • Full control over database session
  • Can use batch processing
  • Better performance for bulk operations
  • Can track progress manually
Cons:
  • More complex to implement
  • Must handle document loading/saving manually
  • Risk of memory issues with large collections

Running Migrations

Migrations run automatically on startup when
instance_name=beta
:
Loading...

Manual

Run all pending migrations:
Loading...
Or invoke the CLI directly:
Loading...
Manual commands run independently of
run_migrations_on_startup
. The CLI opens MongoDB and Redis, initializes Beanie without changing indexes, and closes both clients on success or failure.

Check Status

Loading...
Example output:
Loading...

Rollback

Rollback a specific migration:
Loading...
Or invoke the CLI directly:
Loading...

Configuration

Configure migrations in
config.py
:
Loading...

Transaction Support

MongoDB transactions provide atomicity for migrations, ensuring either all changes succeed or none do.

Enabling Transactions

  • Configure MongoDB replica set (required for transactions)
  • Enable replica set in config:
Loading...

When to Use Transactions

Use transactions for:
  • Multi-collection migrations
  • Critical data transformations
  • Rollback safety is essential
  • Collections with relational dependencies
Avoid transactions for:
  • Very large collections (millions of documents)
  • Simple field additions
  • Non-critical migrations
  • When replica set is not available

Transaction Limitations

MongoDB transactions have limits:
  • Document size: 16MB per document
  • Transaction size: 16MB total
  • Operation count: Varies by MongoDB version
  • Execution time: 60 seconds default
For large collections, use free fall migration without transactions.

Handling Large Collections

Batching Strategy

For collections with millions of documents, implement batching:
Loading...

Progress Monitoring

Add progress logging for long-running migrations:
Loading...

Performance Tips

  • Use appropriate batch size: 1000-10000 typically works well
  • Create indexes before migration: Improves query performance
  • Drop indexes before bulk inserts: Faster for large insertions
  • Monitor memory usage: Adjust batch size if memory issues occur
  • Run during maintenance: Reduces impact on users

Testing

Running Tests

Loading...
Or:
Loading...

Writing Tests

Add tests to
tests/test_migrations.py
:
Loading...

Deployment Workflow

  • Create migration on development branch
  • Test locally:
    make migrate_up
  • Add tests to
    tests/test_migrations.py
  • Test in staging (beta instance)
  • Verify status:
    make migrate_status
  • Open merge request with migration details
  • CI runs tests automatically
  • Merge to main
  • Deploy to beta - migrations run automatically
  • Verify in beta
  • Deploy to stable if needed

Troubleshooting

Migration Failed Mid-Execution

Symptoms:
  • Migration error in logs
  • Some documents migrated, some not
  • Migration state may or may not be recorded
Solutions:
  • Check logs for specific error details
  • Manually fix affected documents if needed
  • Re-run migration - it will skip already-applied steps
  • Consider rolling back if state is inconsistent
Loading...

Migration Too Slow

Symptoms:
  • Migration taking hours to complete
  • High CPU/memory usage
  • Database slow to respond
Solutions:
  • Use
    @free_fall_migration()
    for better control
  • Implement batching with
    migration_batch_size
  • Create appropriate indexes before migration
  • Consider running during maintenance window
  • Monitor and optimize database performance
Loading...

Need to Roll Back

Procedure:
  • Stop application to prevent conflicts
  • Identify migration to rollback:
    Loading...
  • Run rollback:
    Loading...
  • Verify rollback with status check
  • Fix migration code if issue was in migration
  • Re-apply migration if needed
Important:
  • Rollbacks modify data - test in staging first
  • Rollbacks may take time for large collections
  • Monitor logs during rollback
  • Have data backup if possible

Transaction Errors

Symptoms:
  • Transaction numbers do not match
    error
  • Transaction is not in progress
    error
  • Migration fails with transaction-related error
Solutions:
  • Check MongoDB is running as replica set
  • Verify
    mongo_use_replica_set = True
    in config
  • Disable transactions for large collections:
    Loading...
  • Use free fall migration instead

Best Practices

Before Creating Migration

  • Document migration purpose and expected changes
  • Identify affected models and collections
  • Estimate document count for affected collections
  • Plan both Forward and Backward logic
  • Consider impact on running application
  • Add tests for migration
  • Update documentation
  • Get code review

Before Deploying to Production

  • Verify migration passed in CI
  • Test in staging environment
  • Confirm data integrity after migration
  • Document rollback procedure
  • Notify team of deployment window
  • Monitor logs after deployment
  • Verify application functionality

Common Pitfalls

❌ Don't:
  • Create migrations without backward classes
  • Skip testing migrations
  • Run migrations on production without testing
  • Use broad exception handlers that hide errors
  • Forget to document migration impact
  • Run migrations on stable instance (use beta only)
✅ Do:
  • Implement both Forward and Backward classes
  • Test migrations thoroughly in staging
  • Use descriptive migration names
  • Handle large collections with batching
  • Monitor migration progress with logs
  • Document rollback procedures

Migration Checklist

Pre-Migration:
  • Migration file created with proper naming
  • Forward and Backward classes implemented
  • Docstring with description and impact
  • Tests written in
    tests/test_migrations.py
  • Code reviewed by team
  • Tested locally with
    make migrate_up
  • Tested in staging environment
Post-Migration:
  • Migration applied successfully
  • Data integrity verified
  • Application functionality tested
  • Performance acceptable
  • Logs reviewed for errors
  • Documentation updated
  • Rollback procedure documented
Production Deployment:
  • Staging migration successful
  • Rollback procedure documented
  • Team notified of deployment
  • Maintenance window scheduled (if needed)
  • Monitoring configured
  • Backup taken (if possible)