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

[Feature] - 좋아요 순 여행기 목록 조회 페이징 캐싱 #631

Merged
merged 10 commits into from
Jan 6, 2025

Conversation

eunjungL
Copy link

@eunjungL eunjungL commented Jan 1, 2025

✅ 작업 내용

  • 좋아요 순 여행기 목록 조회 페이징 캐싱
  • Page 객체 역직렬화를 위한 Deserializer 추가

🙈 참고 사항

Page 객체는 기본 생성자가 없어 역직렬화가 수행되지 않아 역직렬화 모듈을 만들기 위한 클래스가 두 개 추가됐습니다.
리뷰할 때 참고 부탁드려용

@eunjungL eunjungL added the BE label Jan 1, 2025
@eunjungL eunjungL added this to the sprint 8 milestone Jan 1, 2025
@eunjungL eunjungL linked an issue Jan 1, 2025 that may be closed by this pull request
1 task
Copy link

github-actions bot commented Jan 1, 2025

Test Results

 30 files   30 suites   59s ⏱️
296 tests 295 ✅ 1 💤 0 ❌
308 runs  307 ✅ 1 💤 0 ❌

Results for commit 537d2f7.

♻️ This comment has been updated with latest results.

Copy link

@Libienz Libienz left a comment

Choose a reason for hiding this comment

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

안녕하세요 클로버와 페어로 해당 PR에 참가한 리비입니다.

해당 PR은 담백하게 여행기 페이지를 캐싱하는 기능만 담았어요.
페이지 캐싱 작업은 해당 PR로만 끝날 예정은 아니고요, 현재 저희가 고려중인 사항들이 다음 PR들로 이어짐을 말씀드립니다.

현재 저희가 고려중인 사항들은 다음과 같은데 혹시 알고 있는 부분이나, 얘기해보고 싶은 부분들이 있으시다면 자유롭게 코멘트 남겨주시면 감사하겠습니다 🙇🏻‍♂️

  • TravelogueResponse 등 캐싱하는 클래스의 명세(속성 구성)를 변경하게 되면 캐시를 무효화 해야 하는데.. 자동화할 수 있는 방법이 없나?
  • 캐시를 가져오는 과정에서 예외가 발생하면 (역직렬화 실패) 예외 응답이 클라이언트에게 반환되는데 캐시가 실패하면 캐시 예외가 응답에 전파되지 않도록 하는 방법이 없나?
  • @transactional@Cacheable을 함께 적용하면서 발생하는 문제 상황은 없나?
  • 캐시 기능 동작에 대한 테스트 코드가 필요할까? 필요하다면 어떻게 작성되어야 할까?
  • 분산락이 필요한 맥락이 있을까?

Comment on lines +15 to +23
public class PageDeserializer extends JsonDeserializer<PageImpl<?>> {

@Override
public PageImpl<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
List<Object> content = new ArrayList<>();
int pageNumber = 0;
int pageSize = 0;
long totalElements = 0;
Sort sort = Sort.unsorted();
Copy link

Choose a reason for hiding this comment

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

회의에서 말씀드린데로 저와 클로버는 Page를 캐싱했습니다.
그런데 캐싱된 페이지를 꺼내오는 과정에서 PageImpl로의 역직렬화가 실패했었는데요, 이는 PageImpl의 기본 생성자가 존재하지 않아 ObjectMapper가 리플렉션하지 못하기 때문이었습니다.

문제를 해결하기 위해서는 다음의 두가지 방법 중 하나를 선택해야 합니다.

  • PageImpl을 감싸는 Wrapper Class를 만든다.
  • PageImpl 객체 역직렬화를 커스터마이징

저와 클로버는 Wrapper 클래스를 운용하게 되는 경우 어색한 점이 있다고 판단했습니다.
Wrapper 클래스를 운용하게 되면 기존 코드의 수정이 불가피하고 캐싱하는 메서드와 캐싱하지 않는 메서드의 반환타입이 달라지는 부분이 클래스 운용 명세가 숨겨지는 잠재적 위험이라고 생각한 것이 이유에요.

따라서 PageImpl 객체를 커스텀 역직렬화하는 코드를 작성한 것이 해당 코드입니다.

Copy link
Member

Choose a reason for hiding this comment

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

저도 래퍼로 감싸서 해당 래퍼 클래스를 직접 사용하는 것은 좋지 못한 구조라 생각합니다.
캐시 도입이 어노테이션을 넘어서 동작하고 있는 서비스 코드에 변경을 주지 않는 것이 좋다고 생각합니다.

좋은 방향으로 역직렬화 문제를 해결해 주셨네요!

Comment on lines +15 to +21
public class SortDeserializer extends JsonDeserializer<Sort> {

@Override
public Sort deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
List<Sort.Order> orders = new ArrayList<>();
boolean sorted = false;

Copy link

Choose a reason for hiding this comment

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

해당 역직렬화 설정은 PageImpl을 생성하기 위해 필요한 Sort 객체의 역직렬화 로직입니다.

@Libienz
Copy link

Libienz commented Jan 2, 2025

Jmeter를 통해서 캐시 도입전과 도입 후의 성능을 로컬에서 비교해보았습니다.

설정

  • Application 설정
    • 여행기 콘텐츠 1만개
    • 멤버 5000명
    • 멤버 1명당 랜덤한 여행기에 좋아요 1개
  • 서버 설정
    • local 프로파일의 WAS 로컬 구동
    • Docker 이용 로컬에서 MySQL 및 Redis 컨테이너 구동
  • Jmeter 설정
    • 동시 접속자수 (Thread) : 100
    • Ramp-up period : 0
    • Duration: 180초

With No Cache

image

With Cache

image

캐싱을 도입하지 않는 경우 평균 응답속도 85ms, 캐싱을 도입한 경우 평균 응답속도 7ms로 페이징 api는 약 91.76%의 성능 개선이 이루어졌습니다. 그래프를 보시면 캐싱을 진행하는 경우 초기에 캐싱 데이터가 없는 경우 response가 높지만 이후 안정적으로 응답속도가 유지되는 것을 확인하실 수 있습니다.

Copy link
Member

@nak-honest nak-honest left a comment

Choose a reason for hiding this comment

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

역직렬화와 TTL을 30분으로 잡은 부분 이해하였습니다!
속도도 많이 개선되었네요 ㅎㅎ

리비가 말씀해 주신 고민 포인트는 다음 회의 때 같이 이야기해봐도 좋을 것 같습니다!
고생많으셨습니다!!

Comment on lines +15 to +23
public class PageDeserializer extends JsonDeserializer<PageImpl<?>> {

@Override
public PageImpl<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
List<Object> content = new ArrayList<>();
int pageNumber = 0;
int pageSize = 0;
long totalElements = 0;
Sort sort = Sort.unsorted();
Copy link
Member

Choose a reason for hiding this comment

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

저도 래퍼로 감싸서 해당 래퍼 클래스를 직접 사용하는 것은 좋지 못한 구조라 생각합니다.
캐시 도입이 어노테이션을 넘어서 동작하고 있는 서비스 코드에 변경을 주지 않는 것이 좋다고 생각합니다.

좋은 방향으로 역직렬화 문제를 해결해 주셨네요!

@Libienz Libienz merged commit 8b63f77 into develop/be Jan 6, 2025
3 checks passed
@Libienz Libienz deleted the feature/be/#630 branch January 6, 2025 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[Feature] - 좋아요 순 여행기 목록 조회 페이징 캐싱
3 participants