Repository: aurora Updated Branches: refs/heads/master d7425aa56 -> e76862a39
Enables scalable, high-performance bin-packing approximation by sorting offers. Can be controlled via Scheduler flags. Reviewed at https://reviews.apache.org/r/59480/ Project: http://git-wip-us.apache.org/repos/asf/aurora/repo Commit: http://git-wip-us.apache.org/repos/asf/aurora/commit/e76862a3 Tree: http://git-wip-us.apache.org/repos/asf/aurora/tree/e76862a3 Diff: http://git-wip-us.apache.org/repos/asf/aurora/diff/e76862a3 Branch: refs/heads/master Commit: e76862a39622ba5c236f0c9e8ba94c341c5c4da8 Parents: d7425aa Author: David McLaughlin <[email protected]> Authored: Wed May 31 16:30:49 2017 -0700 Committer: David McLaughlin <[email protected]> Committed: Wed May 31 16:30:49 2017 -0700 ---------------------------------------------------------------------- RELEASE-NOTES.md | 7 + .../aurora/benchmark/SchedulingBenchmarks.java | 7 +- .../aurora/scheduler/offers/OfferManager.java | 35 +--- .../aurora/scheduler/offers/OfferOrder.java | 25 +++ .../scheduler/offers/OfferOrderBuilder.java | 119 +++++++++++++ .../aurora/scheduler/offers/OfferSettings.java | 14 +- .../aurora/scheduler/offers/OffersModule.java | 12 +- .../scheduler/offers/OfferManagerImplTest.java | 167 ++++++++++++++++++- .../scheduler/resources/ResourceTestUtil.java | 6 +- 9 files changed, 359 insertions(+), 33 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/RELEASE-NOTES.md ---------------------------------------------------------------------- diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 75b3ddb..fc77b02 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -28,6 +28,13 @@ - Added ability to inject your own scheduling logic, via a lazy Guice module binding. This is an alpha-level feature and not subject to backwards compatibility considerations. You can specify your custom modules using the `task_assigner_modules` and `preemption_slot_finder_modules` options. +- Added support for resource bin-packing via the '-offer_order' option. You can choose from `CPU`, + `MEMORY`, `DISK`, `RANDOM` or `REVOCABLE_CPU`. You can also compose secondary sorts by combining + orders together: e.g. to bin-pack by CPU and MEMORY you could supply 'CPU,MEMORY'. The current + default is `RANDOM`, which has the strong advantage that users can (usually) relocate their tasks + due to noisy neighbors or machine issues with a task restart. When you have deterministic + bin-packing, they may always end up on the same agent. So be careful enabling this without proper + monitoring and remediation of host failures. 0.17.0 ====== http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java ---------------------------------------------------------------------- diff --git a/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java b/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java index b933a5b..ddbfd37 100644 --- a/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java +++ b/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java @@ -19,6 +19,7 @@ import java.util.concurrent.TimeUnit; import javax.inject.Singleton; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; @@ -52,6 +53,7 @@ import org.apache.aurora.scheduler.filter.SchedulingFilterImpl; import org.apache.aurora.scheduler.mesos.Driver; import org.apache.aurora.scheduler.mesos.TestExecutorSettings; import org.apache.aurora.scheduler.offers.OfferManager; +import org.apache.aurora.scheduler.offers.OfferOrder; import org.apache.aurora.scheduler.offers.OfferSettings; import org.apache.aurora.scheduler.offers.OffersModule; import org.apache.aurora.scheduler.preemptor.BiCache; @@ -142,7 +144,10 @@ public class SchedulingBenchmarks { bind(OfferManager.class).to(OfferManager.OfferManagerImpl.class); bind(OfferManager.OfferManagerImpl.class).in(Singleton.class); bind(OfferSettings.class).toInstance( - new OfferSettings(NO_DELAY, () -> DELAY_FOREVER)); + new OfferSettings( + NO_DELAY, + () -> DELAY_FOREVER, + ImmutableList.of(OfferOrder.RANDOM))); bind(BiCache.BiCacheSettings.class).toInstance( new BiCache.BiCacheSettings(DELAY_FOREVER, "")); bind(TaskScheduler.class).to(TaskScheduler.TaskSchedulerImpl.class); http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/src/main/java/org/apache/aurora/scheduler/offers/OfferManager.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/offers/OfferManager.java b/src/main/java/org/apache/aurora/scheduler/offers/OfferManager.java index 78255e6..96acaf9 100644 --- a/src/main/java/org/apache/aurora/scheduler/offers/OfferManager.java +++ b/src/main/java/org/apache/aurora/scheduler/offers/OfferManager.java @@ -13,8 +13,7 @@ */ package org.apache.aurora.scheduler.offers; -import java.time.Instant; -import java.util.Comparator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentSkipListSet; @@ -31,7 +30,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; -import com.google.common.collect.Ordering; import com.google.common.eventbus.Subscribe; import org.apache.aurora.common.inject.TimedInterceptor.Timed; @@ -54,10 +52,6 @@ import org.slf4j.LoggerFactory; import static java.util.Objects.requireNonNull; -import static org.apache.aurora.gen.MaintenanceMode.DRAINED; -import static org.apache.aurora.gen.MaintenanceMode.DRAINING; -import static org.apache.aurora.gen.MaintenanceMode.NONE; -import static org.apache.aurora.gen.MaintenanceMode.SCHEDULED; import static org.apache.aurora.scheduler.events.PubsubEvent.HostAttributesChanged; /** @@ -170,7 +164,7 @@ public interface OfferManager extends EventSubscriber { this.driver = requireNonNull(driver); this.offerSettings = requireNonNull(offerSettings); this.executor = requireNonNull(executor); - this.hostOffers = new HostOffers(statsProvider); + this.hostOffers = new HostOffers(statsProvider, offerSettings.getOfferOrder()); this.offerRaces = statsProvider.makeCounter(OFFER_ACCEPT_RACES); } @@ -271,34 +265,19 @@ public interface OfferManager extends EventSubscriber { * the different indices used and their consistency. */ private static class HostOffers { - private static final Ordering<HostOffer> AURORA_MAINTENANCE_COMPARATOR = - Ordering.explicit(NONE, SCHEDULED, DRAINING, DRAINED) - .onResultOf(offer -> offer.getAttributes().getMode()); - // We should not prefer offers from agents that are scheduled to become unavailable. - // We should also sort the unavailability start to prefer agents that are starting - // maintenance later. - private static final Ordering<HostOffer> MESOS_MAINTENANCE_COMPARATOR = - Ordering - .natural() - .reverse() - .onResultOf(o -> o.getUnavailabilityStart().or(Instant.MAX)); - - private static final Comparator<HostOffer> PREFERENCE_COMPARATOR = - // Currently, the only preference is based on host maintenance status. - AURORA_MAINTENANCE_COMPARATOR - .compound(MESOS_MAINTENANCE_COMPARATOR) - .compound(Ordering.arbitrary()); - - private final Set<HostOffer> offers = new ConcurrentSkipListSet<>(PREFERENCE_COMPARATOR); + + private final Set<HostOffer> offers; private final Map<OfferID, HostOffer> offersById = Maps.newHashMap(); private final Map<AgentID, HostOffer> offersBySlave = Maps.newHashMap(); private final Map<String, HostOffer> offersByHost = Maps.newHashMap(); + // TODO(maxim): Expose via a debug endpoint. AURORA-1136. // Keep track of offer->groupKey mappings that will never be matched to avoid redundant // scheduling attempts. See VetoGroup for more details on static ban. private final Multimap<OfferID, TaskGroupKey> staticallyBannedOffers = HashMultimap.create(); - HostOffers(StatsProvider statsProvider) { + HostOffers(StatsProvider statsProvider, List<OfferOrder> offerOrder) { + offers = new ConcurrentSkipListSet<>(OfferOrderBuilder.create(offerOrder)); // Potential gotcha - since this is a ConcurrentSkipListSet, size() is more expensive. // Could track this separately if it turns out to pose problems. statsProvider.exportSize(OUTSTANDING_OFFERS, offers); http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/src/main/java/org/apache/aurora/scheduler/offers/OfferOrder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/offers/OfferOrder.java b/src/main/java/org/apache/aurora/scheduler/offers/OfferOrder.java new file mode 100644 index 0000000..7c99c30 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/offers/OfferOrder.java @@ -0,0 +1,25 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.aurora.scheduler.offers; + +/** + * The default ordering for OfferManager to return offers in. + */ +public enum OfferOrder { + CPU, + MEMORY, + DISK, + REVOCABLE_CPU, + RANDOM +} http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/src/main/java/org/apache/aurora/scheduler/offers/OfferOrderBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/offers/OfferOrderBuilder.java b/src/main/java/org/apache/aurora/scheduler/offers/OfferOrderBuilder.java new file mode 100644 index 0000000..2676da0 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/offers/OfferOrderBuilder.java @@ -0,0 +1,119 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.aurora.scheduler.offers; + +import java.time.Instant; +import java.util.List; + +import com.google.common.collect.Ordering; + +import org.apache.aurora.scheduler.HostOffer; +import org.apache.aurora.scheduler.resources.ResourceType; + +import static org.apache.aurora.gen.MaintenanceMode.DRAINED; +import static org.apache.aurora.gen.MaintenanceMode.DRAINING; +import static org.apache.aurora.gen.MaintenanceMode.NONE; +import static org.apache.aurora.gen.MaintenanceMode.SCHEDULED; +import static org.apache.aurora.scheduler.resources.ResourceManager.bagFromMesosResources; +import static org.apache.aurora.scheduler.resources.ResourceManager.getNonRevocableOfferResources; +import static org.apache.aurora.scheduler.resources.ResourceManager.getRevocableOfferResources; + +/** + * Utility class for creating compounded offer orders based on some combination of offer ordering. + */ +final class OfferOrderBuilder { + private OfferOrderBuilder() { + + } + + private static final Ordering<HostOffer> AURORA_MAINTENANCE_COMPARATOR = + Ordering.explicit(NONE, SCHEDULED, DRAINING, DRAINED) + .onResultOf(offer -> offer.getAttributes().getMode()); + + // We should not prefer offers from agents that are scheduled to become unavailable. + // We should also sort the unavailability start to prefer agents that are starting + // maintenance later. + private static final Ordering<HostOffer> MESOS_MAINTENANCE_COMPARATOR = + Ordering + .natural() + .reverse() + .onResultOf(o -> o.getUnavailabilityStart().or(Instant.MAX)); + + private static final Ordering<HostOffer> BASE_COMPARATOR = + AURORA_MAINTENANCE_COMPARATOR.compound(MESOS_MAINTENANCE_COMPARATOR); + + private static final Ordering<Object> RANDOM_COMPARATOR = Ordering.arbitrary(); + private static final Ordering<HostOffer> CPU_COMPARATOR = + nonRevocableResourceOrdering(ResourceType.CPUS); + private static final Ordering<HostOffer> RAM_COMPARATOR = + nonRevocableResourceOrdering(ResourceType.RAM_MB); + private static final Ordering<HostOffer> DISK_COMPARATOR = + nonRevocableResourceOrdering(ResourceType.DISK_MB); + private static final Ordering<HostOffer> REVOCABLE_CPU_COMPARATOR = + revocableResourceOrdering(ResourceType.CPUS); + + private static Ordering<HostOffer> nonRevocableResourceOrdering(ResourceType resourceType) { + return Ordering + .natural() + .onResultOf(o -> bagFromMesosResources( + getNonRevocableOfferResources(o.getOffer())).valueOf(resourceType)); + } + + private static Ordering<HostOffer> revocableResourceOrdering(ResourceType resourceType) { + return Ordering + .natural() + .onResultOf(o -> { + Double resource = bagFromMesosResources( + getRevocableOfferResources(o.getOffer())).valueOf(resourceType); + // resource will be 0.0 if there is no revocable cpus available. Since the purpose of + // this ordering is to bin-pack revocable then we push those offers to the back. + return resource.equals(0.0) ? Double.MAX_VALUE : resource; + }); + } + + private static Ordering<HostOffer> getOrdering(Ordering<HostOffer> base, OfferOrder order) { + // Random is Ordering<Object> so accepting base as a parameter and compounding in here is the + // cleanest way I could come up with to avoid a whole bunch of type finagling. + switch(order) { + case CPU: return base.compound(CPU_COMPARATOR); + case DISK: return base.compound(DISK_COMPARATOR); + case MEMORY: return base.compound(RAM_COMPARATOR); + case REVOCABLE_CPU: return base.compound(REVOCABLE_CPU_COMPARATOR); + default: return base.compound(RANDOM_COMPARATOR); + } + } + + private static Ordering<HostOffer> create(Ordering<HostOffer> base, List<OfferOrder> order) { + if (order.isEmpty()) { + return base; + } + Ordering<HostOffer> compounded = getOrdering(base, order.get(0)); + if (order.size() > 1) { + return create(compounded, order.subList(1, order.size())); + } else { + return compounded; + } + } + + /** + * Create a total offer ordering, based on a general machine maintenance ordering. Additional + * ordering is compounded based on the list order. + * + * @param order The list of offer orders. They will be compounded in the list order. + * @return A HostOffer ordering. + */ + static Ordering<HostOffer> create(List<OfferOrder> order) { + return create(BASE_COMPARATOR, order); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/src/main/java/org/apache/aurora/scheduler/offers/OfferSettings.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/offers/OfferSettings.java b/src/main/java/org/apache/aurora/scheduler/offers/OfferSettings.java index adf7f33..ee80176 100644 --- a/src/main/java/org/apache/aurora/scheduler/offers/OfferSettings.java +++ b/src/main/java/org/apache/aurora/scheduler/offers/OfferSettings.java @@ -13,6 +13,8 @@ */ package org.apache.aurora.scheduler.offers; +import java.util.List; + import com.google.common.base.Supplier; import org.apache.aurora.common.quantity.Amount; @@ -27,13 +29,16 @@ public class OfferSettings { private final Amount<Long, Time> offerFilterDuration; private final Supplier<Amount<Long, Time>> returnDelaySupplier; + private final List<OfferOrder> offerOrder; public OfferSettings( Amount<Long, Time> offerFilterDuration, - Supplier<Amount<Long, Time>> returnDelaySupplier) { + Supplier<Amount<Long, Time>> returnDelaySupplier, + List<OfferOrder> offerOrder) { this.offerFilterDuration = requireNonNull(offerFilterDuration); this.returnDelaySupplier = requireNonNull(returnDelaySupplier); + this.offerOrder = requireNonNull(offerOrder); } /** @@ -50,4 +55,11 @@ public class OfferSettings { public Amount<Long, Time> getOfferReturnDelay() { return returnDelaySupplier.get(); } + + /** + * The ordering to use when fetching offers from OfferManager. + */ + public List<OfferOrder> getOfferOrder() { + return offerOrder; + } } http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/src/main/java/org/apache/aurora/scheduler/offers/OffersModule.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/offers/OffersModule.java b/src/main/java/org/apache/aurora/scheduler/offers/OffersModule.java index 317a2d2..e999ac5 100644 --- a/src/main/java/org/apache/aurora/scheduler/offers/OffersModule.java +++ b/src/main/java/org/apache/aurora/scheduler/offers/OffersModule.java @@ -15,9 +15,11 @@ package org.apache.aurora.scheduler.offers; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.util.List; import javax.inject.Qualifier; import javax.inject.Singleton; +import com.google.common.collect.ImmutableList; import com.google.inject.AbstractModule; import com.google.inject.PrivateModule; import com.google.inject.TypeLiteral; @@ -69,6 +71,13 @@ public class OffersModule extends AbstractModule { private static final Arg<Amount<Long, Time>> UNAVAILABILITY_THRESHOLD = Arg.create(Amount.of(6L, Time.MINUTES)); + @CmdLine(name = "offer_order", + help = "Iteration order for offers, to influence task scheduling. Multiple orderings will be " + + "compounded together. E.g. CPU,MEMORY,RANDOM would sort first by cpus offered, then " + + " memory and finally would randomize any equal offers.") + private static final Arg<List<OfferOrder>> OFFER_ORDER = + Arg.create(ImmutableList.of(OfferOrder.RANDOM)); + /** * Binding annotation for the threshold to veto tasks with unavailability. */ @@ -102,7 +111,8 @@ public class OffersModule extends AbstractModule { new RandomJitterReturnDelay( MIN_OFFER_HOLD_TIME.get().as(Time.MILLISECONDS), OFFER_HOLD_JITTER_WINDOW.get().as(Time.MILLISECONDS), - Random.Util.newDefaultRandom()))); + Random.Util.newDefaultRandom()), + OFFER_ORDER.get())); bind(OfferManager.class).to(OfferManager.OfferManagerImpl.class); bind(OfferManager.OfferManagerImpl.class).in(Singleton.class); expose(OfferManager.class); http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/src/test/java/org/apache/aurora/scheduler/offers/OfferManagerImplTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/aurora/scheduler/offers/OfferManagerImplTest.java b/src/test/java/org/apache/aurora/scheduler/offers/OfferManagerImplTest.java index d7addc0..97febf2 100644 --- a/src/test/java/org/apache/aurora/scheduler/offers/OfferManagerImplTest.java +++ b/src/test/java/org/apache/aurora/scheduler/offers/OfferManagerImplTest.java @@ -54,8 +54,12 @@ import static org.apache.aurora.scheduler.offers.OfferManager.OfferManagerImpl.O import static org.apache.aurora.scheduler.offers.OfferManager.OfferManagerImpl.OUTSTANDING_OFFERS; import static org.apache.aurora.scheduler.offers.OfferManager.OfferManagerImpl.STATICALLY_BANNED_OFFERS; import static org.apache.aurora.scheduler.resources.ResourceTestUtil.mesosRange; +import static org.apache.aurora.scheduler.resources.ResourceTestUtil.mesosScalar; import static org.apache.aurora.scheduler.resources.ResourceTestUtil.offer; +import static org.apache.aurora.scheduler.resources.ResourceType.CPUS; +import static org.apache.aurora.scheduler.resources.ResourceType.DISK_MB; import static org.apache.aurora.scheduler.resources.ResourceType.PORTS; +import static org.apache.aurora.scheduler.resources.ResourceType.RAM_MB; import static org.easymock.EasyMock.expectLastCall; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -112,7 +116,8 @@ public class OfferManagerImplTest extends EasyMockTest { addTearDown(clock::assertEmpty); OfferSettings offerSettings = new OfferSettings( Amount.of(OFFER_FILTER_SECONDS, Time.SECONDS), - () -> RETURN_DELAY); + () -> RETURN_DELAY, + ImmutableList.of(OfferOrder.RANDOM)); statsProvider = new FakeStatsProvider(); offerManager = new OfferManagerImpl(driver, offerSettings, statsProvider, executorMock); } @@ -381,4 +386,164 @@ public class OfferManagerImplTest extends EasyMockTest { offer.getOffer(), IHostAttributes.build(offer.getAttributes().newBuilder().setMode(mode))); } + + private OfferManager createOrderedManager(List<OfferOrder> order) { + OfferSettings settings = new OfferSettings( + Amount.of(OFFER_FILTER_SECONDS, Time.SECONDS), + () -> RETURN_DELAY, + order); + DelayExecutor executorMock = createMock(DelayExecutor.class); + clock = FakeScheduledExecutor.fromDelayExecutor(executorMock); + return new OfferManagerImpl(driver, settings, statsProvider, executorMock); + } + + @Test + public void testCPUOrdering() throws Exception { + OfferManager cpuManager = createOrderedManager(ImmutableList.of(OfferOrder.CPU)); + + HostOffer small = setMode(new HostOffer( + offer("host1", mesosScalar(CPUS, 1.0), mesosScalar(CPUS, 24.0, true)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer medium = setMode(new HostOffer( + offer("host2", mesosScalar(CPUS, 5.0)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer large = setMode(new HostOffer( + offer("host3", mesosScalar(CPUS, 10.0)), + HOST_ATTRIBUTES_A), DRAINING); + + control.replay(); + + cpuManager.addOffer(medium); + cpuManager.addOffer(large); + cpuManager.addOffer(small); + + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers(GROUP_KEY))); + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers())); + } + + @Test + public void testRevocableCPUOrdering() throws Exception { + OfferManager cpuManager = createOrderedManager(ImmutableList.of(OfferOrder.REVOCABLE_CPU)); + + HostOffer small = setMode(new HostOffer( + offer("host2", mesosScalar(CPUS, 5.0), mesosScalar(CPUS, 23.0, true)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer medium = setMode(new HostOffer( + offer("host1", mesosScalar(CPUS, 3.0), mesosScalar(CPUS, 24.0, true)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer large = setMode(new HostOffer( + offer("host3", mesosScalar(CPUS, 1.0)), + HOST_ATTRIBUTES_A), DRAINING); + + control.replay(); + + cpuManager.addOffer(medium); + cpuManager.addOffer(large); + cpuManager.addOffer(small); + + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers(GROUP_KEY))); + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers())); + } + + @Test + public void testDiskOrdering() throws Exception { + OfferManager cpuManager = createOrderedManager(ImmutableList.of(OfferOrder.DISK)); + + HostOffer small = setMode(new HostOffer( + offer("host1", mesosScalar(DISK_MB, 1.0)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer medium = setMode(new HostOffer( + offer("host2", mesosScalar(DISK_MB, 5.0)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer large = setMode(new HostOffer( + offer("host3", mesosScalar(DISK_MB, 10.0)), + HOST_ATTRIBUTES_A), DRAINING); + + control.replay(); + + cpuManager.addOffer(medium); + cpuManager.addOffer(large); + cpuManager.addOffer(small); + + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers(GROUP_KEY))); + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers())); + } + + @Test + public void testMemoryOrdering() throws Exception { + OfferManager cpuManager = createOrderedManager(ImmutableList.of(OfferOrder.MEMORY)); + + HostOffer small = setMode(new HostOffer( + offer("host1", mesosScalar(RAM_MB, 1.0)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer medium = setMode(new HostOffer( + offer("host2", mesosScalar(RAM_MB, 5.0)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer large = setMode(new HostOffer( + offer("host3", mesosScalar(RAM_MB, 10.0)), + HOST_ATTRIBUTES_A), DRAINING); + + control.replay(); + + cpuManager.addOffer(medium); + cpuManager.addOffer(large); + cpuManager.addOffer(small); + + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers(GROUP_KEY))); + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers())); + } + + @Test + public void testCPUMemoryOrdering() throws Exception { + OfferManager cpuManager = createOrderedManager( + ImmutableList.of(OfferOrder.CPU, OfferOrder.MEMORY)); + + HostOffer small = setMode(new HostOffer( + offer("host1", + mesosScalar(CPUS, 1.0), + mesosScalar(RAM_MB, 2.0), + mesosScalar(DISK_MB, 3.0)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer medium = setMode(new HostOffer( + offer("host2", + mesosScalar(CPUS, 1.0), + mesosScalar(RAM_MB, 3.0), + mesosScalar(DISK_MB, 2.0)), + HOST_ATTRIBUTES_A), DRAINING); + + HostOffer large = setMode(new HostOffer( + offer("host3", + mesosScalar(CPUS, 10.0), + mesosScalar(CPUS, 1.0), + mesosScalar(DISK_MB, 1.0)), + HOST_ATTRIBUTES_A), DRAINING); + + control.replay(); + + cpuManager.addOffer(large); + cpuManager.addOffer(medium); + cpuManager.addOffer(small); + + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers(GROUP_KEY))); + assertEquals(ImmutableList.of(small, medium, large), + ImmutableList.copyOf(cpuManager.getOffers())); + } } http://git-wip-us.apache.org/repos/asf/aurora/blob/e76862a3/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java b/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java index 676d305..765a527 100644 --- a/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java +++ b/src/test/java/org/apache/aurora/scheduler/resources/ResourceTestUtil.java @@ -132,10 +132,14 @@ public final class ResourceTestUtil { } public static Protos.Offer offer(Protos.Resource... resources) { + return offer("slave-id", resources); + } + + public static Protos.Offer offer(String agentId, Protos.Resource... resources) { return Protos.Offer.newBuilder() .setId(Protos.OfferID.newBuilder().setValue("offer-id")) .setFrameworkId(Protos.FrameworkID.newBuilder().setValue("framework-id")) - .setAgentId(Protos.AgentID.newBuilder().setValue("slave-id")) + .setAgentId(Protos.AgentID.newBuilder().setValue(agentId)) .setHostname("hostname") .addAllResources(ImmutableSet.copyOf(resources)).build(); }
