Skip to content

RealTime 레지스트리 동시성 테스트 + CI race detector - #218

Merged
i3months merged 1 commit into
devfrom
test/realtime-race-detector
Aug 23, 2026
Merged

RealTime 레지스트리 동시성 테스트 + CI race detector#218
i3months merged 1 commit into
devfrom
test/realtime-race-detector

Conversation

@i3months

Copy link
Copy Markdown
Member

realtime/CLAUDE.md §11 은 동시성 검증에 go test ./... -race 를 규정한다. CI 는 -race 없이 돌고 있었다.

RealTime 은 본업이 동시 fan-out 인 컴포넌트다 — AMQP 컨슈머 고루틴이 Dispatch 하는 동안 HTTP 핸들러들이 Subscribe/Unsubscribe 한다. 여기서 경합을 못 잡으면 운영에서 간헐적 패닉·이벤트 유실로만 드러나고, 재현이 거의 불가능하다.

다만 -race 를 켜는 것만으로는 부족했다. 잡을 게 있으려면 동시 접근을 실제로 만드는 테스트가 있어야 하는데, Registry 테스트 2개는 모두 순차 실행이었다. 서버 전체의 공유 가변 상태인데도.

무엇

1. 레지스트리 동시성 테스트

8개 고루틴이 Subscribe→읽기→Unsubscribe 를 200회씩 돌리는 동안 4개 고루틴이 계속 Dispatch 한다.

겨냥하는 지점은 Dispatch 의 설계다 — 구독자 목록을 락 아래서 복사한 뒤 락을 놓고 채널에 쓴다(느린 구독자가 락을 잡고 있으면 다른 구독자까지 막히므로 의도된 설계다). 그래서 "복사한 목록"과 "지금 살아있는 구독자"가 어긋나는 창이 구조적으로 존재한다.

  • 그 창에서 자료 경합이 생기지 않는지 (-race)
  • churn 중 id 기반 제거가 어긋나 해제된 구독자가 목록에 남지 않는지 — 마지막에 Dispatch 가 0 을 반환하는지로 확인

2. CI 에 -race

go test -race ./.... 기존 Go 잡은 15~21초라 여유가 있다.

3. make test-race

CI 와 같은 형태를 로컬에서 돌릴 수 있게. 동시성 코드를 건드렸으면 push 전에 이걸로 확인한다.

함께 읽었고 문제없던 것

이번에 RealTime 동시성 경로를 훑었는데, 아래는 모두 이미 잘 처리돼 있었다.

지점 확인
Dispatch 락 밖 전송 + 타이머 재사용 (time.After 였다면 구독자×이벤트 만큼 타이머 고루틴 누수)
구독자 채널 close 아무도 닫지 않는다 → send-on-closed 패닉 경로 없음 (CLAUDE.md 안티패턴 목록은 경고이지 현 코드 서술이 아님)
ws.go hijack 된 WS 는 request context 취소가 보장되지 않아 별도 done 채널로 writeLoop 종료. defer 순서가 close(done)UnsubscribeCloseNow 로 정확
ws_audio.go errc 버퍼 2 — 한쪽이 끝나 핸들러가 반환해도 나머지 고루틴이 채널 전송에서 막히지 않는다. 양쪽 conn 이 닫히며 Read 가 풀려 종료

이 PR 은 발견한 버그를 고치는 게 아니라, 지금의 무결함 상태를 회귀로부터 고정하는 것이다.

검증

로컬에 Go 툴체인이 없어 CI 로 검증한다. 이 PR 의 Go 잡이 통과하면 (a) 테스트가 컴파일·통과하고 (b) 현재 코드가 race-free 임이 함께 확인된다. 잡이 빨개지면 실제 경합을 찾은 것이므로 그때 고친다.

realtime/CLAUDE.md §11 은 동시성 검증에 `go test -race` 를 규정하는데 CI 는
`go test ./...` 로만 돌고 있었다. 본업이 동시 fan-out 인 컴포넌트에서 경합은
여기서 못 잡으면 운영에서 간헐적 패닉·유실로만 드러난다.

그런데 -race 를 켜도 잡을 게 있으려면 동시 접근을 실제로 만드는 테스트가
있어야 한다. Registry 는 서버 전체의 공유 가변 상태이고 운영에서는 AMQP 컨슈머가
Dispatch 하는 동안 HTTP 핸들러들이 Subscribe/Unsubscribe 하는데, 기존 테스트
2개는 모두 순차 실행이었다.

Dispatch 는 의도적으로 락을 놓은 뒤 채널에 쓴다(느린 구독자가 락을 잡으면 다른
구독자까지 막히므로). 그 설계상 "복사한 목록"과 "지금 살아있는 구독자"가 어긋나는
창이 생긴다 — 그 창에서 자료 경합이 없는지, 그리고 churn 중 id 기반 제거가
어긋나 해제된 구독자가 목록에 남지 않는지 확인한다.

Makefile 에 test-race 타깃도 추가 — push 전 로컬 확인용.
@i3months
i3months merged commit 16e1ff5 into dev Aug 23, 2026
5 checks passed
@i3months
i3months deleted the test/realtime-race-detector branch August 23, 2026 16:52
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.

1 participant