You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by bm...@apache.org on 2014/12/12 22:58:11 UTC

[1/6] mesos git commit: Added multiplication and division operators for Bytes.

Repository: mesos
Updated Branches:
  refs/heads/master ce2aedf4a -> 87e299f3e


Added multiplication and division operators for Bytes.

Review: https://reviews.apache.org/r/28983


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/035b6c63
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/035b6c63
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/035b6c63

Branch: refs/heads/master
Commit: 035b6c633ee7be70097b9ba1fe685f5bf9debf89
Parents: ce2aedf
Author: Benjamin Mahler <be...@gmail.com>
Authored: Thu Dec 11 22:58:17 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Fri Dec 12 13:34:26 2014 -0800

----------------------------------------------------------------------
 .../3rdparty/stout/include/stout/bytes.hpp      | 56 ++++++++++++++++++--
 1 file changed, 53 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/035b6c63/3rdparty/libprocess/3rdparty/stout/include/stout/bytes.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/bytes.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/bytes.hpp
index 72dea4d..0e8385b 100644
--- a/3rdparty/libprocess/3rdparty/stout/include/stout/bytes.hpp
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/bytes.hpp
@@ -21,9 +21,11 @@
 #include <iostream>
 #include <string>
 
-#include "numify.hpp"
-#include "strings.hpp"
-#include "try.hpp"
+#include <stout/abort.hpp>
+#include <stout/numify.hpp>
+#include <stout/stringify.hpp>
+#include <stout/strings.hpp>
+#include <stout/try.hpp>
 
 
 class Bytes
@@ -96,6 +98,28 @@ public:
     return *this;
   }
 
+  Bytes& operator *= (double multiplier)
+  {
+    if (multiplier < 0) {
+      ABORT("Multiplying Bytes by negative multiplier "
+            "'" + stringify(multiplier) + "'");
+    }
+
+    value *= multiplier;
+    return *this;
+  }
+
+  Bytes& operator /= (double divisor)
+  {
+    if (divisor < 0) {
+      ABORT("Dividing Bytes by negative divisor "
+            "'" + stringify(divisor) + "'");
+    }
+
+    value /= divisor;
+    return *this;
+  }
+
 protected:
   static const uint64_t BYTES = 1;
   static const uint64_t KILOBYTES = 1024 * BYTES;
@@ -170,4 +194,30 @@ inline Bytes operator - (const Bytes& lhs, const Bytes& rhs)
   return diff;
 }
 
+
+inline Bytes operator * (const Bytes& lhs, double multiplier)
+{
+  if (multiplier < 0) {
+    ABORT("Multiplying Bytes by negative multiplier "
+          "'" + stringify(multiplier) + "'");
+  }
+
+  Bytes result = lhs;
+  result *= multiplier;
+  return result;
+}
+
+
+inline Bytes operator / (const Bytes& lhs, double divisor)
+{
+  if (divisor < 0) {
+    ABORT("Dividing Bytes by negative divisor "
+          "'" + stringify(divisor) + "'");
+  }
+
+  Bytes result = lhs;
+  result /= divisor;
+  return result;
+}
+
 #endif // __STOUT_BYTES_HPP__


[5/6] mesos git commit: Added an allocator unit test for allocatable resources.

Posted by bm...@apache.org.
Added an allocator unit test for allocatable resources.

Review: https://reviews.apache.org/r/28987


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/df521b02
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/df521b02
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/df521b02

Branch: refs/heads/master
Commit: df521b029b4cbd60fac8ef14ae109be9351d300d
Parents: 92fb594
Author: Benjamin Mahler <be...@gmail.com>
Authored: Thu Dec 11 22:21:48 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Fri Dec 12 13:39:13 2014 -0800

----------------------------------------------------------------------
 src/tests/hierarchical_allocator_tests.cpp | 72 +++++++++++++++++++++++++
 1 file changed, 72 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/df521b02/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index 5e617ff..e429253 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -32,12 +32,16 @@
 #include <stout/utils.hpp>
 
 #include "master/allocator.hpp"
+#include "master/constants.hpp"
 #include "master/flags.hpp"
 #include "master/hierarchical_allocator_process.hpp"
 
 using namespace mesos;
 using namespace mesos::internal;
 
+using mesos::internal::master::MIN_CPUS;
+using mesos::internal::master::MIN_MEM;
+
 using mesos::internal::master::allocator::Allocator;
 using mesos::internal::master::allocator::AllocatorProcess;
 using mesos::internal::master::allocator::HierarchicalDRFAllocatorProcess;
