-
Notifications
You must be signed in to change notification settings - Fork 1
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
[#31] 기간별 인기 피드 #32
[#31] 기간별 인기 피드 #32
Changes from 9 commits
5263c13
37fffa3
38b4e53
0bd5a48
e9810dd
ee59359
5d632ce
a5d1fdc
59e31df
9f412ce
2bf9736
e24aa60
51a51d0
f4d1144
8f3ed12
c4bae8f
124e3e0
082c0e2
2e77773
eef16d7
2c0e01d
8d106bf
3155b96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package com.stoury.batch; | ||
|
||
import com.stoury.domain.Feed; | ||
import com.stoury.dto.feed.SimpleFeedResponse; | ||
import com.stoury.repository.LikeRepository; | ||
import com.stoury.repository.RankingRepository; | ||
import jakarta.persistence.EntityManagerFactory; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.batch.core.Job; | ||
import org.springframework.batch.core.Step; | ||
import org.springframework.batch.core.job.builder.JobBuilder; | ||
import org.springframework.batch.core.repository.JobRepository; | ||
import org.springframework.batch.core.step.builder.StepBuilder; | ||
import org.springframework.batch.item.ItemProcessor; | ||
import org.springframework.batch.item.ItemWriter; | ||
import org.springframework.batch.item.database.JpaPagingItemReader; | ||
import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.util.Pair; | ||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||
import org.springframework.transaction.PlatformTransactionManager; | ||
|
||
import java.time.temporal.ChronoUnit; | ||
|
||
@Configuration | ||
@RequiredArgsConstructor | ||
@ConditionalOnExpression("'${spring.batch.job.names}'.contains('jobHotFeeds')") | ||
public class BatchHotFeedsConfig { | ||
private final EntityManagerFactory entityManagerFactory; | ||
|
||
private final RankingRepository rankingRepository; | ||
|
||
@Bean | ||
public Job jobDailyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new JobBuilder("jobDailyFeed", jobRepository) | ||
.start(stepDailyFeed(jobRepository, tm, taskExecutor, likeRepository)) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Job jobWeeklyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new JobBuilder("jobWeeklyFeed", jobRepository) | ||
.start(stepWeeklyFeed(jobRepository, tm, taskExecutor, likeRepository)) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Job jobMonthlyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new JobBuilder("jobMonthlyFeed", jobRepository) | ||
.start(stepMonthlyFeed(jobRepository, tm, taskExecutor, likeRepository)) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Step stepDailyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new StepBuilder("stepDailyFeed", jobRepository) | ||
.<Feed, Pair<SimpleFeedResponse, Long>>chunk(100, tm) | ||
.reader(feedReader()) | ||
.processor(feedProcessor(likeRepository, ChronoUnit.DAYS)) | ||
.writer(feedWriter(ChronoUnit.DAYS)) | ||
.taskExecutor(taskExecutor) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Step stepWeeklyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new StepBuilder("stepWeeklyFeed", jobRepository) | ||
.<Feed, Pair<SimpleFeedResponse, Long>>chunk(100, tm) | ||
.reader(feedReader()) | ||
.processor(feedProcessor(likeRepository, ChronoUnit.WEEKS)) | ||
.writer(feedWriter(ChronoUnit.WEEKS)) | ||
.taskExecutor(taskExecutor) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Step stepMonthlyFeed(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor, LikeRepository likeRepository) { | ||
return new StepBuilder("stepMonthlyFeed", jobRepository) | ||
.<Feed, Pair<SimpleFeedResponse, Long>>chunk(100, tm) | ||
.reader(feedReader()) | ||
.processor(feedProcessor(likeRepository, ChronoUnit.MONTHS)) | ||
.writer(feedWriter(ChronoUnit.MONTHS)) | ||
.taskExecutor(taskExecutor) | ||
.build(); | ||
} | ||
|
||
|
||
public JpaPagingItemReader<Feed> feedReader() { | ||
return new JpaPagingItemReaderBuilder<Feed>() | ||
.name("feedReader") | ||
.pageSize(100) | ||
.entityManagerFactory(entityManagerFactory) | ||
.queryString("select f from Feed f") | ||
.build(); | ||
} | ||
|
||
public ItemProcessor<Feed, Pair<SimpleFeedResponse, Long>> feedProcessor(LikeRepository likeRepository, ChronoUnit chronoUnit) { | ||
return feed -> { | ||
Long feedId = feed.getId(); | ||
|
||
if (!likeRepository.existsByFeedId(feedId.toString())) { | ||
return null; | ||
} | ||
long currentLikes = likeRepository.getCountByFeedId(feedId.toString()); | ||
long prevLikes = likeRepository.getCountSnapshotByFeed(feedId.toString(), chronoUnit); | ||
|
||
SimpleFeedResponse simpleFeed = SimpleFeedResponse.from(feed); | ||
|
||
return Pair.of(simpleFeed, currentLikes - prevLikes); | ||
}; | ||
} | ||
|
||
public ItemWriter<Pair<SimpleFeedResponse, Long>> feedWriter(ChronoUnit chronoUnit) { | ||
return list -> { | ||
for (Pair<SimpleFeedResponse, Long> pair : list) { | ||
SimpleFeedResponse rawSimpleFeed = pair.getFirst(); | ||
Long likeIncrease = pair.getSecond(); | ||
|
||
if (likeIncrease > 0) { | ||
rankingRepository.saveHotFeed(rawSimpleFeed, likeIncrease, chronoUnit); | ||
} | ||
} | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package com.stoury.batch; | ||
|
||
import com.stoury.repository.FeedRepository; | ||
import com.stoury.repository.RankingRepository; | ||
import com.stoury.utils.cachekeys.PopularSpotsKey; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.batch.core.Job; | ||
import org.springframework.batch.core.Step; | ||
import org.springframework.batch.core.job.builder.FlowBuilder; | ||
import org.springframework.batch.core.job.builder.JobBuilder; | ||
import org.springframework.batch.core.job.flow.Flow; | ||
import org.springframework.batch.core.repository.JobRepository; | ||
import org.springframework.batch.core.step.builder.StepBuilder; | ||
import org.springframework.batch.repeat.RepeatStatus; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.domain.PageRequest; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||
import org.springframework.transaction.PlatformTransactionManager; | ||
|
||
import java.util.List; | ||
|
||
@Configuration | ||
@RequiredArgsConstructor | ||
@ConditionalOnExpression("'${spring.batch.job.names}'.contains('jobPopularSpots')") | ||
public class BatchPopularSpotsConfig { | ||
Pageable pageable = PageRequest.of(0, 10); | ||
private final FeedRepository feedRepository; | ||
private final RankingRepository rankingRepository; | ||
|
||
|
||
@Bean | ||
public Step stepUpdatePopularDomesticCities(JobRepository jobRepository, PlatformTransactionManager tm) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 약간 사소한건데 Step이나 Job 같이 클래스가 맡고 있는 역할에 대한 정보는 이름 맨 뒤에 붙이는게 정석이에요..! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵 맞습니다! |
||
return new StepBuilder("stepUpdatePopularDomesticCities", jobRepository) | ||
.tasklet((contribution, chunkContext) -> { | ||
List<String> rankedDomesticCities = feedRepository.findTop10CitiesInKorea(pageable); | ||
rankingRepository.update(PopularSpotsKey.POPULAR_DOMESTIC_SPOTS, rankedDomesticCities); | ||
return RepeatStatus.FINISHED; | ||
}, tm) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Step stepUpdatePopularAbroadCities(JobRepository jobRepository, PlatformTransactionManager tm) { | ||
return new StepBuilder("stepUpdatePopularAbroadCities", jobRepository) | ||
.tasklet((contribution, chunkContext) -> { | ||
List<String> rankedCountries = feedRepository.findTop10CountriesNotKorea(pageable); | ||
rankingRepository.update(PopularSpotsKey.POPULAR_ABROAD_SPOTS, rankedCountries); | ||
return RepeatStatus.FINISHED; | ||
}, tm) | ||
.build(); | ||
} | ||
|
||
@Bean | ||
public Job jobUpdatePopularSpots(JobRepository jobRepository, PlatformTransactionManager tm, | ||
ThreadPoolTaskExecutor taskExecutor) { | ||
Flow flowUpdatePopularAbroadCities = new FlowBuilder<Flow>("flowUpdatePopularAbroadCities") | ||
.start(stepUpdatePopularAbroadCities(jobRepository, tm)) | ||
.build(); | ||
Flow flowUpdatePopularDomesticCities = new FlowBuilder<Flow>("flowUpdatePopularDomesticCities") | ||
.start(stepUpdatePopularDomesticCities(jobRepository, tm)) | ||
.build(); | ||
|
||
return new JobBuilder("updatePopularSpots", jobRepository) | ||
.start(flowUpdatePopularAbroadCities) | ||
.split(taskExecutor) | ||
.add(flowUpdatePopularDomesticCities) | ||
.end() | ||
.build(); | ||
} | ||
} |
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.
👍