Skip to content

Commit 6e78a10

Browse files
authored
Merge #93 from fix/spotifyMusic/main
feat: SpotifyMusic 업데이트 시, 낙관적 락 추가 videoID 부분에서 동시성 문제 발생, 해당 문제 해결하기 위해 낙관적 락 적용 데이터 변경이 적은 편이고 조회된 videoID를 기반으로 수정하는 로직이 필요 없으므로 낙관적 락을 사용함
2 parents 9559934 + 63fe707 commit 6e78a10

File tree

3 files changed

+24
-1
lines changed

3 files changed

+24
-1
lines changed

src/main/java/org/dfbf/soundlink/domain/emotionRecord/entity/SpotifyMusic.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ public class SpotifyMusic {
2020
@GeneratedValue(strategy = GenerationType.IDENTITY)
2121
private Long id;
2222

23+
@Version
24+
private Integer version;
25+
2326
@Column(name = "spotify_id")
2427
private String spotifyId;
2528

src/main/java/org/dfbf/soundlink/domain/emotionRecord/service/EmotionRecordService.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.dfbf.soundlink.domain.emotionRecord.service;
22

3+
import jakarta.persistence.OptimisticLockException;
34
import lombok.RequiredArgsConstructor;
45
import lombok.extern.slf4j.Slf4j;
56
import org.dfbf.soundlink.domain.chat.entity.ChatRoom;
@@ -24,6 +25,9 @@
2425
import org.springframework.data.domain.PageRequest;
2526
import org.springframework.data.domain.Pageable;
2627
import org.springframework.data.domain.Sort;
28+
import org.springframework.retry.annotation.Backoff;
29+
import org.springframework.retry.annotation.Recover;
30+
import org.springframework.retry.annotation.Retryable;
2731
import org.springframework.stereotype.Service;
2832
import org.springframework.transaction.annotation.Transactional;
2933

@@ -181,6 +185,11 @@ public ResponseResult getVideoIdBySpotifyId(String spotifyId) {
181185
}
182186

183187
@Transactional
188+
@Retryable(
189+
retryFor = OptimisticLockException.class, // 낙관적 락 충돌 시 재시도
190+
maxAttempts = 3, // 최대 3번 재시도
191+
backoff = @Backoff(delay = 100) // 100ms(0.1초) 대기 후 재시도
192+
)
184193
public ResponseResult updateEmotionRecord(Long recordId, EmotionRecordUpdateRequestDTO updateDTO) {
185194
try {
186195
// 기존 감정 기록 조회
@@ -223,6 +232,16 @@ public ResponseResult updateEmotionRecord(Long recordId, EmotionRecordUpdateRequ
223232
}
224233
}
225234

235+
// 낙관적 락 재시도 실패 시 실행되는 메서드
236+
// Recover 어노테이션에 의해 실패 시, 자동 호출됨
237+
@Recover
238+
public ResponseResult recoverFromOptimisticLock(OptimisticLockException e, Long recordId, EmotionRecordUpdateRequestDTO updateDTO) {
239+
log.error("감정 기록 업데이트 중 동시성 충돌 발생. recordId: {}, spotifyId: {}, error: {}",
240+
recordId, updateDTO.spotifyId(), e.getMessage());
241+
242+
return new ResponseResult(ErrorCode.CONCURRENCY_ERROR, e.getMessage());
243+
}
244+
226245
@Transactional
227246
public ResponseResult deleteEmotionRecord(Long recordId) {
228247
try {

src/main/java/org/dfbf/soundlink/global/exception/ErrorCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ public enum ErrorCode {
7272
CHAT_UNAUTHORIZED(HttpStatus.FORBIDDEN,"권한이 없습니다"),
7373
CHATROOM_NOT_FOUND(HttpStatus.NOT_FOUND,"채팅방을 찾을 수 없습니다."),
7474
CHAT_FAILED(HttpStatus.CONFLICT,"서버 내부 에러. 중복된 레코드가 존재합니다."),
75-
CHAT_REQUEST_FAILED(HttpStatus.CONFLICT, "채팅 요청 실패");
75+
CHAT_REQUEST_FAILED(HttpStatus.CONFLICT, "채팅 요청 실패"),
7676

77+
CONCURRENCY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "재시도 실패. 동시성 충돌로 인해 업데이트 할 수 없습니다." );
7778

7879
private final HttpStatus status;
7980
private final String message;

0 commit comments

Comments
 (0)