Skip to content

Commit

Permalink
[BE] 데이터 정합성을 맞추는 스케줄러 작성 (#789)
Browse files Browse the repository at this point in the history
* feat: 회원 팔로워 수의 데이터 정합성을 배치 처리하는 쿼리 작성

* refactor: 서비스 레이어에서 명시적 flush 대신 벌크 쿼리의 flushAutomatically 모드를 true로 설정

* feat: 1시간마다 회원의 팔로워 수 정합성을 맞추는 배치 쿼리를 실행하는 스케줄러 구현
  • Loading branch information
Ohzzi authored Oct 17, 2022
1 parent 3fc5376 commit cb540b4
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.woowacourse.f12.application.batch;

import com.woowacourse.f12.domain.member.MemberRepository;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class BatchService {

private final MemberRepository memberRepository;

public BatchService(final MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}

@Transactional
public void updateFollowerCount() {
memberRepository.updateFollowerCountBatch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.woowacourse.f12.application.batch;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;

@Slf4j
public class FollowerCountBatchScheduler {

private static final String LOG_FORMAT = "Class : {}, Message : {}";

private final BatchService batchService;

public FollowerCountBatchScheduler(final BatchService batchService) {
this.batchService = batchService;
}

@Scheduled(cron = "0 0 0/1 1/1 * ? *")
public void execute() {
try {
batchService.updateFollowerCount();
} catch (Exception e) {
log.error(LOG_FORMAT, e.getClass().getSimpleName(), e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ public void update(final Long reviewId, final Long memberId, final ReviewRequest
final Review updateReview = updateRequest.toReview(target.getProduct(), target.getMember());
int ratingGap = updateReview.getRating() - target.getRating();
target.update(updateReview);
reviewRepository.flush();
productRepository.updateProductStatisticsForReviewUpdate(target.getProduct().getId(), ratingGap);
}

Expand All @@ -125,8 +124,6 @@ public void delete(final Long reviewId, final Long memberId) {
.orElseThrow(InventoryProductNotFoundException::new);
inventoryProductRepository.delete(inventoryProduct);
reviewRepository.delete(review);
// inventoryProductRepository.flush();
reviewRepository.flush();
productRepository.updateProductStatisticsForReviewDelete(review.getProduct().getId(), review.getRating());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.woowacourse.f12.config;

import com.woowacourse.f12.application.batch.BatchService;
import com.woowacourse.f12.application.batch.FollowerCountBatchScheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@Configuration
public class SchedulerConfig {

private final BatchService batchService;

public SchedulerConfig(final BatchService batchService) {
this.batchService = batchService;
}

@Bean(name = "followerCountBatchScheduler")
public FollowerCountBatchScheduler followerCountBatchScheduler() {
return new FollowerCountBatchScheduler(batchService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {

Optional<Member> findByGitHubId(String gitHubId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(value = "update Member m set m.followerCount = (select count(f) from Following f where f.followingId = m.id)")
void updateFollowerCountBatch();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ public interface ProductRepository extends JpaRepository<Product, Long>, Product

List<Product> findByReviewCountGreaterThanEqualAndRatingGreaterThanEqual(int reviewCount, double rating);

@Modifying(clearAutomatically = true)
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(value = "update Product p "
+ "set p.rating = (p.totalRating + :reviewRating) / cast((p.reviewCount + 1) as double), "
+ "p.reviewCount = p.reviewCount + 1, "
+ "p.totalRating = p.totalRating + :reviewRating "
+ "where p.id = :productId")
void updateProductStatisticsForReviewInsert(Long productId, int reviewRating);

@Modifying(clearAutomatically = true)
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(value = "update Product p "
+ "set p.rating = case p.reviewCount when 1 then 0 "
+ "else ((p.totalRating - :reviewRating) / cast((p.reviewCount - 1) as double)) end , "
Expand All @@ -26,7 +26,7 @@ public interface ProductRepository extends JpaRepository<Product, Long>, Product
+ "where p.id = :productId")
void updateProductStatisticsForReviewDelete(Long productId, int reviewRating);

@Modifying(clearAutomatically = true)
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(value = "update Product p "
+ "set p.rating = (p.totalRating + :ratingGap) / cast(p.reviewCount as double), "
+ "p.totalRating = p.totalRating + :ratingGap "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,4 +315,27 @@ class MemberRepositoryTest {
assertThatThrownBy(() -> memberRepository.save(member2))
.isInstanceOf(DataIntegrityViolationException.class);
}

@Test
void 회원의_팔로워_수의_정합성을_맞춘다() {
// given
Member member = CORINNE.생성();
Member follower = MINCHO.생성();
memberRepository.save(member);
memberRepository.save(follower);
Following following = Following.builder()
.followerId(follower.getId())
.followingId(member.getId())
.build();
followingRepository.save(following);

// when
memberRepository.updateFollowerCountBatch();

// then
Member actual = memberRepository.findById(member.getId())
.orElseThrow();

assertThat(actual.getFollowerCount()).isOne();
}
}

0 comments on commit cb540b4

Please sign in to comment.