Skip to content

Commit

Permalink
Fix latency calculation performance issue (again)
Browse files Browse the repository at this point in the history
  • Loading branch information
akobor committed Aug 23, 2020
1 parent 13036fd commit ba093a4
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.kuvaszuptime.kuvasz.repositories

import arrow.core.Either
import arrow.core.Option
import arrow.core.toOption
import com.kuvaszuptime.kuvasz.models.DuplicationError
import com.kuvaszuptime.kuvasz.models.MonitorDuplicatedError
import com.kuvaszuptime.kuvasz.models.PersistenceError
Expand All @@ -15,7 +13,6 @@ import com.kuvaszuptime.kuvasz.tables.pojos.MonitorPojo
import com.kuvaszuptime.kuvasz.util.getCurrentTimestamp
import com.kuvaszuptime.kuvasz.util.toPersistenceError
import org.jooq.Configuration
import org.jooq.SelectOnConditionStep
import org.jooq.exception.DataAccessException
import org.jooq.impl.DSL.avg
import org.jooq.impl.DSL.field
Expand All @@ -36,32 +33,77 @@ class MonitorRepository @Inject constructor(jooqConfig: Configuration) : Monitor

private val dsl = jooqConfig.dsl()

fun getMonitorDetails(monitorId: Int): Option<MonitorDetailsDto> =
getMonitorDetailsSelect()
.where(MONITOR.ID.eq(monitorId))
.groupBy(
MONITOR.ID,
UPTIME_EVENT.STATUS,
UPTIME_EVENT.STARTED_AT,
UPTIME_EVENT.ERROR
@Suppress("LongMethod")
fun getMonitorsWithDetails(enabledOnly: Boolean, monitorId: Int? = null): List<MonitorDetailsDto> {
val percentilesCTE = "percentiles"
val latency = "latency"
val percentile = "percentile"
val monitorIdAlias = "monitor_id"
val percentileSubselect = select(
field("p1.$monitorIdAlias").`as`(monitorIdAlias),
min(field("p1.$latency", Int::class.java)).`as`("p95"),
min(field("p2.$latency", Int::class.java)).`as`("p99")
)
.from(table(percentilesCTE).`as`("p1"))
.join(table(percentilesCTE).`as`("p2"))
.on(field("p1.$monitorIdAlias", Int::class.java).eq(field("p2.$monitorIdAlias", Int::class.java)))
.where(field("p1.$percentile", Double::class.java).greaterOrEqual(P95))
.and(field("p2.$percentile", Double::class.java).greaterOrEqual(P99))
.groupBy(field("p1.$monitorIdAlias"))
.asTable("p")
val withPercentiles =
dsl.with(percentilesCTE).`as`(
select(
LATENCY_LOG.MONITOR_ID.`as`(monitorIdAlias),
LATENCY_LOG.LATENCY.`as`(latency),
percentRank()
.over().partitionBy(LATENCY_LOG.MONITOR_ID).orderBy(LATENCY_LOG.LATENCY).`as`(percentile)
).from(LATENCY_LOG)
.apply {
if (monitorId != null) {
where(LATENCY_LOG.MONITOR_ID.eq(monitorId))
}
}
)
.fetchOneInto(MonitorDetailsDto::class.java)
.toOption()

fun getMonitorsWithDetails(enabledOnly: Boolean): List<MonitorDetailsDto> =
getMonitorDetailsSelect()
return withPercentiles
.select(
MONITOR.ID.`as`("id"),
MONITOR.NAME.`as`("name"),
MONITOR.URL.`as`("url"),
MONITOR.UPTIME_CHECK_INTERVAL.`as`("uptimeCheckInterval"),
MONITOR.ENABLED.`as`("enabled"),
MONITOR.CREATED_AT.`as`("createdAt"),
MONITOR.UPDATED_AT.`as`("updatedAt"),
UPTIME_EVENT.STATUS.`as`("uptimeStatus"),
UPTIME_EVENT.STARTED_AT.`as`("uptimeStatusStartedAt"),
UPTIME_EVENT.ERROR.`as`("uptimeError"),
avg(LATENCY_LOG.LATENCY).`as`("averageLatencyInMs"),
field("p.p95").`as`("p95LatencyInMs"),
field("p.p99").`as`("p99LatencyInMs")
)
.from(MONITOR)
.leftJoin(percentileSubselect).on(MONITOR.ID.eq(field("p.$monitorIdAlias", Int::class.java)))
.leftJoin(UPTIME_EVENT).on(MONITOR.ID.eq(UPTIME_EVENT.MONITOR_ID).and(UPTIME_EVENT.ENDED_AT.isNull))
.leftJoin(LATENCY_LOG).on(MONITOR.ID.eq(LATENCY_LOG.MONITOR_ID))
.apply {
if (enabledOnly) {
where(MONITOR.ENABLED.isTrue)
}
if (monitorId != null) {
where(MONITOR.ID.eq(monitorId))
}
}
.groupBy(
MONITOR.ID,
UPTIME_EVENT.STATUS,
UPTIME_EVENT.STARTED_AT,
UPTIME_EVENT.ERROR
UPTIME_EVENT.ERROR,
field("p.p95"),
field("p.p99")
)
.fetchInto(MonitorDetailsDto::class.java)
}

fun getMonitors(enabledOnly: Boolean): List<MonitorPojo> =
dsl
Expand Down Expand Up @@ -107,51 +149,6 @@ class MonitorRepository @Inject constructor(jooqConfig: Configuration) : Monitor
e.handle()
}

