-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[test] uploadPhoto, updateEmail 동시 호출 시 문제 확인 및 테스트 #46
Closed
Closed
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
2138bf1
[test] 메서드 실행 시간 측정을 위한 ExeTimer 마킹용 어노테이션
RumosZin 59dcf5b
[feat] email 수정 관련 도메인 메서드 추가
RumosZin e3d3def
[feat] email 수정 서비스 코드 작성 (인터페이스, 구현체)
RumosZin 87696f4
[fix] redis 없이 db에 접근하여 사진 생성 요청의 진행상황 파악 후 새 요청 생성
RumosZin cb21978
[test] photo_request의 이메일 수정 / 이메일 전송이 동시에 일어나는 경우 테스트
RumosZin 3cb902a
[test] 메소드 실행시간 측정 ExecutionTimer 코드 작성
RumosZin f8a4ae9
[refactor] uploadPhoto 작업 관련 주석 작성
RumosZin 5fb6f3c
[fix] 클래스 이름 오타 수정, 이메일 변경
RumosZin 206afa8
[fix] email 수정 메서드 transactional readonly 제거
RumosZin ddbef83
[fix] 불필요한 로그 삭제
RumosZin aa6d478
[test] 낙관적 락의 적용으로 충돌하는 트랜잭션에 대해 최초의 커밋만 인정하는 로직 구현
RumosZin d0cfdd4
[test] 낙관적 잠금 rollback 발생 시 재시도를 위한 Retry AOP 구현
RumosZin 97b10ca
[test] uploadPhoto, updateEmail에 재시도 AOP 적용
RumosZin 3c69103
[test] spring의 @Retryable으로 낙관적 락 메서드에 재시도 적용
RumosZin 4210ea3
[test] photo_request 테이블에 비관적 락을 적용해 동시성 문제 해결하기
RumosZin 1773de1
Merge branch 'main' into refactor/#45
RumosZin 9851972
Merge branch 'main' into refactor/#45
RumosZin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
src/main/java/gdsc/cau/puangbe/common/annotation/Retry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package gdsc.cau.puangbe.common.annotation; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target(ElementType.METHOD) | ||
public @interface Retry { | ||
} |
38 changes: 38 additions & 0 deletions
38
src/main/java/gdsc/cau/puangbe/common/util/OptimisticLockRetry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package gdsc.cau.puangbe.common.util; | ||
|
||
import jakarta.persistence.OptimisticLockException; | ||
import org.aspectj.lang.ProceedingJoinPoint; | ||
import org.aspectj.lang.annotation.Around; | ||
import org.aspectj.lang.annotation.Aspect; | ||
import org.aspectj.lang.annotation.Pointcut; | ||
import org.hibernate.StaleObjectStateException; | ||
import org.springframework.core.Ordered; | ||
import org.springframework.core.annotation.Order; | ||
import org.springframework.orm.ObjectOptimisticLockingFailureException; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Order(Ordered.LOWEST_PRECEDENCE - 1) | ||
@Aspect | ||
@Component | ||
public class OptimisticLockRetry { | ||
private static final int MAX_RETRIES = 1000; // 최대 1000번 재시도 | ||
private static final int RETRY_DELAY_MS = 100; // 0.1초 간격으로 재시도 | ||
|
||
@Pointcut("@annotation(Retry)") | ||
public void retry() { | ||
} | ||
|
||
@Around("retry()") | ||
public Object retryOptimisticLock(ProceedingJoinPoint joinPoint) throws Throwable { | ||
Exception exceptionHolder = null; | ||
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) { | ||
try { | ||
return joinPoint.proceed(); | ||
} catch (OptimisticLockException | ObjectOptimisticLockingFailureException | StaleObjectStateException e) { | ||
exceptionHolder = e; | ||
Thread.sleep(RETRY_DELAY_MS); | ||
} | ||
} | ||
throw exceptionHolder; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
src/test/java/gdsc/cau/puangbe/photorequest/service/PhotoConcurrencyTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package gdsc.cau.puangbe.photorequest.service; | ||
|
||
import gdsc.cau.puangbe.photo.service.PhotoServiceImpl; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
|
||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
@SpringBootTest | ||
public class PhotoConcurrencyTest { | ||
|
||
@Autowired | ||
private PhotoServiceImpl photoService; | ||
|
||
@Autowired | ||
private PhotoRequestServiceImpl photoRequestService; | ||
|
||
@Test | ||
void 수정_조회가_동시에_발생하는_경우_테스트() throws InterruptedException { | ||
|
||
Long userId = 1000L; // 테스트할 사용자 ID | ||
String email = "[email protected]"; // 변경할 이메일 | ||
|
||
CountDownLatch latch = new CountDownLatch(2); | ||
ExecutorService executor = Executors.newFixedThreadPool(2); | ||
|
||
AtomicInteger successCount = new AtomicInteger(); | ||
AtomicInteger failCount = new AtomicInteger(); | ||
|
||
// uploadPhoto 호출 | ||
executor.submit(() -> { | ||
try { | ||
photoService.uploadPhoto(998L, "http://example.com/image.jpg"); | ||
successCount.incrementAndGet(); | ||
} catch (Exception e) { | ||
System.out.println(e.getMessage()); | ||
failCount.incrementAndGet(); | ||
} finally { | ||
latch.countDown(); | ||
} | ||
}); | ||
|
||
// modifyEmail 호출 | ||
executor.submit(() -> { | ||
try { | ||
Thread.sleep(100); | ||
photoRequestService.updateEmail(userId, email); | ||
successCount.incrementAndGet(); | ||
} catch (Exception e) { | ||
System.out.println(e.getMessage()); | ||
failCount.incrementAndGet(); | ||
} finally { | ||
latch.countDown(); | ||
} | ||
}); | ||
|
||
latch.await(); // 두 개의 쓰레드가 완료될 때까지 대기 | ||
executor.shutdown(); | ||
|
||
System.out.println("success count: " + successCount.get()); | ||
System.out.println("fail count: " + failCount.get()); | ||
Comment on lines
+64
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. System.out보다는 log를 활용하시는 게 더 적합할 것 같습니다. |
||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트명을 한글로 작성하셔도 되지만 @DisplayName 어노테이션으로 별도로 분리하시는 것도 좋습니다.