@@ -524,6 +528,74 @@ TEST_F(HierarchicalAllocatorTest, RecoverResources)
 }
 
 
+TEST_F(HierarchicalAllocatorTest, Allocatable)
+{
+  // Pausing the clock is not necessary, but ensures that the test
+  // doesn't rely on the periodic allocation in the allocator, which
+  // would slow down the test.
+  Clock::pause();
+
+  initialize({"role1"});
+
+  FrameworkInfo framework = createFrameworkInfo("role1");
+  allocator->addFramework(framework.id(), framework, Resources());
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  // Not enough memory or cpu to be considered allocatable.
+  SlaveInfo slave1 = createSlaveInfo(
+      "cpus:" + stringify(MIN_CPUS / 2) + ";"
+      "mem:" + stringify((MIN_MEM / 2).megabytes()) + ";"
+      "disk:128");
+  allocator->addSlave(slave1.id(), slave1, slave1.resources(), EMPTY);
+
+  // Enough cpus to be considered allocatable.
+  SlaveInfo slave2 = createSlaveInfo(
+      "cpus:" + stringify(MIN_CPUS) + ";"
+      "mem:" + stringify((MIN_MEM / 2).megabytes()) + ";"
+      "disk:128");
+  allocator->addSlave(slave2.id(), slave2, slave2.resources(), EMPTY);
+
+  Future<Allocation> allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework.id(), allocation.get().frameworkId);
+  EXPECT_EQ(1u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave2.id()));
+  EXPECT_EQ(slave2.resources(), sum(allocation.get().resources.values()));
+
+  // Enough memory to be considered allocatable.
+  SlaveInfo slave3 = createSlaveInfo(
+      "cpus:" + stringify(MIN_CPUS / 2) + ";"
+      "mem:" + stringify((MIN_MEM).megabytes()) + ";"
+      "disk:128");
+  allocator->addSlave(slave3.id(), slave3, slave3.resources(), EMPTY);
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework.id(), allocation.get().frameworkId);
+  EXPECT_EQ(1u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave3.id()));
+  EXPECT_EQ(slave3.resources(), sum(allocation.get().resources.values()));
+
+  // slave4 has enough cpu and memory to be considered allocatable,
+  // but it lies across unreserved and reserved resources!
+  SlaveInfo slave4 = createSlaveInfo(
+      "cpus:" + stringify(MIN_CPUS / 1.5) + ";"
+      "mem:" + stringify((MIN_MEM / 2).megabytes()) + ";"
+      "cpus(role1):" + stringify(MIN_CPUS / 1.5) + ";"
+      "mem(role1):" + stringify((MIN_MEM / 2).megabytes()) + ";"
+      "disk:128");
+  allocator->addSlave(slave4.id(), slave4, slave4.resources(), EMPTY);
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework.id(), allocation.get().frameworkId);
+  EXPECT_EQ(1u, allocation.get().resources.size());
+  EXPECT_TRUE(allocation.get().resources.contains(slave4.id()));
+  EXPECT_EQ(slave4.resources(), sum(allocation.get().resources.values()));
+}
+
+
 // Checks that a slave that is not whitelisted will not have its
 // resources get offered, and that if the whitelist is updated so
 // that it is whitelisted, its resources will then be offered.


[4/6] mesos git commit: Removed Resources::extract in favor of reservation-based helpers.

Posted by bm...@apache.org.
Removed Resources::extract in favor of reservation-based helpers.

Review: https://reviews.apache.org/r/28986


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/92fb5948
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/92fb5948
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/92fb5948

Branch: refs/heads/master
Commit: 92fb5948255a58a5680cbe3bcc2d78945d524480
Parents: d347d4f
Author: Benjamin Mahler <be...@gmail.com>
Authored: Thu Dec 11 17:26:16 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Fri Dec 12 13:39:13 2014 -0800

----------------------------------------------------------------------
 include/mesos/resources.hpp                   |  6 ------
 src/common/resources.cpp                      | 22 +++++++---------------
 src/master/hierarchical_allocator_process.hpp |  4 ++--
 src/tests/hierarchical_allocator_tests.cpp    |  8 ++++----
 4 files changed, 13 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/92fb5948/include/mesos/resources.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/resources.hpp b/include/mesos/resources.hpp
index eae983a..296553a 100644
--- a/include/mesos/resources.hpp
+++ b/include/mesos/resources.hpp
@@ -117,12 +117,6 @@ public:
   // Returns the unreserved resources.
   Resources unreserved() const;
 
