Zero Downtime Deployment and databases

Zero Downtime Deployment and databases

This article explains in detail how to address database compatibility issues during deployment. We'll discuss what can happen to your production applications if you attempt to deploy without proper preparation. Then, we'll walk through the application lifecycle stages necessary for achieving zero downtime (Note: hereafter — zero downtime). The result of our operations will be applying the backwards-incompatible database change in a backwards-compatible way.

If you want to go through code examples from the article, you can find them at GitHub.

Introduction

Zero downtime deployment

What is this mystical zero downtime deployment? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.

How to achieve this? There are several methods, here is one of them:

  • deploy version #1 of your service
  • perform the database migration
  • deploy version #2 of your service alongside version #1
  • once you see that version #2 is working as expected, remove version #1
  • done!

Easy, right? Unfortunately, it’s not that simple, and we will discuss this in detail later. Now, let’s check out another fairly common deployment process — blue-green deployment.

Have you ever heard of blue-green deployment? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на this article, where we describe it in more detail. To summarize briefly, let’s recall how to do a blue-green deployment:

  • ensure the operation of two copies of your production code (“blue” and “green”);
  • direct all traffic to the blue environment, i.e., so the production URLs point there;
  • deploy and test all application changes in the green environment;
  • switch the URLs from the blue to the green environment

Blue-green deployment is an approach that allows you to easily introduce new features without worrying that production will break. This is due to the fact that even if something goes wrong, you can easily roll back to the previous environment by simply 'flipping a switch.'

Having read all of the above, you may ask: What does zero downtime have to do with blue-green deployment?

Well, they have quite a lot in common, as maintaining two copies of the same environment requires double the effort to manage them. That’s why some teams, as stated by Martin Fowler, adhere to a variation of this approach:

Another option involves using the same database by creating blue-green switches for the web and domain layers. In this approach, databases often become a problem, especially when you need to change their schema to support a new version of the software.

And here we reach the main issue of this article. Database. Let's take another look at this phrase.

perform the database migration.

Now you should ask yourself the question — what if backward database changes are incompatible? Won't my first version of the application break? In fact, that's exactly what will happen...

Thus, even with the huge advantages of zero downtime / blue-green deployment, companies tend to follow the next safer process for deploying their applications:

  • prepare a package with the new version of the application
  • shut down the running application
  • run the scripts for the database migration
  • deploy and launch the new version of the application

In this article, we will detail how you can work with the database and code to take advantage of zero downtime deployment.

Database Issues

If you have a stateless application that does not store any data in the database, you can achieve zero downtime deployment right away. Unfortunately, most software needs to store data somewhere. That is why you should think twice before making any changes to the schema. Before we dive into the details of how to change the schema in a way that allows for deployment without downtime, let’s first focus on versioning schemes.

Versioning Scheme

In this article, we will use Flyway as a versioning tool (note: this refers to database migrations). Naturally, we will also write a Spring Boot application that has built-in support for Flyway and will perform the schema migration during application context setup. When using Flyway, you can store migration scripts in your project folders (by default in classpath:db/migration). Here you can see an example of such migration files

└── db
 └── migration
     ├── V1__init.sql
     ├── V2__Add_surname.sql
     ├── V3__Final_migration.sql
     └── V4__Remove_lastname.sql

In this example, we see 4 migration scenarios that, if not previously performed, will execute one after the other when the application starts. Let's take a look at one of the files (V1__init.sql) as an example.

CREATE TABLE PERSON (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
first_name varchar(255) not null,
last_name varchar(255) not null
);

insert into PERSON (first_name, last_name) values ('Dave', 'Syer');

Everything speaks for itself: you can use SQL to define how your database should be altered. For more information about Spring Boot and Flyway, see Spring Boot Docs.

By using a versioning tool with Spring Boot, you gain 2 major advantages:

  • you separate database changes from code changes
  • database migration occurs along with the rollout of your application, i.e. your deployment process becomes simpler

Troubleshooting Database Issues

In the next section of the article, we will focus on discussing two approaches to database changes.

  • backward incompatibility
  • backward compatibility

The first will be discussed as a warning that one should not perform zero downtime deployment without prior preparation… The second offers a solution for how to perform deployment without downtime while maintaining backward compatibility.

The project we will be working on will be a simple Spring Boot Flyway application that has Person with first_name and last_name in the database (Note: Person is a table, and first_name and last_name — are the fields in it). We want to rename last_name downward API support (simultaneously with this in surname.

Assumptions

Before we delve into details, a couple of assumptions regarding our applications must be stated. The main outcome we want to achieve will be a fairly simple process.

Note. Business PRO-TIP. Simplifying processes can save you a lot of money on support (the more people work in your company, the more money you can save)!

No need to roll back the database

This simplifies the deployment process (some database rollbacks are virtually impossible, such as rolling back deletions). We prefer to only roll back applications. Thus, even if you have different databases (like SQL and NoSQL), your deployment pipeline will look the same.

There should ALWAYS be an option to roll back the application to one previous version (no more)

Rollback should only be performed when necessary. If there is a bug in the current version that is difficult to fix, we should be able to revert to the last stable version. We assume that this last stable version is the previous one. Maintaining compatibility for code and database across more than one release would be extremely challenging and costly.

Note. For better readability, in this article we will change the major version of the application.

Step 1: Initial State

Application Version: 1.0.0
Database Version: v1

Comment

This will be the initial state of the application.

Database Changes

The database contains last_name.

CREATE TABLE PERSON (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY,
    first_name varchar(255) not null,
    last_name varchar(255) not null
);

insert into PERSON (first_name, last_name) values ('Dave', 'Syer');

Code Changes

The application saves Person data in last_name:

/*
 * Copyright 2012-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package sample.flyway;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return this.lastName;
    }

    public void setLastName(String lastname) {
        this.lastName = lastname;
    }

    @Override
    public String toString() {
        return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName
                + "]";
    }
}

Backward-incompatible column renaming

Let's consider an example of how to change a column name:

Attention. The following example is intentionally designed to lead to a failure. We are demonstrating this to show the database compatibility issue.

Application Version: 2.0.0.BAD

Database Version: v2bad

Comment

The current changes DO NOT allow us to run two instances (old and new) simultaneously. Therefore, zero downtime deployment will be hard to achieve (given the assumptions, it is virtually impossible).

A/B testing

The current situation is that we have an application version 1.0.0, deployed in production, and the database v1. We need to deploy a second instance of the application, version 2.0.0.BAD, and update the database to v2bad.

Steps:

  1. a new instance of application version 2.0.0.BAD, which updates the database to v2bad
  2. in the database v2bad the column last_name no longer exists — it has been changed to surname
  3. the database and application update was successful, and some instances are running in 1.0.0, others in 2.0.0.BAD. All are connected to the database v2bad
  4. all instances of version 1.0.0 will start throwing errors because they will attempt to insert data into the column last_name, which no longer exists
  5. all instances of version 2.0.0.BAD will work without issues

As you can see, if we make backward-incompatible changes to the database and application, A/B testing becomes impossible.

Application Rollback

Let's assume that after attempting an A/B deployment (note: likely, the author meant A/B testing) we decided that we need to roll back the application to version 1.0.0. Let's say we do not want to roll back the database.

Steps:

  1. we are stopping the application instance of version 2.0.0.BAD
  2. the database is still v2bad
  3. since the version 1.0.0 does not understand what surname, we will see errors
  4. the hell has broken loose, we can no longer return

As you can see, if we make backward incompatible changes to the database and application, we cannot revert to the previous version.

Execution logs of the script

Backward incompatible scenario:

01) Run 1.0.0
02) Wait for the app (1.0.0) to boot
03) Generate a person by calling POST localhost:9991/person to version 1.0.0
04) Run 2.0.0.BAD
05) Wait for the app (2.0.0.BAD) to boot
06) Generate a person by calling POST localhost:9991/person to version 1.0.0 <-- this should fail
07) Generate a person by calling POST localhost:9992/person to version 2.0.0.BAD <-- this should pass

Starting app in version 1.0.0
Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}

Starting app in version 2.0.0.BAD
Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

curl: (22) The requested URL returned error: 500 Internal Server Error

Generate a person in version 2.0.0.BAD
Sending a post to 127.0.0.1:9995/person. This is the response:

{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}

Database Changes

Migration script that renames last_name downward API support (simultaneously with this in surname

Original Flyway script:

CREATE TABLE PERSON (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY,
    first_name varchar(255) not null,
    last_name varchar(255) not null
);

insert into PERSON (first_name, last_name) values ('Dave', 'Syer');

Script that renames last_name.

-- This change is backward incompatible - you can't do A/B testing
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;

Code Changes

We changed the field name lastName to surname.

Renaming the column in a backward-compatible way

This is the most common scenario we may encounter. We need to make backward incompatible changes. We have already proven that to deploy without downtime, we should not just apply the database migration without additional actions. In this section of the article, we will perform 3 deployments of the application along with database migrations to achieve the desired result while maintaining backward compatibility.

Note. Let's remember that we have a database version v1. It contains the columns first_name and last_name. We have to change last_name to surname. We also have an application version 1.0.0, that does not yet use surname.

Step 2: Adding surname

Application Version: 2.0.0
Database Version: v2

Comment

By adding a new column and copying its content, we are making backward-compatible changes to the database. At the same time, if we roll back the JAR or have a working old JAR, it will not break during execution.

Deploying a new version

Steps:

  1. perform the database migration to create the new column surname. Now your database version v2
  2. copy data from last_name downward API support (simultaneously with this in surname. Note that, if you have a lot of this data, you should consider batch migration!
  3. write the code where you use BOTH and new, and old column. Now your application version 2.0.0
  4. read the value from the column surname, if it is not null, or from last_name, if surname not specified. You may remove getLastName() from the code, as it will return null when rolling back your application with 3.0.0 up to 2.0.0.

If you are using Spring Boot Flyway, these two steps will be executed during the application version startup. 2.0.0 If you are running the database versioning tool manually, you will have to perform these two actions separately (first, update the db version manually, and then deploy the new application).

It is important. Remember that the newly created column MUST NOT be NOT NULL. If you perform a rollback, the old application does not know about the new column and will not set it during Insert. But if you add this constraint, and your DB is v2, this will require setting a value for the new column. This will lead to constraint violations.

It is important. You should remove the method getLastName(), as in version 3.0.0 the concept of column does not exist in the code. last_nameThis means that nulls will be set there. You can keep the method and add checks for null, but a much better solution would be to ensure that in the logic getSurname() you have chosen the correct non-null value.

A/B testing

The current situation is that we have an application version 1.0.0, deployed in production, and the DB in v1. We need to deploy the second instance of versioned application 2.0.0, which will update the database to v2.

Steps:

  1. a new instance of application version 2.0.0, which updates the database to v2
  2. meanwhile, some requests were processed by instances of version 1.0.0
  3. the update was successful, and you have several working instances of the versioned application 1.0.0 and other versions 2.0.0. All communicate with the DB in v2
  4. version 1.0.0 does not use the surname column in the DB, while the version 2.0.0 does use it. They do not interfere with each other, and there should be no errors.
  5. version 2.0.0 stores data in both the old and new columns, ensuring backward compatibility.

It is important. If you have any queries that count items based on values from the old/new column, you should remember that now you have duplicate values (likely still migrating). For example, if you want to count users whose last name (whatever the column is called) starts with the letter A, then until the data migration is complete (old → new column) you may have inconsistent data if you query the new column.

Application Rollback

Now we have versioned application 2.0.0 and the database in v2.

Steps:

  1. rollback your application to version 1.0.0.
  2. version 1.0.0 does not use the surname column in the DB. surname, so the rollback should be successful

DB changes

The database contains a column named last_name.

Original Flyway script:

CREATE TABLE PERSON (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY,
    first_name varchar(255) not null,
    last_name varchar(255) not null
);

insert into PERSON (first_name, last_name) values ('Dave', 'Syer');

Add script surname.

Attention. Remember, you MUST NOT ADD any NOT NULL constraints to the added column. If you roll back the JAR, the old version will not know about the added column and will set it to NULL automatically. If such a constraint exists, the old application will simply break.

-- NOTE: This field can't have the NOT NULL constraint cause if you rollback, the old version won't know about this field
-- and will always set it to NULL
ALTER TABLE PERSON ADD surname varchar(255);

-- WE'RE ASSUMING THAT IT'S A FAST MIGRATION - OTHERWISE WE WOULD HAVE TO MIGRATE IN BATCHES
UPDATE PERSON SET PERSON.surname = PERSON.last_name

Code Changes

We store data both in last_name, and in surname. While reading from last_name, since this column is the most relevant. During the deployment process, some requests may have been processed by an instance of the application that has not yet been updated.

/*
 * Copyright 2012-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package sample.flyway;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;
    private String surname;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * Reading from the new column if it's set. If not the from the old one.
     *
     * When migrating from version 1.0.0 -> 2.0.0 this can lead to a possibility that some data in
     * the surname column is not up to date (during the migration process lastName could have been updated).
     * In this case one can run yet another migration script after all applications have been deployed in the
     * new version to ensure that the surname field is updated.
     *
     * However it makes sense since when looking at the migration from 2.0.0 -> 3.0.0. In 3.0.0 we no longer
     * have a notion of lastName at all - so we don't update that column. If we rollback from 3.0.0 -> 2.0.0 if we
     * would be reading from lastName, then we would have very old data (since not a single datum was inserted
     * to lastName in version 3.0.0).
     */
    public String getSurname() {
        return this.surname != null ? this.surname : this.lastName;
    }

    /**
     * Storing both FIRST_NAME and SURNAME entries
     */
    public void setSurname(String surname) {
        this.lastName = surname;
        this.surname = surname;
    }

    @Override
    public String toString() {
        return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName + ", surname=" + this.surname
                + "]";
    }
}

