Database Schema Evolution
xpanse uses Liquibase framework to manage the database schema evolution of xpanse.
Use of hdm2ddl library
Spring Boot uses Hibernate framework's hdm2ddl which does the following
- Automatically generate DDL statements based on Entity classes available in the classpath.
- Execute DDL statements automatically at the server start.
This feature is used in xpanse by default for development purposes and also additionally to prepare a database with all necessary database objects automatically and then use it for generating Liquibase SQL change scripts for the respective release.
We use hdm2ddl for schema evolution in order to simplify the process of generating SQL scripts. The developer need not write SQL scripts. Instead, we let hdm2ddl to generate the scripts and prepare the release preparation database and then use Liquibase's feature to auto generate DDL SQL statements based on the database structure.
The schema generation process isn't fully automated to ensure the changes are verified by the developer before the release is done.
Even though we use Hibernate and Liquibase to generate changes, the developer must still validate the changes generated. This is also the recommended best practice from Liquibase.
Release preparation
When a xpanse release is planned, the developer must prepare the repository with the necessary change scripts using the steps below. The scripts must be pushed and merged to the main branch and only then the release job must be executed. s
There are always two sets of changes to be prepared for each release of xpanse.
- Full Release Change Log - This is used when the xpanse release will be installed on a fresh and empty database.
- Update change log - This contains only the difference between the new release and the previous releases.
Even though, H2 database is used for development purposes, we also deliver SQL scripts for it just for completeness.
Prepare Full Release Snapshot
MySql
- Clone the forked xpanse repository. Ensure the fork is fully in sync with the upstream.
- Checkout a feature branch from
main
branch. - Start a new MySql database container.
- Start xpanse with
mysql
profile. Let hbm2ddl create all necessary database objects. - Run Liquibase's maven plugin to generate the full release snapshot of the database schema.
- Rename the generated file name.
# Start MySql Container
docker run --name mysql-db -p 3306:3306 -e MYSQL_PASSWORD=Xpanse@2023 -e MYSQL_ROOT_PASSWORD=Xpanse@2023 -e MYSQL_DATABASE=xpanse -e MYSQL_USER=xpanse -d mysql:latest
# Start Application with mysql spring profile
# Run Liquibase maven plugin
cd modules/database
mvn liquibase:generateChangeLog -Dliquibase.properties.file=src/main/resources/liquibase/liquibase.mysql.properties
# Rename the generated src/main/resources/liquibase/full/mysql/xpanse-REPLACE_NEW_RELEASE-full.mysql.sql file name.
# Replace REPLACE_NEW_RELEASE value in the file name with the next planned release value.
H2
- Clone the forked xpanse repository. Ensure the fork is fully in sync with the upstream.
- Checkout a feature branch from
main
branch. - Delete existing H2 DB files from you local file system.
- Start xpanse with default profiles which enables H2 database. Let hbm2ddl create all necessary database objects.
- Stop the application. Otherwise, Liquibase can't connect concurrently to the H2 database files while the application is also running.
- Run Liquibase's maven plugin to generate the full release snapshot of the database schema.
- Rename the generated file name.
# Start Application with default profiles.
# Run Liquibase maven plugin
cd modules/database
mvn liquibase:generateChangeLog -Dliquibase.properties.file=src/main/resources/liquibase/liquibase.h2.properties
# Rename the generated src/main/resources/liquibase/full/h2/xpanse-REPLACE_NEW_RELEASE-full.h2.sql file name.
# Replace REPLACE_NEW_RELEASE value in the file name with the next planned release value.
Prepare upgrade to a release
This step is necessary to generate the changes necessary to upgrade a database running on a previous version of the schema to the new version of the schema.
MySql
Step 1 - Prepare the last released DB Schema. This represents the 'is' state to the schema.
- Clone the xpanse upstream repository and checkout GIT
tag
of the previous released version. - Start a new MySql database container with port
- Start xpanse with
mysql
profile. Let Hibernate create all necessary database objects. - Stop the application.
# Clone the upstream repository and checkout the tag.
# Forked repositories will not work.
git clone https://github.com/eclipse-xpanse/xpanse.git
git checkout $vX.X.X
# Start MySql Container
docker run --name mysql-db-current -p 3306:3306 -e MYSQL_PASSWORD=Xpanse@2023 -e MYSQL_ROOT_PASSWORD=Xpanse@2023 -e MYSQL_DATABASE=xpanse -e MYSQL_USER=xpanse -d mysql:latest
# Start Application with mysql spring profile
Step 2 - Prepare the Reference DB Schema. This represents the 'to-be' state of the schema.
- Clone the forked xpanse repository. Ensure the fork is fully in sync with the upstream.
- Checkout a feature branch from the
main
branch. - Start a new MySql database container with another name and port.
- Start xpanse with
mysql
profile and use the new database. Let Hibernate create all necessary database objects. - Stop the application.
# Start MySql Container
docker run --name mysql-db-latest -p 3307:3306 -e MYSQL_PASSWORD=Xpanse@2023 -e MYSQL_ROOT_PASSWORD=Xpanse@2023 -e MYSQL_DATABASE=xpanse -e MYSQL_USER=xpanse -d mysql:latest
# update the application.mysql.properties file -
spring.datasource.url=jdbc:mysql://localhost:3307/xpanse
# Start Application with mysql spring profile
Step 3 - Use Liquibase to compare and these two databases and generate the difference change log.
- Run the maven plugin command with the goal specified below.
- Verify the changes generated by Liquibase.
- Rename the file with correct release numbers.
# Run Liquibase maven plugin
cd modules/database
mvn liquibase:diff -Dliquibase.properties.file=src/main/resources/liquibase/liquibase.mysql.properties
# Rename the generated xpanse-upgrade-REPLACE_OLD_RELEASE-to-REPLACE_NEW_RELEASE.mysql.sql file name.
# Replace REPLACE_NEW_RELEASE value in the file name with the next planned release value.
# Replace REPLACE_OLD_RELEASE value in the file name with the previous released version.
H2
Step 1 - Prepare the last released DB Schema. This represents the 'is' state to the schema.
- Clone the xpanse upstream repository and checkout GIT
tag
of the previous released version. - Delete all existing H2 database files.
- Start xpanse with default profiles. Let Hibernate create all necessary database objects.
- Stop the application.
# checkout git tag
spring.datasource.url=jdbc:h2:file:./xpanse-new;DB_CLOSE_ON_EXIT=FALSE
Step 2 - Prepare the Reference DB Schema. This represents the 'to-be' state of the schema.
- Clone the forked xpanse repository. Ensure the fork is fully in sync with the upstream.
- Checkout a feature branch from
main
branch. - Update spring.datasource.url to use another unique database file.
- Start xpanse with default profiles. Let Hibernate create all necessary database objects.
- Stop the application.
# update the application.properties file -
spring.datasource.url=jdbc:h2:file:./xpanse-new;DB_CLOSE_ON_EXIT=FALSE
Step 3 - Use Liquibase to compare and these two databases and generate the difference change log.
- Run the maven plugin.
- Verify the changes generated by Liquibase.
- Rename the file with correct release numbers.
# Run Liquibase maven plugin
cd modules/database
mvn liquibase:diff -Dliquibase.properties.file=src/main/resources/liquibase/liquibase.h2.properties
# Rename the generated xpanse-upgrade-REPLACE_OLD_RELEASE-to-REPLACE_NEW_RELEASE.h2.sql file name.
# Replace REPLACE_NEW_RELEASE value in the file name with the next planned release value.
# Replace REPLACE_OLD_RELEASE value in the file name with the previous released version.
The steps mentioned above isn't only valid for upgrading just previous release to new release. Same steps can be used to upgrade from any of the previous releases to new release.
Just the tag to be checked out must be changed based on the requirement.
Merging Change Log Files
Once the change log is generated and verified, a PR must be opened to merge these files into the main branch. Only after this merge is completed, the new release can be done.
Installing or Upgrading Database
The below maven command must be updated with the correct change log file and then can be used to execute the upgrade scripts on the target database.
mvn liquibase:update \
-Dliquibase.properties.file=src/main/resources/liquibase/liquibase.mysql.properties \
-Dliquibase.changeLogFile=src/main/resources/liquibase/full/mysql/xpanse-upgrade-v1.0.0-to-v1.0.1.mysql.xml
Most important parameter is the changeLogFile. This must be doubled checked before executing the command. If we plan to execute multiple change log files, then the current sequence of change log files matter.
Design Decisions
- Liquibase is available since many years and has better community support.
- XML change log used - This is the oldest format in Liquibase and also most stable one. Found some issues with SQL formatted change logs.
Known Issues
- If the diff generates a change in data type with the modifyDataType tag, then all constraints on such column is lost. We must add the constraints back to the change log script. This is a MySQL only limitation.