21

I'm using Liquibase 3.3.5 to update my database. Having contexts is a nice way to only execute specific parts of the changelog. But I don't understand, why ALL changesets are executed, when no context is provided on update. Consider the following example:

  • changeset A: context=test
  • changeset B: no context
  • changeset C: context=prod

So

  • executing update with context=test, will execute changeset A+B.
  • executing update with context=prod, will execute changeset B+C.
  • executing update with no context, will execute changeset A+B+C.

For me, this doesn't make sense at all :).

I would expect, that only changeset B will be executed, since it doesn't define a specific context.

In the Liquibase contexts example: http://www.liquibase.org/documentation/contexts.html ("Using Contexts for Test Data") they say, that one should mark the changesets for testing with "test", and executing them with giving the context "test" to apply testdata. Fine - make sense. But

"When it comes time to migrate your production database, don’t include the “test" context, and your test data not be included. "

So, if I wouldn't specify "test" context when executing production update, it would execute the "test" changesets as well, since I didn't specify a context at all.

Again, I would expect that leaving out test on update execution, would only perform the regular changesets without the test changesets.

Or I'm missing something here :)?

javg
  • 405
  • 1
  • 5
  • 10

5 Answers5

16

This is just how Liquibase works - if you do an update and don't specify a context, then all changesets are considered as applicable to that update operation.

There were a couple of ways that this could have been implemented, and the development team had to pick one.

  1. if you don't specify a context during an update operation, then no changesets are considered.
  2. if you don't specify a context, then all changesets are considered.
  3. if you don't specify a context, then only changesets that have no context are considered.
  4. if you don't specify a context and none of the changesets have contexts on them, then all changesets are considered, but if some of the changesets do have contexts, go to option 1, 2, or 3 above.

The team could have gone with option 3 (which matches your expectation) but decided long ago to go with option 2, as that seemed like the 'best' way at the time. I wasn't on the team at that time, so I don't know any more than that.

SteveDonie
  • 8,700
  • 3
  • 43
  • 43
  • 1
    Ok, thanks for the clarification :)! Changing that would break backwards compatibility, that's for sure. But as a new user to Liquibase, that didn't felt "natural" to me, hence the question. If you always use a context, it seems ok to do it that way. Are there any plans to change that behavior? – javg Jun 12 '15 at 10:13
  • As you noted, changing it would break backwards compatibility, so it is very unlikely to change. It would be beneficial to improve the documentation. If you are interested, the documentation is all in GitHub, and the team welcomes pull requests! https://github.com/liquibase/liquibase.github.com ps - if my answer was helpful, please 'accept it' as the correct answer. – SteveDonie Jun 12 '15 at 14:50
  • how is it with labels, is it the same? – mirec Oct 19 '22 at 20:39
  • According to the documentation, "If your changelog includes changesets that have labels and changesets that don't have them, changesets without labels will match any label. Example: If you run the liquibase --labelFilter=release3 update command, it will deploy all changesets with the label release3 and all changesets without any label." – SteveDonie Oct 19 '22 at 20:56
7

I will add solution from me (from my perspective the default Liquibase behavior is not intuitive). In our project to deal with the "problem" we configured liquibase context in this way:

liquibase.setChangeLog("classpath*:liquibase/master.xml");
contexts = StringUtils.isBlank(contexts) ? "none" : contexts;
liquibase.setContexts(contexts);

It cause that liquibase will run all change-sets with context 'none' and all default change-sets (change-sets without context) - yes this is how it works.

So select the name which nobody on your team won't use ('none' in our case) as context name and then run the liquibase by default with that context (take a look at example). With that approach you will run the change-sets without any context what I assume should be default approach!

Przemek Nowak
  • 7,173
  • 3
  • 53
  • 57
4

I just mark the development changeSets as "dev" [or "test"] and don't specify a context on the changesets that run in both. When I do an update on production, I will specify contexts=prod in the update even though there are no changesets marked as prod. That will make it skip all the dev [or "test"] context ones but will still execute all the non-context-ed changeSets. You are also then set up for some point in the future where you need to make a context="prod" changeSet that ... only runs in prod.

Source: https://forum.liquibase.org/t/using-context-for-development-only-and-production-changesets/980/2

Marcel Stör
  • 22,695
  • 19
  • 92
  • 198
Johnathan J.
  • 91
  • 2
  • 6
2

And what happens if you or your admin forgets to specify a context? Yes, it will execute A+B+C, on a production it can break many of things and make your life not so happy.

I'm looking for a solution that benefits in these cases and aborts the liquibase execution at the beginning (when you are running liquibase without any contexts).

It would be cool if the liquibase has a property (in liquibase.properties) to restrict running with/without contexts...

As an solution you can add contexts=default,contexts,of,your,project to the liquibase.properties file.

dsuchka
  • 61
  • 3
1

It might be too late for @javg, but this may benefit future readers. This requirement could be achieved as follows:

changeset A: context=test
changeset B: context=all
changeset C: context=prod

So

executing update with "context=test,all" will execute changeset A+B.
executing update with "context=all,prod" will execute changeset B+C.
executing update with "context=all" will only execute changeset B as you expect.
Taoufik Mohdit
  • 1,910
  • 3
  • 26
  • 39