summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gradle/libs.versions.toml2
-rw-r--r--opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/ExperimentHelpers.kt11
-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
5 files changed, 52 insertions, 42 deletions
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 76846104..4e2fc777 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,6 +2,7 @@
classgraph = "4.8.115"
clikt = "3.2.0"
config = "1.4.1"
+commons-math3 = "3.6.1"
hadoop = "3.3.1"
jackson = "2.12.5"
junit-jupiter = "5.7.2"
@@ -71,3 +72,4 @@ kotlinx-benchmark-runtime-jvm = { module = "org.jetbrains.kotlinx:kotlinx-benchm
classgraph = { module = "io.github.classgraph:classgraph", version.ref = "classgraph" }
hadoop-common = { module = "org.apache.hadoop:hadoop-common", version.ref = "hadoop" }
hadoop-mapreduce-client-core = { module = "org.apache.hadoop:hadoop-mapreduce-client-core", version.ref = "hadoop" }
+commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" }
diff --git a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/ExperimentHelpers.kt b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/ExperimentHelpers.kt
index 0230409e..3d605300 100644
--- a/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/ExperimentHelpers.kt
+++ b/opendc-experiments/opendc-experiments-capelin/src/main/kotlin/org/opendc/experiments/capelin/ExperimentHelpers.kt
@@ -26,6 +26,8 @@ import io.opentelemetry.api.metrics.MeterProvider
import io.opentelemetry.sdk.metrics.SdkMeterProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
+import org.apache.commons.math3.distribution.LogNormalDistribution
+import org.apache.commons.math3.random.Well19937c
import org.opendc.compute.api.*
import org.opendc.compute.service.ComputeService
import org.opendc.compute.service.scheduler.ComputeScheduler
@@ -98,15 +100,16 @@ fun createFaultInjector(
random: Random,
failureInterval: Double
): FaultInjector {
+ val rng = Well19937c(random.nextLong())
+
// Parameters from A. Iosup, A Framework for the Study of Grid Inter-Operation Mechanisms, 2009
// GRID'5000
return CorrelatedFaultInjector(
coroutineScope,
clock,
- iatScale = ln(failureInterval), iatShape = 1.03, // Hours
- sizeScale = ln(2.0), sizeShape = ln(1.0), // Expect 2 machines, with variation of 1
- dScale = ln(60.0), dShape = ln(60.0 * 8), // Minutes
- random = random
+ iat = LogNormalDistribution(rng, ln(failureInterval), 1.03),
+ size = LogNormalDistribution(rng, 1.88, 1.25),
+ duration = LogNormalDistribution(rng, 8.89, 2.71)
)
}
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))
}