-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: Prevent like concurrency by pessimistic lock
- Loading branch information
Showing
5 changed files
with
111 additions
and
34 deletions.
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
5 changes: 4 additions & 1 deletion
5
src/main/java/greeny/backend/domain/community/repository/PostLikeRepository.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 |
---|---|---|
@@ -1,12 +1,15 @@ | ||
package greeny.backend.domain.community.repository; | ||
|
||
import greeny.backend.domain.community.entity.PostLike; | ||
import greeny.backend.domain.member.entity.Member; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Lock; | ||
|
||
import javax.persistence.LockModeType; | ||
import java.util.Optional; | ||
|
||
public interface PostLikeRepository extends JpaRepository<PostLike, Long> { | ||
Optional<PostLike> findByPostIdAndLikerId(Long postId, Long likerId); | ||
@Lock(LockModeType.PESSIMISTIC_WRITE) | ||
Optional<PostLike> findByPostIdAndLiker(Long postId, Member liker); | ||
boolean existsByPostIdAndLikerId(Long postId, Long likerId); | ||
} |
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
77 changes: 77 additions & 0 deletions
77
src/test/java/greeny/backend/service/PostLikeServiceTest.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,77 @@ | ||
package greeny.backend.service; | ||
|
||
import greeny.backend.domain.community.entity.Post; | ||
import greeny.backend.domain.community.repository.PostLikeRepository; | ||
import greeny.backend.domain.community.repository.PostRepository; | ||
import greeny.backend.domain.community.service.PostLikeService; | ||
import greeny.backend.domain.member.entity.Member; | ||
import greeny.backend.domain.member.entity.Role; | ||
import greeny.backend.domain.member.repository.MemberRepository; | ||
import lombok.extern.slf4j.Slf4j; | ||
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 static org.assertj.core.api.Assertions.assertThat; | ||
|
||
@SpringBootTest | ||
@Slf4j | ||
class PostLikeServiceTest { | ||
@Autowired | ||
PostLikeService postLikeService; | ||
@Autowired | ||
MemberRepository memberRepository; | ||
@Autowired | ||
PostRepository postRepository; | ||
@Autowired | ||
PostLikeRepository postLikeRepository; | ||
|
||
@Test | ||
void likeConcurrency() throws InterruptedException { | ||
// Given | ||
log.info("test start"); | ||
Member savedWriter = memberRepository.save(createMember("[email protected]")); | ||
Member savedLiker = memberRepository.save(createMember("[email protected]")); | ||
Post savedPost = postRepository.save(createPost(savedWriter)); | ||
|
||
// When | ||
int numberOfThread = 3; | ||
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThread); | ||
CountDownLatch countDownLatch = new CountDownLatch(numberOfThread); | ||
for (int i = 0; i < numberOfThread; i++) { | ||
executorService.submit(() -> { | ||
log.info("Current thread"); | ||
postLikeService.like(savedPost.getId(), savedLiker); | ||
countDownLatch.countDown(); | ||
}); | ||
} | ||
countDownLatch.await(); | ||
|
||
// Then | ||
assertThat(postLikeRepository.existsByPostIdAndLikerId(savedPost.getId(), savedLiker.getId())).isTrue(); | ||
memberRepository.deleteAll(); | ||
postRepository.deleteAll(); | ||
postLikeRepository.deleteAll(); | ||
log.info("test end"); | ||
} | ||
|
||
Member createMember(String email) { | ||
return Member.builder() | ||
.email(email) | ||
.role(Role.ROLE_USER) | ||
.build(); | ||
} | ||
|
||
Post createPost(Member writer) { | ||
return Post.builder() | ||
.writer(writer) | ||
.title("안녕") | ||
.content("반가워!") | ||
.hits(0) | ||
.build(); | ||
} | ||
} |