Skip to content

[REFACTOR] XRPL 에스크로 큐 backoff 최적화 및 재연결 안정성 개선#49

Merged
Takch02 merged 3 commits into
mainfrom
feat/chaos-test-bull-board
May 15, 2026
Merged

[REFACTOR] XRPL 에스크로 큐 backoff 최적화 및 재연결 안정성 개선#49
Takch02 merged 3 commits into
mainfrom
feat/chaos-test-bull-board

Conversation

@Takch02

@Takch02 Takch02 commented May 15, 2026

Copy link
Copy Markdown
Contributor

Resolves #31

문제

기존 backoff: { type: "exponential", delay: 60_000 } 설정으로
XRPL 네트워크 일시 장애 시 재시도 최대 누적 대기가 ~17시간에 달했습니다.
XRPL 트랜잭션 완료 시간이 3 ~ 5초임을 감안하면 과도한 설정이었습니다.

또한 연결이 끊긴 후 재연결 시 xrpl.js Client를 재사용해
"Websocket connection never cleaned up" 에러가 발생하는 버그가 있었습니다.

변경 사항

1. Backoff delay 단축 (outbox-watcher.service.ts)

  • delay: 60_000delay: 5_000
  • 최대 누적 대기: ~17시간 → ~43분

2. XRPL connectionTimeout 단축 (xrpl.service.ts)

  • xrpl.js 기본값(5s) → connectionTimeout: 3_000
  • 연결 실패 감지 속도 향상

3. 재연결 시 Client 새로 생성 (xrpl.service.ts)

  • 연결 끊김 후 재연결 시 기존 Client를 버리고 새로 생성
  • "Websocket connection never cleaned up" 에러 제거

4. 개발 도구 (scripts/bull-board.ts)

5. MongoDB Replica Set (docker-compose.yml)

  • Change Streams 지원을 위한 단일 노드 replica set 구성 추가

Chaos Test 결과

toxiproxy로 XRPL WebSocket 연결을 강제 차단/복구하며 실측

delay=2000, connectionTimeout=5s (테스트 1)

attempt gap 비고
0→1 22s request timeout (~20s) + 5s backoff
1→2 26s request timeout (~18s) + 8s backoff
2→3 14s "Websocket never cleaned up" (즉시) + 16s backoff
3→4 35s 5s connection timeout + 32s backoff
4→5 67s 5s connection timeout + 64s backoff
~2m44s

delay=5000, connectionTimeout=3s (테스트 2 — never cleaned up 버그 있음)

attempt gap 비고
0→1 25s request timeout (~20s) + 5s backoff
1→2 15s "Websocket never cleaned up" (즉시) + 10s backoff
2→3 35s "Websocket never cleaned up" (즉시) + 20s backoff
3→4 78s 3s connection timeout + 75s backoff
4→5 158s 3s connection timeout + 155s backoff
~5m12s

delay=5000, connectionTimeout=3s (최종 — connect() 수정 후)

attempt gap 비고
0→1 25s request timeout (~20s) + 5s backoff
1→2 18s 3s connection timeout + 15s backoff ✅
2→3 38s 3s connection timeout + 35s backoff ✅
3→4 78s 3s connection timeout + 75s backoff ✅
4→5 158s 3s connection timeout + 155s backoff ✅
~5m17s "never cleaned up" 완전 제거

결과

수정 전 수정 후
attempt=1 에러 유형 "Websocket never cleaned up" connect() timed out (3s) ✅
backoff 공식 delay × (2ⁿ - 1) 동일, delay만 변경
첫 retry 간격 ~25s (5s backoff + 20s request timeout) ~25s (동일)
5회 retry 누적 ~5분 12초 ~5분 17초 (안정적)
최대 누적 (10회) ~17시간 ~43분

attempt=0의 ~20초 지연은 xrpl.js 내부 request timeout으로 backoff 설정과 무관합니다.

Summary by CodeRabbit

릴리스 노트

  • Bug Fixes

    • 이벤트 처리 재시도 로직 조정
    • XRPL WebSocket 연결 안정성 개선
  • Refactor

    • MongoDB 레플리카셋 초기화 및 헬스체크 프로세스 개선

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

이 PR은 XRPL 기반 결제 시스템의 안정성을 개선하기 위해 세 가지 변화를 포함합니다: MongoDB 복제 세트 자동 초기화, 결제 재시도 백오프 타이밍 단축(60초→5초), XRPL WebSocket 재연결 로직 강화로 빠른 장애 복구를 실현합니다.

Changes

XRPL 통합 안정성 개선

Layer / File(s) Summary
MongoDB 복제 세트 초기화
docker-compose.yml
Mongo 서비스에 복제 세트 바인딩, healthcheck 추가, 및 mongo-init 서비스를 통한 자동 replica set 초기화를 구성합니다.
XRPL 결제 재시도 백오프 단축
src/modules/outbox/outbox-watcher.service.ts
Escrow 큐 작업의 exponential backoff 초기 지연을 60초에서 5초로 단축하여, XRPL 트랜잭션 완료(3~5초) 후 더 빠른 재시도를 가능하게 합니다.
XRPL WebSocket 재연결 로직 강화
src/modules/xrpl/xrpl.service.ts
이미 연결된 경우 즉시 반환, 재연결 시 새 Client 생성(3초 connectionTimeout), 상세한 연결 상태 로그 및 Promise 기반 안정적인 재연결 흐름을 구현합니다.

Sequence Diagram

