#migration #migrate #database

migrate-state

Generic interface for managing the migrations state storage

1 unstable release

0.1.0 Aug 25, 2021

#939 in Development tools

Download history 23/week @ 2022-08-12 21/week @ 2022-08-19 13/week @ 2022-08-26 24/week @ 2022-09-02 28/week @ 2022-09-09 15/week @ 2022-09-16 32/week @ 2022-09-23 28/week @ 2022-09-30 30/week @ 2022-10-07 26/week @ 2022-10-14 20/week @ 2022-10-21 33/week @ 2022-10-28 34/week @ 2022-11-04 30/week @ 2022-11-11 24/week @ 2022-11-18 11/week @ 2022-11-25

105 downloads per month
Used in 6 crates (4 directly)

MIT/Apache

7KB

⚠️ Warning

The crates are in an early MVP stage of development. You may already use them and they will provide you with a good-enough subset of features, but some advanced use cases may not be covered yet.

Crate docs.rs crates.io
migrate
migrate-core
migrate-state
migrate-state-dynamodb
migrate-state-file
migrate-state-test

The documentation for the master branch is available here.

migrate

migrate is a general purpose migration tool. It provides a flexible interface for writing migration scripts in Rust taking care of the migration state bookkeeping for you.

Overview

migrate is capable of migrating basically any kind of external state: production databases, cloud resources, etc.. It monitors what migrations should be applied or rolled back. With more advanced setup migrate prevents data races using external locks ensuring that only one migration is running at any point of time (currently not implemented yet).

In the basic case you should be able to just implement up and (optionally) down methods.

Example boilerplate

use async_trait::async_trait;
use migrate::MigrateCli;
use migrate::core::{Migration, Plan, MigrationCtxProvider};
use std::error::Error;
use rusoto_dynamodb::DynamoDb;

// File where `migrate` CLI will record the list of allready applied migrations
// This is called the migration state and there are several different backends
// that implement it. You may also implement your own, e.g. store the migration
// state in your own database.
// See the list of ready-to-use state backends bellow.
const MIGRATION_STATE_FILE_PATH: &str = "./migration-state";

type Result<T, E = Box<dyn Error + Send + Sync>> = std::result::Result<T, E>;

struct MyMigration;

#[async_trait]
impl Migration for MyMigration {
    type Ctx = rusoto_dynamodb::DynamoDbClient;

    async fn up(&mut self, ctx: &mut Self::Ctx) -> Result<()> {
        // Apply forward migration logic with the given context
        ctx.put_item(todo!()).await?;
    }

    async fn down(&mut self, ctx: &mut Self::Ctx) -> Result<()> {
        // Rollback the applied migration.
        // Ideally this should be purely inverse to up()
        ctx.delete_item(todo!()).await?;
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let state_storage = migrate_state_file::FileStateLock::new(MIGRATION_STATE_FILE_PATH);

    let mut plan = Plan::builder(state_storage);

    plan.ctx_provider(DynamoDbClientProvider)
        // Add migrations in order one after each other to the plan
        .migration("migration-1", MyMigration);

    // Run the `migrate` cli to get the parameters of how to
    // build and execute the rest of the migration plan
    // let cli = migrate::MigrateCli::from_cli_args();

    // Run the CLI (this is run in a test, it's commented out not to run CLI)
    // cli.run(plan).await?;

    Ok(())
}

struct DynamoDbClientProvider;

#[async_trait]
impl MigrationCtxProvider for DynamoDbClientProvider {
    type Ctx = rusoto_dynamodb::DynamoDbClient;

    async fn create_in_commit_mode(self: Box<Self>) -> Result<Self::Ctx> {
        // Create real database client that will do real changes on real data
        todo!()
    }

    async fn create_in_no_commit_mode(self: Box<Self>) -> Option<Result<Self::Ctx>> {
        // We can provide some mocked implementation of database client here
        // via the usage of traits or enums so that the client doesn't commit changes to the database
        todo!()
    }
}

Ready-to-use migration state backends

Locking

migrate should support state locking to prevent data races (concurrent migrations). This feature is not implemented yet, but planned for 1.0 release...

New migration bootstrapping

migrate cli should have a subcommand for creating new migrations stubs that should be configurable by the migration context trait implementation. This feature is also planned for 1.0 release but not yet implemented...

Goals

  • Convenient and flexible API
  • Consistency

Non-goals:

  • Performance

Contributing

Any contributions are very welcome, just make sure required CI checks pass :D

References

The idea of migrate was initially inspired by east migration tool (from TypeScript world).

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dually licensed as above, without any additional terms or conditions.

Dependencies

~210–620KB
~15K SLoC