-  // Returns all resources in this object that are marked with the
-  // specified role.
-  // TODO(bmahler): Remove this in favor of the more explicit
-  // reservation-based methods above.
-  Resources extract(const std::string& role) const;
-
   // Returns a Resources object with the same amount of each resource
   // type as these Resources, but with all Resource objects marked as
   // the specified role.

http://git-wip-us.apache.org/repos/asf/mesos/blob/92fb5948/src/common/resources.cpp
----------------------------------------------------------------------
diff --git a/src/common/resources.cpp b/src/common/resources.cpp
index 03d40f8..9bf7ae9 100644
--- a/src/common/resources.cpp
+++ b/src/common/resources.cpp
@@ -477,20 +477,6 @@ Resources Resources::unreserved() const
 }
 
 
-Resources Resources::extract(const string& role) const
-{
-  Resources r;
-
-  foreach (const Resource& resource, resources) {
-    if (resource.role() == role) {
-      r += resource;
-    }
-  }
-
-  return r;
-}
-
-
 Resources Resources::flatten(const string& role) const
 {
   Resources flattened;
@@ -514,7 +500,13 @@ public:
 
   Resources apply(const Resources& resources) const
   {
-    return type == ANY? resources : resources.extract(role);
+    if (type == ANY) {
+      return resources;
+    } else if (role == "*") {
+      return resources.unreserved();
+    } else {
+      return resources.reserved(role);
+    }
   }
 
 private:

http://git-wip-us.apache.org/repos/asf/mesos/blob/92fb5948/src/master/hierarchical_allocator_process.hpp
----------------------------------------------------------------------
diff --git a/src/master/hierarchical_allocator_process.hpp b/src/master/hierarchical_allocator_process.hpp
index 610fcd9..94995c0 100644
--- a/src/master/hierarchical_allocator_process.hpp
+++ b/src/master/hierarchical_allocator_process.hpp
@@ -711,10 +711,10 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::allocate(
         FrameworkID frameworkId;
         frameworkId.set_value(frameworkIdValue);
 
-        Resources unreserved = slaves[slaveId].available.extract("*");
+        Resources unreserved = slaves[slaveId].available.unreserved();
         Resources resources = unreserved;
         if (role != "*") {
-          resources += slaves[slaveId].available.extract(role);
+          resources += slaves[slaveId].available.reserved(role);
         }
 
         // If the resources are not allocatable, ignore.

http://git-wip-us.apache.org/repos/asf/mesos/blob/92fb5948/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index 7e3dcd5..5e617ff 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -444,7 +444,7 @@ TEST_F(HierarchicalAllocatorTest, Reservations)
   EXPECT_EQ(2u, allocation.get().resources.size());
   EXPECT_TRUE(allocation.get().resources.contains(slave1.id()));
   EXPECT_TRUE(allocation.get().resources.contains(slave2.id()));
-  EXPECT_EQ(slave1.resources() + Resources(slave2.resources()).extract("*"),
+  EXPECT_EQ(slave1.resources() + Resources(slave2.resources()).unreserved(),
             sum(allocation.get().resources.values()));
 
   // framework2 should get all of its reserved resources on slave2.
@@ -456,7 +456,7 @@ TEST_F(HierarchicalAllocatorTest, Reservations)
   EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
   EXPECT_EQ(1u, allocation.get().resources.size());
   EXPECT_TRUE(allocation.get().resources.contains(slave2.id()));
-  EXPECT_EQ(Resources(slave2.resources()).extract("role2"),
+  EXPECT_EQ(Resources(slave2.resources()).reserved("role2"),
             sum(allocation.get().resources.values()));
 }
 
@@ -487,7 +487,7 @@ TEST_F(HierarchicalAllocatorTest, RecoverResources)
   EXPECT_EQ(slave.resources(), sum(allocation.get().resources.values()));
 
   // Recover the reserved resources, expect them to be re-offered.
-  Resources reserved = Resources(slave.resources()).extract("role1");
+  Resources reserved = Resources(slave.resources()).reserved("role1");
 
   allocator->recoverResources(
       allocation.get().frameworkId,
@@ -505,7 +505,7 @@ TEST_F(HierarchicalAllocatorTest, RecoverResources)
   EXPECT_EQ(reserved, sum(allocation.get().resources.values()));
 
   // Recover the unreserved resources, expect them to be re-offered.
-  Resources unreserved = Resources(slave.resources()).extract("*");
+  Resources unreserved = Resources(slave.resources()).unreserved();
 
   allocator->recoverResources(
       allocation.get().frameworkId,


[2/6] mesos git commit: Split apart the Bytes test.

Posted by bm...@apache.org.
Split apart the Bytes test.

Review: https://reviews.apache.org/r/28984


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/edab96ee
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/edab96ee
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/edab96ee

Branch: refs/heads/master
Commit: edab96ee5c1017a4c6d12e043266d58dfe67308f
Parents: 035b6c6
Author: Benjamin Mahler <be...@gmail.com>
Authored: Thu Dec 11 22:58:36 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Fri Dec 12 13:34:27 2014 -0800

----------------------------------------------------------------------
 .../3rdparty/stout/tests/bytes_tests.cpp        | 45 +++++++++++++++++---
 1 file changed, 38 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/edab96ee/3rdparty/libprocess/3rdparty/stout/tests/bytes_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/tests/bytes_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/bytes_tests.cpp
index 18b2474..dc05284 100644
--- a/3rdparty/libprocess/3rdparty/stout/tests/bytes_tests.cpp
+++ b/3rdparty/libprocess/3rdparty/stout/tests/bytes_tests.cpp
@@ -8,20 +8,51 @@
 #include <stout/try.hpp>
 
 
-TEST(Stout, Bytes)
+TEST(BytesTest, Parse)
 {
-  Try<Bytes> _1terabyte = Bytes::parse("1TB");
+  EXPECT_SOME_EQ(Terabytes(1), Bytes::parse("1TB"));
+  EXPECT_SOME_EQ(Gigabytes(1), Bytes::parse("1GB"));
+  EXPECT_SOME_EQ(Megabytes(1), Bytes::parse("1MB"));
+  EXPECT_SOME_EQ(Kilobytes(1), Bytes::parse("1KB"));
+  EXPECT_SOME_EQ(Bytes(1), Bytes::parse("1B"));
 
-  EXPECT_SOME_EQ(Terabytes(1), _1terabyte);
-  EXPECT_SOME_EQ(Gigabytes(1024), _1terabyte);
-  EXPECT_SOME_EQ(Megabytes(1024 * 1024), _1terabyte);
-  EXPECT_SOME_EQ(Kilobytes(1024 * 1024 * 1024), _1terabyte);
-  EXPECT_SOME_EQ(Bytes(1024LLU * 1024 * 1024 * 1024), _1terabyte);
+  // Cannot have fractional bytes.
+  EXPECT_ERROR(Bytes::parse("1.5B"));
+
+  // Parsing fractions is unsupported.
+  EXPECT_ERROR(Bytes::parse("1.5GB"));
+
+  // Unknown unit.
+  EXPECT_ERROR(Bytes::parse("1PB"));
+}
+
+
+TEST(Stout, Arithmetic)
+{
+  EXPECT_EQ(Terabytes(1), Gigabytes(512) + Gigabytes(512));
+  EXPECT_EQ(Terabytes(1), Terabytes(2) - Terabytes(1));
+
+  EXPECT_EQ(Terabytes(1), Gigabytes(1) * 1024);
+
+  EXPECT_EQ(Gigabytes(1), Terabytes(1) / 1024);
+}
+
+
+TEST(BytesTest, Comparison)
+{
+  EXPECT_GT(Terabytes(1), Gigabytes(1));
+  EXPECT_GT(Gigabytes(1), Megabytes(1));
+  EXPECT_GT(Megabytes(1), Kilobytes(1));
+  EXPECT_GT(Kilobytes(1), Bytes(1));
 
   EXPECT_EQ(Bytes(1024), Kilobytes(1));
   EXPECT_LT(Bytes(1023), Kilobytes(1));
   EXPECT_GT(Bytes(1025), Kilobytes(1));
+}
+
 
+TEST(BytesTest, Stringify)
+{
   EXPECT_NE(Megabytes(1023), Gigabytes(1));
 
   EXPECT_EQ("0B", stringify(Bytes()));


[3/6] mesos git commit: Updated Resources interface to expose reservations.

Posted by bm...@apache.org.
Updated Resources interface to expose reservations.

Review: https://reviews.apache.org/r/28985


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/d347d4fa
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/d347d4fa
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/d347d4fa

Branch: refs/heads/master
Commit: d347d4faf44d9fbf44283b1dbe09011f1e9d2bae
Parents: edab96e
Author: Benjamin Mahler <be...@gmail.com>
Authored: Thu Dec 11 16:34:25 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Fri Dec 12 13:39:12 2014 -0800

----------------------------------------------------------------------
 include/mesos/resources.hpp   | 12 +++++++++++
 src/common/resources.cpp      | 43 ++++++++++++++++++++++++++++++++++++++
 src/tests/resources_tests.cpp | 27 ++++++++++++++++++++++++
 3 files changed, 82 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d347d4fa/include/mesos/resources.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/resources.hpp b/include/mesos/resources.hpp
index 10777a6..eae983a 100644
--- a/include/mesos/resources.hpp
+++ b/include/mesos/resources.hpp
@@ -107,8 +107,20 @@ public:
   // Checks if this Resources is a superset of the given Resources.
   bool contains(const Resources& that) const;
 
+  // Returns the reserved resources, by role.
+  hashmap<std::string, Resources> reserved() const;
+
+  // Returns the reserved resources for the role. Note that the "*"
+  // role represents unreserved resources, and will be ignored.
+  Resources reserved(const std::string& role) const;
+
+  // Returns the unreserved resources.
+  Resources unreserved() const;
+
   // Returns all resources in this object that are marked with the
   // specified role.
+  // TODO(bmahler): Remove this in favor of the more explicit
+  // reservation-based methods above.
   Resources extract(const std::string& role) const;
 
   // Returns a Resources object with the same amount of each resource

http://git-wip-us.apache.org/repos/asf/mesos/blob/d347d4fa/src/common/resources.cpp
----------------------------------------------------------------------
diff --git a/src/common/resources.cpp b/src/common/resources.cpp
index 535a0ea..03d40f8 100644
--- a/src/common/resources.cpp
+++ b/src/common/resources.cpp
@@ -434,6 +434,49 @@ bool Resources::contains(const Resources& that) const
 }
 
 
+hashmap<string, Resources> Resources::reserved() const
+{
+  hashmap<string, Resources> result;
+
+  foreach (const Resource& resource, resources) {
+    if (resource.role() != "*") {
+      result[resource.role()] += resource;
+    }
+  }
+
+  return result;
+}
+
+
+Resources Resources::reserved(const string& role) const
+{
+  Resources result;
+
+  foreach (const Resource& resource, resources) {
+    if (resource.role() != "*" &&
+        resource.role() == role) {
+      result += resource;
+    }
+  }
+
+  return result;
+}
+
+
+Resources Resources::unreserved() const
+{
+  Resources result;
+
+  foreach (const Resource& resource, resources) {
+    if (resource.role() == "*") {
+      result += resource;
+    }
+  }
+
+  return result;
+}
+
+
 Resources Resources::extract(const string& role) const
 {
   Resources r;

http://git-wip-us.apache.org/repos/asf/mesos/blob/d347d4fa/src/tests/resources_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/resources_tests.cpp b/src/tests/resources_tests.cpp
index cfad938..bac092e 100644
--- a/src/tests/resources_tests.cpp
+++ b/src/tests/resources_tests.cpp
@@ -744,6 +744,33 @@ TEST(ResourcesTest, EmptyUnequal)
 }
 
 
+TEST(ResourcesTest, Reservations)
+{
+  Resources unreserved = Resources::parse(
+      "cpus:1;mem:2;disk:4").get();
+  Resources role1 = Resources::parse(
+      "cpus(role1):2;mem(role1):4;disk(role1):8;").get();
+  Resources role2 = Resources::parse(
+      "cpus(role2):4;mem(role2):8;disk(role2):6;").get();
+
+  Resources resources = unreserved + role1 + role2;
+
+  hashmap<string, Resources> reserved = resources.reserved();
+
+  EXPECT_EQ(2u, reserved.size());
+  EXPECT_EQ(role1, reserved["role1"]);
+  EXPECT_EQ(role2, reserved["role2"]);
+
+  EXPECT_EQ(role1, resources.reserved("role1"));
+  EXPECT_EQ(role2, resources.reserved("role2"));
+
+  // Resources with role "*" are not considered reserved.
+  EXPECT_EQ(Resources(), resources.reserved("*"));
+
+  EXPECT_EQ(unreserved, resources.unreserved());
+}
+
+
 TEST(ResourcesTest, FlattenRoles)
 {
   Resource cpus1 = Resources::parse("cpus", "1", "role1").get();


[6/6] mesos git commit: Fixed the two level sorting in the allocator.

Posted by bm...@apache.org.
Fixed the two level sorting in the allocator.

Review: https://reviews.apache.org/r/28824


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/87e299f3
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/87e299f3
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/87e299f3

Branch: refs/heads/master
Commit: 87e299f3ec628682f43a90cc310f6f32024c2905
Parents: df521b0
Author: Benjamin Mahler <be...@gmail.com>
Authored: Mon Dec 8 12:09:26 2014 -0800
Committer: Benjamin Mahler <be...@gmail.com>
Committed: Fri Dec 12 13:43:38 2014 -0800

----------------------------------------------------------------------
 src/master/hierarchical_allocator_process.hpp | 95 +++++++++++++---------
 src/master/sorter.hpp                         |  3 +
 src/tests/hierarchical_allocator_tests.cpp    | 74 ++++++++++++++++-
 3 files changed, 133 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/87e299f3/src/master/hierarchical_allocator_process.hpp
----------------------------------------------------------------------
diff --git a/src/master/hierarchical_allocator_process.hpp b/src/master/hierarchical_allocator_process.hpp
index 94995c0..95fa520 100644
--- a/src/master/hierarchical_allocator_process.hpp
+++ b/src/master/hierarchical_allocator_process.hpp
@@ -197,8 +197,6 @@ protected:
   //   Both reserved resources and unreserved resources are used
   //   in the fairness calculation. This is because reserved
   //   resources can be allocated to any framework in the role.
-  // TODO(bmahler): The code is currently inconsistent in following
-  // this specification, see MESOS-2176.
   RoleSorter* roleSorter;
   hashmap<std::string, FrameworkSorter*> frameworkSorters;
 };
@@ -275,7 +273,11 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::initialize(
     frameworkSorters[name] = new FrameworkSorter();
   }
 
-  VLOG(1) << "Initializing hierarchical allocator process";
+  if (roleSorter->count() == 0) {
+    LOG(ERROR) << "No roles specified, cannot allocate resources!";
+  }
+
+  VLOG(1) << "Initialized hierarchical allocator process";
 
   delay(flags.allocation_interval, self(), &Self::batch);
 }
@@ -297,8 +299,11 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::addFramework(
   CHECK(!frameworkSorters[role]->contains(frameworkId.value()));
   frameworkSorters[role]->add(frameworkId.value());
 
+  // TODO(bmahler): Validate that the reserved resources have the
+  // framework's role.
+
   // Update the allocation to this framework.
-  roleSorter->allocated(role, used);
+  roleSorter->allocated(role, used.unreserved());
   frameworkSorters[role]->add(used);
   frameworkSorters[role]->allocated(frameworkId.value(), used);
 
@@ -327,7 +332,8 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::removeFramework(
   if (frameworkSorters[role]->contains(frameworkId.value())) {
     Resources allocation =
       frameworkSorters[role]->allocation(frameworkId.value());
-    roleSorter->unallocated(role, allocation);
+
+    roleSorter->unallocated(role, allocation.unreserved());
     frameworkSorters[role]->remove(allocation);
     frameworkSorters[role]->remove(frameworkId.value());
   }
@@ -403,6 +409,7 @@ Resources sum(const Iterable& resources)
 
 } // namespace internal {
 
+
 template <class RoleSorter, class FrameworkSorter>
 void
 HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::addSlave(
@@ -414,17 +421,20 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::addSlave(
   CHECK(initialized);
   CHECK(!slaves.contains(slaveId));
 
-  roleSorter->add(total);
+  roleSorter->add(total.unreserved());
 
   foreachpair (const FrameworkID& frameworkId,
-               const Resources& resources,
+               const Resources& allocated,
                used) {
     if (frameworks.contains(frameworkId)) {
       const std::string& role = frameworks[frameworkId].role;
 
-      frameworkSorters[role]->add(resources);
-      frameworkSorters[role]->allocated(frameworkId.value(), resources);
-      roleSorter->allocated(role, resources);
+      // TODO(bmahler): Validate that the reserved resources have the
+      // framework's role.
+
+      roleSorter->allocated(role, allocated.unreserved());
+      frameworkSorters[role]->add(allocated);
+      frameworkSorters[role]->allocated(frameworkId.value(), allocated);
     }
   }
 
@@ -451,7 +461,13 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::removeSlave(
   CHECK(initialized);
   CHECK(slaves.contains(slaveId));
 
-  roleSorter->remove(slaves[slaveId].total);
+  // TODO(bmahler): Per MESOS-621, this should remove the allocations
+  // that any frameworks have on this slave. Otherwise the caller may
+  // "leak" allocated resources accidentally if they forget to recover
+  // all the resources. Fixing this would require more information
+  // than what we currently track in the allocator.
+
+  roleSorter->remove(slaves[slaveId].total.unreserved());
 
   slaves.erase(slaveId);
 
@@ -552,7 +568,7 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::recoverResources(
     if (frameworkSorters[role]->contains(frameworkId.value())) {
       frameworkSorters[role]->unallocated(frameworkId.value(), resources);
       frameworkSorters[role]->remove(resources);
-      roleSorter->unallocated(role, resources);
+      roleSorter->unallocated(role, resources.unreserved());
     }
   }
 
@@ -665,13 +681,13 @@ void
 HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::allocate(
     const SlaveID& slaveId)
 {
-  hashset<SlaveID> slaveIds;
-  slaveIds.insert(slaveId);
-
   Stopwatch stopwatch;
   stopwatch.start();
 
-  allocate(slaveIds);
+  // TODO(bmahler): Add initializer list constructor for hashset.
+  hashset<SlaveID> slaves;
+  slaves.insert(slaveId);
+  allocate(slaves);
 
   VLOG(1) << "Performed allocation for slave " << slaveId << " in "
           << stopwatch.elapsed();
@@ -684,21 +700,22 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::allocate(
     const hashset<SlaveID>& slaveIds_)
 {
   if (roleSorter->count() == 0) {
-    VLOG(1) << "No roles to allocate resources!";
+    LOG(ERROR) << "No roles specified, cannot allocate resources!";
     return;
   }
 
-  if (slaveIds_.empty()) {
-    VLOG(1) << "No resources available to allocate!";
-    return;
-  }
+  // Compute the offerable resources, per framework:
+  //   (1) For reserved resources on the slave, allocate these to a
+  //       framework having the corresponding role.
+  //   (2) For unreserved resources on the slave, allocate these
+  //       to a framework of any role.
+  hashmap<FrameworkID, hashmap<SlaveID, Resources> > offerable;
 
   // Randomize the order in which slaves' resources are allocated.
   // TODO(vinod): Implement a smarter sorting algorithm.
   std::vector<SlaveID> slaveIds(slaveIds_.begin(), slaveIds_.end());
   std::random_shuffle(slaveIds.begin(), slaveIds.end());
 
-  hashmap<FrameworkID, hashmap<SlaveID, Resources> > offerable;
   foreach (const SlaveID& slaveId, slaveIds) {
     // Don't send offers for non-whitelisted and deactivated slaves.
     if (!isWhitelisted(slaveId) || !slaves[slaveId].activated) {
@@ -706,10 +723,10 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::allocate(
     }
 
     foreach (const std::string& role, roleSorter->sort()) {
-      foreach (const std::string& frameworkIdValue,
+      foreach (const std::string& frameworkId_,
                frameworkSorters[role]->sort()) {
         FrameworkID frameworkId;
-        frameworkId.set_value(frameworkIdValue);
+        frameworkId.set_value(frameworkId_);
 
         Resources unreserved = slaves[slaveId].available.unreserved();
         Resources resources = unreserved;
@@ -727,28 +744,32 @@ HierarchicalAllocatorProcess<RoleSorter, FrameworkSorter>::allocate(
           continue;
         }
 
-        VLOG(1)
-          << "Offering " << resources << " on slave " << slaveId
-          << " to framework " << frameworkId;
+        VLOG(2) << "Allocating " << resources << " on slave " << slaveId
+                << " to framework " << frameworkId;
 
+        // Note that we perform "coarse-grained" allocation,
+        // meaning that we always allocate the entire remaining
+        // slave resources to a single framework.
         offerable[frameworkId][slaveId] = resources;
-
-        // Update slave resources.
         slaves[slaveId].available -= resources;
 
-        // Update the sorters.
-        // We only count resources not reserved for this role
-        // in the share the sorter considers.
-        frameworkSorters[role]->add(unreserved);
-        frameworkSorters[role]->allocated(frameworkIdValue, unreserved);
+        // Reserved resources are only accounted for in the framework
+        // sorter, since the reserved resources are not shared across
+        // roles.
+        frameworkSorters[role]->add(resources);
+        frameworkSorters[role]->allocated(frameworkId_, resources);
         roleSorter->allocated(role, unreserved);
       }
     }
   }
 
-  // Now offer the resources to each framework.
-  foreachkey (const FrameworkID& frameworkId, offerable) {
-    offerCallback(frameworkId, offerable[frameworkId]);
+  if (offerable.empty()) {
+    VLOG(1) << "No resources available to allocate!";
+  } else {
+    // Now offer the resources to each framework.
+    foreachkey (const FrameworkID& frameworkId, offerable) {
+      offerCallback(frameworkId, offerable[frameworkId]);
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/87e299f3/src/master/sorter.hpp
----------------------------------------------------------------------
diff --git a/src/master/sorter.hpp b/src/master/sorter.hpp
index 818967f..0150f90 100644
--- a/src/master/sorter.hpp
+++ b/src/master/sorter.hpp
@@ -32,6 +32,9 @@ namespace allocator {
 // Sorters implement the logic for determining the
 // order in which users or frameworks should receive
 // resource allocations.
+// TODO(bmahler): Templatize this on Client, so that callers can
+// don't need to do string conversion, e.g. FrameworkID, string role,
+// etc.
 class Sorter
 {
 public:

http://git-wip-us.apache.org/repos/asf/mesos/blob/87e299f3/src/tests/hierarchical_allocator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp
index e429253..c43d352 100644
--- a/src/tests/hierarchical_allocator_tests.cpp
+++ b/src/tests/hierarchical_allocator_tests.cpp
@@ -164,7 +164,7 @@ Resources sum(const Iterable& iterable)
 // TODO(bmahler): These tests were transformed directly from
 // integration tests into unit tests. However, these tests
 // should be simplified even further to each test a single
-// expected behavor, at which point we can have more tests
+// expected behavior, at which point we can have more tests
 // that are each very small.
 
 
@@ -175,7 +175,7 @@ Resources sum(const Iterable& iterable)
 // framework currently has the smallest share. Checking for proper DRF
 // logic when resources are returned, frameworks exit, etc. is handled
 // by SorterTest.DRFSorter.
-TEST_F(HierarchicalAllocatorTest, DRF)
+TEST_F(HierarchicalAllocatorTest, UnreservedDRF)
 {
   // Pausing the clock is not necessary, but ensures that the test
   // doesn't rely on the periodic allocation in the allocator, which
@@ -294,6 +294,76 @@ TEST_F(HierarchicalAllocatorTest, DRF)
 }
 
 
+// This test ensures that reserved resources do not affect the sharing
+// across roles. However, reserved resources should be shared fairly
+// *within* a role.
+TEST_F(HierarchicalAllocatorTest, ReservedDRF)
+{
+  // Pausing the clock is not necessary, but ensures that the test
+  // doesn't rely on the periodic allocation in the allocator, which
+  // would slow down the test.
+  Clock::pause();
+
+  initialize({"role1", "role2"});
+
+  hashmap<FrameworkID, Resources> EMPTY;
+
+  SlaveInfo slave1 = createSlaveInfo(
+      "cpus:1;mem:512;disk:0;"
+      "cpus(role1):100;mem(role1):1024;disk(role1):0");
+  allocator->addSlave(slave1.id(), slave1, slave1.resources(), EMPTY);
+
+  // framework1 will be offered all of the resources.
+  FrameworkInfo framework1 = createFrameworkInfo("role1");
+  allocator->addFramework(framework1.id(), framework1, Resources());
+
+  Future<Allocation> allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave1.resources(), sum(allocation.get().resources.values()));
+
+  FrameworkInfo framework2 = createFrameworkInfo("role2");
+  allocator->addFramework(framework2.id(), framework2, Resources());
+
+  // framework2 will be allocated the new resoures.
+  SlaveInfo slave2 = createSlaveInfo("cpus:2;mem:512;disk:0");
+  allocator->addSlave(slave2.id(), slave2, slave2.resources(), EMPTY);
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework2.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave2.resources(), sum(allocation.get().resources.values()));
+
+  // Now, even though framework1 has more resources allocated to
+  // it than framework2, reserved resources are not considered for
+  // fairness across roles! We expect framework1 to receive this
+  // slave's resources, since it has fewer unreserved resources.
+  SlaveInfo slave3 = createSlaveInfo("cpus:2;mem:512;disk:0");
+  allocator->addSlave(slave3.id(), slave3, slave3.resources(), EMPTY);
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework1.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave3.resources(), sum(allocation.get().resources.values()));
+
+  // Now add another framework in role1. Since the reserved resources
+  // should be allocated fairly between frameworks within a role, we
+  // expect framework3 to receive the next allocation of role1
+  // resources.
+  FrameworkInfo framework3 = createFrameworkInfo("role1");
+  allocator->addFramework(framework3.id(), framework3, Resources());
+
+  SlaveInfo slave4 = createSlaveInfo(
+      "cpus(role1):2;mem(role1):1024;disk(role1):0");
+  allocator->addSlave(slave4.id(), slave4, slave4.resources(), EMPTY);
+
+  allocation = queue.get();
+  AWAIT_READY(allocation);
+  EXPECT_EQ(framework3.id(), allocation.get().frameworkId);
+  EXPECT_EQ(slave4.resources(), sum(allocation.get().resources.values()));
+}
+
+
 // This test ensures that allocation is done per slave. This is done
 // by having 2 slaves and 2 frameworks and making sure each framework
 // gets only one slave's resources during an allocation.