Compare commits
No commits in common. "e0d48712acd37422fb0a927f3ca29781cc293972" and "4097e5a13318bee58e926dfd09f0d169acf85bc3" have entirely different histories.
e0d48712ac
...
4097e5a133
|
@ -26,7 +26,6 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter-aop")
|
|
||||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||||
|
@ -34,7 +33,6 @@ dependencies {
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||||
implementation("org.springframework.retry:spring-retry")
|
implementation("org.springframework.retry:spring-retry")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||||
implementation("org.redisson:redisson-spring-boot-starter:3.17.7")
|
|
||||||
|
|
||||||
// jwt
|
// jwt
|
||||||
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
|
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
|
||||||
|
|
|
@ -4,10 +4,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
import org.springframework.retry.annotation.EnableRetry
|
import org.springframework.retry.annotation.EnableRetry
|
||||||
import org.springframework.scheduling.annotation.EnableAsync
|
import org.springframework.scheduling.annotation.EnableAsync
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
@EnableRetry
|
@EnableRetry
|
||||||
class SodaLiveApplication
|
class SodaLiveApplication
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.common.annotation
|
|
||||||
|
|
||||||
import org.aspectj.lang.annotation.Aspect
|
|
||||||
import org.aspectj.lang.annotation.Before
|
|
||||||
import org.springframework.stereotype.Component
|
|
||||||
|
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
|
||||||
annotation class SchedulerOnly
|
|
||||||
|
|
||||||
@Aspect
|
|
||||||
@Component
|
|
||||||
class SchedulerOnlyAspect {
|
|
||||||
|
|
||||||
@Before("@annotation(SchedulerOnly)")
|
|
||||||
fun checkSchedulerAccess() {
|
|
||||||
if (!isSchedulerThread()) {
|
|
||||||
throw IllegalStateException("잘못된 접근입니다.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSchedulerThread(): Boolean {
|
|
||||||
// 스케줄러 스레드 여부를 판단하는 간단한 로직
|
|
||||||
return Thread.currentThread().name.contains("scheduler")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,5 @@
|
||||||
package kr.co.vividnext.sodalive.configs
|
package kr.co.vividnext.sodalive.configs
|
||||||
|
|
||||||
import org.redisson.Redisson
|
|
||||||
import org.redisson.api.RedissonClient
|
|
||||||
import org.redisson.config.Config
|
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.cache.annotation.EnableCaching
|
import org.springframework.cache.annotation.EnableCaching
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
|
@ -29,17 +26,6 @@ class RedisConfig(
|
||||||
@Value("\${spring.redis.port}")
|
@Value("\${spring.redis.port}")
|
||||||
private val port: Int
|
private val port: Int
|
||||||
) {
|
) {
|
||||||
@Bean
|
|
||||||
fun redissonClient(): RedissonClient {
|
|
||||||
val config = Config()
|
|
||||||
config.useSingleServer()
|
|
||||||
.setAddress("redis://$host:$port")
|
|
||||||
.setConnectionMinimumIdleSize(1) // 최소 유휴 연결: 1
|
|
||||||
.setConnectionPoolSize(5) // 최대 연결 풀 크기: 5
|
|
||||||
|
|
||||||
return Redisson.create(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun redisConnectionFactory(): RedisConnectionFactory {
|
fun redisConnectionFactory(): RedisConnectionFactory {
|
||||||
val clientConfiguration = LettuceClientConfiguration.builder()
|
val clientConfiguration = LettuceClientConfiguration.builder()
|
||||||
|
|
|
@ -120,7 +120,7 @@ interface AudioContentQueryRepository {
|
||||||
|
|
||||||
fun getAudioContentCurationList(isAdult: Boolean, offset: Long, limit: Long): List<AudioContentCuration>
|
fun getAudioContentCurationList(isAdult: Boolean, offset: Long, limit: Long): List<AudioContentCuration>
|
||||||
|
|
||||||
fun getNotReleaseContent(): List<AudioContent>
|
fun getNotReleaseContentId(): List<Long>
|
||||||
|
|
||||||
fun isContentCreator(contentId: Long, memberId: Long): Boolean
|
fun isContentCreator(contentId: Long, memberId: Long): Boolean
|
||||||
|
|
||||||
|
@ -762,14 +762,15 @@ class AudioContentQueryRepositoryImpl(
|
||||||
.fetch()
|
.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNotReleaseContent(): List<AudioContent> {
|
override fun getNotReleaseContentId(): List<Long> {
|
||||||
val where = audioContent.isActive.isFalse
|
val where = audioContent.isActive.isFalse
|
||||||
.and(audioContent.releaseDate.isNotNull)
|
.and(audioContent.releaseDate.isNotNull)
|
||||||
.and(audioContent.releaseDate.loe(LocalDateTime.now()))
|
.and(audioContent.releaseDate.loe(LocalDateTime.now()))
|
||||||
.and(audioContent.duration.isNotNull)
|
.and(audioContent.duration.isNotNull)
|
||||||
|
|
||||||
return queryFactory
|
return queryFactory
|
||||||
.selectFrom(audioContent)
|
.select(audioContent.id)
|
||||||
|
.from(audioContent)
|
||||||
.where(where)
|
.where(where)
|
||||||
.fetch()
|
.fetch()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
||||||
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
|
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
|
||||||
import kr.co.vividnext.sodalive.common.SodaException
|
import kr.co.vividnext.sodalive.common.SodaException
|
||||||
import kr.co.vividnext.sodalive.common.annotation.SchedulerOnly
|
|
||||||
import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
||||||
import kr.co.vividnext.sodalive.content.hashtag.AudioContentHashTag
|
import kr.co.vividnext.sodalive.content.hashtag.AudioContentHashTag
|
||||||
import kr.co.vividnext.sodalive.content.hashtag.HashTag
|
import kr.co.vividnext.sodalive.content.hashtag.HashTag
|
||||||
|
@ -403,12 +402,14 @@ class AudioContentService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SchedulerOnly
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun releaseContent() {
|
fun releaseContent() {
|
||||||
val notReleasedAudioContent = repository.getNotReleaseContent()
|
val contentIdList = repository.getNotReleaseContentId()
|
||||||
|
|
||||||
|
for (contentId in contentIdList) {
|
||||||
|
val audioContent = repository.findByIdOrNull(contentId)
|
||||||
|
?: throw SodaException("잘못된 요청입니다.")
|
||||||
|
|
||||||
for (audioContent in notReleasedAudioContent) {
|
|
||||||
audioContent.isActive = true
|
audioContent.isActive = true
|
||||||
|
|
||||||
applicationEventPublisher.publishEvent(
|
applicationEventPublisher.publishEvent(
|
||||||
|
@ -417,7 +418,7 @@ class AudioContentService(
|
||||||
title = audioContent.member!!.nickname,
|
title = audioContent.member!!.nickname,
|
||||||
message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}",
|
message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}",
|
||||||
isAuth = audioContent.isAdult,
|
isAuth = audioContent.isAdult,
|
||||||
contentId = audioContent.id!!,
|
contentId = contentId,
|
||||||
creatorId = audioContent.member!!.id,
|
creatorId = audioContent.member!!.id,
|
||||||
container = "ios"
|
container = "ios"
|
||||||
)
|
)
|
||||||
|
@ -429,7 +430,7 @@ class AudioContentService(
|
||||||
title = audioContent.member!!.nickname,
|
title = audioContent.member!!.nickname,
|
||||||
message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}",
|
message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}",
|
||||||
isAuth = audioContent.isAdult,
|
isAuth = audioContent.isAdult,
|
||||||
contentId = audioContent.id!!,
|
contentId = contentId,
|
||||||
creatorId = audioContent.member!!.id,
|
creatorId = audioContent.member!!.id,
|
||||||
container = "aos"
|
container = "aos"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package kr.co.vividnext.sodalive.scheduler
|
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.content.AudioContentService
|
|
||||||
import org.redisson.api.RedissonClient
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class AudioContentReleaseSchedulerService(
|
|
||||||
private val redissonClient: RedissonClient,
|
|
||||||
private val audioContentService: AudioContentService
|
|
||||||
) {
|
|
||||||
@Scheduled(fixedRate = 1000 * 60 * 5)
|
|
||||||
fun release() {
|
|
||||||
val lock = redissonClient.getLock("lock:audioContentRelease")
|
|
||||||
|
|
||||||
if (lock.tryLock(10, TimeUnit.SECONDS)) {
|
|
||||||
try {
|
|
||||||
println("락을 획득하여 배포를 시작합니다.")
|
|
||||||
audioContentService.releaseContent()
|
|
||||||
} finally {
|
|
||||||
if (lock.isHeldByCurrentThread) {
|
|
||||||
lock.unlock()
|
|
||||||
println("락 해제")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println("락을 획득하지 못해서 배포를 건너뜁니다")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue