db migration툴이다.
db의 변경사항을 추적해 업데이트를 도와준다. 유료버전이라면 롤백도 지원해준다.
shiny라는 이름의 소프트웨어가 있다고 가정하면 개발 과정에서 이에 대한 버전관리는 git같은 도구로 잘 되고 있지만 db의 버전관리는 보통 그만큼 잘 이뤄지지 않는다.
이런 db의 버전관리를 자체적인 문서, 직접 sql문을 서버에 들어가서 실행하는 건 실수의 여지가 많은 작업이다.
이를 툴로써 보조하면 더 안정적인 마이그레이션이 가능하다.
우리 프로젝트에서도 위와 같은 이유로 flyway적용을 하기로 했다.
현재 개발과정에서 개발서버, 운영서버의 db스키마가 바뀔 경우가 있다.
스키마의 변경이 있는 변경사항을 배포할 때 매번 그 db스키마 변경을 인지해 직접 서버에 들어가 alter문으로 변경하는 걸 놓치는 경우가 있었다.
db의 변화를 안정적으로 관리하기위해 flyway를 적용하기로 했다.
flyway를 적용하면 flyway_history테이블에 그 시점의 테이블 스키마에 대한 메타데이터가 기록된다.
이후 flyway는 사용자가 정의한 sql파일을 scan해 migration의 버전과 작업을 인지한다.
이 migration작업의 버전이 이미 적용된 것 이하이면 무시한다.
새로운 버전이면 migration을 진행해 db스키마를 변경하고 history테이블에 새로운 메타데이터를 추가한다.
main/resources/db/migration
경로에 최초의 스키마를 등록하겠다.
//V20230901153300__init.sql
create table if not exists `yozm-cafe`.cafe
(
like_count int default 0 not null,
id bigint auto_increment primary key,
address varchar(50) not null,
description text null,
map_url varchar(512) not null,
name varchar(20) not null,
phone varchar(20) null
);
create table if not exists `yozm-cafe`.cafe_available_times
(
is_opened bit not null,
cafe_id bigint not null,
close time(6) null,
open time(6) null,
day enum ('FRIDAY', 'MONDAY', 'SATURDAY', 'SUNDAY', 'THURSDAY', 'TUESDAY', 'WEDNESDAY') not null,
constraint cafe_available_times_CAFE_ID
foreign key (cafe_id) references cafe (id)
);
...
V20230901153300__init.sql
라는 이름으로 sql파일을 정해진 경로에 생성했다.
이때 파일이름은 매우 중요한 부분이다.
정해진 컨벤션에 따라 이름이 지어지지 않으면 작업을 할 수 없다.
위와 같은 형식으로 해야할 작업에 따라 다른 prefix를 붙여 작성해야한다.
특히 seperator가 __ 언더바 두개 인 것을 인지하자.
우리 프로젝트에서 이름을 20230901153300와 같이 timestamp로 한 것 또한 이유가 있다. 아래에서 설명하겠다.
V1, V2처럼 단순 순차적인 이름으로 버전을 정하면 동시에 작업중인 여러 기능들에서
같은 버전의 sql파일을 생성할 수도 있다. 즉 버전이름이 unique하지 못해질 수 있다는 것이다.
이 경우 migration작업이 의도치 않게 무시될 수 있다.
예를 들어
1.V1__init.sql
2.V2__add_like.sql
3.V2__add_menu.sql
2번 sql파일을 포함한 pr이 먼저 merge가 되고, 3번이 merge될 경우 V2의 migration은 이미 진행했기에 무시될 수 있다.
그렇다고 여러 pr(pull request)에서 매번 스키마 변경이 있는지, 각자 버전을 어떻게 해야하는 지 소통하는 것은 소모적인 일이다.
이 때문에 timestamp로 서로 버전이 겹치는 일이 없게 이름을 정했다.
다만 그래도 문제점이 있다.
경우에 따라 먼저 올라온, timestamp가 빠른 migration작업이 있는 pr이더라도 이것의 배포 작업은 다른 pr보다 더 나중에 할 수도 있다.
1.V20230901153300__init.sql
2.V20230901154000__add_like.sql
3.V20230901153700__add_menu.sql
2번 sql파일을 포함한 pr이 먼저 배포된 경우 3번의 migration작업에 문제가 생길 것이다. 이전 버전으로 취급되어 migration작업이 무시될 수 있다.
이를 어떻게 해결해야할까?
flyway는 out-of-order라는 옵션을 제공한다. 이 옵션을 통해 migration작업을 버전의 순서에 상관없이 수행하도록 설정할 수 있다.
https://documentation.red-gate.com/fd/out-of-order-184127522.html
이 경우 순서에 상관없이 migration작업이 진행되기에 순차적으로 버전 관리를 하는 의미가 퇴색될 수 있다. 때문에 차라리 out-of-order를 적용하지 않고 배포에 따라 timestamp를 조정하도록 팀 내에서 합의할 수도 있겠다.
결과적으로 application.properties에서의 flyway설정은 이렇게 된다.
그런데 궁금한 점이 있다.
out-of-order옵션을 통해 순서에 상관없이 migration하는데,
만약 새로운 버전이 baseline보다도 이전 버전이면 이 migration은 수행될까?(물론 이런 상황은 baseline에 대한 인지 없이 버전명을 잘못 작성한 예외적인 상황이다.)
history테이블까지 정상반영된다.
validation에 걸린다. 즉 baseline이 정상동작했다.
정상적으로 out-of-order도 동작한다.
즉, baseline과 out-of-order옵션을 같이 걸면, baseline validation을 우선적으로 한다.
flyway로 db스키마를 관리할 수 있다.
버전명이 겹치지 않게 timestamp를 이름으로 이용하는 걸 추천한다. timestamp순서에 상관없이 migrate 되게 하려면 out-of-order옵션을 활용하자.
baseline과 out-of-order를 같이 사용하면 baseline보다 이후 버전일 경우에만 순서에 상관없이 migration한다.
-내 생각: 보안 상 db스키마,인프라를 개발자에게 알려주지 않고 일부 dba가 따로 관리하는 회사하면 flyway를 사용하지는 못하겠다. DB에 대한 담당 관리인력이 있다면 기능개발 pr에서 스키마 변경까지 일어나는 걸 원치 않을 것 같다. 하지만 그런 상황이 아니고 개발자들이 db스키마를 이미 알고 있다면 db형상관리 툴을 이용하는 장점이 크다고 생각한다.
https://documentation.red-gate.com/fd/quickstart-how-flyway-works-184127223.html
https://www.baeldung.com/flyway-migrations
https://www.baeldung.com/flyway-migrations
https://documentation.red-gate.com/fd/out-of-order-184127522.html
https://dev.to/kirekov/flyway-migrations-naming-strategy-in-a-big-project-51fp