-
Notifications
You must be signed in to change notification settings - Fork 0
[fix] 게시글 좋아요 500 error(deadlock) 해결 #334
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
base: develop
Are you sure you want to change the base?
Conversation
…like-pessimistic-lock
Walkthrough게시글 좋아요의 동시성·정합성 문제 해결을 위해 Feed/Record/Vote 조회에 PESSIMISTIC_WRITE 락 기반의 read-for-update 경로를 추가하고, PostLikeService 흐름을 업데이트했으며 k6 부하 테스트 스크립트 3개와 동시성 단위 테스트를 추가했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant PostLikeService
participant PostHandler
participant CommandPort
participant JpaRepository
participant DB
Client->>PostLikeService: changeLikeStatusPost(command)
PostLikeService->>PostHandler: findPostForUpdate(type, postId)
PostHandler->>CommandPort: getByIdOrThrowForUpdate(id)
CommandPort->>JpaRepository: findByPostIdForUpdate(postId)
JpaRepository->>DB: SELECT ... FOR UPDATE (PESSIMISTIC_WRITE)
rect rgb(220,235,255)
note over DB: 다른 트랜잭션은 대기
end
DB-->>JpaRepository: Entity + lock
JpaRepository-->>CommandPort: Optional<Entity>
CommandPort-->>PostHandler: Domain entity
PostHandler-->>PostLikeService: CountUpdatable
rect rgb(220,255,220)
PostLikeService->>PostLikeService: 사전 like count 업데이트 및 persist
end
PostLikeService->>PostLikeService: 좋아요/취소 로직 실행
rect rgb(255,235,235)
PostLikeService->>PostLikeService: (if like) sendNotifications()
end
PostLikeService-->>Client: 응답(성공/오류)
rect rgb(220,235,255)
note over DB: 트랜잭션 종료 시 락 해제
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
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. Comment |
Test Results488 tests 488 ✅ 44s ⏱️ Results for commit 7677566. ♻️ This comment has been updated with latest results. |
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.
Actionable comments posted: 8
🧹 Nitpick comments (1)
src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/record/RecordJpaRepository.java (1)
21-23: 비관적 락 타임아웃 설정 추가 권장
PESSIMISTIC_WRITE락이 올바르게 적용되었습니다. 그러나 락 대기 시 무한 대기를 방지하기 위해@QueryHints로 타임아웃을 설정하는 것이 좋습니다.다음 diff를 적용하여 타임아웃을 추가하세요:
@Lock(LockModeType.PESSIMISTIC_WRITE) +@QueryHints(@QueryHint(name = "jakarta.persistence.lock.timeout", value = "3000")) @Query("SELECT r FROM RecordJpaEntity r WHERE r.postId = :postId") Optional<RecordJpaEntity> findByPostIdForUpdate(@Param("postId") Long postId);import 추가:
import jakarta.persistence.QueryHint; import org.springframework.data.jpa.repository.QueryHints;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (17)
loadtest/feed/feed_like_concurrency_test1.js(1 hunks)loadtest/feed/feed_like_concurrency_test2.js(1 hunks)loadtest/feed/feed_like_concurrency_test3.js(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedJpaRepository.java(2 hunks)src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java(1 hunks)src/main/java/konkuk/thip/post/application/service/PostLikeService.java(1 hunks)src/main/java/konkuk/thip/post/application/service/handler/PostHandler.java(1 hunks)src/main/java/konkuk/thip/roompost/adapter/out/persistence/RecordCommandPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/roompost/adapter/out/persistence/VoteCommandPersistenceAdapter.java(1 hunks)src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/record/RecordJpaRepository.java(2 hunks)src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/vote/VoteJpaRepository.java(2 hunks)src/main/java/konkuk/thip/roompost/application/port/out/RecordCommandPort.java(1 hunks)src/main/java/konkuk/thip/roompost/application/port/out/VoteCommandPort.java(1 hunks)src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeLikeStatusApiTest.java(1 hunks)src/test/java/konkuk/thip/feed/concurrency/FeedChangeLikeStatusConcurrencyTest.java(1 hunks)src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusApiTest.java(2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-07-26T06:09:00.850Z
Learnt from: seongjunnoh
Repo: THIP-TextHip/THIP-Server PR: 101
File: src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java:36-39
Timestamp: 2025-07-26T06:09:00.850Z
Learning: THIP 프로젝트에서 Record와 Vote는 Room에 속하지만 Feed는 Room에 속하지 않는 구조이며, 댓글 작성 시 Record/Vote에 대해서만 사용자가 해당 Room의 참가자인지 검증이 필요하다.
Applied to files:
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusApiTest.java
📚 Learning: 2025-07-03T03:05:05.031Z
Learnt from: seongjunnoh
Repo: THIP-TextHip/THIP-Server PR: 43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.
Applied to files:
src/main/java/konkuk/thip/roompost/application/port/out/RecordCommandPort.javasrc/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.javasrc/main/java/konkuk/thip/roompost/application/port/out/VoteCommandPort.java
📚 Learning: 2025-09-01T13:18:13.652Z
Learnt from: seongjunnoh
Repo: THIP-TextHip/THIP-Server PR: 287
File: src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserJpaRepository.java:8-14
Timestamp: 2025-09-01T13:18:13.652Z
Learning: seongjunnoh는 JpaRepository의 findById 메서드 재정의보다는 도메인별 명시적 메서드(findByUserId, findByRoomId 등)를 정의하여 Hibernate Filter 적용을 보장하는 방식을 선호하며, 이를 통해 더 안전하고 의도가 명확한 코드 구조를 구축한다.
Applied to files:
src/main/java/konkuk/thip/roompost/adapter/out/persistence/RecordCommandPersistenceAdapter.java
📚 Learning: 2025-07-14T14:19:38.796Z
Learnt from: buzz0331
Repo: THIP-TextHip/THIP-Server PR: 75
File: src/main/java/konkuk/thip/vote/adapter/out/persistence/VoteQueryRepositoryImpl.java:50-83
Timestamp: 2025-07-14T14:19:38.796Z
Learning: Vote와 VoteItem 엔티티는 자주 함께 사용되므로, N+1 문제를 방지하기 위해 양방향 매핑과 fetch join을 고려하는 것이 좋습니다. 특히 기록장 조회 API 등에서도 함께 사용될 가능성이 높습니다.
Applied to files:
src/main/java/konkuk/thip/roompost/adapter/out/persistence/VoteCommandPersistenceAdapter.javasrc/main/java/konkuk/thip/roompost/application/port/out/VoteCommandPort.java
🧬 Code graph analysis (4)
loadtest/feed/feed_like_concurrency_test2.js (2)
loadtest/feed/feed_like_concurrency_test1.js (8)
BASE_URL(5-5)setup(21-34)tokens(22-22)likeStatus(23-23)userId(26-26)res(27-27)res(56-56)token(38-38)loadtest/feed/feed_like_concurrency_test3.js (10)
BASE_URL(5-5)setup(30-43)maxVUs(32-32)tokens(33-33)userId(36-36)res(37-37)res(56-56)res(95-95)token(48-48)token(73-73)
loadtest/feed/feed_like_concurrency_test1.js (2)
loadtest/feed/feed_like_concurrency_test2.js (10)
BASE_URL(5-5)options(8-20)options(8-20)setup(23-38)tokens(26-26)likeStatus(27-27)userId(30-30)res(31-31)res(60-60)token(42-42)loadtest/feed/feed_like_concurrency_test3.js (11)
BASE_URL(5-5)options(8-27)options(8-27)setup(30-43)tokens(33-33)userId(36-36)res(37-37)res(56-56)res(95-95)token(48-48)token(73-73)
loadtest/feed/feed_like_concurrency_test3.js (2)
loadtest/feed/feed_like_concurrency_test1.js (10)
BASE_URL(5-5)options(9-18)options(9-18)setup(21-34)tokens(22-22)userId(26-26)res(27-27)res(56-56)token(38-38)handleSummary(75-79)loadtest/feed/feed_like_concurrency_test2.js (11)
BASE_URL(5-5)options(8-20)options(8-20)setup(23-38)maxVUs(25-25)tokens(26-26)userId(30-30)res(31-31)res(60-60)token(42-42)handleSummary(79-83)
src/test/java/konkuk/thip/feed/concurrency/FeedChangeLikeStatusConcurrencyTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
TestEntityFactory(35-417)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (10)
src/main/java/konkuk/thip/post/application/service/handler/PostHandler.java (1)
34-40: LGTM! 일관된 패턴으로 update용 조회 메서드 추가기존
findPost메서드와 동일한 구조로 update-lock 경로를 제공하여 코드 일관성이 유지됩니다.src/main/java/konkuk/thip/roompost/adapter/out/persistence/RecordCommandPersistenceAdapter.java (1)
59-63: LGTM! Adapter 레이어의 update-lock 경로 구현Repository의
findByPostIdForUpdate를 호출하고 도메인 엔티티로 매핑하는 일관된 패턴입니다.src/main/java/konkuk/thip/roompost/application/port/out/VoteCommandPort.java (1)
23-31: LGTM! Port 인터페이스의 update-lock 경로 추가기존
findById/getByIdOrThrow패턴과 일관되게 update용 메서드를 제공합니다. default 메서드를 통해 예외 처리를 캡슐화한 것도 좋습니다.src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java (1)
47-51: LGTM! Feed Adapter의 update-lock 경로 구현다른 도메인(Record, Vote)과 일관된 패턴으로 구현되어 있습니다.
src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java (1)
15-23: LGTM! Feed Port의 update-lock 경로 추가다른 CommandPort들(VoteCommandPort, RecordCommandPort)과 동일한 패턴으로 일관성이 유지됩니다.
src/main/java/konkuk/thip/roompost/adapter/out/persistence/VoteCommandPersistenceAdapter.java (1)
84-88: LGTM! Vote Adapter의 update-lock 경로 구현다른 Adapter들과 일관된 패턴으로 구현되어 있습니다.
src/main/java/konkuk/thip/roompost/application/port/out/RecordCommandPort.java (1)
18-26: LGTM! Record Port의 update-lock 경로 추가다른 CommandPort들과 동일한 패턴으로 일관성이 잘 유지됩니다.
src/test/java/konkuk/thip/feed/concurrency/FeedChangeLikeStatusConcurrencyTest.java (1)
32-58: 테스트 설정이 적절합니다.동시성 테스트를 위한 기본 설정이 올바르게 구성되어 있습니다.
src/main/java/konkuk/thip/roompost/adapter/out/persistence/repository/vote/VoteJpaRepository.java (1)
21-23: 비관적 락 구현이 올바릅니다.
PESSIMISTIC_WRITE락을 사용한findByPostIdForUpdate메서드가 올바르게 구현되었습니다. 데드락 해결을 위한 적절한 접근 방식입니다.src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedJpaRepository.java (1)
21-23: 비관적 락 구현이 올바릅니다.
PESSIMISTIC_WRITE락을 사용한findByPostIdForUpdate메서드가 올바르게 구현되었습니다. Feed 엔티티에 대한 동시성 제어가 적절하게 적용되었습니다.
src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeLikeStatusApiTest.java
Outdated
Show resolved
Hide resolved
| @Test | ||
| public void concurrentLikeToggleTest() throws InterruptedException { | ||
|
|
||
| int threadCount = 2; | ||
| int repeat = 10; // 스레드별 몇 번 반복할지 | ||
| ExecutorService executor = Executors.newFixedThreadPool(threadCount); | ||
| CountDownLatch latch = new CountDownLatch(threadCount * repeat); | ||
|
|
||
| AtomicInteger successCount = new AtomicInteger(); | ||
| AtomicInteger failCount = new AtomicInteger(); | ||
|
|
||
| // 각 스레드별로 현재 상태(true/false)를 관리하기 위한 배열 | ||
| boolean[] likeStatus = new boolean[threadCount]; | ||
|
|
||
| for (int i = 0; i < threadCount; i++) { | ||
| final int userIndex = i; | ||
| executor.submit(() -> { | ||
| likeStatus[userIndex] = true; | ||
| for (int r = 0; r < repeat; r++) { | ||
| boolean isLike = likeStatus[userIndex]; | ||
| try { | ||
| // 각 스레드별로 서로 다른 user를 사용하도록 user1, user2 분기 처리 | ||
| Long userId = (userIndex == 0) ? user1.getUserId() : user2.getUserId(); | ||
|
|
||
| postLikeService.changeLikeStatusPost( | ||
| new PostIsLikeCommand(userId, feed.getPostId(), PostType.FEED, isLike) | ||
| ); | ||
| successCount.getAndIncrement(); | ||
| // 성공했을 때만 현재 상태를 반전 | ||
| likeStatus[userIndex] = !likeStatus[userIndex]; | ||
| } catch (Exception e) { | ||
| log.error(e.getMessage(), e); | ||
| failCount.getAndIncrement(); | ||
| } finally { | ||
| latch.countDown(); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| latch.await(); | ||
| executor.shutdown(); | ||
|
|
||
| // then | ||
| assertAll( | ||
| () -> assertThat(successCount.get()).isEqualTo(threadCount * repeat), | ||
| () -> assertThat(failCount.get()).isEqualTo(0) | ||
| ); | ||
| } |
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.
최종 데이터베이스 상태 검증이 누락되었습니다.
현재 테스트는 작업 성공 횟수만 확인하지만, 실제 데이터베이스의 최종 상태(좋아요 개수, PostLike 엔티티 존재 여부)를 검증하지 않습니다. 각 사용자가 10번씩 토글하므로 최종적으로 두 사용자 모두 좋아요 상태여야 하며, feed의 likeCount는 2여야 합니다.
테스트 마지막에 다음 검증을 추가하는 것을 권장합니다:
latch.await();
executor.shutdown();
+
+// 최종 데이터베이스 상태 검증
+FeedJpaEntity finalFeed = feedJpaRepository.findById(feed.getPostId()).orElseThrow();
+long finalLikeCount = postLikeQueryPort.countByPostId(feed.getPostId());
// then
assertAll(
() -> assertThat(successCount.get()).isEqualTo(threadCount * repeat),
- () -> assertThat(failCount.get()).isEqualTo(0)
+ () -> assertThat(failCount.get()).isEqualTo(0),
+ () -> assertThat(finalFeed.getLikeCount()).isEqualTo(2),
+ () -> assertThat(finalLikeCount).isEqualTo(2)
);🤖 Prompt for AI Agents
In
src/test/java/konkuk/thip/feed/concurrency/FeedChangeLikeStatusConcurrencyTest.java
around lines 61 to 109, the test only asserts success/fail counters but misses
verifying the final DB state; update the end of the test to reload the feed and
post-like data from the repository (or service) after
latch.await()/executor.shutdown(), then assert that each user has a PostLike
record with isLike==true and that feed.getLikeCount() (reloaded) equals 2 (or
the expected value), ensuring you use repository/service methods to fetch fresh
entities (and wrap reload/asserts in the same transactional/test context if
needed).
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.
코리가 말하는 것처럼 최종적으로 마지막에 DB 정합성이 깨지는지 확인하는 것도 좋을 것 같네욥
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.
@buzz0331, 좋은 지적입니다! 동시성 테스트에서 최종 DB 정합성 검증을 추가하는 것이 중요하네요.
다만 테스트 로직을 분석해보니, 각 사용자가 true로 시작해서 10번 토글하므로:
- 1번째: 좋아요 추가
- 2번째: 좋아요 취소
- 3번째: 좋아요 추가
- ...
- 10번째: 좋아요 취소
최종적으로 두 사용자 모두 좋아요 취소 상태가 되어, likeCount는 0이 되어야 합니다.
다음과 같이 최종 DB 상태 검증을 추가하는 것을 권장합니다:
latch.await();
executor.shutdown();
// 최종 데이터베이스 상태 검증
FeedJpaEntity finalFeed = feedJpaRepository.findById(feed.getPostId()).orElseThrow();
// then
assertAll(
() -> assertThat(successCount.get()).isEqualTo(threadCount * repeat),
() -> assertThat(failCount.get()).isEqualTo(0),
() -> assertThat(finalFeed.getLikeCount()).isEqualTo(0),
() -> assertThat(finalFeed.getLikeCount()).as("DB의 likeCount와 실제 상태가 일치해야 함").isEqualTo(0)
);더 완벽한 검증을 위해서는 PostLike 엔티티의 실제 개수도 확인하면 좋습니다. 만약 PostLikeCrudRepository 같은 레포지토리가 있다면:
@Autowired private PostLikeCrudRepository postLikeCrudRepository;
// 테스트 마지막에
long actualPostLikeCount = postLikeCrudRepository.countByPostIdAndIsLike(feed.getPostId(), true);
assertThat(actualPostLikeCount).isEqualTo(0);이렇게 하면 동시성 제어가 제대로 작동해서 데이터 정합성이 유지되는지 확실하게 검증할 수 있습니다! 👍
🧠 Learnings used
Learnt from: buzz0331
Repo: THIP-TextHip/THIP-Server PR: 309
File: src/main/java/konkuk/thip/notification/application/service/RoomNotificationOrchestratorSyncImpl.java:36-44
Timestamp: 2025-09-23T08:31:05.161Z
Learning: buzz0331은 기술적 이슈에 대해 실용적인 해결책을 제시하면서도 과도한 엔지니어링을 피하는 균형감을 선호한다. 복잡도 대비 실제 발생 가능성을 고려하여 "굳이" 불필요한 솔루션보다는 심플함을 유지하는 것을 중요하게 생각한다.
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusApiTest.java
Outdated
Show resolved
Hide resolved
src/test/java/konkuk/thip/room/adapter/in/web/RoomPostChangeLikeStatusApiTest.java
Outdated
Show resolved
Hide resolved
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.
수고하셨습니다~ 문서가 아주 깔끔하네여 👍🏻 👍🏻
희진님도 찾아보셨겠지만 업데이트 충돌이 잦은 환경에서는 낙관락보다는 비즈니스 로직 성공을 반드시 보장해주는 비관락을 권장하는 것으로 알고있어욥
다만, 현재 희진님이 작성해두신 노션을 확인해보니 비관락을 걸었을때 대략 300 RPS까지 오르고 요청 실패가 한번도 없었던 것으로 확인되는데 비관락이 기본적으로 명시적으로 락을 걸면서 리소스 접근 자체를 막는거다보니 더 높은 RPS에서 과연 request time out이 나지 않고 버틸 수 있을지 궁금해지네요.
RPS를 조금 비현실적이더라도 비약적으로 높여보면서 request time out이 나는 지점을 확인하고 그 다음 단계인 Redis 도입, 이벤트 처리를 적용시켜보는 것도 좋을 것 같다는 생각입니다!
| @Test | ||
| public void concurrentLikeToggleTest() throws InterruptedException { | ||
|
|
||
| int threadCount = 2; | ||
| int repeat = 10; // 스레드별 몇 번 반복할지 | ||
| ExecutorService executor = Executors.newFixedThreadPool(threadCount); | ||
| CountDownLatch latch = new CountDownLatch(threadCount * repeat); | ||
|
|
||
| AtomicInteger successCount = new AtomicInteger(); | ||
| AtomicInteger failCount = new AtomicInteger(); | ||
|
|
||
| // 각 스레드별로 현재 상태(true/false)를 관리하기 위한 배열 | ||
| boolean[] likeStatus = new boolean[threadCount]; | ||
|
|
||
| for (int i = 0; i < threadCount; i++) { | ||
| final int userIndex = i; | ||
| executor.submit(() -> { | ||
| likeStatus[userIndex] = true; | ||
| for (int r = 0; r < repeat; r++) { | ||
| boolean isLike = likeStatus[userIndex]; | ||
| try { | ||
| // 각 스레드별로 서로 다른 user를 사용하도록 user1, user2 분기 처리 | ||
| Long userId = (userIndex == 0) ? user1.getUserId() : user2.getUserId(); | ||
|
|
||
| postLikeService.changeLikeStatusPost( | ||
| new PostIsLikeCommand(userId, feed.getPostId(), PostType.FEED, isLike) | ||
| ); | ||
| successCount.getAndIncrement(); | ||
| // 성공했을 때만 현재 상태를 반전 | ||
| likeStatus[userIndex] = !likeStatus[userIndex]; | ||
| } catch (Exception e) { | ||
| log.error(e.getMessage(), e); | ||
| failCount.getAndIncrement(); | ||
| } finally { | ||
| latch.countDown(); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| latch.await(); | ||
| executor.shutdown(); | ||
|
|
||
| // then | ||
| assertAll( | ||
| () -> assertThat(successCount.get()).isEqualTo(threadCount * repeat), | ||
| () -> assertThat(failCount.get()).isEqualTo(0) | ||
| ); | ||
| } |
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.
코리가 말하는 것처럼 최종적으로 마지막에 DB 정합성이 깨지는지 확인하는 것도 좋을 것 같네욥
seongjunnoh
left a comment
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.
확인했습니다! 그런데 비관적 락에 타임아웃 관련해서 명시적으로 설정 하지 않으셨던데, 비즈니스 로직 상 게시글 좋아요에 그렇게 많은 부하가 발생할라나? 라는 의도로 락 타임아웃 설정은 제외하신 걸까요??
mysql은 타임아웃의 디폴트 값이 50초라고 알고 있는데, jpa 환경에서도 이게 유지되는지는 확인해봐야 할 것 같습니다!
개인적으로 락을 걸 때 혹시 모를 장애상황을 대비해서 타임아웃 + 이후 처리(디스코드 알림 or 보상 재시도 등등) 를 고려하는게 좋지 않나 생각합니다!
제가 진행해본 부하테스트(최대 vu200명이 동시에 좋아요 요청/취소를 반복하는경우)로는 락 타임아웃의 요청이 1건도 발생하지않아서 고려하지않았습니다!! 지금은 성준님이 작성한 부하 테스트 스크립트처럼 어떤 특정상황에 좋아요요청이 동시에 몰릴경우 부하 테스트를 진행하려고하는데 이테스트로 리퀘스트 타임아웃이 나는 지점을 확인해보고 그 후 처리도 고려해보겠습니닷!! |
#️⃣ 연관된 이슈
📝 작업 내용
https://separate-snowplow-d8b.notion.site/API-294b701eb0b880088501d46c49b434ad?source=copy_link
📸 스크린샷
💬 리뷰 요구사항
📌 PR 진행 시 이러한 점들을 참고해 주세요
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정
테스트