Step 3: Removing last_name from the code

Application Version: 3.0.0

Database Version:v3

Comment

Note: Apparently, in the original article, the author mistakenly copied this block's text from step 2. At this step, changes should be made to the application code aimed at removing functionality that uses the column last_name.

By adding a new column and copying its contents, we created backward-compatible changes to the DB. Also, if we roll back the JAR or have a working old JAR, it will not break during execution.

Application Rollback

Currently, we have an application version 3.0.0 and a database v3. Version 3.0.0 does not store data in last_name. This means that in surname the most up-to-date information is stored.

Steps:

  1. rollback your application to version 2.0.0.
  2. version 2.0.0 uses and last_name and surname.
  3. version 2.0.0 will take surname, if it is not null, otherwise —last_name

Database Changes

There are no structural changes in the DB. The following script is executed, which performs the final migration of old data:

-- WE'RE ASSUMING THAT IT'S A FAST MIGRATION - OTHERWISE WE WOULD HAVE TO MIGRATE IN BATCHES
-- ALSO WE'RE NOT CHECKING IF WE'RE NOT OVERRIDING EXISTING ENTRIES. WE WOULD HAVE TO COMPARE
-- ENTRY VERSIONS TO ENSURE THAT IF THERE IS ALREADY AN ENTRY WITH A HIGHER VERSION NUMBER
-- WE WILL NOT OVERRIDE IT.
UPDATE PERSON SET PERSON.surname = PERSON.last_name;

-- DROPPING THE NOT NULL CONSTRAINT; OTHERWISE YOU WILL TRY TO INSERT NULL VALUE OF THE LAST_NAME
-- WITH A NOT_NULL CONSTRAINT.
ALTER TABLE PERSON MODIFY COLUMN last_name varchar(255) NULL DEFAULT NULL;

Code Changes

Note: The description of this block was also mistakenly copied by the author from step 2. According to the narrative logic of the article, the changes in the code at this step should be aimed at removing elements that work with the column last_name.

We store data both in last_name, and in surname. In addition, we read from the column last_name, since it is the most relevant. During deployment, some requests may be handled by an instance that has not yet been updated.

/*
 * Copyright 2012-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package sample.flyway;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String surname;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getSurname() {
        return this.surname;
    }

    public void setSurname(String lastname) {
        this.surname = lastname;
    }

    @Override
    public String toString() {
        return "Person [firstName=" + this.firstName + ", surname=" + this.surname
                + "]";
    }
}

Step 4: Remove last_name from the database

Application Version: 4.0.0

Database Version: v4

Comment

Because the version code 3.0.0 did not use the column last_name, nothing bad will happen during execution if we roll back to 3.0.0 after removing the column from the database.

Execution logs of the script

We will do it in the following way:

01) Run 1.0.0
02) Wait for the app (1.0.0) to boot
03) Generate a person by calling POST localhost:9991/person to version 1.0.0
04) Run 2.0.0
05) Wait for the app (2.0.0) to boot
06) Generate a person by calling POST localhost:9991/person to version 1.0.0
07) Generate a person by calling POST localhost:9992/person to version 2.0.0
08) Kill app (1.0.0)
09) Run 3.0.0
10) Wait for the app (3.0.0) to boot
11) Generate a person by calling POST localhost:9992/person to version 2.0.0
12) Generate a person by calling POST localhost:9993/person to version 3.0.0
13) Kill app (3.0.0)
14) Run 4.0.0
15) Wait for the app (4.0.0) to boot
16) Generate a person by calling POST localhost:9993/person to version 3.0.0
17) Generate a person by calling POST localhost:9994/person to version 4.0.0

Starting app in version 1.0.0
Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}

Starting app in version 2.0.0

Generate a person in version 1.0.0
Sending a post to 127.0.0.1:9991/person. This is the response:

{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}

Generate a person in version 2.0.0
Sending a post to 127.0.0.1:9992/person. This is the response:

{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}

Killing app 1.0.0

Starting app in version 3.0.0

Generate a person in version 2.0.0
Sending a post to 127.0.0.1:9992/person. This is the response:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}

Generate a person in version 3.0.0
Sending a post to 127.0.0.1:9993/person. This is the response:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}

Killing app 2.0.0

Starting app in version 4.0.0

Generate a person in version 3.0.0
Sending a post to 127.0.0.1:9993/person. This is the response:

{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}

Generate a person in version 4.0.0
Sending a post to 127.0.0.1:9994/person. This is the response:

{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}

Database changes

Regarding v3 we simply remove the column last_name and add the missing constraints.

-- REMOVE THE COLUMN
ALTER TABLE PERSON DROP last_name;

-- ADD CONSTRAINTS
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;

Code Changes

No changes in the code.

Output

We successfully applied a backward-incompatible column name change by performing several backward-compatible deployments. Below is a summary of the actions taken:

  1. deploying the application version 1.0.0 with v1 database schema (column name = last_name)
  2. deploying the application version 2.0.0, which stores the data in last_name and surname. The application reads from last_name. The database is at version v2, containing columns like last_nameand surname. surname is a copy of last_name. (NOTE: this column should not have a not null constraint)
  3. deploying the application version 3.0.0, which stores data only in surname and reads from surname. As for the database, the latest migration is happening last_name downward API support (simultaneously with this in surname. The constraint is also NOT NULL removed from last_name. The database is now at version v3
  4. deploying the application version 4.0.0 — no changes are made in the code. The database deployment v4, which removes last_name. Here you can add any missing constraints to the database.

By following this approach, you can always roll back one version without breaking database/application compatibility.

Code

All the code used in this article is available at Github. Below is additional description.

Projects

After cloning the repository, you will see the following folder structure.

├── boot-flyway-v1              - 1.0.0 version of the app with v1 of the schema
├── boot-flyway-v2              - 2.0.0 version of the app with v2 of the schema (backward-compatible - app can be rolled back)
├── boot-flyway-v2-bad          - 2.0.0.BAD version of the app with v2bad of the schema (backward-incompatible - app cannot be rolled back)
├── boot-flyway-v3              - 3.0.0 version of the app with v3 of the schema (app can be rolled back)
└── boot-flyway-v4              - 4.0.0 version of the app with v4 of the schema (app can be rolled back)

Scripts

You can run the scripts described below, which will demonstrate backward-compatible and incompatible changes in the database.

To see the case of backward-compatible changes, run:

./scripts/scenario_backward_compatible.sh

And to see the case of backward-incompatible changes, run:

./scripts/scenario_backward_incompatible.sh

Spring Boot Sample Flyway

All examples are taken from Spring Boot Sample Flyway.

You can take a look at http://localhost:8080/flyway, there is a list of scripts.

This example also includes an H2 console (at http://localhost:8080/h2-console), so you can view the state of the database (default jdbc URL is jdbc:h2:mem:testdb).

Additional

Also, read other articles in our blog:

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster