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.