diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2022-09-21 11:12:19 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-21 11:12:19 +0200 |
| commit | 8d1d091f093e6ac32dba1e6a4f74490b280fcc4b (patch) | |
| tree | 2e45d57e3a06886648585ee0c00fbc793422d991 /opendc-compute | |
| parent | f7ba5cd9bbf1f4d145c3d3d171c2632d44b5f94a (diff) | |
feat(compute): Add support for affinity scheduling (#101)
This change adds support for (anti-)affinity scheduling of servers onto hosts,
which happens at the compute service level.
In the future, we might add support for server groups, which also enables
soft (anti-)affinity scheduling.
Implements #26
## Implementation Notes :hammer_and_pick:
* Add `DifferentHostFilter` to schedule instances on different hosts from a set of instances.
* Add `SameHostFilter` to schedule instances on the same hosts as a set of instances.
Diffstat (limited to 'opendc-compute')
3 files changed, 154 insertions, 0 deletions
diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/DifferentHostFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/DifferentHostFilter.kt new file mode 100644 index 00000000..54f2f303 --- /dev/null +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/DifferentHostFilter.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 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.compute.service.scheduler.filters + +import org.opendc.compute.api.Server +import org.opendc.compute.service.internal.HostView +import java.util.* + +/** + * A [HostFilter] that ensures an instance is scheduled on a different host from a set of instances. + */ +public class DifferentHostFilter : HostFilter { + override fun test(host: HostView, server: Server): Boolean { + @Suppress("UNCHECKED_CAST") + val affinityUUIDs = server.meta["scheduler_hint:different_host"] as? Set<UUID> ?: return true + return host.host.instances.none { it.uid in affinityUUIDs } + } +} diff --git a/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/SameHostFilter.kt b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/SameHostFilter.kt new file mode 100644 index 00000000..febb085d --- /dev/null +++ b/opendc-compute/opendc-compute-service/src/main/kotlin/org/opendc/compute/service/scheduler/filters/SameHostFilter.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 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.compute.service.scheduler.filters + +import org.opendc.compute.api.Server +import org.opendc.compute.service.internal.HostView +import java.util.* + +/** + * A [HostFilter] that ensures an instance is scheduled on the same host as all other instances in a set of instances. + */ +public class SameHostFilter : HostFilter { + override fun test(host: HostView, server: Server): Boolean { + @Suppress("UNCHECKED_CAST") + val affinityUUIDs = server.meta["scheduler_hint:same_host"] as? Set<UUID> ?: return true + return host.host.instances.any { it.uid in affinityUUIDs } + } +} diff --git a/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/scheduler/FilterSchedulerTest.kt b/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/scheduler/FilterSchedulerTest.kt index 3f2ce43b..350ac944 100644 --- a/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/scheduler/FilterSchedulerTest.kt +++ b/opendc-compute/opendc-compute-service/src/test/kotlin/org/opendc/compute/service/scheduler/FilterSchedulerTest.kt @@ -323,6 +323,84 @@ internal class FilterSchedulerTest { } @Test + fun testAffinityFilter() { + val scheduler = FilterScheduler( + filters = listOf(SameHostFilter()), + weighers = emptyList(), + ) + + val serverA = mockk<Server>() + every { serverA.uid } returns UUID.randomUUID() + every { serverA.flavor.cpuCount } returns 2 + every { serverA.flavor.memorySize } returns 1024 + + val hostA = mockk<HostView>() + every { hostA.host.state } returns HostState.UP + every { hostA.host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { hostA.host.instances } returns emptySet() + every { hostA.provisionedCores } returns 3 + + val hostB = mockk<HostView>() + every { hostB.host.state } returns HostState.UP + every { hostB.host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { hostB.host.instances } returns setOf(serverA) + every { hostB.provisionedCores } returns 0 + + scheduler.addHost(hostA) + scheduler.addHost(hostB) + + val serverB = mockk<Server>() + every { serverB.flavor.cpuCount } returns 2 + every { serverB.flavor.memorySize } returns 1024 + every { serverB.meta } returns emptyMap() + + assertEquals(hostA, scheduler.select(serverB)) + + every { serverB.meta } returns mapOf("scheduler_hint:same_host" to setOf(serverA.uid)) + + assertEquals(hostB, scheduler.select(serverB)) + } + + @Test + fun testAntiAffinityFilter() { + val scheduler = FilterScheduler( + filters = listOf(DifferentHostFilter()), + weighers = emptyList(), + ) + + val serverA = mockk<Server>() + every { serverA.uid } returns UUID.randomUUID() + every { serverA.flavor.cpuCount } returns 2 + every { serverA.flavor.memorySize } returns 1024 + + val hostA = mockk<HostView>() + every { hostA.host.state } returns HostState.UP + every { hostA.host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { hostA.host.instances } returns setOf(serverA) + every { hostA.provisionedCores } returns 3 + + val hostB = mockk<HostView>() + every { hostB.host.state } returns HostState.UP + every { hostB.host.model } returns HostModel(4 * 2600.0, 4, 2048) + every { hostB.host.instances } returns emptySet() + every { hostB.provisionedCores } returns 0 + + scheduler.addHost(hostA) + scheduler.addHost(hostB) + + val serverB = mockk<Server>() + every { serverB.flavor.cpuCount } returns 2 + every { serverB.flavor.memorySize } returns 1024 + every { serverB.meta } returns emptyMap() + + assertEquals(hostA, scheduler.select(serverB)) + + every { serverB.meta } returns mapOf("scheduler_hint:different_host" to setOf(serverA.uid)) + + assertEquals(hostB, scheduler.select(serverB)) + } + + @Test fun testRamWeigher() { val scheduler = FilterScheduler( filters = emptyList(), |
