Upgrading 21-Points Health from v1 to v4

When I wrote about release v4.0 of the JHipster Mini-Book, I said I planned to update 21-Points Health in the near future. I started looking into the process Tuesday morning, and I'm happy to say I finished updating everything by early Wednesday morning.

To give you some background, the codebase on www.21-points.com was running AngularJS + Spring Boot from the first version I wrote with JHipster 2.x (October 2015). When I wrote the 2nd edition of the JHipster Mini-Book, I changed the entity relationships so the preferences table contained a user_id column instead of adding a preferences_id to the jhi_user table. I never deployed that codebase because everything was running smoothly in production.

My strategy to upgrade from the exiting code/database to v4 was fairly simple:

  1. Export the production database to a local instance.
  2. Figure out what needed to change between the new schema and the old one.
  3. Use Liquibase to make the necessary changes.
  4. Deploy the new codebase, migrating the database on startup.

To export the production database to a local database, I started with Heroku's import/export docs for PostgreSQL. I found pg:pull and went to work.

heroku pg:pull HEROKU_POSTGRESQL_GRAY_URL health --app health-by-points

This created a local health database that contained my production schema and data. I ended up dropping and re-importing the production database several times. This Heroku feature came in very handy!

Next, I downloaded Liquibase and installed it in /opt/tools. I ran the following command to copy the PostgreSQL JDBC driver from my local Maven repo to Liquibase's classpath.

[mraible:/opt/tools/liquibase-3.5.3-bin] % cp ~/.m2/repository/org/postgresql/postgresql/9.4.1212/postgresql-9.4.1212.jar lib/.

I set up a new local database with 21-Points v4's configuration in my application-prod.yml:

datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:postgresql://localhost:5432/twentyonepoints
    username: twentyonepoints
    password: 21points

I ran Liquibase to generate an XML changelog for the differences between the two databases:

./liquibase \
--driver=org.postgresql.Driver --url=jdbc:postgresql://localhost:5432/health --username=postgres \
diffChangeLog \
--referenceUrl=jdbc:postgresql://localhost:5432/twentyonepoints \
--referenceUsername=twentyonepoints --referencePassword=21points

I copied the results to a ${date}-migrate_from_v1.xml file in src/main/resources/config/liquibase/changelog and added a reference to it in src/main/resources/config/liquibase/master.xml. I updated application-prod.xml to point to the production database that I downloaded from Heroku.

I started 21-Points with ./gradlew -Pprod and watched everything fail:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in 
  class path resource [org/jhipster/health/config/DatabaseConfiguration.class]: Invocation of init method failed; 
  nested exception is liquibase.exception.ValidationFailedException: Validation Failed:
 2 change sets check sum
      config/liquibase/changelog/00000000000000_initial_schema.xml::00000000000000::jhipster 
        was: 7:eda8cd7fd15284e6128be97bd8edea82 but is now: 7:a6235f40597a13436aa36c6d61db2269
      config/liquibase/changelog/00000000000000_initial_schema.xml::00000000000001::jhipster 
        was: 7:e87abbb935c561251405aea5c91bc3a4 but is now: 7:29cf7a7467c2961e7a2366c4347704d7

I posted my issue to Stack Overflow and kept trying to solve it. Googling the error led me to a SQL statement I could use to clear the checksums:

update databasechangelog set md5sum=null;

I ran this on a fresh pull and started my app again. This time it failed when trying to create the first table.

2017-10-24 12:12:02.670 ERROR 22960 --- [           main] liquibase                                : classpath:config/liquibase/master.xml:
 config/liquibase/changelog/20160831020048_added_entity_Points.xml::20160831020048-1::jhipster: Change Set config/liquibase/changelog/20160831020048_added_entity_Points.xml::
 20160831020048-1::jhipster failed.  Error: ERROR: relation "points" already exists [Failed SQL: CREATE TABLE public.points 

To get past this, I found I had to add preconditions to many of my existing Liquibase files to prevent tables and foreign constraints from being created. You can see all the changes I needed to make in PR #27 on GitHub. Migrating from the old database to the new one was powered by Liquibase and worked great!

Once I had everything migrating the database successfully, I grabbed a cold Coors out of the fridge, and decided it was time to go to production. I asked Trish if she thought it was a good idea. She was honest: "No". I went for it anyway.

I used Heroku's Dashboard to turn on maintance mode, ran the heroku-deploy.sh script, and started everything back up. It worked! 🤗

This was a great experience and I learned a bunch by doing it. For your viewing pleasure, here's some screenshots of the new version.

v4 Index v4 Login

v4 Home

v4 History

For comparison's sake, here's the same screens from v1.

v1 Index v1 Login

v1 Home

v1 History

I hope you enjoyed this story about migrating a JHipster app from v2.x to v4.x in production. If you have similar migration stories, I'd love to hear about them!


← Back to Home All Posts