Skip to content

Commit

Permalink
Merge pull request #125 from woowa-techcamp-2024/refactor/124-cache-s…
Browse files Browse the repository at this point in the history
…ervice-async

[refactor] 124 cache service async
  • Loading branch information
huiseung authored Aug 25, 2024
2 parents 0fff5f9 + 1b5f8c9 commit 4cc8c0c
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
service/search-service/src/main/resources/updated_restaurants_es.csv
service/search-service/src/main/resources/updated_menu_es.csv
service/search-service/src/main/resources/updated_restaurants_es.csv
local
HELP.md
.gradle
Expand Down
2 changes: 2 additions & 0 deletions domain/cache-domain-redis/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
implementation project(":domain:exposure-common-domain")
implementation project(":domain:search-domain-rdb")
implementation project(":domain:restaurant-domain-rdb")
implementation project(":domain:review-domain-rdb")
implementation project(":event:review-event")
implementation project(":event:common-event")
implementation project(":event:cache-event")
implementation project(":event:event-publisher")
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
package woowa.team4.bff.cache.redis.service;

import com.fasterxml.jackson.core.type.TypeReference;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import woowa.team4.bff.cache.redis.repository.RestaurantReviewStatisticsRepository;
import woowa.team4.bff.cache.redis.utils.JsonConverter;
import woowa.team4.bff.cache.redis.utils.RedisKeyMaker;
import woowa.team4.bff.domain.RestaurantSummary;
import woowa.team4.bff.event.cache.DeliveryLocationAndKeywordCreateEvent;
import woowa.team4.bff.event.cache.RestaurantSummaryCacheEvent;
import woowa.team4.bff.event.reviewstatistics.ReviewStatisticsUpdateEvent;
import woowa.team4.bff.search.repository.RestaurantSummaryRepository;

@Service
@RequiredArgsConstructor
public class CacheManagerService {

private final RedisTemplate<String, String> redisTemplate;
private final JsonConverter jsonConverter;
private final RestaurantSummaryRepository restaurantSummaryRepository;


@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
Expand Down Expand Up @@ -51,4 +59,29 @@ public void handleDeliveryLocationAndKeywordCreate(DeliveryLocationAndKeywordCre
TimeUnit.HOURS
);
}

@Async
@EventListener
public void handle(RestaurantSummaryCacheEvent event){
List<RestaurantSummary> restaurantSummaries = restaurantSummaryRepository.findByRestaurantIds(event.restaurantIds());
bulkPutRestaurantSummaries(restaurantSummaries);
}

// redis 네트워크 통신 비용 때문에 한번의 통신에 여러 insert 보냄
private void bulkPutRestaurantSummaries(List<RestaurantSummary> restaurantSummaries) {
redisTemplate.execute(new SessionCallback<List<Object>>() {
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
// 트랜잭션 시작
operations.multi();

for (RestaurantSummary summary : restaurantSummaries) {
String key = RedisKeyMaker.makeRestaurantKey(summary.getId());
operations.opsForValue().set(key, jsonConverter.convert(summary));
}
// 명령어 전송
return operations.exec();
}
});
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package woowa.team4.bff.cache.redis.service;

import com.fasterxml.jackson.core.type.TypeReference;
import java.util.ArrayList;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
Expand All @@ -9,18 +11,22 @@
import woowa.team4.bff.cache.redis.utils.JsonConverter;
import woowa.team4.bff.cache.redis.utils.RedisKeyMaker;
import woowa.team4.bff.domain.RestaurantSummary;
import woowa.team4.bff.event.cache.RestaurantSummaryCacheEvent;
import woowa.team4.bff.interfaces.CacheService;

import java.util.List;
import java.util.Objects;
import woowa.team4.bff.publisher.EventPublisher;
import woowa.team4.bff.search.repository.RestaurantSummaryRepository;

@Service
@RequiredArgsConstructor
public class CacheRedisService implements CacheService {

private final RedisTemplate<String, String> redisTemplate;
private final RestaurantReviewStatisticsRepository restaurantReviewStatisticsRepository;
private final JsonConverter jsonConverter;
private final EventPublisher eventPublisher;
private final RestaurantSummaryRepository restaurantSummaryRepository;

@Override
public List<Long> findIdsByKeywordAndDeliveryLocation(String keyword, String deliveryLocation) {
Expand All @@ -34,22 +40,33 @@ public List<Long> findIdsByKeywordAndDeliveryLocation(String keyword, String del

@Override
public List<RestaurantSummary> findByRestaurantIds(List<Long> ids) {
return ids.stream()
.map(this::findById)
.filter(Objects::nonNull)
.toList();
}
// 1. Redis keys 생성
List<String> keys = ids.stream()
.map(RedisKeyMaker::makeRestaurantKey)
.collect(Collectors.toList());
// 2. Redis에서 한 번에 모든 값 조회
List<String> cachedValues = redisTemplate.opsForValue().multiGet(keys);
// 3. 캐시 miss된 ID들 혹은 hit 데이터 추출
List<Long> missingIds = new ArrayList<>();
List<RestaurantSummary> results = new ArrayList<>();

for (int i = 0; i < ids.size(); i++) {
String json = cachedValues.get(i);
if (json != null) {
results.add(jsonConverter.convert(json, RestaurantSummary.class));
} else {
missingIds.add(ids.get(i));
}
}

// 4. miss 데이터 조회
if (!missingIds.isEmpty()) {
List<RestaurantSummary> dbResults = restaurantSummaryRepository.findByRestaurantIds(missingIds);
results.addAll(dbResults);
// 5. 캐시 이벤트 발행
eventPublisher.publish(new RestaurantSummaryCacheEvent(missingIds));

public RestaurantSummary findById(Long id) {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
String restaurantKey = RedisKeyMaker.makeRestaurantKey(id);
String json = valueOperations.get(restaurantKey);
if (json == null) {
RestaurantSummary restaurantSummary = restaurantReviewStatisticsRepository.findByRestaurantId(id);
// ToDo: Event Publisher
valueOperations.set(restaurantKey, jsonConverter.convert(restaurantSummary));
return restaurantSummary;
}
return jsonConverter.convert(json, RestaurantSummary.class);
return results;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ public List<RestaurantSummary> findByKeywordAndDeliveryLocation(String keyword,
public List<RestaurantSummary> findByRestaurantIds(List<Long> restaurantIds){
return restaurantSummaryEntityRepository.findByIds(restaurantIds);
}

public RestaurantSummary findByRestaurantId(Long restaurantIds){
return restaurantSummaryEntityRepository.findByIds(List.of(restaurantIds)).getFirst();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package woowa.team4.bff.event.cache;

import java.util.List;
import woowa.team4.bff.event.Event;

public record RestaurantSummaryCacheEvent(List<Long> restaurantIds) implements Event {
}

0 comments on commit 4cc8c0c

Please sign in to comment.