Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Liquibase does not rollback failed changesets with MySQL

I am using Liquibase 3.4.1 with MySQL56 and run Liquibase via Spring Boot.

I have a changeset that includes adding a column to the existing table. The new column has valueComputed attribute with a simple select.

When I made a mistake in that select the changeset failed and migration stopped at that changeset. However the new column was commited to the database but without the proper value and, what's really bad, this changeset was not marked as run! The next time I ran migration Liquibase attempted to execute the changeset again, but failed because the column had been created already.

Why didn't Liquibase rollback a transaction when the changeset failed? How can I make my changeset transactional?

EDIT: Apparently, MySQL commits after every DDL command. If I specify rollback commands in rollback tag will Liquibase run then in case of failed changeset?

like image 808
Ilya Novoseltsev Avatar asked Oct 27 '15 10:10

Ilya Novoseltsev


People also ask

Does Liquibase support rollback?

Liquibase provides commands to allow you to undo changes you have made to your database, either automatically or with a custom rollback command. The intention of a rollback script is to return the database to a previous specified point in time. Note: Rollback support is available in command line, Ant, and Maven.

Does Liquibase support MySQL?

To use Liquibase and MySQL, you need the JDBC driver JAR file (Maven download). Place your JAR file(s) in the liquibase/lib directory. Read more: Adding and Updating Liquibase Drivers. If you use Maven, you must include the driver JAR as a dependency in your pom.


1 Answers

Liquibase tries to run changeset in transaction, but I think that mysql commits after ddl (column added): https://dev.mysql.com/doc/refman/5.5/en/implicit-commit.html

I'm not sure that it is possible to do it transactional, but you may split it into two changesets and if column population fails it will be rolled back and after rerun you will not have issues (column is already added in previous changeset).

EDIT by Ilya Novoseltsev:

Changesets involving DDL changes should be made atomic.

Bad:

<changeSet author="novoseltsevib (generated)" id="1445871764871-19">
    <addColumn tableName="account_range">
        <column name="cash2card_participation" type="BIT(1)" valueBoolean="false"/>
    </addColumn>
    <addColumn tableName="account_range_aud">
        <column name="cash2card_participation" type="BIT(1)"/>
    </addColumn>
</changeSet>

Good:

<changeSet author="novoseltsevib (generated)" id="1445871764871-19">
    <addColumn tableName="account_range">
        <column name="cash2card_participation" type="BIT(1)" valueBoolean="false"/>
    </addColumn>
</changeSet>
<changeSet author="novoseltsevib (generated)" id="1445871764871-20">
    <addColumn tableName="account_range_aud">
        <column name="cash2card_participation" type="BIT(1)"/>
    </addColumn>
</changeSet>

Writing your changesets this way allows to repeat a failed changeset without worrying about changes being partially commited.

One should be careful when using value = ..., especially valueComputed. These statements break addColumn atomicity. They are executed as a separate update command after the DDL command. The proper way is to move them to their own update tag in a separate changeset.

like image 191
dbf Avatar answered Sep 25 '22 06:09

dbf