A Persistence Framework for Scala and NoSQL

View project on GitHub

running your migration

Now that we have built our migration, we can run it like so:

sbt migrateSchema 0_1_to_0_2

Here, the migration process will look for a longevity.migrations.Migration[_, _] instance in a field named 0_1_to_0_2 in the migrationsPackage setting we configured when we set up the plugin.

The migrateSchema task will perform a series of checks before running the migration. First, it checks that the migration is valid, as described towards the bottom of the previous section. It also asks the user for a couple confirmations.

We ask you to confirm that you have backed up your database before running the migration. As with any migration process, things can go wrong along the way, leaving your database in a corrupted state. One common example of something that can go wrong is this: You define a new key for your persistent type, and two existing objects have the same value for the new key. Depending on your back end, the type of key, and whether or not your database is partitioned, an error like this can result in different outcomes. It could cause the migration process to halt with an error, it could silently overwrite, or it could allow the dupicate key values in, violating your integrity constraint. This kind of lack of strict integrity is fairly typical when working with NoSQL databases. With the SQLite or JDBC back ends, the migration will definitely halt with an error in this scenario.

We also ask you to confirm that you have no applications running against the database while the migration is running. Any concurrent writes to the initial version of the model may or may not be lost by the migration process. Any concurrent reads from the initial version of the model will produce correct results, but note that one of the last steps of the migration process is to tear down the initial version, so any read-only clients will probably error out towards the end of the migration process.

We have considered ways to allow you to keep running your application while the migration is running. In particular, see these three items on our “migrations wishlist”:

If you are interested in taking on one of these enhancements, please let us know! We would be more than happy to help.

You can avoid these interactive confirmation queries by supplying a --nonInteractive flag to the SBT task invocation:

sbt migrateSchema 0_1_to_0_2 --nonInteractive

This will come in handy if you want to run this task from within a script.

Please note that due to the interactive nature of the migrateSchema rule, you may encounter difficulties running the rule if your build forks runs, for instance like so:

fork in run := true

In such situations, you may wish to run migrateSchema with the --nonInteractive flag. Or you could add the following settings to your build to allow forked runs to share the STDIN and STDOUT streams of the SBT process:

outputStrategy := Some(StdoutOutput)
connectInput in run := true

The SBT task will let you know once the migration is complete. This migration process has been designed to be restartable, so if it does get interrupted mid-way through, you do not have to restore from a backup and start over. Just re-run the SBT migrateSchema command.

After the migration, you will need to make a change to your longevity configuration to indicate that you are running against a new model version. In our example, we specified “version_0_2” as the final version of our migration, so we would update our application.conf file as follows:

longevity.modelVersion = version_0_2
prev: defining your migration
up: migrating to a new version of your domain
next: integration points