diff options
Diffstat (limited to 'simulator/opendc-serverless/opendc-serverless-simulator')
8 files changed, 215 insertions, 47 deletions
diff --git a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/SimFunctionDeployer.kt b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/SimFunctionDeployer.kt index f28a926b..2945a279 100644 --- a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/SimFunctionDeployer.kt +++ b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/SimFunctionDeployer.kt @@ -24,10 +24,11 @@ package org.opendc.serverless.simulator import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel -import org.opendc.serverless.api.ServerlessFunction +import org.opendc.serverless.service.FunctionObject import org.opendc.serverless.service.deployer.FunctionDeployer import org.opendc.serverless.service.deployer.FunctionInstance import org.opendc.serverless.service.deployer.FunctionInstanceState +import org.opendc.serverless.simulator.delay.DelayInjector import org.opendc.serverless.simulator.workload.SimServerlessWorkloadMapper import org.opendc.simulator.compute.SimBareMetalMachine import org.opendc.simulator.compute.SimMachine @@ -48,10 +49,11 @@ public class SimFunctionDeployer( private val clock: Clock, private val scope: CoroutineScope, private val model: SimMachineModel, + private val delayInjector: DelayInjector, private val mapper: SimServerlessWorkloadMapper ) : FunctionDeployer { - override fun deploy(function: ServerlessFunction): Instance { + override fun deploy(function: FunctionObject): Instance { val instance = Instance(function) instance.start() return instance @@ -60,7 +62,7 @@ public class SimFunctionDeployer( /** * A simulated [FunctionInstance]. */ - public inner class Instance(override val function: ServerlessFunction) : FunctionInstance { + public inner class Instance(override val function: FunctionObject) : FunctionInstance { /** * The workload associated with this instance. */ @@ -111,33 +113,35 @@ public class SimFunctionDeployer( internal fun start() { check(state == FunctionInstanceState.Provisioning) { "Invalid state of function instance" } job = scope.launch { - workload.onStart() + delay(delayInjector.getColdStartDelay(this@Instance)) - try { - while (isActive) { - chan.receive() + launch { + try { + machine.run(workload) + } finally { + state = FunctionInstanceState.Terminated + } + } - if (queue.isNotEmpty()) { - state = FunctionInstanceState.Active - } + while (isActive) { + if (queue.isEmpty()) { + chan.receive() + } - while (queue.isNotEmpty()) { - val request = queue.poll() - try { - machine.run(workload.onInvoke()) - request.cont.resume(Unit) - } catch (cause: CancellationException) { - request.cont.resumeWithException(cause) - throw cause - } catch (cause: Throwable) { - request.cont.resumeWithException(cause) - } + state = FunctionInstanceState.Active + while (queue.isNotEmpty()) { + val request = queue.poll() + try { + workload.invoke() + request.cont.resume(Unit) + } catch (cause: CancellationException) { + request.cont.resumeWithException(cause) + throw cause + } catch (cause: Throwable) { + request.cont.resumeWithException(cause) } - state = FunctionInstanceState.Idle } - } finally { - state = FunctionInstanceState.Terminated - workload.onStop() + state = FunctionInstanceState.Idle } } } diff --git a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/ColdStartModel.kt b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/ColdStartModel.kt new file mode 100644 index 00000000..f9f3718e --- /dev/null +++ b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/ColdStartModel.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.serverless.simulator.delay + +/** + * Model parameters for the cold start times of serverless services. + */ +public enum class ColdStartModel { + // Min and max memory values from [Peeking Behind The Curtains of Serverless Platforms][2018], + // other values deduced from linear curve. + LAMBDA { + override fun coldStartParam(provisionedMemory: Int): Pair<Double, Double> { + return when (provisionedMemory) { + 128 -> Pair(265.21, 354.43) + 256 -> Pair(261.46, 334.23) + 512 -> Pair(257.71, 314.03) + 1024 -> Pair(253.96, 293.83) + 1536 -> Pair(250.07, 273.63) + 2048 -> Pair(246.11, 253.43) + else -> Pair(0.0, 1.0) + } + } + }, + AZURE { + // Azure by default uses 1.5gb memory to instantiate functions + override fun coldStartParam(provisionedMemory: Int): Pair<Double, Double> { + return Pair(242.66, 340.67) + } + }, + + GOOGLE { + override fun coldStartParam(provisionedMemory: Int): Pair<Double, Double> { + return when (provisionedMemory) { + 128 -> Pair(493.04, 345.8) + 256 -> Pair(416.59, 301.5) + 512 -> Pair(340.14, 257.2) + 1024 -> Pair(263.69, 212.9) + 1536 -> Pair(187.24, 168.6) + 2048 -> Pair(110.77, 124.3) + else -> Pair(0.0, 1.0) + } + } + }; + + /** + * Obtain the stochastic parameters for the cold start models. + */ + public abstract fun coldStartParam(provisionedMemory: Int): Pair<Double, Double> +} diff --git a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/DelayInjector.kt b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/DelayInjector.kt new file mode 100644 index 00000000..f882031b --- /dev/null +++ b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/DelayInjector.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.serverless.simulator.delay + +import org.opendc.serverless.service.deployer.FunctionInstance + +/** + * An interface for modeling the delay caused by function cold starts. + */ +public interface DelayInjector { + /** + * Returns the cold start delay duration sampled from a normal distribution, the distribution is + * initialized using custom mean and standard deviation based on provisioned memory, language and + * failure model + */ + public fun getColdStartDelay(instance: FunctionInstance): Long +} diff --git a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/StochasticDelayInjector.kt b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/StochasticDelayInjector.kt new file mode 100644 index 00000000..154378e1 --- /dev/null +++ b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/StochasticDelayInjector.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.serverless.simulator.delay + +import org.opendc.serverless.service.deployer.FunctionInstance +import java.util.* +import kotlin.math.abs + +/* + * Interface for instance deployment delay estimation. + */ +public class StochasticDelayInjector(private val model: ColdStartModel, private val random: Random) : DelayInjector { + override fun getColdStartDelay(instance: FunctionInstance): Long { + val (mean, sd) = model.coldStartParam(instance.function.memorySize.toInt()) + return abs(random.nextGaussian() * sd + mean).toLong() + } +} diff --git a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/ZeroDelayInjector.kt b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/ZeroDelayInjector.kt new file mode 100644 index 00000000..0895ee18 --- /dev/null +++ b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/delay/ZeroDelayInjector.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.opendc.serverless.simulator.delay + +import org.opendc.serverless.service.deployer.FunctionInstance + +public object ZeroDelayInjector : DelayInjector { + override fun getColdStartDelay(instance: FunctionInstance): Long = 0 +} diff --git a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/workload/SimServerlessWorkload.kt b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/workload/SimServerlessWorkload.kt index afdc05af..121bf915 100644 --- a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/workload/SimServerlessWorkload.kt +++ b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/workload/SimServerlessWorkload.kt @@ -27,19 +27,9 @@ import org.opendc.simulator.compute.workload.SimWorkload /** * A model for a serverless workload, which may be invoked multiple times. */ -public interface SimServerlessWorkload { - /** - * This method is invoked when a function instance is launched. - */ - public fun onStart() {} - +public interface SimServerlessWorkload : SimWorkload { /** * This method is invoked when an active function instance is invoked. */ - public fun onInvoke(): SimWorkload - - /** - * This method is invoked when the function instance is stopped. - */ - public fun onStop() {} + public suspend fun invoke() } diff --git a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/workload/SimServerlessWorkloadMapper.kt b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/workload/SimServerlessWorkloadMapper.kt index 670f978d..3a47eb53 100644 --- a/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/workload/SimServerlessWorkloadMapper.kt +++ b/simulator/opendc-serverless/opendc-serverless-simulator/src/main/kotlin/org/opendc/serverless/simulator/workload/SimServerlessWorkloadMapper.kt @@ -23,6 +23,7 @@ package org.opendc.serverless.simulator.workload import org.opendc.serverless.api.ServerlessFunction +import org.opendc.serverless.service.FunctionObject /** * A [SimServerlessWorkloadMapper] is responsible for mapping a [ServerlessFunction] to a [SimServerlessWorkload] that @@ -32,5 +33,5 @@ public fun interface SimServerlessWorkloadMapper { /** * Map the specified [function] to a [SimServerlessWorkload] that can be simulated. */ - public fun createWorkload(function: ServerlessFunction): SimServerlessWorkload + public fun createWorkload(function: FunctionObject): SimServerlessWorkload } diff --git a/simulator/opendc-serverless/opendc-serverless-simulator/src/test/kotlin/org/opendc/serverless/simulator/SimServerlessServiceTest.kt b/simulator/opendc-serverless/opendc-serverless-simulator/src/test/kotlin/org/opendc/serverless/simulator/SimServerlessServiceTest.kt index f68e206a..3a070475 100644 --- a/simulator/opendc-serverless/opendc-serverless-simulator/src/test/kotlin/org/opendc/serverless/simulator/SimServerlessServiceTest.kt +++ b/simulator/opendc-serverless/opendc-serverless-simulator/src/test/kotlin/org/opendc/serverless/simulator/SimServerlessServiceTest.kt @@ -22,8 +22,9 @@ package org.opendc.serverless.simulator +import io.mockk.coVerify import io.mockk.spyk -import io.mockk.verify +import io.opentelemetry.api.metrics.MeterProvider import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.test.runBlockingTest @@ -33,6 +34,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import org.opendc.serverless.service.ServerlessService import org.opendc.serverless.service.router.RandomRoutingPolicy +import org.opendc.serverless.simulator.delay.ZeroDelayInjector import org.opendc.serverless.simulator.workload.SimServerlessWorkload import org.opendc.simulator.compute.SimMachineModel import org.opendc.simulator.compute.model.MemoryUnit @@ -62,16 +64,17 @@ internal class SimServerlessServiceTest { @Test fun testSmoke() = runBlockingTest { + val meter = MeterProvider.noop().get("opendc-serverless") val clock = DelayControllerClockAdapter(this) - val workload = spyk(object : SimServerlessWorkload { - override fun onInvoke(): SimWorkload = SimFlopsWorkload(1000) + val workload = spyk(object : SimServerlessWorkload, SimWorkload by SimFlopsWorkload(1000) { + override suspend fun invoke() {} }) - val deployer = SimFunctionDeployer(clock, this, machineModel) { workload } - val service = ServerlessService(coroutineContext, clock, deployer, RandomRoutingPolicy()) + val deployer = SimFunctionDeployer(clock, this, machineModel, ZeroDelayInjector) { workload } + val service = ServerlessService(coroutineContext, clock, meter, deployer, RandomRoutingPolicy()) val client = service.newClient() - val function = client.newFunction("test") + val function = client.newFunction("test", 128) function.invoke() delay(2000) @@ -80,9 +83,7 @@ internal class SimServerlessServiceTest { yield() assertAll( - { verify { workload.onStart() } }, - { verify { workload.onInvoke() } }, - { verify { workload.onStop() } } + { coVerify { workload.invoke() } }, ) } } |
