Skip to content

Commit

Permalink
jOOQ transaction fix (#51)
Browse files Browse the repository at this point in the history
* Upgrade Testcontainers

* Upgrade Gradle

* Upgrade jOOQ and fix transaction bug #50

* Exclude /health endpoint from HTTP communication log

* Upgrade Micronaut, Kotlin and other dependencies

* Add test coverage verification to the test flow

* Upgrade Graal base image to 20.3.0

* Revert Micronaut and jOOQ version due to GraalVM compatibility issues
  • Loading branch information
adamkobor authored Nov 22, 2020
1 parent 20b0928 commit 17fb944
Show file tree
Hide file tree
Showing 17 changed files with 176 additions and 88 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM oracle/graalvm-ce:20.2.0-java11 as graalvm
FROM oracle/graalvm-ce:20.3.0-java11 as graalvm
ARG VERSION=""
RUN gu install native-image

Expand All @@ -8,7 +8,7 @@ WORKDIR /home/app/kuvasz
RUN native-image --no-server --verbose -cp build/libs/kuvasz-${VERSION}-all.jar \
--initialize-at-build-time=com.sun.mail.util.LineInputStream

FROM frolvlad/alpine-glibc:alpine-3.12_glibc-2.31
FROM frolvlad/alpine-glibc:alpine-3.12_glibc-2.32
RUN apk --no-cache add libstdc++
EXPOSE 8080
COPY --from=graalvm /home/app/kuvasz/kuvasz /app/kuvasz
Expand Down
32 changes: 31 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
buildscript {
configurations['classpath'].resolutionStrategy.eachDependency {
if (requested.group == 'org.jooq') {
useVersion jooqVersion
}
}
}

plugins {
id "org.jetbrains.kotlin.jvm" version "${kotlinVersion}"
id "org.jetbrains.kotlin.kapt" version "${kotlinVersion}"
Expand Down Expand Up @@ -98,6 +106,29 @@ jacocoTestReport {
)
}

jacocoTestCoverageVerification {
afterEvaluate {
classDirectories.setFrom(
fileTree("build/classes/kotlin/main") {
exclude("com/kuvaszuptime/kuvasz/Application.class")
}
)
}
violationRules {
rule {
limit {
counter = "INSTRUCTION"
value = "COVEREDRATIO"
minimum = 0.9
}
}
}
}

check {
dependsOn("jacocoTestCoverageVerification")
}

detekt {
input = files(
"src/main/kotlin",
Expand Down Expand Up @@ -210,7 +241,6 @@ jooq {
driver = "org.postgresql.Driver"
}
generator {
name = "org.jooq.codegen.DefaultGenerator"
target {
directory = "src/jooq/java"
packageName = "com.kuvaszuptime.kuvasz"
Expand Down
16 changes: 8 additions & 8 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
arrowDataVersion=0.11.0
checkUpdatesVersion=1.1.0
checkUpdatesVersion=1.1.4
detektVersion=1.14.2
jibVersion=2.6.0
jooqPluginVersion=5.2
jooqVersion=3.13.1
kotlinCoroutinesVersion=1.3.9
kotlinVersion=1.4.10
logbookVersion=2.3.0
micronautPluginVersion=1.0.5
micronautVersion=2.1.2
kotlinCoroutinesVersion=1.4.1
kotlinVersion=1.4.20
logbookVersion=2.4.1
micronautPluginVersion=1.2.0
micronautVersion=2.1.4
mockkVersion=1.10.2
palantirGitVersion=0.12.3
postgresTestContainersVersion=1.14.3
postgresTestContainersVersion=1.15.0
postgresVersion=42.2.18
shadowVersion=6.1.0
simpleJavaMailVersion=6.4.3
simpleJavaMailVersion=6.4.4
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#Sat Jul 18 09:10:10 CEST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.kuvaszuptime.kuvasz.factories

import io.micronaut.context.annotation.Factory
import org.zalando.logbook.Conditions.exclude
import org.zalando.logbook.Conditions.requestTo
import org.zalando.logbook.DefaultHttpLogWriter
import org.zalando.logbook.DefaultSink
import org.zalando.logbook.Logbook
Expand All @@ -15,6 +17,7 @@ class LogbookFactory {
@Singleton
fun logbook(): Logbook =
Logbook.builder()
.condition(exclude(requestTo("/health")))
.strategy(SecurityStrategy())
.strategy(WithoutBodyStrategy())
.sink(DefaultSink(JsonHttpLogFormatter(), DefaultHttpLogWriter()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import com.kuvaszuptime.kuvasz.repositories.LatencyLogRepository
import com.kuvaszuptime.kuvasz.repositories.SSLEventRepository
import com.kuvaszuptime.kuvasz.repositories.UptimeEventRepository
import com.kuvaszuptime.kuvasz.services.EventDispatcher
import com.kuvaszuptime.kuvasz.util.transaction
import io.micronaut.context.annotation.Context
import org.jooq.Configuration
import org.jooq.impl.DSL
import org.slf4j.LoggerFactory

@Context
class DatabaseEventHandler(
private val eventDispatcher: EventDispatcher,
private val uptimeEventRepository: UptimeEventRepository,
private val latencyLogRepository: LatencyLogRepository,
private val sslEventRepository: SSLEventRepository
private val sslEventRepository: SSLEventRepository,
private val jooqConfig: Configuration
) {
companion object {
private val logger = LoggerFactory.getLogger(DatabaseEventHandler::class.java)
Expand Down Expand Up @@ -52,9 +54,9 @@ class DatabaseEventHandler(
private fun handleUptimeMonitorEvent(currentEvent: UptimeMonitorEvent) {
currentEvent.previousEvent?.let { previousEvent ->
if (currentEvent.statusNotEquals(previousEvent)) {
uptimeEventRepository.transaction {
uptimeEventRepository.endEventById(previousEvent.id, currentEvent.dispatchedAt)
uptimeEventRepository.insertFromMonitorEvent(currentEvent)
DSL.using(jooqConfig).transaction { config ->
uptimeEventRepository.endEventById(previousEvent.id, currentEvent.dispatchedAt, config)
uptimeEventRepository.insertFromMonitorEvent(currentEvent, config)
}
} else {
uptimeEventRepository.updateEventUpdatedAt(previousEvent.id, currentEvent.dispatchedAt)
Expand All @@ -65,9 +67,9 @@ class DatabaseEventHandler(
private fun handleSSLMonitorEvent(currentEvent: SSLMonitorEvent) {
currentEvent.previousEvent?.let { previousEvent ->
if (currentEvent.statusNotEquals(previousEvent)) {
sslEventRepository.transaction {
sslEventRepository.endEventById(previousEvent.id, currentEvent.dispatchedAt)
sslEventRepository.insertFromMonitorEvent(currentEvent)
DSL.using(jooqConfig).transaction { config ->
sslEventRepository.endEventById(previousEvent.id, currentEvent.dispatchedAt, config)
sslEventRepository.insertFromMonitorEvent(currentEvent, config)
}
} else {
sslEventRepository.updateEventUpdatedAt(previousEvent.id, currentEvent.dispatchedAt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import com.kuvaszuptime.kuvasz.tables.SslEvent.SSL_EVENT
import com.kuvaszuptime.kuvasz.tables.UptimeEvent.UPTIME_EVENT
import com.kuvaszuptime.kuvasz.tables.daos.MonitorDao
import com.kuvaszuptime.kuvasz.tables.pojos.MonitorPojo
import com.kuvaszuptime.kuvasz.tables.records.MonitorRecord
import com.kuvaszuptime.kuvasz.util.fetchOneIntoOrThrow
import com.kuvaszuptime.kuvasz.util.getCurrentTimestamp
import com.kuvaszuptime.kuvasz.util.toPersistenceError
import org.jooq.Configuration
Expand Down Expand Up @@ -62,8 +64,7 @@ class MonitorRepository(jooqConfig: Configuration) : MonitorDao(jooqConfig) {
.insertInto(MONITOR)
.set(dsl.newRecord(MONITOR, monitorPojo))
.returning(MONITOR.asterisk())
.fetchOne()
.into(MonitorPojo::class.java)
.fetchOneIntoOrThrow<MonitorRecord, MonitorPojo>()
)
} catch (e: DataAccessException) {
e.handle()
Expand All @@ -83,8 +84,7 @@ class MonitorRepository(jooqConfig: Configuration) : MonitorDao(jooqConfig) {
.set(MONITOR.PAGERDUTY_INTEGRATION_KEY, updatedPojo.pagerdutyIntegrationKey)
.where(MONITOR.ID.eq(updatedPojo.id))
.returning(MONITOR.asterisk())
.fetchOne()
.into(MonitorPojo::class.java)
.fetchOneIntoOrThrow<MonitorRecord, MonitorPojo>()
)
} catch (e: DataAccessException) {
e.handle()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import com.kuvaszuptime.kuvasz.tables.SslEvent.SSL_EVENT
import com.kuvaszuptime.kuvasz.tables.daos.SslEventDao
import com.kuvaszuptime.kuvasz.tables.pojos.SslEventPojo
import org.jooq.Configuration
import org.jooq.impl.DSL
import java.time.OffsetDateTime
import javax.inject.Singleton

@Singleton
class SSLEventRepository(jooqConfig: Configuration) : SslEventDao(jooqConfig) {
class SSLEventRepository(private val jooqConfig: Configuration) : SslEventDao(jooqConfig) {
private val dsl = jooqConfig.dsl()

fun insertFromMonitorEvent(event: SSLMonitorEvent) {
fun insertFromMonitorEvent(event: SSLMonitorEvent, configuration: Configuration? = jooqConfig) {
val eventToInsert = SslEventPojo()
.setMonitorId(event.monitor.id)
.setStatus(event.sslStatus)
Expand All @@ -25,7 +26,10 @@ class SSLEventRepository(jooqConfig: Configuration) : SslEventDao(jooqConfig) {
eventToInsert.error = event.error.message
}

insert(eventToInsert)
DSL.using(configuration)
.insertInto(SSL_EVENT)
.set(dsl.newRecord(SSL_EVENT, eventToInsert))
.execute()
}

fun getPreviousEventByMonitorId(monitorId: Int): SslEventPojo? =
Expand All @@ -35,8 +39,9 @@ class SSLEventRepository(jooqConfig: Configuration) : SslEventDao(jooqConfig) {
.and(SSL_EVENT.ENDED_AT.isNull)
.fetchOneInto(SslEventPojo::class.java)

fun endEventById(eventId: Int, endedAt: OffsetDateTime) =
dsl.update(SSL_EVENT)
fun endEventById(eventId: Int, endedAt: OffsetDateTime, configuration: Configuration? = jooqConfig) =
DSL.using(configuration)
.update(SSL_EVENT)
.set(SSL_EVENT.ENDED_AT, endedAt)
.set(SSL_EVENT.UPDATED_AT, endedAt)
.where(SSL_EVENT.ID.eq(eventId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import com.kuvaszuptime.kuvasz.tables.UptimeEvent.UPTIME_EVENT
import com.kuvaszuptime.kuvasz.tables.daos.UptimeEventDao
import com.kuvaszuptime.kuvasz.tables.pojos.UptimeEventPojo
import org.jooq.Configuration
import org.jooq.impl.DSL
import java.time.OffsetDateTime
import javax.inject.Singleton

@Singleton
class UptimeEventRepository(jooqConfig: Configuration) : UptimeEventDao(jooqConfig) {
class UptimeEventRepository(private val jooqConfig: Configuration) : UptimeEventDao(jooqConfig) {
private val dsl = jooqConfig.dsl()

fun insertFromMonitorEvent(event: UptimeMonitorEvent) {
fun insertFromMonitorEvent(event: UptimeMonitorEvent, configuration: Configuration? = jooqConfig) {
val eventToInsert = UptimeEventPojo()
.setMonitorId(event.monitor.id)
.setStatus(event.uptimeStatus)
Expand All @@ -26,7 +27,10 @@ class UptimeEventRepository(jooqConfig: Configuration) : UptimeEventDao(jooqConf
eventToInsert.error = event.error.message
}

insert(eventToInsert)
DSL.using(configuration)
.insertInto(UPTIME_EVENT)
.set(dsl.newRecord(UPTIME_EVENT, eventToInsert))
.execute()
}

fun getPreviousEventByMonitorId(monitorId: Int): UptimeEventPojo? =
Expand All @@ -36,8 +40,9 @@ class UptimeEventRepository(jooqConfig: Configuration) : UptimeEventDao(jooqConf
.and(UPTIME_EVENT.ENDED_AT.isNull)
.fetchOneInto(UptimeEventPojo::class.java)

fun endEventById(eventId: Int, endedAt: OffsetDateTime) =
dsl.update(UPTIME_EVENT)
fun endEventById(eventId: Int, endedAt: OffsetDateTime, configuration: Configuration? = jooqConfig) =
DSL.using(configuration)
.update(UPTIME_EVENT)
.set(UPTIME_EVENT.ENDED_AT, endedAt)
.set(UPTIME_EVENT.UPDATED_AT, endedAt)
.where(UPTIME_EVENT.ID.eq(eventId))
Expand Down
15 changes: 9 additions & 6 deletions src/main/kotlin/com/kuvaszuptime/kuvasz/util/Jooq+.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ package com.kuvaszuptime.kuvasz.util

import com.kuvaszuptime.kuvasz.models.DuplicationError
import com.kuvaszuptime.kuvasz.models.PersistenceError
import org.jooq.Configuration
import org.jooq.DAO
import org.jooq.InsertResultStep
import org.jooq.TableRecord
import org.jooq.UpdateResultStep
import org.jooq.exception.DataAccessException
import org.jooq.exception.NoDataFoundException
import org.postgresql.util.PSQLException

fun <R : TableRecord<R>, P, T> DAO<R, P, T>.transaction(block: () -> Unit) {
configuration().dsl().transaction { _: Configuration -> block() }
}

fun DataAccessException.toPersistenceError(): PersistenceError =
getCause(PSQLException::class.java)?.message?.let { message ->
if (message.contains("duplicate key")) DuplicationError() else PersistenceError(message)
} ?: PersistenceError(message)

inline fun <R : TableRecord<R>, reified O> InsertResultStep<R>.fetchOneIntoOrThrow(): O =
fetchOne()?.into(O::class.java) ?: throw NoDataFoundException()

inline fun <R : TableRecord<R>, reified O> UpdateResultStep<R>.fetchOneIntoOrThrow(): O =
fetchOne()?.into(O::class.java) ?: throw NoDataFoundException()
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ class MonitorControllerTest(

then("it should delete the integration key of the given monitor") {
response.status shouldBe HttpStatus.NO_CONTENT
monitorInDb.pagerdutyIntegrationKey shouldBe null
monitorInDb!!.pagerdutyIntegrationKey shouldBe null
}
}

Expand Down
Loading

0 comments on commit 17fb944

Please sign in to comment.