-
Notifications
You must be signed in to change notification settings - Fork 1
Elasticsearch 게시글 검색에 Redis 도입 #165
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
Conversation
- jmhJar 작업에 Zip64 지원 추가하여 대용량 JMH Jar 패키징 가능 - 특정 환경에서 발생할 수 있는 압축 파일 크기 제한 문제 해결 - JMH 성능 테스트 결과의 안정적 배포 지원을 목표
- RedisConfig에 @EnableConfigurationProperties 추가하여 PostSearchCacheProperties 사용 가능하도록 설정 - Redis 관련 설정 확장성을 높이고 구성 관리 효율성 강화 - Cache 관련 속성 정의 및 유연한 설정 적용 기반 마련
- 검색 결과 캐시 TTL(time-to-live)을 설정할 수 있는 PostSearchCacheProperties 클래스 추가 - 기본 TTL 값은 3분으로 설정 - Spring Boot의 @ConfigurationProperties 사용으로 유연한 구성 지원 - 캐시 TTL 설정을 별도 속성으로 분리하여 유지보수성 강화 및 확장성 확보
- 게시글 검색 결과 캐시에 대한 Redis 기반 구현 클래스 추가 - PostSearchCacheRepository 인터페이스 구현하여 통합 가능성 제공 - 검색 키워드와 페이징 정보를 기반으로 캐시 조회 및 저장 처리 지원 - 검색 캐시 전체 삭제 기능 제공으로 관리 용이성 향상 - 콘텐츠 직렬화/역직렬화 오류 처리 로직 추가로 안정성 강화 - TTL 설정을 사용하여 캐시 만료 시간 관리
- 게시글 관련 이벤트(PostCreated, PostUpdated, PostPublished, PostHidden, PostDeleted) 처리하여 검색 캐시를 자동으로 무효화하는 PostSearchCacheEventHandler 추가 - PostSearchCacheRepository.evictAll 호출로 캐시 무효화 처리 - 이벤트 비동기 처리와 트랜잭션 커밋 이후 실행 보장으로 성능 및 데이터 일관성 강화 - PostSearchCacheEvictCommand를 통한 수동 캐시 무효화 지원으로 유연성 제공 test(cache): add unit tests for PostSearchCacheEventHandler - 게시글 이벤트(PostCreated, PostUpdated 등) 처리 시 캐시 무효화 동작 검증 테스트 추가 - PostSearchCacheEvictCommand 처리에 대한 테스트로 전체 캐시 초기화 로직의 신뢰성 강화
- 게시글 검색 요청 시 Redis 캐시를 조회하고 없을 경우 검색 결과를 캐싱하는 로직 추가 - PostQueryService에 검색 캐싱 관련 fetchAndCacheSearchResults 메서드 구현 - Redis 캐시에 실패하더라도 검색 흐름에 영향을 주지 않도록 기록만 남기도록 설계 - PostFinderTest에 Redis 캐싱 기능을 검증하는 테스트 케이스 추가 - 기존 Elasticsearch 검색에 Redis 캐시 계층 추가로 검색 성능 최적화
- 검색 시나리오별 벤치마킹 클래스를 분리하며 확장 가능한 상태 관리 설계 추가 - AbstractBenchmarkState 추상화로 Cold, Cached, Database SearchState에 공통 로직 재사용 - ColdSearchState에서 캐시 초기화 로직 추가로 콜드 스타트 성능 측정 용이성 제공 - CachedSearchState에서 캐시 적중 상태 벤치마킹 지원 - BenchmarkState의 중복 제거 및 분리로 코드 가독성과 유지보수성 강화 - Elasticsearch 및 데이터베이스 호출의 성능 비교를 위한 명확한 벤치마크 항목 정의
…tates - Gatling 시뮬레이션에 cold cache와 warm cache 상태 추가로 성능 테스트 시나리오 확장 - coldKeyword와 hotKeyword 분리로 캐시되지 않은 상태와 이미 적중된 상태의 검색 성능 측정 가능 - es-cache-warm 및 es-cache-hit 액션 추가로 엘라스틱서치 캐시 시뮬레이션 개선 - dbSearch와 esSearch 호출 흐름에 캐시 관련 단계 추가로 테스트 정확성 강화 - 캐시 초기화부터 캐시 적중까지의 전 과정 성능 테스트 시나리오 제공
- Elasticsearch 검색 결과를 Redis에 캐싱하는 기능 설명 추가 - 검색 응답 지연을 줄이기 위해 3분 TTL을 적용하는 방식 명시 - 프로젝트 주요 기능 목록에 검색 캐시 항목 추가하여 명확성 강화
WalkthroughElasticsearch 게시글 검색 결과에 Redis 기반 캐시를 도입했습니다. 캐시 인터페이스와 Redis 구현체, TTL 설정, 캐시 무효화 이벤트 핸들러, PostQueryService의 캐시 적용, 벤치마크/시뮬레이션 및 테스트와 CI(Redis 서비스) 구성이 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Service as PostQueryService
participant Cache as PostSearchCacheRepository
participant ES as Elasticsearch
Client->>Service: searchPosts(keyword, pageable)
Service->>Cache: find(keyword, pageable)
alt 캐시 히트
Cache-->>Service: Optional[Page]
Service-->>Client: 반환(캐시)
else 캐시 미스
Service->>ES: search request
ES-->>Service: Page<PostSearchResult>
Service->>Cache: save(keyword, pageable, page)
Cache-->>Service: ack
Service-->>Client: 반환(검색결과)
end
sequenceDiagram
participant Post as Post Lifecycle Event
participant Handler as PostSearchCacheEventHandler
participant Cache as PostSearchCacheRepository
Post->>Handler: PostCreated/Updated/Published/Hidden/Deleted
note over Handler: @TransactionalEventListener(AFTER_COMMIT)\n@Async("applicationTaskExecutor")
Handler->>Handler: evict(source, postId)
Handler->>Cache: evictAll()
Cache-->>Handler: completed
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/main/java/dooya/see/application/post/required/PostSearchCacheRepository.java (1)
12-35: 캐시 무효화 전략 개선 제안현재
evictAll()메서드만 제공되어 전체 캐시를 삭제합니다. 특정 게시글 변경 시 관련된 검색 키워드 캐시만 선택적으로 무효화할 수 있는 메서드를 추가하면 성능이 개선될 수 있습니다.다음과 같은 메서드 추가를 고려해보세요:
/** * 검색 캐시 전체를 삭제합니다. */ void evictAll(); + + /** + * 특정 키워드의 캐시를 삭제합니다. + * + * @param keyword 삭제할 검색 키워드 + */ + void evict(String keyword);참고: 현재는 전체 무효화로도 충분할 수 있지만, 트래픽이 증가하면 선택적 무효화가 유용할 것입니다.
src/main/java/dooya/see/adapter/integration/cache/config/PostSearchCacheProperties.java (1)
7-21: TTL 값 검증 추가 고려현재 TTL 설정에 대한 검증이 없어 음수 또는 과도하게 큰 값이 설정될 수 있습니다. 운영 환경에서 잘못된 설정으로 인한 문제를 방지하기 위해 검증을 추가하는 것이 좋습니다.
다음과 같이 검증 로직을 추가할 수 있습니다:
+import jakarta.validation.constraints.Positive; + @ConfigurationProperties(prefix = "see.cache.post-search") public class PostSearchCacheProperties { /** * 검색 결과 캐시 TTL. */ + @Positive private Duration ttl = Duration.ofMinutes(3); public Duration getTtl() { return ttl; } public void setTtl(Duration ttl) { + if (ttl == null || ttl.isNegative() || ttl.isZero()) { + throw new IllegalArgumentException("TTL must be positive"); + } this.ttl = ttl; } }src/main/java/dooya/see/application/post/PostSearchCacheEventHandler.java (1)
59-66: 선택적 캐시 무효화를 고려해보세요.현재 구현은 특정 게시글 변경 시 모든 검색 캐시를 무효화합니다(
evictAll()). 이는 간단하지만, 변경된 게시글과 관련된 검색 결과만 선택적으로 무효화하면 캐시 효율성을 개선할 수 있습니다.예를 들어, 게시글 제목/내용에 특정 키워드가 포함된 경우에만 해당 키워드의 캐시를 무효화하는 방식을 고려할 수 있습니다. 다만, 현재의 전체 무효화 방식도 구현이 단순하고 일관성을 보장하므로, 캐시 부하가 문제가 되지 않는다면 현재 방식을 유지해도 무방합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
README.md(1 hunks)build.gradle.kts(1 hunks)src/gatling/scala/dooya/see/search/PostSearchSimulation.scala(2 hunks)src/jmh/java/dooya/see/benchmark/PostSearchBenchmark.java(3 hunks)src/main/java/dooya/see/adapter/integration/cache/RedisPostSearchCacheRepository.java(1 hunks)src/main/java/dooya/see/adapter/integration/cache/config/PostSearchCacheProperties.java(1 hunks)src/main/java/dooya/see/adapter/integration/cache/config/RedisConfig.java(1 hunks)src/main/java/dooya/see/application/post/PostQueryService.java(4 hunks)src/main/java/dooya/see/application/post/PostSearchCacheEventHandler.java(1 hunks)src/main/java/dooya/see/application/post/required/PostSearchCacheRepository.java(1 hunks)src/test/java/dooya/see/application/post/PostSearchCacheEventHandlerTest.java(1 hunks)src/test/java/dooya/see/application/post/provided/PostFinderTest.java(5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-26T11:11:27.801Z
Learnt from: Do-oya
PR: see-projects/back#124
File: src/main/java/dooya/see/application/post/PostModifyService.java:19-21
Timestamp: 2025-08-26T11:11:27.801Z
Learning: PostModifyService에서 현재 EventListener를 사용하는 이유: 테스트 환경에서 RestrictedTransactionalEventListenerFactory 에러가 발생하여 TransactionalEventListener 적용이 지연됨. 현재는 애플리케이션 레이어에서 멱등성(isAlreadyLiked 체크)으로 안전성을 보장하고 있음.
Applied to files:
src/main/java/dooya/see/application/post/PostSearchCacheEventHandler.java
📚 Learning: 2025-05-25T09:48:50.258Z
Learnt from: Do-oya
PR: see-projects/back#53
File: src/test/java/dooya/see/post/application/PostQueryServiceTest.java:51-61
Timestamp: 2025-05-25T09:48:50.258Z
Learning: In PostQueryServiceTest, when testing failure cases where entities don't exist, explicit mocking of repository methods to return Optional.empty() is not necessary since Mockito's default behavior already handles this. The user prefers to keep tests simple by relying on Mockito's default behavior rather than over-mocking.
Applied to files:
src/test/java/dooya/see/application/post/provided/PostFinderTest.javasrc/test/java/dooya/see/application/post/PostSearchCacheEventHandlerTest.javasrc/main/java/dooya/see/application/post/PostQueryService.java
🧬 Code graph analysis (1)
src/main/java/dooya/see/adapter/integration/cache/RedisPostSearchCacheRepository.java (1)
src/main/java/dooya/see/adapter/integration/cache/key/PostCacheKey.java (1)
PostCacheKey(9-64)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: test
- GitHub Check: build-check
🔇 Additional comments (20)
build.gradle.kts (1)
86-88: LGTM: JMH JAR 파일 크기 제한 해제JMH 벤치마크 JAR 생성 시 Zip64 형식을 활성화하여 대용량 파일 처리를 지원합니다. 벤치마크 시나리오 확장에 적절한 설정입니다.
src/gatling/scala/dooya/see/search/PostSearchSimulation.scala (2)
20-26: LGTM: 캐시 성능 테스트를 위한 Hot/Cold 키워드 분리
hotKeyword와coldKeyword를 구분하여 캐시 히트/미스 시나리오를 명확하게 테스트할 수 있습니다.nanoTime()사용으로 고유한 cold 키워드를 보장하는 것도 적절합니다.
42-48: LGTM: 캐시 워밍 시나리오 추가캐시를 미리 워밍하기 위한 별도의 HTTP 요청을
.silent모드로 실행하여 결과 리포트에서 제외하는 것이 적절합니다.src/jmh/java/dooya/see/benchmark/PostSearchBenchmark.java (3)
87-93: ColdSearchState의 캐시 무효화 시점 확인 필요
@Setup(Level.Invocation)으로 매 호출마다 캐시를 무효화하는 것은 cold search를 정확히 측정하기 위해 적절합니다. 다만 워밍업(warmup) 단계에서도 이 동작이 적용되므로, 워밍업 효과가 제한적일 수 있습니다.현재 구성이 의도한 대로 동작하는지 확인하세요:
- 워밍업 단계: 캐시가 반복적으로 무효화되어 JVM 최적화 효과만 얻음
- 측정 단계: 매 측정마다 캐시 미스 상황을 정확히 측정
JMH 결과를 분석하여 cold search의 측정값이 예상 범위 내인지 확인하세요.
53-56: 캐시 워밍 전략 검토setup에서 동일한
cacheHitQuery로 DB 조회와 ES 조회를 모두 수행하여 캐시를 워밍합니다. 이는CachedSearchState의 캐시 히트 벤치마크에 적합하지만,ColdSearchState는 매 호출마다 캐시를 무효화하므로 이 워밍업의 영향을 받지 않습니다.
114-122: LGTM: 데이터베이스 검색 벤치마크 추가ES 검색과 DB 검색의 성능을 비교할 수 있도록
databaseSearch벤치마크를 추가한 것이 좋습니다.PostSearchRequest를 올바르게 구성하고 있습니다.README.md (1)
30-30: LGTM: Redis 캐시 기능 문서화Elasticsearch 검색 결과 캐싱 기능을 README에 명확히 문서화했습니다. TTL 3분 설정도 구현과 일치합니다.
src/main/java/dooya/see/adapter/integration/cache/config/RedisConfig.java (1)
3-3: LGTM: Configuration Properties 활성화
@EnableConfigurationProperties를 사용하여PostSearchCacheProperties를 Spring Bean으로 등록하는 것이 적절합니다.Also applies to: 11-11
src/test/java/dooya/see/application/post/PostSearchCacheEventHandlerTest.java (1)
32-66: LGTM: 포괄적인 캐시 무효화 이벤트 테스트모든 도메인 이벤트(생성, 업데이트, 발행, 숨김, 삭제)와 명시적 무효화 명령에 대한 테스트가 잘 작성되어 있습니다. Mock 설정도 최소화하여 Mockito의 기본 동작을 활용하고 있습니다.
Based on learnings
src/main/java/dooya/see/application/post/PostQueryService.java (2)
77-78: 캐시 조회 로직이 깔끔합니다.Optional 패턴과 orElseGet을 사용한 캐시 미스 처리가 적절합니다.
99-108: 캐시 저장 실패 처리가 적절합니다.캐시 저장 실패 시 검색 흐름에 영향을 주지 않도록 경고 로그만 남기는 방식이 올바릅니다. 캐시는 성능 최적화를 위한 것이므로 실패해도 핵심 기능에 영향을 주지 않아야 합니다.
src/main/java/dooya/see/adapter/integration/cache/RedisPostSearchCacheRepository.java (5)
34-50: 역직렬화 실패 처리가 잘 되어 있습니다.역직렬화 실패 시 손상된 캐시 엔트리를 삭제하는 방식이 적절합니다. 이를 통해 다음 요청 시 새로운 데이터로 캐시가 갱신됩니다.
52-68: TTL 처리 로직이 올바릅니다.TTL이 null, 0, 또는 음수인 경우 TTL 없이 저장하는 로직이 적절합니다.
79-89: 키워드 정규화가 적절합니다.
Locale.ROOT를 사용한 소문자 변환은 로케일별 대소문자 변환 문제(예: 터키어의 'I' 문제)를 방지합니다. "Spring"과 "spring"이 동일한 캐시 키를 생성하도록 하는 것도 올바른 접근입니다.
91-102: 캐시용 레코드 구조가 효율적입니다.
CachedPage레코드가 필요한 최소한의 데이터(content, totalElements)만 저장하여 직렬화 크기를 최적화했습니다.
70-77: 검증 완료: 키 패턴 일관성 확인됨
SEARCH_KEY_PATTERN은PostCacheKey.search()로 생성된 키와 완벽하게 일치합니다.
PostCacheKey.search()는post:search:<hash>:<page>:<size>형식으로 키 생성 (src/main/java/dooya/see/adapter/integration/cache/key/PostCacheKey.java:28-30)SEARCH_KEY_PATTERN = "post:search:*"는 이 형식의 모든 키를 정확하게 매칭evictAll()메서드의 패턴 매칭이 올바르게 동작함src/test/java/dooya/see/application/post/provided/PostFinderTest.java (2)
47-47: 테스트 격리가 잘 되어 있습니다.각 테스트 전에 캐시를 초기화하여 테스트 간 상태 공유를 방지합니다.
356-379: 캐시 동작을 잘 검증하는 테스트입니다.Elasticsearch 데이터를 삭제한 후에도 캐시된 결과가 반환되는지 확인하여 캐시가 제대로 작동하는지 검증합니다.
src/main/java/dooya/see/application/post/PostSearchCacheEventHandler.java (2)
23-51: 이벤트 리스너 설정이 적절합니다.
AFTER_COMMIT페이즈를 사용하여 트랜잭션 성공 후에만 캐시를 무효화하고,@Async를 통해 비동기로 처리하여 메인 트랜잭션을 차단하지 않습니다.
59-66: 캐시 무효화 실패 처리가 안전합니다.캐시 무효화 실패 시 예외를 다시 던지지 않아 이벤트 처리 흐름이 중단되지 않습니다. 캐시는 부가 기능이므로 실패해도 핵심 비즈니스 로직에 영향을 주지 않아야 합니다.
- CI 환경에서 Redis 컨테이너 추가로 Redis 연동 테스트 지원 - Redis 초기화 및 상태 확인을 위한 health check 설정 포함 - Elasticsearch, Kafka 대기 로직에 Redis 상태 검사 로직 통합 - 검색 캐시 및 Redis 관련 기능의 CI 테스트 커버리지 확장
📌 Issue
🧐 현재 상황
🎯 목표
🛠 작업 내용
작업 할 내용을 입력해주세요.
Elasticsearch 게시글 검색에 Redis 도입
테스트 작성
🚀 기타 사항
Summary by CodeRabbit
New Features
Tests
Chores