Test cases for dynamic weights + allocation behaviour. Review: https://reviews.apache.org/r/41672/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/46cce8e3 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/46cce8e3 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/46cce8e3 Branch: refs/heads/master Commit: 46cce8e3523eaa58ea8b28d35508bf1b3d09e5a4 Parents: 815a596 Author: Yongqiao Wang <[email protected]> Authored: Thu Feb 18 23:51:10 2016 -0800 Committer: Adam B <[email protected]> Committed: Thu Feb 18 23:51:10 2016 -0800 ---------------------------------------------------------------------- src/tests/hierarchical_allocator_tests.cpp | 206 ++++++++++++++++++++++++ 1 file changed, 206 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/46cce8e3/src/tests/hierarchical_allocator_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/hierarchical_allocator_tests.cpp b/src/tests/hierarchical_allocator_tests.cpp index 990f372..5f771f0 100644 --- a/src/tests/hierarchical_allocator_tests.cpp +++ b/src/tests/hierarchical_allocator_tests.cpp @@ -219,6 +219,15 @@ protected: return resource; } + static WeightInfo createWeightInfo(const string& role, double weight) + { + WeightInfo weightInfo; + weightInfo.set_role(role); + weightInfo.set_weight(weight); + + return weightInfo; + } + protected: master::Flags flags; @@ -2594,6 +2603,203 @@ TEST_F(HierarchicalAllocator_BENCHMARK_Test, DeclineOffers) Clock::resume(); } + +// This test ensures that resource allocation is done per role's weight. +// This is done by having six slaves and three frameworks and making sure each +// framework gets the number of resources by their role's weight. +TEST_F(HierarchicalAllocatorTest, UpdateWeight) +{ + // 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(); + + // Register six slaves with the same resources (cpus:2;mem:1024). + vector<SlaveInfo> slaves; + const string SINGLE_RESOURCE = "cpus:2;mem:1024"; + const string DOUBLE_RESOURCES = "cpus:4;mem:2048"; + const string TRIPLE_RESOURCES = "cpus:6;mem:3072"; + const string FOURFOLD_RESOURCES = "cpus:8;mem:4096"; + const string TOTAL_RESOURCES = "cpus:12;mem:6144"; + for (unsigned i = 0; i < 6; i++) { + slaves.push_back(createSlaveInfo(SINGLE_RESOURCE)); + } + + foreach (const SlaveInfo& slave, slaves) { + allocator->addSlave( + slave.id(), + slave, + None(), + slave.resources(), + hashmap<FrameworkID, Resources>()); + } + + // Framework1 registers with 'role1' which uses the default + // weight (1.0), and all resources will be offered to this framework. + FrameworkInfo framework1 = createFrameworkInfo("role1"); + allocator->addFramework( + framework1.id(), framework1, hashmap<SlaveID, Resources>()); + + // Framework2 registers with 'role2' which also uses the + // default weight (1.0). + FrameworkInfo framework2 = createFrameworkInfo("role2"); + allocator->addFramework( + framework2.id(), framework2, hashmap<SlaveID, Resources>()); + + // Framework1 gets one allocation with all resources, and Framework2 + // does not get any offers due to all resources having outstanding + // offers to framework1 when it registered. + // Recover all resources owned by framework1 so they can be offered + // again next time. + Future<Allocation> allocation = allocations.get(); + AWAIT_READY(allocation); + ASSERT_EQ(allocation.get().frameworkId, framework1.id()); + ASSERT_EQ(6u, allocation.get().resources.size()); + EXPECT_EQ(Resources::parse(TOTAL_RESOURCES).get(), + Resources::sum(allocation.get().resources)); + foreachpair (const SlaveID& slaveId, + const Resources& resources, + allocation.get().resources) { + allocator->recoverResources( + allocation.get().frameworkId, + slaveId, + resources, + None()); + } + + // Because each framework's role has a weight of 1.0 by default, test to + // ensure that all resources are offered equally between both frameworks. + hashmap<FrameworkID, size_t> counts; + Clock::advance(flags.allocation_interval); + Resources totalAllocatedResources1; + for (unsigned i = 0; i < 2; i++) { + Future<Allocation> allocation = allocations.get(); + AWAIT_READY(allocation); + counts[allocation.get().frameworkId]++; + totalAllocatedResources1 += Resources::sum(allocation.get().resources); + + // Each framework will get one allocation with three slaves. + ASSERT_EQ(3u, allocation.get().resources.size()); + EXPECT_EQ(Resources::parse(TRIPLE_RESOURCES).get(), + Resources::sum(allocation.get().resources)); + + // Recover the offered resources so they can be offered again next time. + foreachpair (const SlaveID& slaveId, + const Resources& resources, + allocation.get().resources) { + allocator->recoverResources( + allocation.get().frameworkId, + slaveId, + resources, + None()); + } + } + + // Check to ensure that these two allocations sum to the total resources, + // this check can ensure there are only two allocations in this case. + EXPECT_EQ(Resources::parse(TOTAL_RESOURCES).get(), totalAllocatedResources1); + EXPECT_EQ(1u, counts[framework1.id()]); + EXPECT_EQ(1u, counts[framework2.id()]); + + // Update the weight of framework2's role to 2.0, then their + // weights should be 1:2. + vector<WeightInfo> weightInfos1; + weightInfos1.push_back(createWeightInfo(framework2.role(), 2.0)); + allocator->updateWeights(weightInfos1); + + // Now that the frameworks's weights are 1:2, test to ensure that all + // resources are offered with a ratio of 1:2 between both frameworks. + counts.clear(); + Resources totalAllocatedResources2; + Clock::advance(flags.allocation_interval); + for (unsigned i = 0; i < 2; i++) { + Future<Allocation> allocation = allocations.get(); + AWAIT_READY(allocation); + counts[allocation.get().frameworkId]++; + totalAllocatedResources2 += Resources::sum(allocation.get().resources); + + // Framework1 should get one allocation with two slaves. + if (allocation.get().frameworkId == framework1.id()) { + ASSERT_EQ(2u, allocation.get().resources.size()); + EXPECT_EQ(Resources::parse(DOUBLE_RESOURCES).get(), + Resources::sum(allocation.get().resources)); + } else { + // Framework2 should get one allocation with four slaves. + ASSERT_EQ(allocation.get().frameworkId, framework2.id()); + ASSERT_EQ(4u, allocation.get().resources.size()); + EXPECT_EQ(Resources::parse(FOURFOLD_RESOURCES).get(), + Resources::sum(allocation.get().resources)); + } + + // Recover the allocated resources so they can be offered again next time. + foreachpair (const SlaveID& slaveId, + const Resources& resources, + allocation.get().resources) { + allocator->recoverResources( + allocation.get().frameworkId, + slaveId, + resources, + None()); + } + } + // Check to ensure that these two allocations sum to the total resources, + // this check can ensure there are only two allocations in this case. + EXPECT_EQ(Resources::parse(TOTAL_RESOURCES).get(), totalAllocatedResources2); + EXPECT_EQ(1u, counts[framework1.id()]); + EXPECT_EQ(1u, counts[framework2.id()]); + + // Add a new role with a weight of 3.0. + vector<WeightInfo> weightInfos2; + weightInfos2.push_back(createWeightInfo("role3", 3.0)); + allocator->updateWeights(weightInfos2); + + // Framework3 registers with 'role3'. + FrameworkInfo framework3 = createFrameworkInfo("role3"); + allocator->addFramework( + framework3.id(), framework3, hashmap<SlaveID, Resources>()); + + // Currently, there are three frameworks and six slaves in this cluster, + // and the weight ratio of these frameworks is 1:2:3, therefore frameworks + // will get the proper resource ratio of 1:2:3. + counts.clear(); + Resources totalAllocatedResources3; + for (unsigned i = 0; i < 3; i++) { + Future<Allocation> allocation = allocations.get(); + AWAIT_READY(allocation); + counts[allocation.get().frameworkId]++; + totalAllocatedResources3 += Resources::sum(allocation.get().resources); + + // Framework1 should get one allocation with one slave. + if (allocation.get().frameworkId == framework1.id()) { + ASSERT_EQ(1u, allocation.get().resources.size()); + EXPECT_EQ(Resources::parse(SINGLE_RESOURCE).get(), + Resources::sum(allocation.get().resources)); + } else if (allocation.get().frameworkId == framework2.id()) { + // Framework2 should get one allocation with two slaves. + ASSERT_EQ(2u, allocation.get().resources.size()); + EXPECT_EQ(Resources::parse(DOUBLE_RESOURCES).get(), + Resources::sum(allocation.get().resources)); + } else { + // Framework3 should get one allocation with three slaves. + ASSERT_EQ(allocation.get().frameworkId, framework3.id()); + ASSERT_EQ(3u, allocation.get().resources.size()); + EXPECT_EQ(Resources::parse(TRIPLE_RESOURCES).get(), + Resources::sum(allocation.get().resources)); + } + } + + // Check to ensure that these three allocations sum to the total resources, + // this check can ensure there are only three allocations in this case. + EXPECT_EQ(Resources::parse(TOTAL_RESOURCES).get(), totalAllocatedResources3); + EXPECT_EQ(1u, counts[framework1.id()]); + EXPECT_EQ(1u, counts[framework2.id()]); + EXPECT_EQ(1u, counts[framework3.id()]); + + Clock::resume(); +} + } // namespace tests { } // namespace internal { } // namespace mesos {
