Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SSE 구독 시 초기 아바타 목록 미전송 버그 수정
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SSE 연결 관리 로직 개선
There was a problem hiding this comment.
Code Review
This pull request refactors the SSE subscription logic to use an event-driven approach by introducing AvatarSseSubscribedEvent and publishing it asynchronously upon subscription. It also simplifies connection tracking and cleanup logic, and reduces the heartbeat interval. The review feedback highlights a potential connection leak in the asynchronous onAvatarSseSubscribed event listener if a database error occurs, and suggests wrapping the logic in a runCatching block to guarantee proper cleanup.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| @Async | ||
| @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
| @EventListener | ||
| @Transactional(readOnly = true) | ||
| fun onAvatarSseSubscribed(event: AvatarSseSubscribedEvent) { | ||
| send(event.emitter, findAvatarChanges()) | ||
| } |
There was a problem hiding this comment.
In onAvatarSseSubscribed, if findAvatarChanges() throws an exception (e.g., due to a database connection timeout or query failure), the exception will propagate out of the @Async method. Because send is never called, the cleanup method is never invoked, leaving the SseEmitter in the emitters list indefinitely (until it eventually times out, which is set to 30 minutes). This can leak connection slots and quickly exhaust maxConnections under database error conditions.
We should wrap the execution in a runCatching block and ensure cleanup is called with complete = true if an exception occurs.
@Async
@EventListener
@Transactional(readOnly = true)
fun onAvatarSseSubscribed(event: AvatarSseSubscribedEvent) {
runCatching { findAvatarChanges() }
.onSuccess { send(event.emitter, it) }
.onFailure { e ->
logger.error("Failed to send initial avatar changes", e)
cleanup(event.emitter, "initial send failed", complete = true)
}
}
✨ 작업 내용
🔍 리뷰 시 참고사항
✅ 체크리스트
.env.example등) 변경이 필요한 경우 작성 또는 수정했나요?📎 관련 이슈(선택)