Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1단계 - DB 복제와 캐시] 로빈(임수빈) 미션 제출합니다. #2

Open
wants to merge 8 commits into
base: robinjoon
Choose a base branch
from

Conversation

robinjoon
Copy link

안녕하세요 명오! 처음 만나는 것 같네요. 리뷰 잘 부탁드립니다.

데이터베이스 스키마

반드시 아래 스키마 대로 테이블을 미리 만들어둬야 동작합니다.

CREATE TABLE `coupon` (
  `discountMoney` int NOT NULL,
  `endDate` date DEFAULT NULL,
  `minOrderMoney` int NOT NULL,
  `startDate` date DEFAULT NULL,
  `id` bigint NOT NULL AUTO_INCREMENT,
  `category` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

프로덕션 서버 상황

복제 지연 시간 : 3초 미만

프로덕션 환경에서 복제 지연을 테스트했고, 95%의 복제 지연이 3초 미만으로 발생한다는 것을 확인했다는 가정을 적용했습니다.

�최소 2대 이상의 WAS로 서비스되는 상황을 가정했습니다.

적용한 방법

Redis 를 캐시 저장소로 사용해 쿠폰 생성 시점에 3초 유효기간(복제 지연이 3초 미만이라 가정했으므로)인 캐시를 추가하고, 캐시가 없는 경우에만 읽기 DB에서 데이터를 조회하도록 했습니다. 현재는 로컬에서 도커를 이용해 동작하고 있지만, 실제 프로덕션이라면 Redis는 별개의 서버에서 실행중일 것입니다.

복제 지연이 발생했을 때, 가장 쉽게 이를 해결하는 방법은 쓰기 DB에서 데이터를 다시 읽는 것입니다. 이는 무조건 데이터를 조회할 수 있는 것이 보장되고, 별도의 시스템 구축이 필요하지 않습니다. 그러나, 이 방법은 데이터 수정에서는 사용할 수 없습니다. 데이터가 수정된 경우 복제 지연이 발생한 읽기 DB에서는 이전 데이터가 조회됩니다. 그러나, 별도의 기록(캐시나 수정 일자 기록)이 없다면 이것이 이전 데이터인지 알 수 없습니다.

현재는 쿠폰 생성 기능이기 때문에 엄밀히 말하면 쓰기 DB를 추가로 읽는 방법으로 문제를 해결할 수 있습니다. 하지만, 이는 결국 쓰기 DB를 읽기 용도로 사용하기에 부하 분산의 목적에 맞지 않습니다.

기타 코드 레벨에서 고민할 부분

중복 엔티티 클래스

현재 쿠폰 전체를 캐싱하고 있습니다. 그런데, Redis 를 위한 쿠폰 엔티티 클래스와 JPA를 위한 엔티티 클래스가 동일한 형태임에도 두개 존재합니다. 개선할 방법이 있을것도 같은데 단순히 한 클래스로 통합해버리면 Jpa 레포지토리와 Redis 레포지토리가 모종의 충돌이 발생해 스프링 컨테이너가 실행되지 않더라구요.

Copy link
Member

@HyungHoKim00 HyungHoKim00 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 로빈! 처음 뵙겠습니다.
코드도 잘 구현해주셨고, 캐시를 사용하신 이유도 납득이 가서 특별한 코멘트는 없네요.
domain과 entity를 분리한 설계도 인상깊었습니다.
간단한 코멘트 있으니 확인해주세요.

import org.springframework.data.redis.core.RedisHash;

@Getter
@RedisHash(value = "coupon", timeToLive = 3L)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

95%가 3초 안에 복제가 완료된다고 하셨는데, 굳이 3초로 하신 이유가 있을까요? 사용자 입장에서 생성 후 3초까지는 보이던 쿠폰이, 3초 뒤에는 안보이게 된다면 당황할 수 있을 것 같아요.


private String name;

private int discountMoney;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

db인데 필드명을 snake case로 사용하신 이유가 있나요?

import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;

@Configuration
@EnableJpaRepositories(basePackages = "coupon")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 어노테이션의 basePackage를 JpaCouponRepository가 있는 패키지로 설정하고, @EnableRedisRepositories() 어노테이션을 RedisConfig 등에 달아 주어 basePackage를 RedisCouponRepository가 있는 패키지로 설정하면 코멘트에서 말씀하신 오류를 해결할 수 있는 것으로 알고 있습니다.

@Override
protected Object determineCurrentLookupKey() {
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
return readOnly ? "read" : "write";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오랜만에 보니까 삼항연산자도 반갑네요

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개인적으로 상수화를 크게 신경쓰진 않는 편인데, key값으로 다른 곳에서 사용되는 필드는 public static으로 선언하는 것을 선호합니다. 어떻게 생각하시나요?


import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaCouponRepository extends JpaRepository<CouponEntity, Long> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@repository 어노테이션을 붙이지 않아도 빈으로 등록되나요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants