Skip to content

hotfix: 운영 notification FCM 컬럼 누락 복구 (main)#2310

Closed
taejinn wants to merge 1 commit into
mainfrom
hotfix/2309-notification-fcm-columns
Closed

hotfix: 운영 notification FCM 컬럼 누락 복구 (main)#2310
taejinn wants to merge 1 commit into
mainfrom
hotfix/2309-notification-fcm-columns

Conversation

@taejinn

@taejinn taejinn commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

⚡ 간단 요약

  • 문제: develop(stage)은 V236 적용 후 baseline으로 전환됐지만, production은 V236 없이 baseline으로 전환되어 notification의 FCM 컬럼 3개가 누락됐고 애플리케이션이 기동하지 못했습니다.
  • 해결: 누락된 컬럼만 추가하고 이미 존재하는 환경에서는 아무 작업도 하지 않는 repeatable migration으로 production 스키마를 복구합니다.

🔍 문제 발생 배경

  • FCM 전송 결과를 저장하기 위해 #2283에서 notification 테이블에 다음 컬럼을 추가하는 V236 migration이 반영되었습니다.
    • is_push_success
    • fcm_error_code
    • fcm_messaging_error_code
  • 당시 stage DB는 V236까지 적용됐지만, production DB는 이전 배포 상태인 V235에 머물러 있었습니다.
  • 이후 #2290에서 stage 스키마를 기준으로 V1__baseline_schema.sql을 생성하고 기존 V1–V236 migration을 실행 경로에서 제외했습니다. 따라서 새 V1 파일에는 세 컬럼이 포함되어 있지만, production의 실제 테이블에는 아직 존재하지 않는 상태가 됐습니다.
  • release: 2026.07.21 배포 #2308 배포를 위해 production의 Flyway history를 V1 BASELINE으로 전환했을 때, Flyway는 비어 있지 않은 DB에서 V1 SQL을 실행하지 않고 BASELINE 기록만 생성했습니다.
  • 그 결과 새 애플리케이션은 Flyway 이후 Hibernate schema validation 단계에서 missing column [fcm_error_code] in table [notification] 오류로 기동에 실패했습니다.

🚨 원인 정리

이 문제는 V1 baseline 파일의 컬럼 정의가 잘못된 것이 아니라, baseline 생성 기준이었던 stage와 전환 대상인 production의 migration 적용 상태가 달랐던 것이 원인입니다.

  1. stage: 기존 V236 적용 완료 → FCM 컬럼 존재
  2. production: 기존 V235 → FCM 컬럼 미존재
  3. 새 V1 baseline: stage 스키마를 반영하므로 FCM 컬럼 포함
  4. 기존 production DB: baseline-on-migrate=true에 의해 V1 SQL은 실행되지 않음
  5. Flyway history는 V1이지만 실제 스키마는 V1 baseline이 전제하는 상태보다 세 컬럼 부족

따라서 현재 필요한 작업은 새로운 기능을 위한 스키마 변경이 아니라, production 스키마를 이미 선언된 V1 baseline 상태와 일치시키는 복구 작업입니다.


🧭 해결 방법 검토

방법 검토 결과 판단
기존 V236을 다시 migration 경로에 추가 현재 main은 V1, develop은 V1–V4입니다. main에서 V236을 적용하면 이후 V2–V4가 현재 버전보다 낮아져 기본 설정에서 실행되지 않습니다. 제외
새로운 V5 versioned migration 추가 main이 V1에서 V5로 먼저 이동하므로 이후 develop의 V2–V4를 main에 반영할 때 동일한 순서 문제가 발생합니다. 제외
outOfOrder=true로 낮은 버전 실행 허용 환경별 migration 실행 순서가 달라지고, 재구축 시의 실행 순서와 production 이력이 일치하지 않게 됩니다. 제외
production DB에 수동 ALTER 실행 즉시 복구는 가능하지만 저장소와 Flyway history에 해결 근거가 남지 않고 다른 영속 환경의 동일 상태를 자동으로 처리하지 못합니다. 긴급 수동 복구안으로만 유지
멱등 repeatable migration 사용 버전이 없어 main V1과 develop V1–V4 모두에서 versioned migration 이후 실행할 수 있고, 컬럼 존재 여부에 따라 복구 또는 no-op 처리가 가능합니다. 선택

현재 분리된 migration 이력을 변경하지 않으면서 모든 환경에서 같은 파일을 안전하게 실행할 수 있다는 점에서 repeatable migration이 가장 영향 범위가 작고 재현 가능한 방법이라고 판단했습니다.


✅ 선택한 해결 방법

R__ensure_notification_fcm_columns.sql을 추가합니다.

  1. information_schema.COLUMNS에서 필요한 세 컬럼의 존재 여부를 확인합니다.
  2. 누락된 컬럼의 DDL만 동적으로 조합합니다.
  3. 누락 컬럼이 있으면 하나의 ALTER TABLE ... ALGORITHM=INSTANT로 추가합니다.
  4. 세 컬럼이 모두 존재하면 DO 0으로 종료합니다.
  5. Flyway가 파일명과 checksum을 history에 기록하므로 같은 내용은 다시 적용되지 않습니다.

ALGORITHM=INSTANT를 명시해 MySQL이 테이블 복사 방식으로 자동 전환하지 않고, instant ADD COLUMN을 지원하지 않는 조건에서는 즉시 실패하도록 했습니다. 세 컬럼은 모두 nullable이므로 기존 운영 애플리케이션과도 하위 호환됩니다.


🌍 환경별 동작

환경 현재 상태 hotfix 동작
production/main V1 BASELINE 기록, FCM 컬럼 누락 Hibernate 초기화 전에 세 컬럼 추가
stage/develop V1–V4 적용, FCM 컬럼 존재 컬럼 변경 없이 no-op
신규 로컬/CI DB V1 SQL이 세 컬럼 생성 V1 이후 repeatable은 no-op
일부 컬럼만 존재하는 DB 부분 적용 상태 존재하는 컬럼은 유지하고 누락된 컬럼만 추가

🧪 검증

MySQL 8.0.29와 실제 사용 중인 Flyway 9.16.3 조합으로 다음을 검증했습니다.

  • production과 같은 비어 있지 않은 DB를 V1 BASELINE 처리한 뒤 세 컬럼 복구
  • 신규 DB에서 V1 SQL 실행 후 repeatable migration no-op
  • 동일 Flyway 인스턴스에서 두 번째 migrate() 실행 시 중복 적용 없음
  • 컬럼 타입, 길이, nullable 조건과 flyway_schema_history 기록 확인
  • 최신 develop의 V1–V4 전체 migration과 함께 실행 확인

검증 명령:

  • main: ./gradlew test --tests in.koreatech.koin.acceptance.migration.NotificationFcmMigrationTest
  • main: ./gradlew build
  • develop + hotfix: ./gradlew test --tests in.koreatech.koin.acceptance.migration.NotificationFcmMigrationTest --tests in.koreatech.koin.acceptance.migration.DepartmentContactMigrationTest
  • develop + hotfix: ./gradlew build

GitHub build, Flyway version 검사, 위험 SQL 검사, PR 제목 검사도 모두 통과했습니다.


🛡️ 배포 전 확인 사항

  • production의 활성 flyway_schema_history가 V1 BASELINE 상태인지 확인합니다.
  • baseline 전환 전 history 백업 테이블이 보존되어 있는지 확인합니다.
  • notification의 세 FCM 컬럼이 실제로 누락됐는지 확인합니다.
  • hotfix가 병합되기 전에는 V1 BASELINE 이력과 맞지 않는 구버전 애플리케이션을 재시작하지 않습니다.

✅ Checklist (완료 조건)

  • 코드 스타일 가이드 준수
  • 테스트 코드 포함됨
  • Reviewers / Assignees / Labels 지정 완료
  • 보안 및 민감 정보 검증 (API 키, 환경 변수, 개인정보 등)

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 067f55fa-7f73-4995-a49e-0f19153d485f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/2309-notification-fcm-columns

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added 공통 백엔드 공통으로 작업할 이슈입니다. 버그 정상적으로 동작하지 않는 문제상황입니다. labels Jul 21, 2026
@github-actions
github-actions Bot requested review from DHkimgit and dh2906 July 21, 2026 15:09
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

Unit Test Results

774 tests   771 ✔️  1m 44s ⏱️
187 suites      3 💤
187 files        0

Results for commit eddcbe7.

♻️ This comment has been updated with latest results.

@taejinn
taejinn requested a review from Soundbar91 July 22, 2026 03:29

@Soundbar91 Soundbar91 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

236이랑 쿼리 내용이 완전 다른데, 이렇게 하신 이유가 있으실까요

@Soundbar91 Soundbar91 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 내부 쿼리의 문법은 처음보는데, 자세한 설명이 없으면 이해하기 어려울 거 같습니다. 제 개인적인 경험으로 동적 SQL은 학부에서 배웠던 기억도 없고, 저희 코인에서도 SQL 상으로는 다루지 않았기 때문입니다.
  2. 테스트 코드가 필요한지가 의문입니다. 테스트 코드는 해당 PR 이후에도 계속 돌아가는데, 그 과정에서 현재 작성한 테스트 코드가 운영 상 필요한 테스트 코드인지 궁금합니다.
  3. 지금 상황은 flyway를 적용하지 않고, 프로덕션 DB에 V236를 직접 적용해도 괜찮을 거 같습니다. 스테이지에는 이미 적용된 컬럼이기도 하고, 프로덕션 DB에 직접 적용한다고 해서 큰 문제는 없을 거 같아요. 다른 개발자 로컬 PC에서는 V236이 적용된지 꽤 됐기 때문에 큰 문제는 없을 거 같습니다.

@taejinn

taejinn commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@Soundbar91 검토해 주셔서 감사합니다.

말씀해 주신 대로 repeatable migration과 테스트는 반영하지 않고 production DB의 컬럼 상태를 확인한 뒤 기존 V236 SQL을 직접 적용하는 방향으로 진행하겠습니다.

@taejinn taejinn closed this Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

공통 백엔드 공통으로 작업할 이슈입니다. 버그 정상적으로 동작하지 않는 문제상황입니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants