feat(home): 응원 스냅샷 갱신 lock을 적용한다

This commit is contained in:
2026-07-10 06:06:30 +09:00
parent 64b05deee7
commit ce43cf2c67
2 changed files with 182 additions and 21 deletions

View File

@@ -48,7 +48,7 @@ open class RecommendationSnapshotRefreshService(
runCatching {
val aiCharacterCount = refreshAiCharacterSnapshotsWithSectionLock(now)
val cheerCreatorCount = replaceCheerCreatorSnapshots(windowStart, snapshotAt)
val cheerCreatorCount = refreshCheerCreatorSnapshotsWithSectionLock(now)
val popularCommunityCount = replacePopularCommunitySnapshots(windowStart, snapshotAt)
RefreshCounts(aiCharacterCount, cheerCreatorCount, popularCommunityCount)
}.onSuccess { counts ->
@@ -95,14 +95,38 @@ open class RecommendationSnapshotRefreshService(
return snapshots.size
}
@Transactional
open fun refreshCheerCreatorSnapshots(nowUtc: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC)): Int {
val startedAt = System.currentTimeMillis()
val window = windowPolicy.previousKstDayUtcWindow(nowUtc)
val snapshots = queryPort.findCheerCreatorSnapshots(
window.startUtc,
window.endExclusiveUtc,
CHEER_CREATOR_SNAPSHOT_LIMIT
)
snapshotPort.replaceSnapshots(RecommendedSectionType.CHEER_CREATOR, window.snapshotAt, snapshots)
afterCommit {
log.info(
"event=cheer_creator_recommendation_snapshot_refresh_success " +
"snapshotAt={} windowStartUtc={} windowEndExclusiveUtc={} savedCount={} elapsedMs={}",
window.snapshotAt,
window.startUtc,
window.endExclusiveUtc,
snapshots.size,
System.currentTimeMillis() - startedAt
)
}
return snapshots.size
}
private fun refreshAiCharacterSnapshotsWithSectionLock(nowUtc: LocalDateTime): Int {
val client = redissonClient ?: return refreshAiCharacterSnapshots(nowUtc)
val lock = client.getLock(AiCharacterSnapshotFallbackService.LOCK_KEY)
val lock = client.getLock(RecommendationSnapshotFallbackService.AI_CHARACTER_LOCK_KEY)
if (!lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
log.info(
"event=ai_character_recommendation_snapshot_refresh_lock_missed lockKey={}",
AiCharacterSnapshotFallbackService.LOCK_KEY
RecommendationSnapshotFallbackService.AI_CHARACTER_LOCK_KEY
)
return 0
}
@@ -111,6 +135,22 @@ open class RecommendationSnapshotRefreshService(
return refreshAiCharacterSnapshots(nowUtc)
}
private fun refreshCheerCreatorSnapshotsWithSectionLock(nowUtc: LocalDateTime): Int {
val client = redissonClient ?: return refreshCheerCreatorSnapshots(nowUtc)
val lock = client.getLock(RecommendationSnapshotFallbackService.CHEER_CREATOR_LOCK_KEY)
if (!lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
log.info(
"event=cheer_creator_recommendation_snapshot_refresh_lock_missed lockKey={}",
RecommendationSnapshotFallbackService.CHEER_CREATOR_LOCK_KEY
)
return 0
}
unlockAfterTransaction(lock)
return refreshCheerCreatorSnapshots(nowUtc)
}
private fun unlockAfterTransaction(lock: org.redisson.api.RLock) {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
if (lock.isHeldByCurrentThread) {
@@ -130,12 +170,6 @@ open class RecommendationSnapshotRefreshService(
)
}
private fun replaceCheerCreatorSnapshots(windowStart: LocalDateTime, snapshotAt: LocalDateTime): Int {
val snapshots = queryPort.findCheerCreatorSnapshots(windowStart, snapshotAt, CHEER_CREATOR_SNAPSHOT_LIMIT)
snapshotPort.replaceSnapshots(RecommendedSectionType.CHEER_CREATOR, snapshotAt, snapshots)
return snapshots.size
}
private fun replacePopularCommunitySnapshots(windowStart: LocalDateTime, snapshotAt: LocalDateTime): Int {
val snapshots = queryPort.findPopularCommunitySnapshots(windowStart, snapshotAt, POPULAR_COMMUNITY_SNAPSHOT_LIMIT)
snapshotPort.replaceSnapshots(RecommendedSectionType.POPULAR_COMMUNITY, snapshotAt, snapshots)