230506 TIL #78 Redis Timeout 설정

김춘복·2023년 5월 6일
0

TIL : Today I Learned

목록 보기
78/571

230506 Today I Learned

실전 프로젝트 6주차. 오늘은 하루종일 프로젝트 브로셔를 작성했다. 트러블 슈팅을 정리하면서 생각보다 이번 프로젝트 때 한 것이 많아 뿌듯했다.


Redis Timeout 설정

  • 문제 :
    데이터 무결성이 이제까지 잘 맞다가 프로젝트가 끝나려고 하니 갑자기 맞질 않았다.
    새로 바꾼 로직에서는 key 확인 절차를 빼고 바로 Redis로 남은 좌석 수를 줄이는데 거기서 에러가 발생해 DB로 남은 좌석 수 차감 절차가 실행되면서 문제가 발생했다.

  • 시도 및 해결:
    catch 절차에서 Exception e를 log로 찍어보며 어떤 에러가 떴는지 확인했다.
    로그에 뜬 것은 RedisCommandTimeoutException 이었다.
    리팩토링 절차 중 CacheConfig에서 Redis timeout 설정을 500ms에서 100ms로 바꿨는데 너무 짧게 설정되어 예외가 발생한 것이었다.

  @Bean
  public RedisConnectionFactory redisConnectionFactory() {
    LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
        .commandTimeout(Duration.ofMillis(100)) // 타임아웃 설정
        .build();

    RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(host, port);

    return new LettuceConnectionFactory(serverConfig, clientConfig);
  }

다시 500으로 설정 후 원래 로직으로 돌리니 문제는 해결되었다.

  • 알게된 것 : Redis 타임아웃 설정은 너무 짧아도 좋지 않다. 적당히 설정해야한다.

  • 최종 예매 서비스 로직

  // 2.예매하기
  @Transactional(isolation = Isolation.READ_COMMITTED)
  public Long makeReservation(ReservationRequestDto dto, User user) {
    try {
      Boolean success = redisRepository.decrementLeftSeatInRedis(dto.getTicketInfoId(),
          dto.getCount());
      if (!success) {
        throw new CustomException(ExceptionType.OUT_OF_TICKET_EXCEPTION);
      }
    } catch (Exception e) {
      if (e instanceof CustomException || e instanceof RedisCommandTimeoutException) {
        throw e;
      }
      decrementLeftSeatInDB(dto);
    }
    return reservationRepository.save(new Reservation(dto, user)).getId();

  }

  // 2-1.캐시 없으면 DB로 좌석수 변경
  private void decrementLeftSeatInDB(ReservationRequestDto dto) {
    TicketInfo ticketInfo = ticketInfoRepository.findByIdWithLock(dto.getTicketInfoId())
        .orElseThrow(
            () -> new CustomException(ExceptionType.NOT_FOUND_TICKET_INFO_EXCEPTION)
        );
    ticketInfo.minusSeats(dto.getCount());
    ticketInfoRepository.save(ticketInfo);
  }
profile
Backend Dev / Data Engineer

0개의 댓글