Repository navigation
Feat: 외부 API 장애 격리(벌크헤드) 적용 - #135
Merged
Merged
Conversation
FCM·카카오·Google 토큰 서버가 멈추거나 느릴 때 다른 API가 받는 영향을 재는 k6·WireMock 시나리오 3개(S1~S3)를 추가한다. 이 커밋은 격리를 적용하기 전 상태라, 같은 스크립트로 적용 전 수치를 재는 기준점이다. - 카카오 user/me와 Google 토큰 엔드포인트 주소를 설정 키로 뺀다. 기본값은 지금 주소이고 부하 테스트에서만 WireMock으로 바꾼다 - bootTestRun용 실행 설정은 테스트 소스에만 둔다. WireMock으로 돌릴 수 없는 FCM과 Google ID 토큰 검증은 대역으로 바꾸고, 시드 데이터를 만든다 - loadtest/README.md에 실행 방법과 결과를 읽을 때 주의할 점을 적는다 Refs #134 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FCM이 응답하지 않으면 알림 작업이 DB 커넥션을 쥔 채 무한히 기다려, 커넥션 10개 중 8개가 묶이고 다른 API까지 느려지던 문제를 없앤다(실험으로 재현). - Firebase 연결·읽기·쓰기 타임아웃(3초·5초·5초)과 배치당 대기 상한(30초)을 건다. SDK 기본값은 무제한이다 - 알림 이력은 이벤트를 발행한 트랜잭션의 커밋 직전에 저장한다. 원래 작업과 함께 저장·취소되므로 푸시를 못 보내도 알림센터는 실제 상태와 맞다 - 이력 INSERT 전에 Repository flush로 여행방 UPDATE를 먼저 보낸다. 빼면 같은 방의 동시 일정 확인 30회 중 29회가 데드락이었다 - FCM 발송은 커밋 후 전용 실행기(스레드 4개, 대기열 200)가 트랜잭션 밖에서 처리하고, 대기열이 차면 푸시만 건너뛴다. 무효 토큰 삭제만 짧은 트랜잭션 - 정기 리마인드는 500명마다 쓰기 트랜잭션을 따로 열고, 한 묶음이 실패해도 나머지는 계속 보낸다 - 방장 탈퇴가 여행방 행을 사용자 행보다 먼저 잠그도록 정리 뒤 flush한다. 알림 이력 저장과 잠금 순서를 맞추기 위해서다 - @async와 @EnableAsync를 없애고, ArchitectureTest로 @async 사용을 막는다 Refs #134 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
탈퇴 때 연결 해제에만 쓰는 refresh token을 받으려고 로그인 응답이 토큰 서버를 최대 8초 기다리던 것을, 응답을 먼저 보내고 전용 실행기에서 교환하도록 바꾼다. - 교환은 전용 실행기(스레드 4개, 대기열 100)에서 실행한다. 대기열이 차면 기존 "교환 실패 시 저장만 생략" 정책대로 건너뛴다 - 저장 트랜잭션은 사용자 행을 잠금 조회해 탈퇴 여부를 확인하고, 탈퇴했으면 저장하지 않고 받은 토큰을 바로 revoke한다 - 탈퇴는 DB 정리가 커밋된 뒤 Apple·Google 로그인 credential을 한 번 더 revoke·삭제한다. 로그인 직후 탈퇴해 교환이 늦게 끝난 경우를 잡는다 Refs #134 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
카카오가 느려지면 로그인 요청이 요청 처리 스레드 200개를 다 차지해 다른 API까지 응답하지 못하던 것을 막는다. - 카카오 user/me 호출을 KakaoUserInfoClient로 빼고 @ConcurrencyLimit(20, REJECT)를 건다. 같은 빈 안에서 부르면 어노테이션이 적용되지 않기 때문이다 - 상한을 넘으면 기다리지 않고 기존 503 AUTH_SOCIAL_PROVIDER_UNAVAILABLE을 돌려준다. 새 ErrorCode는 없고 프론트 처리(잠시 후 재시도)도 같다 - ResilienceConfig(@EnableResilientMethods)는 #131과 같은 파일을 같은 내용으로 추가해 두 PR의 머지 순서와 무관하게 충돌하지 않는다 - 503 Swagger 설명과 AUTH_SOCIAL_PROVIDER_UNAVAILABLE 설명에 상한 초과를 더한다 Refs #134 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#134 스펙(external-api-bulkhead.md)에 결정 B-1~B-16, 부하 테스트 적용 전후 측정 결과, 검증 시나리오와 테스트를 기록하고 바뀐 동작을 관련 문서에 반영한다. - how-it-works: 알림 이력·푸시 분리, 소셜 로그인 서버가 느릴 때의 동작 - notification·auth-social-login·google-login-revoke·apple-oauth-multi-audience ·user-account-withdrawal·auth-error-code-granularity: 변경 이력과 인라인 주석 - spring-boot-java 규칙: BEFORE_COMMIT 저장 전 flush, @async 대신 전용 실행기 - package-layout·specs README 갱신, loadtest README에 적용 전 기준 커밋 명시 Refs #134 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
oasdiff로 확인).AUTH_SOCIAL_PROVIDER_UNAVAILABLE을 돌려준다. 새 에러 코드는 없고, 프론트 처리("잠시 후 다시 시도")도 같아Breaking-Change-Reason트레일러는 붙이지 않았다(스펙 B-14).Related
docs/specs/cross-cutting/external-api-bulkhead.mdloadtest/README.md— 적용 전 기준은 첫 커밋97c73afUserWithdrawalService충돌을 푼다(#131의 재시도 줄을 남기고 그 뒤에 이 PR의 5단계를 붙임). 이렇게 합친 상태에서 전체 테스트 569개 통과를 확인했다.ResilienceConfig는 두 PR이 같은 내용이라 충돌하지 않는다.주요 변경
BEFORE_COMMIT에서 원래 트랜잭션에 저장하고, 발송은 커밋 후 전용 실행기(스레드 4·대기열 200)가 트랜잭션 밖에서 처리한다. 대기열이 차면 푸시만 건너뛴다.flush()로 여행방 UPDATE를 먼저 보낸다(빼면 동시 일정 확인 30회 중 29회 데드락). 방장 탈퇴도 여행방 행을 사용자 행보다 먼저 잠그도록 정리 뒤 flush한다.user/me호출을KakaoUserInfoClient로 빼고@ConcurrencyLimit(20, REJECT).@Async제거 —@EnableAsync를 지우고ArchitectureTest로 사용을 막는다. 비동기 작업은 용도별 실행기에 직접 제출한다.Test plan
./gradlew test— 541개 통과./gradlew build@ConcurrencyLimit)를 하나씩 빼면 해당 테스트가 실패하는지 확인oasdiff breaking0건 — 차이는 로그인 503 설명뿐Checklist
docs/product/release-milestones.mdMilestone(출시 이후) 확인docs/specs/링크 또는 스펙 반영Breaking-Change-Reason:트레일러 — N/A (필드·enum·ErrorCode·경로 변경 없음, 503 설명만 보강)Risk / Rollback
🤖 Generated with Claude Code