-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ee97b46
commit 59785be
Showing
17 changed files
with
303 additions
and
193 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 8 additions & 6 deletions
14
...runner/client/src/main/kotlin/com/avito/runner/scheduler/metrics/TestMetricsAggregator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,20 @@ | ||
package com.avito.runner.scheduler.metrics | ||
|
||
import com.avito.android.Result | ||
|
||
internal interface TestMetricsAggregator { | ||
|
||
fun initialDelay(): Long? | ||
fun initialDelay(): Result<Long> | ||
|
||
fun endDelay(): Long? | ||
fun endDelay(): Result<Long> | ||
|
||
fun medianQueueTime(): Long? | ||
fun medianQueueTime(): Result<Long> | ||
|
||
fun medianInstallationTime(): Long? | ||
fun medianInstallationTime(): Result<Long> | ||
|
||
fun suiteTime(): Long? | ||
fun suiteTime(): Result<Long> | ||
|
||
fun totalTime(): Long | ||
|
||
fun medianDeviceUtilization(): Long? | ||
fun medianDeviceUtilization(): Result<Long> | ||
} |
78 changes: 41 additions & 37 deletions
78
...er/client/src/main/kotlin/com/avito/runner/scheduler/metrics/TestMetricsAggregatorImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,70 @@ | ||
package com.avito.runner.scheduler.metrics | ||
|
||
import com.avito.android.Result | ||
import com.avito.math.median | ||
import com.avito.runner.scheduler.metrics.model.DeviceKey | ||
import com.avito.runner.scheduler.metrics.model.DeviceTimestamps | ||
import com.avito.runner.scheduler.metrics.model.TestTimestamps | ||
|
||
internal class TestMetricsAggregatorImpl( | ||
internal data class TestMetricsAggregatorImpl( | ||
private val testSuiteStartedTime: Long, | ||
private val testSuiteEndedTime: Long, | ||
private val deviceTimestamps: Map<DeviceKey, DeviceTimestamps> | ||
) : TestMetricsAggregator { | ||
|
||
private val testTimestamps = deviceTimestamps.flatMap { it.value.testTimestamps.values } | ||
|
||
private val firstTestStarted = testTimestamps | ||
.mapNotNull { it.started } | ||
private val firstTestStarted: Result<Long> = testTimestamps.filterIsInstance<TestTimestamps.Finished>() | ||
.map { it.startTime } | ||
.minOrNull() | ||
.toResult { "Cannot calculate first started test time" } | ||
|
||
private val lastTestEnded = testTimestamps | ||
.mapNotNull { it.finished } | ||
private val lastTestEnded: Result<Long> = testTimestamps.filterIsInstance<TestTimestamps.Finished>() | ||
.map { it.finishTime } | ||
.maxOrNull() | ||
.toResult { "Cannot calculate last ended test time" } | ||
|
||
private val queueTimes: List<Long> = testTimestamps | ||
.mapNotNull { it.onDevice } | ||
.map { it - testSuiteStartedTime } | ||
private val queueTimes: List<Long> = testTimestamps.filterIsInstance<TestTimestamps.Finished>() | ||
.map { it.onDevice - testSuiteStartedTime } | ||
|
||
private val installationTimes: List<Long> = testTimestamps | ||
.mapNotNull { it.installationTime } | ||
private val installationTimes: List<Long> = testTimestamps.filterIsInstance<TestTimestamps.Finished>() | ||
.map { it.installationTime } | ||
|
||
override fun initialDelay(): Long? = firstTestStarted?.let { it - testSuiteStartedTime } | ||
override fun initialDelay(): Result<Long> = firstTestStarted.map { it - testSuiteStartedTime } | ||
|
||
override fun endDelay(): Long? = lastTestEnded?.let { testSuiteEndedTime - it } | ||
override fun endDelay(): Result<Long> = lastTestEnded.map { testSuiteEndedTime - it } | ||
|
||
override fun medianQueueTime(): Long? = queueTimes.aggregateOrNull { it.median() } | ||
override fun medianQueueTime(): Result<Long> = queueTimes.aggregate( | ||
{ it.median() }, | ||
{ "Cannot calculate median queue time" } | ||
) | ||
|
||
override fun medianInstallationTime(): Long? = installationTimes.aggregateOrNull { it.median() } | ||
override fun medianInstallationTime(): Result<Long> = installationTimes.aggregate( | ||
{ it.median() }, | ||
{ "Cannot calculate median installation time" } | ||
) | ||
|
||
override fun suiteTime(): Long? = if (lastTestEnded != null && firstTestStarted != null) { | ||
lastTestEnded - firstTestStarted | ||
} else { | ||
null | ||
} | ||
override fun suiteTime(): Result<Long> = lastTestEnded.combine(firstTestStarted) { last, first -> last - first } | ||
|
||
override fun totalTime() = testSuiteEndedTime - testSuiteStartedTime | ||
|
||
override fun medianDeviceUtilization(): Long? = | ||
deviceTimestamps.values | ||
.mapNotNull { it.utilizationPercent } | ||
.aggregateOrNull { it.median() } | ||
|
||
/** | ||
* return null if no data | ||
*/ | ||
private fun List<Number>.aggregateOrNull(aggregateFunc: (List<Number>) -> Number): Long? { | ||
return if (isNotEmpty()) { | ||
val result = aggregateFunc.invoke(this).toLong() | ||
if (result > 0) { | ||
result | ||
} else { | ||
null | ||
} | ||
} else { | ||
null | ||
override fun medianDeviceUtilization(): Result<Long> = | ||
deviceTimestamps.values.filterIsInstance<DeviceTimestamps.Finished>() | ||
.map { it.utilizationPercent } | ||
.aggregate({ it.median() }) { "Cannot calculate median device utilization" } | ||
|
||
private inline fun Long?.toResult(lazyMessage: () -> String): Result<Long> = when (this) { | ||
null -> Result.Failure(IllegalStateException(lazyMessage())) | ||
else -> Result.Success(this) | ||
} | ||
|
||
private inline fun List<Number>.aggregate( | ||
aggregateFunc: (List<Number>) -> Number, | ||
lazyMessage: () -> String | ||
): Result<Long> { | ||
return when { | ||
this.isEmpty() -> Result.Failure(IllegalStateException(lazyMessage())) | ||
else -> Result.Success(aggregateFunc(this).toLong()) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 18 additions & 20 deletions
38
...-runner/client/src/main/kotlin/com/avito/runner/scheduler/metrics/model/TestTimestamps.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,25 @@ | ||
package com.avito.runner.scheduler.metrics.model | ||
|
||
internal data class TestTimestamps( | ||
val onDevice: Long? = null, | ||
val started: Long? = null, | ||
val finished: Long? = null | ||
) { | ||
val effectiveWorkTime: Long? = if (finished != null && onDevice != null) { | ||
finished - onDevice | ||
} else { | ||
null | ||
} | ||
internal sealed class TestTimestamps { | ||
|
||
val installationTime: Long? = if (started != null && onDevice != null) { | ||
started - onDevice | ||
} else { | ||
null | ||
} | ||
data class NotStarted(val onDevice: Long) : TestTimestamps() | ||
|
||
data class Started(val onDevice: Long, val startTime: Long) : TestTimestamps() | ||
|
||
val executionTime: Long? = if (finished != null && started != null) { | ||
finished - started | ||
} else { | ||
null | ||
data class Finished(val onDevice: Long, val startTime: Long, val finishTime: Long) : TestTimestamps() { | ||
val effectiveWorkTime = finishTime - onDevice | ||
val installationTime = startTime - onDevice | ||
} | ||
} | ||
|
||
internal fun TestTimestamps.start(currentTimeMillis: Long): TestTimestamps = when (this) { | ||
is TestTimestamps.NotStarted -> TestTimestamps.Started(this.onDevice, currentTimeMillis) | ||
is TestTimestamps.Started -> error("Can't start already started $this") | ||
is TestTimestamps.Finished -> error("Can't start already finished $this") | ||
} | ||
|
||
companion object | ||
internal fun TestTimestamps.finish(currentTimeMillis: Long): TestTimestamps = when (this) { | ||
is TestTimestamps.NotStarted -> error("Can't finish not started $this") | ||
is TestTimestamps.Started -> TestTimestamps.Finished(this.onDevice, this.startTime, currentTimeMillis) | ||
is TestTimestamps.Finished -> error("Can't finish already finished $this") | ||
} |
Oops, something went wrong.