@@ -63,13 +63,40 @@ public void reconcileStock() {
6363
6464 // 해당 레슨이 현재 배치 처리 중인지 확인
6565 String busyKey = "lesson:busy:" + lessonId ;
66+ String lastActiveKey = "lesson:busy:last_active:" + lessonId ;
6667 String busyCountStr = coreRedisTemplate .opsForValue ().get (busyKey );
67- int busyCount = (busyCountStr == null ) ? 0 : Integer .parseInt (busyCountStr );
6868
69- if (busyCount > 0 ) {
70- log .info ("Lesson [{}] is still being processed by batch consumers (Busy count: {}). Skipping reconciliation for now." ,
71- lessonId , busyCount );
72- continue ;
69+ // 보정 로직 미실행 조건 체크
70+ if (busyCountStr != null ) {
71+ // 마지막 활동 시간 로드
72+ String lastActiveStr = coreRedisTemplate .opsForValue ().get (lastActiveKey );
73+ long now = System .currentTimeMillis ();
74+ int busyCount = Integer .parseInt (busyCountStr );
75+
76+ // 일정 시간 무응답시 교착상태 방지
77+ boolean isStale = (lastActiveStr != null && (now - Long .parseLong (lastActiveStr ) > 600000 )); // 10분 이상 무응답
78+ boolean isRecent = (lastActiveStr != null && (now - Long .parseLong (lastActiveStr ) < 5000 )); // 5초 이내 활동
79+ boolean isNegative = busyCount < 0 ;
80+
81+ // busy 카운터 존재 & 응답 10분 미만
82+ if (busyCount > 0 && !isStale ) {
83+ log .info ("Lesson [{}] is still being processed (Busy count: {}). Skipping reconciliation." ,
84+ lessonId , busyCountStr );
85+ continue ;
86+ }
87+
88+ // busy 카운터 = 0 & 5초 내 활동이 있었음 (DB트랜잭션 잠시 대기)
89+ if (busyCount == 0 && isRecent ) {
90+ log .info ("Lesson [{}] recently active. Waiting for safety margin." , lessonId );
91+ continue ;
92+ }
93+
94+ // 응답 10분 초과 OR 음수 카운터 발생 (교착상태 방지 및 회복)
95+ if (isStale || isNegative ) {
96+ log .warn ("Detected stale or invalid busy state for lesson [{}] (Count: {}, Stale: {}). Forcing reset." ,
97+ lessonId , busyCountStr , isStale );
98+ coreRedisTemplate .opsForValue ().set (busyKey , "0" , java .time .Duration .ofMinutes (10 ));
99+ }
73100 }
74101
75102 // 실제 참여자 수 계산 (DB)
0 commit comments