sequenceDiagram
  participant Caller as 호출자
  participant XrplService
  participant Client as XRPL Client
  
  Caller->>XrplService: connect() 호출
  alt 이미 연결됨
    XrplService-->>Caller: 즉시 반환
  else 연결 필요
    alt connectPromise 존재 (연결 중)
      XrplService->>XrplService: 기존 Promise 대기
    else 재연결 필요
      XrplService->>XrplService: 기존 Client 폐기
      XrplService->>Client: 새 Client 생성 (3초 timeout)
      Client-->>XrplService: Client 생성 완료
    end
    XrplService->>Client: connect() 시도
    alt 성공
      Client-->>XrplService: 연결 완료
      XrplService-->>Caller: Promise 해제 후 반환
    else 실패
      Client-->>XrplService: 에러 (URL 포함 로그)
      XrplService-->>Caller: 예외 재던짐
    end
  end
Loading

핵심 검토 포인트

  1. 백오프 타이밍 검증 (가장 중요): XRPL 트랜잭션이 3~5초 소요이므로 5초 초기 backoff는 적절하나, 최대 누적 대기 시간(~43분)이 비즈니스 요구사항과 부합하는지 확인 필요. 조정 전 60초 설정에서는 최대 ~17시간 대기로 과도했습니다.

  2. WebSocket reconnection 타이밍: 3초 connectionTimeout이 XRPL 네트워크 환경에서 충분한가, 그리고 기존 Client 폐기 후 새 Client 생성 시 메모리 누수나 리소스 정리 문제는 없는가 검토.

  3. MongoDB 복제 세트 의존성: mongo-init 서비스가 정상 완료하지 않으면 애플리케이션 시작 시 블로킹될 수 있으니, healthcheck 타임아웃과 재시도 정책 확인 필요.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🔄 XRPL 재시도 춤을 추다
60초는 너무 길었던 것 같아,
5초로 줄여 빠르게 회복하네 🚀
MongoDB도 처음부터 준비되어,
WebSocket은 똑똑해졌고
장애는 재빨리 복구된다 ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning docker-compose.yml의 MongoDB replica set 설정과 scripts/bull-board.ts 추가는 #31 범위를 벗어나 있습니다. docker-compose.yml과 bull-board.ts 변경사항을 별도 PR로 분리하고, 현재 PR은 outbox-watcher.service.ts와 xrpl.service.ts 변경만 포함하도록 정리하세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 주요 변경 사항인 XRPL 재시도 backoff 최적화와 재연결 안정성 개선을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR은 #31의 요구사항인 backoff 시간 단축(60초→5초), connectionTimeout 개선(5초→3초), 그리고 혼돈 테스트를 통한 안정성 검증을 모두 충족합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/chaos-test-bull-board

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/modules/xrpl/xrpl.service.ts (1)

99-101: ⚡ Quick win

재연결 전 기존 Client 정리 권장

새로운 Client를 생성하기 전에 기존 this.client가 존재하는 경우 명시적으로 정리하는 것이 좋습니다. 현재 코드는 연결되지 않은 상태의 기존 Client를 덮어쓰기만 하므로, 이벤트 리스너나 타이머 등의 리소스가 남아있을 수 있습니다.

반복적인 재연결 시나리오에서 점진적 리소스 누수 가능성이 있습니다.

♻️ 권장 수정
    if (!this.connectPromise) {
      // 연결이 끊긴 상태에서 재연결 시 기존 Client를 버리고 새로 생성
      // 기존 Client를 재사용하면 "Websocket connection never cleaned up" 에러 발생
+     if (this.client) {
+       await this.client.disconnect().catch(() => {});
+     }
      this.client = new Client(this.wsUrl, { connectionTimeout: 3_000 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/xrpl/xrpl.service.ts` around lines 99 - 101, Before creating a
new Client(this.wsUrl...), ensure any existing this.client is cleaned up: if
this.client exists, call its graceful shutdown method (e.g., await
this.client.disconnect() or this.client.close() if available), remove any event
listeners (e.g., this.client.removeAllListeners()), clear related timers, and
set this.client = undefined before assigning the new Client; wrap shutdown in
try/catch to avoid swallowing errors and reference the this.client and
Client(...) symbols so the cleanup occurs immediately prior to new Client(...)
creation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docker-compose.yml`:
- Around line 18-26: The mongo-init service's rs.initiate call sets the replica
member host to "localhost:27017" which will mismatch client connections; update
the rs.initiate invocation (the entrypoint block that calls rs.initiate) to use
the Docker service hostname "mongo:27017" as the member host so the replica set
member address matches how the app connects (i.e., change the host value inside
rs.initiate from "localhost:27017" to "mongo:27017").

---

Nitpick comments:
In `@src/modules/xrpl/xrpl.service.ts`:
- Around line 99-101: Before creating a new Client(this.wsUrl...), ensure any
existing this.client is cleaned up: if this.client exists, call its graceful
shutdown method (e.g., await this.client.disconnect() or this.client.close() if
available), remove any event listeners (e.g., this.client.removeAllListeners()),
clear related timers, and set this.client = undefined before assigning the new
Client; wrap shutdown in try/catch to avoid swallowing errors and reference the
this.client and Client(...) symbols so the cleanup occurs immediately prior to
new Client(...) creation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b2956ee4-889d-430b-9777-8891c55a27c5

📥 Commits

Reviewing files that changed from the base of the PR and between f1b2eb0 and af2139f.

📒 Files selected for processing (3)
  • docker-compose.yml
  • src/modules/outbox/outbox-watcher.service.ts
  • src/modules/xrpl/xrpl.service.ts

Comment thread docker-compose.yml
@Takch02 Takch02 merged commit 7eba00a into main May 15, 2026
3 checks passed
@Takch02 Takch02 deleted the feat/chaos-test-bull-board branch May 15, 2026 05:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] XRPL 결제 실패 시 재시도 backoff 값 수정

1 participant