From 64214e34f9e8e157260c99eeae8688099e8fe7a0 Mon Sep 17 00:00:00 2001 From: ihun-il Date: Wed, 4 Dec 2024 21:40:52 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20=EC=B1=84?= =?UTF-8?q?=ED=8C=85=201=EB=B6=84=EB=A7=88=EB=8B=A4=20DB=EC=97=90=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20-=20scheduler=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=201=EB=B6=84=EB=A7=88=EB=8B=A4=20DB?= =?UTF-8?q?=EC=97=90=20=EC=B1=84=ED=8C=85=20=EC=A0=80=EC=9E=A5=20-=20Batch?= =?UTF-8?q?=20insert=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=ED=95=9C=20=EB=B2=88=EC=97=90=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/repository/ChatJdbcRepository.java | 59 ++++++++++++++ .../chat/service/ChatRedisService.java | 59 ++++++++++++++ .../chat/service/ChatWriteBackScheduling.java | 79 +++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 src/main/java/com/palettee/chat/repository/ChatJdbcRepository.java create mode 100644 src/main/java/com/palettee/chat/service/ChatWriteBackScheduling.java diff --git a/src/main/java/com/palettee/chat/repository/ChatJdbcRepository.java b/src/main/java/com/palettee/chat/repository/ChatJdbcRepository.java new file mode 100644 index 00000000..abe37c37 --- /dev/null +++ b/src/main/java/com/palettee/chat/repository/ChatJdbcRepository.java @@ -0,0 +1,59 @@ +package com.palettee.chat.repository; + +import com.palettee.chat.domain.Chat; +import com.palettee.chat.domain.ChatImage; +import lombok.RequiredArgsConstructor; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class ChatJdbcRepository { + + private final JdbcTemplate jdbcTemplate; + + public void batchInsertChats(List chats, List chatImages) { + String sql = "INSERT INTO chat" + + "(chat_id, user_id, chat_room_id, content, send_at) VALUE(?, ?, ?, ?, ?)"; + + jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + Chat chat = chats.get(i); + ps.setString(1, chat.getId()); + ps.setLong(2, chat.getUser().getId()); + ps.setLong(3, chat.getChatRoom().getId()); + ps.setString(4, chat.getContent()); + ps.setTimestamp(5, Timestamp.valueOf(chat.getSendAt())); + } + + @Override + public int getBatchSize() { + return chats.size(); + } + }); + + String chatImageSql = "INSERT INTO chat_image" + + "(chat_id, image_url) VALUE(?, ?)"; + jdbcTemplate.batchUpdate(chatImageSql, new BatchPreparedStatementSetter() { + + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + ChatImage chatImage = chatImages.get(i); + ps.setString(1, chatImage.getChat().getId()); + ps.setString(2, chatImage.getImageUrl()); + } + + @Override + public int getBatchSize() { + return chatImages.size(); + } + }); + } +} diff --git a/src/main/java/com/palettee/chat/service/ChatRedisService.java b/src/main/java/com/palettee/chat/service/ChatRedisService.java index d01a0479..677727e7 100644 --- a/src/main/java/com/palettee/chat/service/ChatRedisService.java +++ b/src/main/java/com/palettee/chat/service/ChatRedisService.java @@ -68,7 +68,66 @@ public ChatResponse addChat(String email, Long chatRoomId, ChatRequest chatReque return chatResponse; } + public ChatCustomResponse getChats(Long chatRoomId, int size, LocalDateTime lastSendAt) { + String chatRoomIdStr = TypeConverter.LongToString(chatRoomId); + long offset = 0; + LocalDateTime findSendAt = lastSendAt; + + if (lastSendAt == null) { + log.info("Local = {}",LocalDateTime.now()); + findSendAt = LocalDateTime.now(); + } else { + offset = 1; + } + + Double cursor = TypeConverter.LocalDateTimeToDouble(findSendAt); + log.info("cursor = {}", cursor); + Set objects + = zSetOperations.reverseRangeByScore(chatRoomIdStr, Double.NEGATIVE_INFINITY, cursor, offset, size+1); + List results = objects.stream().collect(Collectors.toList()); + + log.info("results size = {}", results.size()); + + if(results.size() <= size) { + ChatCustomResponse chatDataInDB = findOtherChatDataInDB(results, lastSendAt, chatRoomId, size - results.size()); + + if(!results.isEmpty()) { + for(ChatResponse chatResponse : chatDataInDB.getChats()) { + results.add(chatResponse); + } + return ChatCustomResponse.toResponseFromDto(results, chatDataInDB.isHasNext(), chatDataInDB.getLastSendAt()); + } + + return chatDataInDB; + } + + LocalDateTime nextSendAt = TypeConverter.StringToLocalDateTime(results.get(size - 1).getSendAt()); + List chats = results.subList(0, size); + return ChatCustomResponse.toResponseFromDto(chats, true, nextSendAt); + } + + public ChatCustomResponse findOtherChatDataInDB(List results, LocalDateTime lastSendAt, + Long chatRoomId, int size) { + if(!results.isEmpty()) { + lastSendAt = TypeConverter.StringToLocalDateTime(results.get(results.size() - 1).getSendAt()); + } + ChatCustomResponse chatNoOffset = chatCustomRepository.findChatNoOffset(chatRoomId, size, lastSendAt); + + if (!chatNoOffset.getChats().isEmpty()) { + cachingDBDataToRedis(chatNoOffset.getChats()); + } + return chatNoOffset; + } + + public void cachingDBDataToRedis(List chatsInDB) { + for(ChatResponse chatResponse : chatsInDB) { + LocalDateTime sendAt = TypeConverter.StringToLocalDateTime(chatResponse.getSendAt()); + redisTemplate + .opsForZSet() + .add(TypeConverter.LongToString(chatResponse.getChatRoomId()), chatResponse, TypeConverter.LocalDateTimeToDouble(sendAt)); + } + } private User getUser(String email) { return userRepository diff --git a/src/main/java/com/palettee/chat/service/ChatWriteBackScheduling.java b/src/main/java/com/palettee/chat/service/ChatWriteBackScheduling.java new file mode 100644 index 00000000..21065d19 --- /dev/null +++ b/src/main/java/com/palettee/chat/service/ChatWriteBackScheduling.java @@ -0,0 +1,79 @@ +package com.palettee.chat.service; + +import com.palettee.chat.controller.dto.response.ChatResponse; +import com.palettee.chat.domain.Chat; +import com.palettee.chat.domain.ChatImage; +import com.palettee.chat.repository.ChatJdbcRepository; +import com.palettee.chat_room.domain.ChatRoom; +import com.palettee.chat_room.service.ChatRoomService; +import com.palettee.user.domain.User; +import com.palettee.user.exception.UserNotFoundException; +import com.palettee.user.repository.UserRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.*; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +@Slf4j +public class ChatWriteBackScheduling { + private final RedisTemplate redisTemplate; + private final UserRepository userRepository; + private final ChatRoomService chatRoomService; + private final ChatJdbcRepository chatJdbcRepository; + + public ChatWriteBackScheduling(@Qualifier("chatRedisTemplate") RedisTemplate redisTemplate, + UserRepository userRepository, + ChatRoomService chatRoomService, + ChatJdbcRepository chatJdbcRepository) { + this.redisTemplate = redisTemplate; + this.userRepository = userRepository; + this.chatRoomService = chatRoomService; + this.chatJdbcRepository = chatJdbcRepository; + } + + // Todo user와 chatRoom 조회는 성능이 떨어진다. 그니깐 중간에 DTO를 하나 두어서 필요한 정보만 넣고 batchInsert를 하자. + // Todo 하지만 연관관계 매핑에서 문제가 발생함 이 부분 질문 사항 + @Scheduled(cron = "0 0/1 * * * *") + public void writeBack() { + BoundZSetOperations setOperation = redisTemplate.boundZSetOps("NEW_CHAT"); + + ScanOptions scanOptions = ScanOptions.scanOptions().build(); + + List chatList = new ArrayList<>(); + List chatImageList = new ArrayList<>(); + + Cursor> cursor = setOperation.scan(scanOptions); + while (cursor.hasNext()) { + ZSetOperations.TypedTuple tupleChatResponse = cursor.next(); + ChatResponse chatResponse = tupleChatResponse.getValue(); + + User user = getUser(chatResponse.getEmail()); + ChatRoom chatRoom = chatRoomService.getChatRoom(chatResponse.getChatRoomId()); + + Chat chatEntity = chatResponse.toChatEntity(user, chatRoom); + chatList.add(chatEntity); + + if(chatResponse.getImgUrls() != null || !chatResponse.getImgUrls().isEmpty()) { + for(ChatImage chatImage : chatResponse.toChatImageEntities(chatEntity)) { + chatImageList.add(chatImage); + } + } + } + + if(!chatList.isEmpty() || !chatImageList.isEmpty()) { + chatJdbcRepository.batchInsertChats(chatList, chatImageList); + redisTemplate.delete("NEW_CHAT"); + } + } + + private User getUser(String email) { + return userRepository + .findByEmail(email) + .orElseThrow(() -> UserNotFoundException.EXCEPTION); + } +}