In some of our projects we configure ragtime migrations by explicitly defining each individual migration like so:
{:duct.migrator/ragtime
{:database #ig/ref :duct.database/sql
:logger #ig/ref :duct/logger
:strategy :raise-error
:migrations [#ig/ref :foo.migration/create-foo-table]}
[:duct.migrator.ragtime/sql :foo.migration/create-foo-table]
{:up [#duct/resource "foo/migrations/create-foo-table.up.sql"]
:down [#duct/resource "foo/migrations/create-foo-table.down.sql"]}}
Is other projects, however, we use the sugar to include all migrations found in a path:
{:duct.migrator/ragtime
{:database #ig/ref :duct.database/sql
:logger #ig/ref :duct/logger
:strategy :raise-error
:migrations [#ig/ref :foo.migrations/dev
#ig/ref :foo.migrations/prod]}
[:duct.migrator.ragtime/resources :foo.migrations/dev]
{:path "dev/migrations"}
[:duct.migrator.ragtime/resources :foo.migrations/prod]
{:path "prod/migrations"}}
This is useful, yes. But it has some serious downsides:
It is harder to catch a conflict when 2 developers push new
migrations. Especially if the conflict is in the ordering of the migrations
only. While if we would be using explicit :migrations []
listing then there
will be obvious code conflicts.
There is a problem of partial changes being applied and the other few changes failing when a SQL sentence fails within a single migration file. On the other hand, doing explicit listing treats the whole migration file like a single transaction. So either all the SQL sentences in the migration file are applied, or all are rolled back.
There is an orthogonal issue that is related to this. The development migrations
always go at the beginning of the migrations list (or at the end, depending on
the order you specify in the :migrations
key). But once you have at least one
production migration applied and you need to add a new for development migration
(or vice-versa, depending on the order specified in :migrations
), it fails. It
expects to add the new development migration after the last development one (and
before any of the production ones) and it cannot. The reason why is that the
first production migration is already at that position.
Thus we need to make sure development migrations and production migrations are not intertwined. One way to achieve this is by using separate ragtime migration tables for each class of migrations. ragtime library let us specify the name of the table where a given list of migrations will be recorded. By using different table names for development and production we can keep the migrations separate and guarantee the order of application in all cases.
In this case, as we will have more than one :duct.migrator/ragtime
configuration, we will need to use composite Integrant keys for the development
and production configurations.
We need to change :duct.migrator/ragtime
in config.edn
to:
{[:duct.migrator/ragtime :foo/prod]
{:database #ig/ref :duct.database/sql
:logger #ig/ref :duct/logger
:strategy :raise-error
:migrations-table "ragtime_migrations"
:migrations [#ig/ref :foo.migration/create-foo-table]}
[:duct.migrator.ragtime/sql :foo.migration/create-foo-table]
{:up [#duct/resource "foo/migrations/create-foo-table.up.sql"]
:down [#duct/resource "foo/migrations/create-foo-table.down.sql"]}}
and change :duct.migrator/ragtime
in dev.edn
to:
{[:duct.migrator/ragtime :foo/dev]
{:database #ig/ref :duct.database/sql
:logger #ig/ref :duct/logger
:migrations-table "ragtime_migrations_dev"
:fake-dependency-to-force-initialization-order #ig/ref [:duct.migrator/ragtime :foo/prod]}
:migrations [#ig/ref :foo.dev-migration/create-dev-table]}
[:duct.migrator.ragtime/sql :foo.dev-migration/create-dev-table]
{:up [#duct/resource "foo/dev_migrations/create-dev-table.up.sql"]
:down [#duct/resource "foo/dev_migrations/create-dev-table.down.sql"]}
We also need to add a fake dependency in [:duct.migrator/ragtime :foo/dev]
(that :duct.migration/ragtime
library completely ignores) to force the order
of application of the migrations. With the configuration shown above, production
migrations will always be applied before development ones.
We will change the template to configure ragtime to use explicit listings of
migrations. The template will also configure two :duct.migrator/ragtime
Integrant keys, one for development and one for production. And set the order of
application of the migrations to production before development.
When using explicit listings of migrations, we generally use #duct/resource
to
get the migration files. If we want to keep development and production migration
files in separate directories, we need to make sure we use different names for
the directories where we keep them. If we use foo/migrations
in both cases,
#duct/resource
doesn't know if that migrations/create-foo-table.up.sql
is in
the development migrations directory, or the production migrations directory. It
will look for the migration file in one of them first, and if it does not find
it there, in the other one. But if we use the same migration file name in two
migrations, one for development and the other for production, we may end up
applying the wrong one.
The easiest way to avoid that is using different migrations directory names in
each case, like in the following examples. We use "migrations" as the directory
name in config.edn
:
{[:duct.migrator/ragtime :foo/prod]
{:database #ig/ref :duct.database/sql
:logger #ig/ref :duct/logger
:strategy :raise-error
:migrations-table "ragtime_migrations"
:migrations [#ig/ref :foo.migration/create-foo-table]}
[:duct.migrator.ragtime/sql :foo.migration/create-foo-table]
{:up [#duct/resource "foo/migrations/create-foo-table.up.sql"]
:down [#duct/resource "foo/migrations/create-foo-table.down.sql"]}}
and "dev_migrations" as the directory name in dev.edn
:
{[:duct.migrator/ragtime :foo/dev]
{:database #ig/ref :duct.database/sql
:logger #ig/ref :duct/logger
:migrations-table "ragtime_migrations_dev"
:fake-dependency-to-force-initialization-order #ig/ref [:duct.migrator/ragtime :foo/prod]}
:migrations [#ig/ref :foo.dev-migration/create-foo-table]}
[:duct.migrator.ragtime/sql :foo.dev-migration/create-foo-table]
{:up [#duct/resource "foo/dev_migrations/create-foo-table.up.sql"]
:down [#duct/resource "foo/dev_migrations/create-foo-table.down.sql"]}
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close