aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dmclaugh...@apache.org
Subject aurora git commit: Enables scalable, high-performance bin-packing approximation by sorting offers. Can be controlled via Scheduler flags.
Date Wed, 31 May 2017 23:41:06 GMT
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 <david@dmclaughlin.com>
Authored: Wed May 31 16:30:49 2017 -0700
Committer: David McLaughlin <dmclaughlin@twitter.com>
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();
   }


Mime
View raw message