Skip to content

Commit

Permalink
[Refactor/#235] jooq 관련 수정 (#237)
Browse files Browse the repository at this point in the history
* refactor: JooQ 버전 수정
3.19.9 -> 3.19.10

* refactor: JooQ 의존성 버전 지정

* refactor: jooq starter 의존성 사용 가능으로 중복 선언 의존성 삭제

* feat: ExceptionTranslator 생성

* feat: NativeSQLLogger 생성

* feat: PerformanceListener 생성

* feat: JooqConfig 생성

* feat: SlowQueryException 생성

* refactor: SlowQueryException 적용

* feat: SlowQuery 훅 설정

* refactor: 이벤트 발행 방식으로 수정

* refactor: ExceptionTranslator로 인한 발생 예외 변경 반영

* chore: 코드 줄바꿈 린트 적용

* refactor: 변수로 분리

* refactor: 비동기 동작 코드가 MDC 컨텍스트 공유할 수 있도록 수정

* refactor: PerformanceListener watch 생성 구현 수정

* refactor: ExceptionTranslator 구현 수정

* refactor: IntegrityConstraintViolationException로 반환 수정

* refactor: DuplicateKeyException로 반환 수정
  • Loading branch information
belljun3395 authored Jul 30, 2024
1 parent ffaf871 commit ceae194
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 23 deletions.
8 changes: 2 additions & 6 deletions api-repo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ dependencies {
/** module */
api(project(":data"))

/** spring starter */
api("org.springframework.boot:spring-boot-starter-data-jdbc")
/** mysql */
implementation("com.mysql:mysql-connector-j")

/** jooq */
implementation("org.springframework.boot:spring-boot-starter-jooq")
implementation("org.jooq:jooq:${DependencyVersion.JOOQ}")
implementation("org.jooq:jooq-meta:${DependencyVersion.JOOQ}")
implementation("org.jooq:jooq-codegen:${DependencyVersion.JOOQ}")
api("org.springframework.boot:spring-boot-starter-jooq")
jooqCodegen("org.jooq:jooq-meta-extensions:${DependencyVersion.JOOQ}")

/** flyway */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.few.api.repo.common

import org.jooq.ExecuteContext
import org.jooq.ExecuteListener
import org.springframework.jdbc.support.SQLExceptionTranslator

class ExceptionTranslator(
private val translator: SQLExceptionTranslator,
) : ExecuteListener {

override fun exception(context: ExecuteContext) {
context.exception(
translator
.translate("Access database using Jooq", context.sql(), context.sqlException()!!)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.few.api.repo.common

import io.github.oshai.kotlinlogging.KotlinLogging
import org.jooq.ExecuteContext
import org.jooq.ExecuteListener
import org.jooq.impl.DSL

class NativeSQLLogger : ExecuteListener {
private val log = KotlinLogging.logger {}

override fun executeEnd(ctx: ExecuteContext) {
ctx.query()?.let {
log.debug { "Query executed: ${DSL.using(ctx.configuration()).renderInlined(ctx.query())}" }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.few.api.repo.common

import io.github.oshai.kotlinlogging.KotlinLogging
import org.jooq.ExecuteContext
import org.jooq.ExecuteListener
import org.jooq.tools.StopWatch
import org.springframework.context.ApplicationEventPublisher

class PerformanceListener(
private val applicationEventPublisher: ApplicationEventPublisher,
private var watch: StopWatch = StopWatch(),
) : ExecuteListener {
private val log = KotlinLogging.logger {}

companion object {
const val SLOW_QUERY_STANDARD = 5000000000L // 5 seconds
}
override fun executeStart(ctx: ExecuteContext) {
super.executeStart(ctx)
watch = StopWatch()
}

override fun executeEnd(ctx: ExecuteContext) {
super.executeEnd(ctx)
if (watch.split() > SLOW_QUERY_STANDARD) {
log.warn { "Slow Query Detected: \n${ctx.query()}" }
applicationEventPublisher.publishEvent(SlowQueryEvent(ctx.query().toString()))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.few.api.repo.common

data class SlowQueryEvent(
val slowQuery: String,
)
41 changes: 41 additions & 0 deletions api-repo/src/main/kotlin/com/few/api/repo/config/JooqConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.few.api.repo.config

import com.few.api.repo.common.ExceptionTranslator
import com.few.api.repo.common.NativeSQLLogger
import com.few.api.repo.common.PerformanceListener
import org.jooq.SQLDialect
import org.jooq.impl.DataSourceConnectionProvider
import org.jooq.impl.DefaultConfiguration
import org.jooq.impl.DefaultDSLContext
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
import javax.sql.DataSource

@Configuration
class JooqConfig(
private val dataSource: DataSource,
private val applicationEventPublisher: ApplicationEventPublisher,
) {
@Bean
fun dsl(): DefaultDSLContext {
return DefaultDSLContext(configuration())
}

@Bean
fun configuration(): DefaultConfiguration {
val jooqConfiguration = DefaultConfiguration()
jooqConfiguration.set(connectionProvider())
val translator =
SQLErrorCodeSQLExceptionTranslator(SQLDialect.MYSQL.name)
jooqConfiguration.set(ExceptionTranslator(translator), NativeSQLLogger(), PerformanceListener(applicationEventPublisher))
jooqConfiguration.set(SQLDialect.MYSQL)
return jooqConfiguration
}

@Bean
fun connectionProvider(): DataSourceConnectionProvider {
return DataSourceConnectionProvider(dataSource)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package com.few.api.repo.jooq
import jooq.jooq_dsl.tables.Member
import org.jooq.DSLContext
import org.jooq.JSON
import org.jooq.exception.DataAccessException
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.dao.DuplicateKeyException
import org.springframework.test.annotation.Rollback
import org.springframework.transaction.annotation.Transactional
Expand Down Expand Up @@ -70,7 +70,7 @@ class _SampleJooqTest : JooqTestSpec() {
@Transactional
fun `이메일 값을 입력하지 않은면 저장에 실패합니다`() {
// when & then
assertThrows<DataIntegrityViolationException> {
assertThrows<DataAccessException> {
dslContext.insertInto(Member.MEMBER)
.set(Member.MEMBER.TYPE_CD, TYPECD)
.execute()
Expand All @@ -81,7 +81,7 @@ class _SampleJooqTest : JooqTestSpec() {
@Transactional
fun `타입 코드 값을 입력하지 않은면 저장에 실패합니다`() {
// when & then
assertThrows<DataIntegrityViolationException> {
assertThrows<DataAccessException> {
dslContext.insertInto(Member.MEMBER)
.set(Member.MEMBER.EMAIL, EMAIL)
.execute()
Expand Down
7 changes: 0 additions & 7 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")

/** jooq */
implementation("org.springframework.boot:spring-boot-starter-jooq")
implementation("org.jooq:jooq:${DependencyVersion.JOOQ}")
implementation("org.jooq:jooq-meta:${DependencyVersion.JOOQ}")
implementation("org.jooq:jooq-codegen:${DependencyVersion.JOOQ}")
jooqCodegen("org.jooq:jooq-meta-extensions:${DependencyVersion.JOOQ}")

/** swagger & restdocs */
implementation("org.springdoc:springdoc-openapi-ui:${DependencyVersion.SPRINGDOC}")
implementation("org.springframework.restdocs:spring-restdocs-webtestclient")
Expand Down
62 changes: 62 additions & 0 deletions api/src/main/kotlin/com/few/api/client/repo/RepoClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.few.api.client.repo

import com.few.api.client.config.properties.DiscordBodyProperty
import com.few.api.client.config.properties.Embed
import com.few.api.client.repo.dto.RepoAlterArgs
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.HttpEntity
import org.springframework.http.HttpMethod
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate

@Service
class RepoClient(
private val restTemplate: RestTemplate,
@Value("\${webhook.discord}") private val discordWebhook: String,
) {
private val log = KotlinLogging.logger {}

fun announceRepoAlter(args: RepoAlterArgs) {
val embedsList = ArrayList<Embed>()
args.let {
embedsList.add(
Embed(
title = "Exception",
description = "Slow Query Detected"
)
)
it.requestURL.let { requestURL ->
embedsList.add(
Embed(
title = "Request URL",
description = requestURL
)
)
}
it.query?.let { query ->
embedsList.add(
Embed(
title = "Slow Query Detected",
description = query
)
)
}
}
args.let {
DiscordBodyProperty(
content = "😭 DB 이상 발생",
embeds = embedsList
)
}.let { body ->
restTemplate.exchange(
discordWebhook,
HttpMethod.POST,
HttpEntity(body),
String::class.java
).let { res ->
log.info { "Discord webhook response: ${res.statusCode}" }
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.few.api.client.repo.dto

data class RepoAlterArgs(
val requestURL: String,
val query: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.few.api.client.repo.event

import com.few.api.client.repo.RepoClient
import com.few.api.client.repo.dto.RepoAlterArgs
import com.few.api.config.ApiThreadPoolConfig.Companion.DISCORD_HOOK_EVENT_POOL
import com.few.api.repo.common.SlowQueryEvent
import org.slf4j.MDC
import org.springframework.context.event.EventListener
import org.springframework.scheduling.annotation.Async
import org.springframework.stereotype.Component

@Component
class SlowQueryEventListener(
private val repoClient: RepoClient,
) {

@Async(value = DISCORD_HOOK_EVENT_POOL)
@EventListener
fun handleSlowQueryEvent(event: SlowQueryEvent) {
RepoAlterArgs(
requestURL = MDC.get("Request-URL") ?: "",
query = event.slowQuery
).let {
repoClient.announceRepoAlter(it)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ApiThreadPoolConfig {
setRejectedExecutionHandler { r, _ ->
log.warn { "Discord Hook Event Task Rejected: $r" }
}
setTaskDecorator(ClonedTaskDecorator())
initialize()
}
}
16 changes: 16 additions & 0 deletions api/src/main/kotlin/com/few/api/config/ClonedTaskDecorator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.few.api.config

import org.slf4j.MDC
import org.springframework.core.task.TaskDecorator

class ClonedTaskDecorator : TaskDecorator {
override fun decorate(runnable: Runnable): Runnable {
val contextMap = MDC.getCopyOfContextMap()
return Runnable {
if (contextMap != null) {
MDC.setContextMap(contextMap)
}
runnable.run()
}
}
}
8 changes: 2 additions & 6 deletions batch/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ dependencies {
/** module */
implementation(project(":email"))

/** spring starter */
api("org.springframework.boot:spring-boot-starter-data-jdbc")
/** mysql */
implementation("com.mysql:mysql-connector-j")

/** jooq */
implementation("org.springframework.boot:spring-boot-starter-jooq")
implementation("org.jooq:jooq:${DependencyVersion.JOOQ}")
implementation("org.jooq:jooq-meta:${DependencyVersion.JOOQ}")
implementation("org.jooq:jooq-codegen:${DependencyVersion.JOOQ}")
api("org.springframework.boot:spring-boot-starter-jooq")
jooqCodegen("org.jooq:jooq-meta-extensions:${DependencyVersion.JOOQ}")

/** test container */
Expand Down
10 changes: 10 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ subprojects {
allOpen {
}

dependencyManagement {
dependencies {
/**
* spring boot starter jooq 3.2.5 default jooq version is 3.18.14.
* But jooq-codegen-gradle need over 3.19.0.
* */
dependency("org.jooq:jooq:${DependencyVersion.JOOQ}")
}
}

dependencies {
/** spring starter */
implementation("org.springframework.boot:spring-boot-starter-validation")
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/DependencyVersion.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ object DependencyVersion {
const val FLYWAY = "9.16.0"

/** jooq */
const val JOOQ = "3.19.9"
const val JOOQ = "3.19.10"

/** test */
const val MOCKK = "1.13.9"
Expand Down

0 comments on commit ceae194

Please sign in to comment.