I am investigating using Liquibase for a new project using Oracle and I am wondering how I can ensure my changeSets are robust enough to recover from any types of failures without manual intervention. Ideally I would use the runInTransaction attribute, which would allow DDLs to be rolled back on failures, but Oracle auto-commits on DDLs. For this situation, the documentation recommends:
Therefore, it is usually best to have just one change per changeSet unless there is a group of non-auto-committing changes that you want applied as a transaction such as inserting data.
Having one DDL per changeSet would reduce the chances of problems, but doesn't eliminate them. If the DDL succeeds, but the update to DATABASECHANGELOG fails, from my tests it seems that Liquibase just gets stuck and manual intervention is required.
It is necessary to use preconditions on each step to avoid this problem? This makes the resulting changeSets pretty verbose. This is one of the Liquibase example table definitions:
<changeSet author="jsmith" id="1">
<createTable tableName="departments"
remarks="The departments of this company. Does not include geographical divisions.">
<column name="id" type="number(4,0)">
<constraints nullable="false" primaryKey="true"
primaryKeyName="DPT_PK"/>
</column>
<column name="dname" type="varchar2(14)"
remarks="The official department name as registered on the internal website."/>
</createTable>
<addUniqueConstraint constraintName="departments_uk1"
columnNames="dname" tableName="departments"/>
<createSequence sequenceName="departments_seq"/>
</changeSet>
To make this idempotent, I think it would have to change to something like the following:
<changeSet author="jsmith" id="1">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="departments" />
</not>
</preConditions>
<createTable tableName="departments"
remarks="The departments of this company. Does not include geographical divisions.">
<column name="id" type="number(4,0)" / column>
<column name="dname" type="varchar2(14)"
remarks="The official department name as registered on the internal website." />
</createTable>
</changeSet>
<changeSet author="jsmith" id="2">
<preConditions onFail="MARK_RAN">
<not>
<primaryKeyExists primaryKeyName="pk_departments" />
</not>
</preConditions>
<addPrimaryKey tableName="departments" columnNames="id"
constraintName="pk_departments" />
</changeSet>
<changeSet author="jsmith" id="3">
<preConditions onFail="MARK_RAN">
<not>
<uniqueConstraintExists constraintName="departments_uk1" />
</not>
</preConditions>
<addUniqueConstraint constraintName="departments_uk1"
columnNames="dname" tableName="departments" />
</changeSet>
<changeSet author="jsmith" id="4">
<preConditions onFail="MARK_RAN">
<not>
<sequenceExists sequenceName="departments_seq" />
</not>
</preConditions>
<createSequence sequenceName="departments_seq" />
</changeSet>
Is there some simpler way to achieve this? I would have thought that Liquibase would have been able to generate these preconditions.
Thanks