diff --git a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/chat/service/ChatMemberJoinService.java b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/chat/service/ChatMemberJoinService.java index bceff1af..e4f4cdbf 100644 --- a/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/chat/service/ChatMemberJoinService.java +++ b/pennyway-app-external-api/src/main/java/kr/co/pennyway/api/apis/chat/service/ChatMemberJoinService.java @@ -9,8 +9,6 @@ import kr.co.pennyway.domain.domains.chatroom.exception.ChatRoomErrorCode; import kr.co.pennyway.domain.domains.chatroom.exception.ChatRoomErrorException; import kr.co.pennyway.domain.domains.member.domain.ChatMember; -import kr.co.pennyway.domain.domains.member.exception.ChatMemberErrorCode; -import kr.co.pennyway.domain.domains.member.exception.ChatMemberErrorException; import kr.co.pennyway.domain.domains.user.domain.User; import kr.co.pennyway.domain.domains.user.exception.UserErrorCode; import kr.co.pennyway.domain.domains.user.exception.UserErrorException; @@ -49,11 +47,6 @@ public class ChatMemberJoinService { public Triple execute(Long userId, Long chatRoomId, Integer password) { ChatRoom chatRoom = chatRoomService.readChatRoom(chatRoomId).orElseThrow(() -> new ChatRoomErrorException(ChatRoomErrorCode.NOT_FOUND_CHAT_ROOM)); - if (chatMemberService.isExists(chatRoomId, userId)) { - log.warn("이미 채팅방에 참여한 사용자입니다. chatRoomId: {}, userId: {}", chatRoomId, userId); - throw new ChatMemberErrorException(ChatMemberErrorCode.ALREADY_JOINED); - } - Long currentMemberCount = chatMemberService.countActiveMembers(chatRoomId); if (isFullRoom(currentMemberCount)) { log.warn("채팅방이 가득 찼습니다. chatRoomId: {}", chatRoomId); diff --git a/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/chat/integration/ChatMemberJoinIntegrationTest.java b/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/chat/integration/ChatMemberJoinIntegrationTest.java index 8823a944..34c7d22f 100644 --- a/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/chat/integration/ChatMemberJoinIntegrationTest.java +++ b/pennyway-app-external-api/src/test/java/kr/co/pennyway/api/apis/chat/integration/ChatMemberJoinIntegrationTest.java @@ -15,6 +15,7 @@ import kr.co.pennyway.domain.domains.chatroom.domain.ChatRoom; import kr.co.pennyway.domain.domains.chatroom.exception.ChatRoomErrorCode; import kr.co.pennyway.domain.domains.chatroom.repository.ChatRoomRepository; +import kr.co.pennyway.domain.domains.member.exception.ChatMemberErrorCode; import kr.co.pennyway.domain.domains.member.repository.ChatMemberRepository; import kr.co.pennyway.domain.domains.user.domain.User; import kr.co.pennyway.domain.domains.user.repository.UserRepository; @@ -220,7 +221,44 @@ void successJoinPrivateRoomWithValidPassword() { // then assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + @DisplayName("같은 사용자가 하나의 채팅방에 동시에 100개의 요청을 보내면, 100개의 가입 요청 중 1개만 성공한다") + void concurrentJoinRequestsFromSameUser() throws InterruptedException { + // given + User admin = userRepository.save(UserFixture.GENERAL_USER.toUser()); + ChatRoom chatRoom = chatRoomRepository.save(ChatRoomFixture.PUBLIC_CHAT_ROOM.toEntity(idGenerator.generate())); + chatMemberRepository.save(ChatMemberFixture.ADMIN.toEntity(admin, chatRoom)); + + User user = userRepository.save(UserFixture.GENERAL_USER.toUser()); + + // when + CountDownLatch latch = new CountDownLatch(100); + List> futures = IntStream.range(0, 100) + .mapToObj(i -> CompletableFuture.supplyAsync(() -> { + try { + return JoinResult.from( + postJoining(user, chatRoom.getId(), new ChatMemberReq.Join(null)) + ); + } finally { + latch.countDown(); + } + })) + .toList(); + + latch.await(); + + List results = futures.stream() + .map(CompletableFuture::join) + .toList(); + // then + assertAll( + () -> assertEquals(1, results.stream().filter(JoinResult::isSuccess).count()), + () -> assertEquals(99, results.stream().filter(JoinResult::isAlreadyJoinedError).count()), + () -> assertEquals(2, chatMemberRepository.countByChatRoomIdAndActive(chatRoom.getId())) + ); } private ResponseEntity postJoining(User user, Long chatRoomId, ChatMemberReq.Join request) { @@ -282,5 +320,12 @@ public boolean isFullRoomError() { } return false; } + + public boolean isAlreadyJoinedError() { + if (!isSuccess && body instanceof ErrorResponse errorResponse) { + return errorResponse.getCode().equals(ChatMemberErrorCode.ALREADY_JOINED.causedBy().getCode()); + } + return false; + } } }