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

refactor: 보호 동물 목록 조회 캐시를 리팩토링한다. #458

Merged
merged 4 commits into from
Mar 13, 2024

Conversation

pushedrumex
Copy link
Member

@pushedrumex pushedrumex commented Mar 12, 2024

⛏ 작업 사항

  • 보호 동물 목록 조회 캐시를 리팩토링

📝 작업 요약

캐시 크기가 최대 크기를 넘어가면 trim 해줄 때 발생하는 동시성 이슈

(동시에 100개 저장 시 데이터가 단 한개도 캐싱되지 않음)

  • 기존: 캐시 크기가 최대 크기가 넘어갈 경우 마지막 데이터 삭제
private void trimCache() {
    if (zSetOperations.size(ANIMAL_ZSET_KEY) > ANIMAL_CACHE_SIZE) {
        zSetOperations.removeRange(ANIMAL_ZSET_KEY, LAST_INDEX, LAST_INDEX);
    }
}
  • 개선: ZSet 의 removeRange 를 사용하여 최대 크기의 마지막 인덱스 이후의 모든 데이터 삭제
private void trimCache() {
    zSetOperations.removeRange(ANIMAL_ZSET_KEY, ANIMAL_CACHE_SIZE, LAST_INDEX);
}

보호 동물 총 개수 캐시 갱신 관련 동시성 문제

  • 기존: 캐시된 개수를 조회한 후 + 1 하여 저장
public void increaseTotalNumberOfAnimals() {
    Long cachedCount = getTotalNumberOfAnimals();
    valueOperations.set(TOTAL_NUMBER_OF_ANIMALS_KEY, cachedCount + 1);
}
  • 개선: ZSet 의 INCR, DECR 를 사용하여 원자적으로 증가
public void increaseTotalNumberOfAnimals() {
    valueOperations.increment(TOTAL_NUMBER_OF_ANIMALS_KEY);
}

Cache Miss 일 경우의 로직 개선

  • 기존: db 에서 데이터를 가져와 캐시에 저장 후, 캐시에서 다시 조회하여 반환
  public FindAnimalsResponse findAnimals(int size, long count) {
      if (requiresCacheUpdate(size)) {
          synchronizeCache();
      }
      Set<Object> cachedResponses = zSetOperations.range(ANIMAL_ZSET_KEY, 0, size - LAST_INDEX);
  • 개선: 캐시에서 다시 조회하지 않고 db 에서 가져온 데이터를 반환
   public FindAnimalsResponse findAnimals(int size, long count) {
        Set<Object> cachedResponses = zSetOperations.range(ANIMAL_ZSET_KEY, 0, size - 1L);
        PageInfo pageInfo = PageInfo.of(count, count > size);
		...
		// CACHE MISS
        zSetOperations.removeRange(ANIMAL_ZSET_KEY, 0, LAST_INDEX);

        Slice<FindAnimalsResult> animalsResult = getFindAnimalsResults();
        animalsResult.forEach(this::saveAnimal);

        List<FindAnimalResponse> responses = animalsResult.stream()
            .map(FindAnimalResponse::from)
            .toList();
        return new FindAnimalsResponse(pageInfo, responses);
}

보호 동물 갱신 시 캐시 대상이 아닌 데이터도 캐시에 추가되는 문제

  • 기존: 기존에 캐시된 데이터가 존재하면 삭제하고 갱신되는 데이터를 캐싱
public void updateAnimal(...) {
		animalCacheRepository.deleteAnimal(animal);
		...
        animal.updateAnimal(...);
        animalCacheRepository.saveAnimal(animal);
}
  • 개선: 삭제되는 데이터의 개수를 통해 기존에 캐싱되어있던 데이터일 때만 데이터를 캐싱
public void updateAnimal(...) {
	long number = animalCacheRepository.deleteAnimal(animal);
	...
    animal.updateAnimal(...);

    if (number > 0) {
        animalCacheRepository.saveAnimal(animal);
    }
}

💡 관련 이슈

@github-actions github-actions bot added the 🔨 Refactor 코드 리팩토링 및 구조 개선 label Mar 12, 2024
Copy link

github-actions bot commented Mar 12, 2024

Test Results

287 files  ±0  287 suites  ±0   27s ⏱️ ±0s
659 tests +2  659 ✅ +2  0 💤 ±0  0 ❌ ±0 
668 runs  +2  668 ✅ +2  0 💤 ±0  0 ❌ ±0 

Results for commit b46fa60. ± Comparison against base commit 0ac55c2.

This pull request removes 13 and adds 15 tests. Note that renamed tests count towards both.
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$DeleteAnimal ‑ 성공: 1개 캐싱 -> 0개 캐싱
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$DeleteAnimal ‑ 성공: 30개 캐싱 -> 29개 캐싱
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$FindAnimalsTest ‑ 성공: 0개 캐싱, animalSize 20 -> 0개 반환
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$FindAnimalsTest ‑ 성공: 10개 캐싱, animalSize 20 -> 10개 반환
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$FindAnimalsTest ‑ 성공: 20개 캐싱, animalSize 20 -> 20개 반환
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$FindAnimalsTest ‑ 성공: 30개 캐싱, animalSize 20 -> 20개 반환
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$SaveAnimalTest ‑ 성공: 0개 캐싱 -> 1개 캐싱
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$SaveAnimalTest ‑ 성공: 29개 캐싱 -> 30개 캐싱
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$SaveAnimalTest ‑ 성공: 30개 캐싱 -> 30개 캐싱
com.clova.anifriends.domain.animal.service.AnimalCacheServiceTest$SynchronizeCacheTest ‑ 성공: 0개 데이터 -> 0개 캐싱
…
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest ‑ 성공: 동시에 보호 동물 추가
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest ‑ 성공: 동시에 총 동물 수 증가
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest$DeleteAnimal ‑ 성공: 1개 캐싱 -> 0개 캐싱
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest$DeleteAnimal ‑ 성공: 30개 캐싱 -> 29개 캐싱
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest$FindAnimalsTest ‑ 성공: 0개 캐싱, animalSize 20 -> 0개 반환
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest$FindAnimalsTest ‑ 성공: 10개 캐싱, animalSize 20 -> 10개 반환
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest$FindAnimalsTest ‑ 성공: 20개 캐싱, animalSize 20 -> 20개 반환
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest$FindAnimalsTest ‑ 성공: 30개 캐싱, animalSize 20 -> 20개 반환
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest$SaveAnimalTest ‑ 성공: 0개 캐싱 -> 1개 캐싱
com.clova.anifriends.domain.animal.repository.AnimalRedisRepositoryTest$SaveAnimalTest ‑ 성공: 29개 캐싱 -> 30개 캐싱
…

♻️ This comment has been updated with latest results.

Copy link
Member

@bjo6300 bjo6300 left a comment

Choose a reason for hiding this comment

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

zset으로 동시성 해결 👍🏻👍🏻👍🏻

Copy link

@pushedrumex pushedrumex merged commit 28e4936 into dev Mar 13, 2024
4 checks passed
@pushedrumex pushedrumex deleted the refactor/#457 branch March 13, 2024 02:15
@pushedrumex pushedrumex self-assigned this Mar 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 Refactor 코드 리팩토링 및 구조 개선
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

refactor: 보호 동물 목록 조회 캐시
2 participants