private fun getMonitorDetailsSelect(): SelectOnConditionStep<*> {
val percentilesCTE = "percentiles"
val latency = "latency"
val percentile = "percentile"
val monitorId = "monitor_id"
val p95 = table(percentilesCTE).`as`("p95")
val p99 = table(percentilesCTE).`as`("p99")

return dsl
.with(percentilesCTE).`as`(
select(
LATENCY_LOG.MONITOR_ID.`as`(monitorId),
LATENCY_LOG.LATENCY.`as`(latency),
percentRank().over()
.partitionBy(LATENCY_LOG.MONITOR_ID).orderBy(LATENCY_LOG.LATENCY).`as`(percentile)
).from(LATENCY_LOG)
)
.select(
MONITOR.ID.`as`("id"),
MONITOR.NAME.`as`("name"),
MONITOR.URL.`as`("url"),
MONITOR.UPTIME_CHECK_INTERVAL.`as`("uptimeCheckInterval"),
MONITOR.ENABLED.`as`("enabled"),
MONITOR.CREATED_AT.`as`("createdAt"),
MONITOR.UPDATED_AT.`as`("updatedAt"),
UPTIME_EVENT.STATUS.`as`("uptimeStatus"),
UPTIME_EVENT.STARTED_AT.`as`("uptimeStatusStartedAt"),
UPTIME_EVENT.ERROR.`as`("uptimeError"),
avg(LATENCY_LOG.LATENCY).`as`("averageLatencyInMs"),
min(field("p95.$latency", Int::class.java)).`as`("p95LatencyInMs"),
min(field("p99.$latency", Int::class.java)).`as`("p99LatencyInMs")
)
.from(MONITOR)
.leftJoin(p95).on(
MONITOR.ID.eq(field("p95.$monitorId", Int::class.java))
.and(field("p95.$percentile", Double::class.java).greaterOrEqual(P95))
)
.leftJoin(p99).on(
MONITOR.ID.eq(field("p99.$monitorId", Int::class.java))
.and(field("p99.$percentile", Double::class.java).greaterOrEqual(P99))
)
.leftJoin(UPTIME_EVENT).on(MONITOR.ID.eq(UPTIME_EVENT.MONITOR_ID).and(UPTIME_EVENT.ENDED_AT.isNull))
.leftJoin(LATENCY_LOG).on(MONITOR.ID.eq(LATENCY_LOG.MONITOR_ID))
}

private fun DataAccessException.handle(): Either<PersistenceError, Nothing> {
val persistenceError = toPersistenceError()
return Either.left(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.kuvaszuptime.kuvasz.services

import arrow.core.Option
import arrow.core.firstOrNone
import arrow.core.toOption
import com.kuvaszuptime.kuvasz.models.MonitorNotFoundError
import com.kuvaszuptime.kuvasz.models.dto.MonitorCreateDto
Expand All @@ -17,7 +18,8 @@ class MonitorCrudService @Inject constructor(
private val checkScheduler: CheckScheduler
) {

fun getMonitorDetails(monitorId: Int): Option<MonitorDetailsDto> = monitorRepository.getMonitorDetails(monitorId)
fun getMonitorDetails(monitorId: Int): Option<MonitorDetailsDto> =
monitorRepository.getMonitorsWithDetails(false, monitorId).firstOrNone()

fun getMonitorsWithDetails(enabledOnly: Boolean): List<MonitorDetailsDto> =
monitorRepository.getMonitorsWithDetails(enabledOnly)
Expand Down

0 comments on commit ba093a4

Please sign in to comment.