Skip to content

Conversation

@Hyun0828
Copy link
Collaborator

@Hyun0828 Hyun0828 commented Aug 27, 2025

PR 타입(하나 이상의 PR 타입을 선택해주세요)

  • 기능 추가
  • 기능 삭제
  • 버그 수정
  • 의존성, 환경 변수, 빌드 관련 코드 업데이트
  • 리팩터링

반영 브랜치

dev -> main

작업 내용

배포 반영

테스트 결과

ex) 베이스 브랜치에 포함되기 위한 코드는 모두 정상적으로 동작해야 합니다. 결과물에 대한 스크린샷, GIF, 혹은 라이브

Summary by CodeRabbit

  • 신기능
    • SSE 연결에 정기 하트비트 이벤트를 도입하여 연결 유지 및 타임아웃 감소. 비정상 연결은 자동 정리되어 안정성이 향상됩니다.
  • 버그 수정
    • SSE 최초 연결 과정의 예외 로깅을 강화하여 문제 진단이 쉬워지고, 간헐적 연결 끊김 현상이 완화됩니다.

@coderabbitai
Copy link

coderabbitai bot commented Aug 27, 2025

Walkthrough

SseService에 주기적 하트비트 전송 로직이 추가되었고, 초기 SSE 연결 시 예외 로깅이 보강되었습니다. 하트비트는 30초 간격으로 활성 SSE Emitter 전체에 전송되며, 전송 실패 시 해당 Emitter를 제거합니다. 필요한 스케줄러/IO 예외 관련 import가 추가되었습니다.

Changes

Cohort / File(s) Change Summary
SSE 서비스 하트비트 및 로깅 보강
src/main/java/stackpot/stackpot/event/SseService.java
- @Scheduled(fixedRate = 30000)sendHeartbeat() 추가: 모든 활성 Emitter에 "heartbeat" 이벤트(타임스탬프 포함) 전송
- 하트비트 전송 중 IOException 발생 시 경고 로그 후 해당 Emitter 제거
- 초기 SSE 연결 예외 시 에러 로그 추가
- Scheduled, IOException import 추가

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant S as Scheduler
  participant SS as SseService
  participant E1 as SseEmitter A
  participant E2 as SseEmitter B

  rect rgba(230,245,255,0.6)
    note over S,SS: 주기적 하트비트 (30s)
    S->>SS: trigger sendHeartbeat()
    SS->>E1: send event "heartbeat" {timestamp}
    SS->>E2: send event "heartbeat" {timestamp}
  end

  alt 전송 실패 (IOException)
    SS-->>SS: 경고 로그 기록
    SS->>SS: 실패한 Emitter 제거
  else 전송 성공
    SS-->>S: 완료
  end

  note over SS: 초기 SSE connect 시 예외 발생 시 에러 로그 추가
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

:bug: bug

Poem

토끼는 귀 쫑긋, 팡팡 하트비트 뚜벅뚜벅
30초마다 톡, 살아있다 신호 쏙
삐걱 소리 나면 살짝 빼고 정리 끝
로그로 적고, 다시 콩닥 콩닥 핑!
봄풀 흔들리듯, SSE도 안녕! 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/main/java/stackpot/stackpot/event/SseService.java (2)

45-56: 초기 연결 예외 로깅 보강 + onError 콜백/기존 연결 정리 제안

스택트레이스가 로그에 남도록 Throwable을 함께 전달하고, onError 콜백 및 기존 Emitter 정리를 추가하면 누수/중복 연결을 줄일 수 있습니다.

         emitter.onCompletion(() -> emitters.remove(userId));
         emitter.onTimeout(() -> emitters.remove(userId));
-        emitters.put(userId, emitter);
+        emitter.onError(ex -> emitters.remove(userId));
+        SseEmitter prev = emitters.put(userId, emitter);
+        if (prev != null) {
+            prev.complete();
+        }
         try {
             emitter.send(SseEmitter.event().name("connect").data("연결 완료!"));
         } catch (Exception e) {
-            log.error("SSE 연결 중 에러 발생: {}", e.getMessage());
+            log.error("SSE 연결 중 에러 발생", e);
             emitters.remove(userId);
         }

60-72: 하트비트 전송: 중복 실행 방지, 실패 시 자원 정리, 동시 전송 경합 완화, 로깅 개선

  • fixedRate는 실행 시간이 길어질 때 중첩 실행이 발생할 수 있습니다. fixedDelay(+ 초기 지연)와 프로퍼티화 권장.
  • 전송 실패 시 completeWithError로 자원 정리.
  • 다른 쓰레드의 이벤트 전송과 경합을 줄이기 위해 emitter 단위 동기화 권장.
  • 실패 로그에 Throwable 포함.
-    @Scheduled(fixedRate = 30000) // 30초마다 실행
+    @Scheduled(fixedDelayString = "${sse.heartbeat.delay-ms:30000}", initialDelayString = "${sse.heartbeat.initial-delay-ms:5000}") // 완료 후 지연
     public void sendHeartbeat() {
         emitters.forEach((userId, emitter) -> {
             try {
-                emitter.send(SseEmitter.event()
-                        .name("heartbeat")
-                        .data("keep-alive at " + System.currentTimeMillis()));
+                synchronized (emitter) {
+                    emitter.send(SseEmitter.event()
+                            .name("heartbeat")
+                            .data("keep-alive at " + System.currentTimeMillis()));
+                }
             } catch (IOException e) {
-                log.warn("Failed to send heartbeat to user {}, removing emitter.", userId);
-                emitters.remove(userId);
+                log.warn("Heartbeat 전송 실패로 emitter 제거: userId={}", userId, e);
+                try {
+                    emitter.completeWithError(e);
+                } finally {
+                    emitters.remove(userId);
+                }
             }
         });
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a62b5a0 and f977357.

📒 Files selected for processing (1)
  • src/main/java/stackpot/stackpot/event/SseService.java (3 hunks)
🔇 Additional comments (3)
src/main/java/stackpot/stackpot/event/SseService.java (3)

23-23: IOException import 적절

하트비트 전송 실패 처리에 필요한 예외 타입으로 적절합니다.


60-72: ConcurrentHashMap forEach 중 remove 사용은 안전

ConcurrentHashMap에 대해 같은 키를 제거하는 패턴은 안전하며 ConcurrentModificationException을 유발하지 않습니다. 현 접근 방식은 타당합니다.


7-7: 스케줄러 활성화 확인 완료

  • src/main/java/stackpot/stackpot/StackpotApplication.java 13번째 줄에 @EnableScheduling이 적용되어 있어 @Scheduled 기반 하트비트가 정상 작동합니다.
  • 필요 시 아래 프로퍼티를 추가로 설정해 스케줄러 풀 사이즈나 하트비트를 조정할 수 있습니다(선택 사항):
    • spring.task.scheduling.pool.size
    • sse.heartbeat

@Hyun0828 Hyun0828 merged commit 3497683 into main Aug 27, 2025
2 checks passed
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.

2 participants