@@ -110,6 +110,12 @@ public void reconcileStock() {
110110 // 실제 참여자 수 계산 (DB)
111111 long actualParticipantCount = lessonParticipantRepository .countByLessonId (lessonId );
112112
113+ // Redis 재고가 음수라면 producer의 DECR -> INCR rollback 중으로 판단
114+ String redisStockBefore = coreRedisTemplate .opsForValue ().get (stockKey );
115+ if (isNegativeRedisStock (lessonId , redisStockBefore )) {
116+ continue ;
117+ }
118+
113119 // Lesson 테이블 카운트 보정 및 상태 변경
114120 lessonRepository .updateParticipantCount (lessonId , (int )actualParticipantCount ,
115121 com .threestar .trainus .domain .lesson .teacher .entity .LessonStatus .RECRUITMENT_COMPLETED );
@@ -125,7 +131,6 @@ public void reconcileStock() {
125131 currentStock = 0 ;
126132
127133 Long waitingRoomSize = coreRedisTemplate .opsForZSet ().size (waitingRoomKey );
128- String redisStockBefore = coreRedisTemplate .opsForValue ().get (stockKey );
129134 Long streamSize = mqRedisTemplate .opsForStream ().size (LessonApplyStreamConstant .STREAM_KEY );
130135 log .info (
131136 "Reconciliation diagnostics. lessonId={}, dbCount={}, maxParticipants={}, calculatedStock={}, redisStockBefore={}, waitingRoomSize={}, streamSize={}, busyCount={}, lastActive={}" ,
@@ -150,6 +155,26 @@ public void reconcileStock() {
150155 log .info ("Finished Smart Stock Reconciliation for {} lessons." , processedCount );
151156 }
152157
158+ private boolean isNegativeRedisStock (Long lessonId , String redisStock ) {
159+ if (redisStock == null ) {
160+ return false ;
161+ }
162+
163+ try {
164+ int currentStock = Integer .parseInt (redisStock );
165+ if (currentStock < 0 ) {
166+ log .info ("Redis stock is negative. lessonId={}, stock={}. Postponing reconciliation." , lessonId ,
167+ redisStock );
168+ return true ;
169+ }
170+ return false ;
171+ } catch (NumberFormatException e ) {
172+ log .warn ("Failed to parse Redis stock. lessonId={}, stock={}. Postponing reconciliation." , lessonId ,
173+ redisStock );
174+ return true ;
175+ }
176+ }
177+
153178 // 미처리 메세지 확인 메서드 (Core Redis)
154179 private boolean hasWaitingRoomMessages () {
155180 String dirtySetKey = LessonApplyStreamConstant .DIRTY_SET_KEY ;
0 commit comments