From f301aa8c2fadd0819eb29345ef14db1dfcd7aa0f Mon Sep 17 00:00:00 2001 From: Dltmd202 Date: Sat, 24 Aug 2024 23:35:15 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=BA=90=EC=8B=B1=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EB=A0=88=EB=94=94=EC=8A=A4=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/chat/config/ChatServiceConfig.kt | 3 + .../api/chat/service/ChatFacadeService.kt | 3 - .../service/ChatUserCacheFacadeService.kt | 18 +++++- .../ChatPipelinedQueryRepository.kt | 64 +++++++++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 infra/src/main/kotlin/com/backgu/amaker/infra/redis/chat/repository/ChatPipelinedQueryRepository.kt diff --git a/api/src/main/kotlin/com/backgu/amaker/api/chat/config/ChatServiceConfig.kt b/api/src/main/kotlin/com/backgu/amaker/api/chat/config/ChatServiceConfig.kt index d9f993ac..f29a456e 100644 --- a/api/src/main/kotlin/com/backgu/amaker/api/chat/config/ChatServiceConfig.kt +++ b/api/src/main/kotlin/com/backgu/amaker/api/chat/config/ChatServiceConfig.kt @@ -15,6 +15,7 @@ import com.backgu.amaker.infra.jpa.chat.repository.ChatRoomRepository import com.backgu.amaker.infra.jpa.chat.repository.ChatRoomUserRepository import com.backgu.amaker.infra.redis.chat.repository.ChatCacheRepository import com.backgu.amaker.infra.redis.chat.repository.ChatRoomUserCacheRepository +import com.backgu.amaker.infra.redis.chat.repository.ChatPipelinedQueryRepository import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -43,12 +44,14 @@ class ChatServiceConfig { fun chatUserCacheService( chatCacheService: ChatCacheService, userCacheService: UserCacheService, + chatPipelinedQueryRepository: ChatPipelinedQueryRepository, chatRoomUserCacheService: ChatRoomUserCacheService, userService: UserService, chatService: ChatService, eventAssignedUserService: EventAssignedUserService, ) = ChatUserCacheFacadeService( chatCacheService, + chatPipelinedQueryRepository, userCacheService, chatRoomUserCacheService, userService, diff --git a/api/src/main/kotlin/com/backgu/amaker/api/chat/service/ChatFacadeService.kt b/api/src/main/kotlin/com/backgu/amaker/api/chat/service/ChatFacadeService.kt index 5aa9631d..b1be3e3c 100644 --- a/api/src/main/kotlin/com/backgu/amaker/api/chat/service/ChatFacadeService.kt +++ b/api/src/main/kotlin/com/backgu/amaker/api/chat/service/ChatFacadeService.kt @@ -113,9 +113,6 @@ class ChatFacadeService( ?.let { return ChatListDto.of(chatQuery, it.map { chatWithUser -> ChatWithUserDto.of(chatWithUser) }) } val chatList = chatQueryService.findAfterChatList(chatQuery.chatRoomId, chatQuery.cursor, chatQuery.size) - for (defaultChatWithUser in chatList) { - println(defaultChatWithUser.id) - } val eventMap = eventService.findEventByIdsToMap(chatList.filter { ChatType.isEventChat(it.chatType) }.map { it.id }) diff --git a/infra/src/main/kotlin/com/backgu/amaker/application/chat/service/ChatUserCacheFacadeService.kt b/infra/src/main/kotlin/com/backgu/amaker/application/chat/service/ChatUserCacheFacadeService.kt index 63f7b942..fbcc60b2 100644 --- a/infra/src/main/kotlin/com/backgu/amaker/application/chat/service/ChatUserCacheFacadeService.kt +++ b/infra/src/main/kotlin/com/backgu/amaker/application/chat/service/ChatUserCacheFacadeService.kt @@ -11,12 +11,14 @@ import com.backgu.amaker.domain.user.User import com.backgu.amaker.infra.redis.chat.data.ChatWithUserCache import com.backgu.amaker.infra.redis.chat.data.DefaultChatWithUserCache import com.backgu.amaker.infra.redis.chat.data.EventChatWithUserCache +import com.backgu.amaker.infra.redis.chat.repository.ChatPipelinedQueryRepository import org.springframework.stereotype.Service import org.springframework.transaction.event.TransactionalEventListener @Service class ChatUserCacheFacadeService( private val chatCacheService: ChatCacheService, + private val chatPipelinedQueryRepository: ChatPipelinedQueryRepository, private val userCacheService: UserCacheService, private val chatRoomUserCacheService: ChatRoomUserCacheService, private val userService: UserService, @@ -103,7 +105,21 @@ class ChatUserCacheFacadeService( .map { chat -> mapChatToDto(chatRoomId, chat, cachedUsersMap) } } - fun mapChatToDto( + fun findAfterChatsWithPipelinedQuery( + chatRoomId: Long, + cursor: Long, + count: Int, + ): List>? = + chatPipelinedQueryRepository.findAfterChats(chatRoomId, cursor, count)?.let { chats -> + userCacheService + .findAllByUserIds(chatRoomUserCacheService.findUserIds(chatRoomId).toList()) + .associateBy { it.id } + .let { cachedUsersMap -> + chats.map { chat -> mapChatToDto(chatRoomId, chat, cachedUsersMap) } + } + } + + private fun mapChatToDto( chatRoomId: Long, chat: ChatWithUserCache<*>, cachedUsersMap: Map, diff --git a/infra/src/main/kotlin/com/backgu/amaker/infra/redis/chat/repository/ChatPipelinedQueryRepository.kt b/infra/src/main/kotlin/com/backgu/amaker/infra/redis/chat/repository/ChatPipelinedQueryRepository.kt new file mode 100644 index 00000000..01008ce9 --- /dev/null +++ b/infra/src/main/kotlin/com/backgu/amaker/infra/redis/chat/repository/ChatPipelinedQueryRepository.kt @@ -0,0 +1,64 @@ +package com.backgu.amaker.infra.redis.chat.repository + +import com.backgu.amaker.infra.redis.chat.data.ChatWithUserCache +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.data.redis.core.script.RedisScript +import org.springframework.stereotype.Repository + +@Repository +class ChatPipelinedQueryRepository( + private val redisChatTemplate: RedisTemplate, + private val timeObjectMapper: ObjectMapper, +) { + companion object { + const val PREFIX = "chatRoom:" + + fun key(chatRoomId: Long) = "$PREFIX$chatRoomId" + + const val AFTER_CHAT_SCRIPT = + """ + local key = KEYS[1] + local cursor = tonumber(ARGV[1]) + local count = tonumber(ARGV[2]) + + local firstResult = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES') + + if #firstResult > 0 then + local firstChat = firstResult[1] + local firstScore = tonumber(firstResult[2]) -- 첫 번째 요소의 score 값을 가져옴 + + if firstScore > cursor then + local result = redis.call('ZRANGEBYSCORE', key, cursor + 1, math.huge, 'LIMIT', 0, count) + if #result > 0 then + return table.concat(result, ",") + else + return "" + end + else + return firstChat + end + else + return nil + end + """ + } + + fun findAfterChats( + chatRoomId: Long, + cursor: Long, + count: Int, + ): List>? { + val result: List = + redisChatTemplate.execute( + RedisScript.of(AFTER_CHAT_SCRIPT.trimIndent(), List::class.java as Class>), + listOf(key(chatRoomId)), + cursor.toString(), + count.toString(), + ) + + return result.map { chat -> + timeObjectMapper.readValue(chat, ChatWithUserCache::class.java) + } + } +} From d420f23215a0a2473ad9bb0a1c9fa9cdbf01a210 Mon Sep 17 00:00:00 2001 From: Dltmd202 Date: Sat, 24 Aug 2024 23:40:39 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=EB=A6=B0=ED=8C=85=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/backgu/amaker/api/chat/config/ChatServiceConfig.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/kotlin/com/backgu/amaker/api/chat/config/ChatServiceConfig.kt b/api/src/main/kotlin/com/backgu/amaker/api/chat/config/ChatServiceConfig.kt index f29a456e..34d6d3b1 100644 --- a/api/src/main/kotlin/com/backgu/amaker/api/chat/config/ChatServiceConfig.kt +++ b/api/src/main/kotlin/com/backgu/amaker/api/chat/config/ChatServiceConfig.kt @@ -14,8 +14,8 @@ import com.backgu.amaker.infra.jpa.chat.repository.ChatRepository import com.backgu.amaker.infra.jpa.chat.repository.ChatRoomRepository import com.backgu.amaker.infra.jpa.chat.repository.ChatRoomUserRepository import com.backgu.amaker.infra.redis.chat.repository.ChatCacheRepository -import com.backgu.amaker.infra.redis.chat.repository.ChatRoomUserCacheRepository import com.backgu.amaker.infra.redis.chat.repository.ChatPipelinedQueryRepository +import com.backgu.amaker.infra.redis.chat.repository.ChatRoomUserCacheRepository import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration