[mesos] branch 1.10.x updated: Fixed performance of tracking resource totals in allocator's roles tree.

2020-05-14 Thread asekretenko
This is an automated email from the ASF dual-hosted git repository.

asekretenko pushed a commit to branch 1.10.x
in repository https://gitbox.apache.org/repos/asf/mesos.git


The following commit(s) were added to refs/heads/1.10.x by this push:
 new 1ff2fcd  Fixed performance of tracking resource totals in allocator's 
roles tree.
1ff2fcd is described below

commit 1ff2fcd90eabd98786531748869b8596120f7dfe
Author: Andrei Sekretenko 
AuthorDate: Wed May 13 18:15:13 2020 +0200

Fixed performance of tracking resource totals in allocator's roles tree.

Before this patch, the roles tree was tracking total resources
offered/allocated to a role as a single `Resources` objects.
In the case when each agent has a limited number of unique resources
(for example, a single persistent voulme), this resulted in poor
asymptotic complexity of allocation versus the number of agents
(O(N^2)) that was clearly observable in
`HierarchicalAllocations_BENCHMARK_Test.PersistentVolumes`.
In addition, the role tree code was violating the convention that
`Resources` belonging to different agents should never be added.

This patch implements per-agent tracking of `Resources` in the roles
tree, thus improving the performance of allocation (and getting rid of
the potentially problematic O(N^2) asymptotic) in the case of many
agents with a limited number of unique resources each.

Review: https://reviews.apache.org/r/72508
---
 src/master/allocator/mesos/hierarchical.cpp | 139 
 src/master/allocator/mesos/hierarchical.hpp |  69 ++
 2 files changed, 152 insertions(+), 56 deletions(-)

diff --git a/src/master/allocator/mesos/hierarchical.cpp 
b/src/master/allocator/mesos/hierarchical.cpp
index 5fe9ffc..9e50799 100644
--- a/src/master/allocator/mesos/hierarchical.cpp
+++ b/src/master/allocator/mesos/hierarchical.cpp
@@ -202,6 +202,46 @@ static hashmap> 
unpackFrameworkOfferFilters(
 }
 
 
+void ScalarResourceTotals::add(
+const SlaveID& slaveID,
+const Resources& resources)
+{
+  if (resources.scalars().empty()) {
+// In this case, we avoid adding an entry to `scalars` to maintain the
+// invariant that `scalars` doesn't track agents with empty resources.
+return;
+  }
+
+  scalarsTotal -= ResourceQuantities::fromScalarResources(scalars[slaveID]);
+  scalars.at(slaveID) += resources.scalars();
+  scalarsTotal += ResourceQuantities::fromScalarResources(scalars.at(slaveID));
+}
+
+
+void ScalarResourceTotals::subtract(
+const SlaveID& slaveID,
+const Resources& resources)
+{
+  if (resources.scalars().empty()) {
+// `scalars` does not track agents with empty resources, thus subtracting
+// empty resources from an agent is valid regardless of whether its
+// resources are tracked in `scalars`.
+return;
+  }
+
+  CHECK_CONTAINS(scalars, slaveID);
+  CHECK_CONTAINS(scalars.at(slaveID), resources.scalars());
+
+  scalarsTotal -= ResourceQuantities::fromScalarResources(scalars.at(slaveID));
+  scalars.at(slaveID) -= resources.scalars();
+  scalarsTotal += ResourceQuantities::fromScalarResources(scalars.at(slaveID));
+
+  if (scalars.at(slaveID).empty()) {
+scalars.erase(slaveID);
+  }
+}
+
+
 Role::Role(const string& _role, Role* _parent)
   : role(_role),
 basename(strings::split(role, "/").back()),
@@ -315,10 +355,10 @@ bool RoleTree::tryRemove(const std::string& role)
   break;
 }
 
-CHECK(current->allocatedScalars_.empty())
+CHECK(current->allocatedUnreservedNonRevocable.empty())
   << "An empty role " << current->role
   << " has non-empty allocated scalar resources: "
-  << current->allocatedScalars_;
+  << current->allocatedUnreservedNonRevocable.quantities();
 
 Role* parent = CHECK_NOTNULL(current->parent);
 
@@ -328,9 +368,17 @@ bool RoleTree::tryRemove(const std::string& role)
   (*metrics)->removeRole(current->role);
 }
 
-CHECK(current->offeredOrAllocatedScalars_.empty())
-  << " role: " << role
-  << " offeredOrAllocated: " << current->offeredOrAllocatedScalars_;
+CHECK(current->offeredOrAllocatedUnreservedNonRevocable.empty())
+  << "An empty role " << current->role
+  << " has non-empty offered or allocated"
+  << " unreserved non-revocable scalar resources: "
+  << current->offeredOrAllocatedUnreservedNonRevocable.quantities();
+
+CHECK(current->offeredOrAllocatedReserved.empty())
+  << "An empty role " << current->role
+  << " has non-empty offered or allocated reserved scalar resources: "
+  << current->offeredOrAllocatedReserved.quantities();
+
 roles_.erase(current->role);
 
 current = parent;
@@ -385,28 +433,32 @@ void RoleTree::untrackReservations(const Resources& 
resources)
 }
 
 
-void RoleTree::trackAllocated(const Resources& resources_)
+void RoleTree::trackAllocated(
+const SlaveID& slaveId,
+const Resources& resources_)
 {
   foreachpair (
   

[mesos] branch master updated: Fixed performance of tracking resource totals in allocator's roles tree.

2020-05-14 Thread asekretenko
This is an automated email from the ASF dual-hosted git repository.

asekretenko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git


The following commit(s) were added to refs/heads/master by this push:
 new dcb5585  Fixed performance of tracking resource totals in allocator's 
roles tree.
dcb5585 is described below

commit dcb558597767aba9bcdcbc23e1a0a2674f653161
Author: Andrei Sekretenko 
AuthorDate: Wed May 13 18:15:13 2020 +0200

Fixed performance of tracking resource totals in allocator's roles tree.

Before this patch, the roles tree was tracking total resources
offered/allocated to a role as a single `Resources` objects.
In the case when each agent has a limited number of unique resources
(for example, a single persistent voulme), this resulted in poor
asymptotic complexity of allocation versus the number of agents
(O(N^2)) that was clearly observable in
`HierarchicalAllocations_BENCHMARK_Test.PersistentVolumes`.
In addition, the role tree code was violating the convention that
`Resources` belonging to different agents should never be added.

This patch implements per-agent tracking of `Resources` in the roles
tree, thus improving the performance of allocation (and getting rid of
the potentially problematic O(N^2) asymptotic) in the case of many
agents with a limited number of unique resources each.

Review: https://reviews.apache.org/r/72508
---
 src/master/allocator/mesos/hierarchical.cpp | 139 
 src/master/allocator/mesos/hierarchical.hpp |  69 ++
 2 files changed, 152 insertions(+), 56 deletions(-)

diff --git a/src/master/allocator/mesos/hierarchical.cpp 
b/src/master/allocator/mesos/hierarchical.cpp
index 5fe9ffc..9e50799 100644
--- a/src/master/allocator/mesos/hierarchical.cpp
+++ b/src/master/allocator/mesos/hierarchical.cpp
@@ -202,6 +202,46 @@ static hashmap> 
unpackFrameworkOfferFilters(
 }
 
 
+void ScalarResourceTotals::add(
+const SlaveID& slaveID,
+const Resources& resources)
+{
+  if (resources.scalars().empty()) {
+// In this case, we avoid adding an entry to `scalars` to maintain the
+// invariant that `scalars` doesn't track agents with empty resources.
+return;
+  }
+
+  scalarsTotal -= ResourceQuantities::fromScalarResources(scalars[slaveID]);
+  scalars.at(slaveID) += resources.scalars();
+  scalarsTotal += ResourceQuantities::fromScalarResources(scalars.at(slaveID));
+}
+
+
+void ScalarResourceTotals::subtract(
+const SlaveID& slaveID,
+const Resources& resources)
+{
+  if (resources.scalars().empty()) {
+// `scalars` does not track agents with empty resources, thus subtracting
+// empty resources from an agent is valid regardless of whether its
+// resources are tracked in `scalars`.
+return;
+  }
+
+  CHECK_CONTAINS(scalars, slaveID);
+  CHECK_CONTAINS(scalars.at(slaveID), resources.scalars());
+
+  scalarsTotal -= ResourceQuantities::fromScalarResources(scalars.at(slaveID));
+  scalars.at(slaveID) -= resources.scalars();
+  scalarsTotal += ResourceQuantities::fromScalarResources(scalars.at(slaveID));
+
+  if (scalars.at(slaveID).empty()) {
+scalars.erase(slaveID);
+  }
+}
+
+
 Role::Role(const string& _role, Role* _parent)
   : role(_role),
 basename(strings::split(role, "/").back()),
@@ -315,10 +355,10 @@ bool RoleTree::tryRemove(const std::string& role)
   break;
 }
 
-CHECK(current->allocatedScalars_.empty())
+CHECK(current->allocatedUnreservedNonRevocable.empty())
   << "An empty role " << current->role
   << " has non-empty allocated scalar resources: "
-  << current->allocatedScalars_;
+  << current->allocatedUnreservedNonRevocable.quantities();
 
 Role* parent = CHECK_NOTNULL(current->parent);
 
@@ -328,9 +368,17 @@ bool RoleTree::tryRemove(const std::string& role)
   (*metrics)->removeRole(current->role);
 }
 
-CHECK(current->offeredOrAllocatedScalars_.empty())
-  << " role: " << role
-  << " offeredOrAllocated: " << current->offeredOrAllocatedScalars_;
+CHECK(current->offeredOrAllocatedUnreservedNonRevocable.empty())
+  << "An empty role " << current->role
+  << " has non-empty offered or allocated"
+  << " unreserved non-revocable scalar resources: "
+  << current->offeredOrAllocatedUnreservedNonRevocable.quantities();
+
+CHECK(current->offeredOrAllocatedReserved.empty())
+  << "An empty role " << current->role
+  << " has non-empty offered or allocated reserved scalar resources: "
+  << current->offeredOrAllocatedReserved.quantities();
+
 roles_.erase(current->role);
 
 current = parent;
@@ -385,28 +433,32 @@ void RoleTree::untrackReservations(const Resources& 
resources)
 }
 
 
-void RoleTree::trackAllocated(const Resources& resources_)
+void RoleTree::trackAllocated(
+const SlaveID& slaveId,
+const Resources& resources_)
 {
   foreachpair (