summaryrefslogtreecommitdiff
path: root/opendc-simulator/opendc-simulator-failures
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-09-09 16:21:41 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2021-09-10 16:13:23 +0200
commitf20d615e3f6e5b9d02526ac033778fb0419fed4e (patch)
tree27f35a6bd2bb9a157bd88755ea567bdeb0eb6156 /opendc-simulator/opendc-simulator-failures
parent58ac6e203f38ea0122d08c74adf7644c478c3afe (diff)
feat(simulator): Support generic distribution in fault injector
This change adds support for specifying the distribution of the failures, group size and duration for the fault injector.
Diffstat (limited to 'opendc-simulator/opendc-simulator-failures')
-rw-r--r--opendc-simulator/opendc-simulator-failures/build.gradle.kts1
-rw-r--r--opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt49
-rw-r--r--opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt31
3 files changed, 43 insertions, 38 deletions
diff --git a/opendc-simulator/opendc-simulator-failures/build.gradle.kts b/opendc-simulator/opendc-simulator-failures/build.gradle.kts
index 57cd0a35..edd48b40 100644
--- a/opendc-simulator/opendc-simulator-failures/build.gradle.kts
+++ b/opendc-simulator/opendc-simulator-failures/build.gradle.kts
@@ -29,4 +29,5 @@ plugins {
dependencies {
api(platform(projects.opendcPlatform))
api(libs.kotlinx.coroutines)
+ api(libs.commons.math3)
}
diff --git a/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt b/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt
index 0e15f338..c3b85666 100644
--- a/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt
+++ b/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/CorrelatedFaultInjector.kt
@@ -23,26 +23,29 @@
package org.opendc.simulator.failures
import kotlinx.coroutines.*
+import org.apache.commons.math3.distribution.RealDistribution
import java.time.Clock
-import kotlin.math.exp
-import kotlin.math.max
-import kotlin.random.Random
-import kotlin.random.asJavaRandom
+import java.util.*
+import kotlin.math.roundToInt
+import kotlin.math.roundToLong
/**
* A [FaultInjector] that injects fault in the system which are correlated to each other. Failures do not occur in
* isolation, but will trigger other faults.
+ *
+ * @param scope The scope to run the fault injector in.
+ * @param clock The [Clock] to keep track of simulation time.
+ * @param iat The inter-arrival time distribution of the failures (in hours).
+ * @param size The failure group size distribution.
+ * @param duration The failure duration (in seconds).
*/
public class CorrelatedFaultInjector(
- private val coroutineScope: CoroutineScope,
+ private val scope: CoroutineScope,
private val clock: Clock,
- private val iatScale: Double,
- private val iatShape: Double,
- private val sizeScale: Double,
- private val sizeShape: Double,
- private val dScale: Double,
- private val dShape: Double,
- random: Random = Random(0)
+ private val iat: RealDistribution,
+ private val size: RealDistribution,
+ private val duration: RealDistribution,
+ private val random: Random = Random(0)
) : FaultInjector {
/**
* The active failure domains that have been registered.
@@ -55,11 +58,6 @@ public class CorrelatedFaultInjector(
private var job: Job? = null
/**
- * The [Random] instance to use.
- */
- private val random: java.util.Random = random.asJavaRandom()
-
- /**
* Enqueue the specified [FailureDomain] to fail some time in the future.
*/
override fun enqueue(domain: FailureDomain) {
@@ -67,7 +65,7 @@ public class CorrelatedFaultInjector(
// Clean up the domain if it finishes
domain.scope.coroutineContext[Job]!!.invokeOnCompletion {
- this@CorrelatedFaultInjector.coroutineScope.launch {
+ this@CorrelatedFaultInjector.scope.launch {
active -= domain
if (active.isEmpty()) {
@@ -81,21 +79,21 @@ public class CorrelatedFaultInjector(
return
}
- job = this.coroutineScope.launch {
+ job = this.scope.launch {
while (active.isNotEmpty()) {
ensureActive()
// Make sure to convert delay from hours to milliseconds
- val d = lognvariate(iatScale, iatShape) * 3.6e6
+ val d = (iat.sample() * 3.6e6).roundToLong()
// Handle long overflow
if (clock.millis() + d <= 0) {
return@launch
}
- delay(d.toLong())
+ delay(d)
- val n = lognvariate(sizeScale, sizeShape).toInt()
+ val n = size.sample().roundToInt()
val targets = active.shuffled(random).take(n)
for (failureDomain in targets) {
@@ -103,14 +101,14 @@ public class CorrelatedFaultInjector(
failureDomain.fail()
}
- val df = max(lognvariate(dScale, dShape) * 6e4, 15 * 6e4)
+ val df = (duration.sample() * 1000).roundToLong() // seconds to milliseconds
// Handle long overflow
if (clock.millis() + df <= 0) {
return@launch
}
- delay(df.toLong())
+ delay(df)
for (failureDomain in targets) {
failureDomain.recover()
@@ -123,7 +121,4 @@ public class CorrelatedFaultInjector(
job = null
}
}
-
- // XXX We should extract this in some common package later on.
- private fun lognvariate(scale: Double, shape: Double) = exp(scale + shape * random.nextGaussian())
}
diff --git a/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt b/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt
index b3bd737e..8f99f758 100644
--- a/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt
+++ b/opendc-simulator/opendc-simulator-failures/src/main/kotlin/org/opendc/simulator/failures/UncorrelatedFaultInjector.kt
@@ -24,38 +24,47 @@ package org.opendc.simulator.failures
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
+import org.apache.commons.math3.distribution.RealDistribution
import java.time.Clock
-import kotlin.math.ln1p
-import kotlin.math.pow
-import kotlin.random.Random
+import kotlin.math.roundToLong
/**
* A [FaultInjector] that injects uncorrelated faults into the system, meaning that failures of the subsystems are
* independent.
+ *
+ * @param clock The [Clock] to keep track of simulation time.
+ * @param iat The failure inter-arrival time distribution (in hours)
+ * @param duration The failure duration distribution (in seconds).
*/
public class UncorrelatedFaultInjector(
private val clock: Clock,
- private val alpha: Double,
- private val beta: Double,
- private val random: Random = Random(0)
+ private val iat: RealDistribution,
+ private val duration: RealDistribution,
) : FaultInjector {
/**
* Enqueue the specified [FailureDomain] to fail some time in the future.
*/
override fun enqueue(domain: FailureDomain) {
domain.scope.launch {
- val d = random.weibull(alpha, beta) * 1e3 // Make sure to convert delay to milliseconds
+ val d = (iat.sample() * 3.6e6).roundToLong() // Make sure to convert delay to milliseconds
// Handle long overflow
if (clock.millis() + d <= 0) {
return@launch
}
- delay(d.toLong())
+ delay(d)
domain.fail()
+
+ val df = (duration.sample() * 1000).roundToLong() // seconds to milliseconds
+
+ // Handle long overflow
+ if (clock.millis() + df <= 0) {
+ return@launch
+ }
+
+ delay(df)
+ domain.recover()
}
}
-
- // XXX We should extract this in some common package later on.
- private fun Random.weibull(alpha: Double, beta: Double) = (beta * (-ln1p(-nextDouble())).pow(1.0 / alpha))
}