Skip to content

Commit

Permalink
Simulation for load balancing logic. (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
RomanDzhabarov authored Oct 7, 2016
1 parent 55b82cd commit 6f5ce8c
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ add_executable(envoy-test
common/upstream/health_checker_impl_test.cc
common/upstream/host_utility_test.cc
common/upstream/load_balancer_impl_test.cc
common/upstream/load_balancer_simulation_test.cc
common/upstream/logical_dns_cluster_test.cc
common/upstream/resource_manager_impl_test.cc
common/upstream/sds_test.cc
Expand Down
133 changes: 133 additions & 0 deletions test/common/upstream/load_balancer_simulation_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#include "common/runtime/runtime_impl.h"
#include "common/upstream/load_balancer_impl.h"
#include "common/upstream/upstream_impl.h"

#include "test/mocks/runtime/mocks.h"
#include "test/mocks/upstream/mocks.h"

using testing::NiceMock;
using testing::Return;

namespace Upstream {

static HostPtr newTestHost(const Upstream::Cluster& cluster, const std::string& url,
uint32_t weight = 1, const std::string& zone = "") {
return HostPtr{new HostImpl(cluster, url, false, weight, zone)};
}

/**
* This test is for simulation only and should not be run as part of unit tests.
*/
class DISABLED_SimulationTest : public testing::Test {
public:
DISABLED_SimulationTest() : stats_(ClusterImplBase::generateStats("", stats_store_)) {
ON_CALL(runtime_.snapshot_, getInteger("upstream.healthy_panic_threshold", 50U))
.WillByDefault(Return(50U));
ON_CALL(runtime_.snapshot_, featureEnabled("upstream.zone_routing.enabled", 100))
.WillByDefault(Return(true));
ON_CALL(runtime_.snapshot_, getInteger("upstream.zone_routing.percent_diff", 3))
.WillByDefault(Return(3));
}

/**
* Run simulation with given parameters. Generate statistics on per host requests.
*
* @param originating_cluster total number of hosts in each zone in originating cluster.
* @param all_destination_cluster total number of hosts in each zone in upstream cluster.
* @param healthy_destination_cluster total number of healthy hosts in each zone in upstream
* cluster.
*/
void run(std::vector<uint32_t> originating_cluster, std::vector<uint32_t> all_destination_cluster,
std::vector<uint32_t> healthy_destination_cluster) {
stats_.upstream_zone_count_.set(all_destination_cluster.size());

std::unordered_map<std::string, std::vector<HostPtr>> healthy_map =
generateHostsPerZone(healthy_destination_cluster);
std::unordered_map<std::string, std::vector<HostPtr>> all_map =
generateHostsPerZone(all_destination_cluster);

std::vector<HostPtr> originating_hosts = generateHostList(originating_cluster);
cluster_.healthy_hosts_ = generateHostList(healthy_destination_cluster);
cluster_.hosts_ = generateHostList(all_destination_cluster);

std::map<std::string, uint32_t> hits;
for (uint32_t i = 0; i < total_number_of_requests; ++i) {
HostPtr from_host = selectOriginatingHost(originating_hosts);

cluster_.local_zone_hosts_ = all_map[from_host->zone()];
cluster_.local_zone_healthy_hosts_ = healthy_map[from_host->zone()];

ConstHostPtr selected = lb_.chooseHost();
hits[selected->url()]++;
}

for (const auto& host_hit_num_pair : hits) {
std::cout << fmt::format("url:{}, hits:{}", host_hit_num_pair.first, host_hit_num_pair.second)
<< std::endl;
}
}

HostPtr selectOriginatingHost(const std::vector<HostPtr>& hosts) {
// Originating cluster should have roughly the same per host request distribution.
return hosts[random_.random() % hosts.size()];
}

/**
* Generate list of hosts based on number of hosts in the given zone.
* @param hosts number of hosts per zone.
*/
std::vector<HostPtr> generateHostList(const std::vector<uint32_t>& hosts) {
std::vector<HostPtr> ret;
for (size_t i = 0; i < hosts.size(); ++i) {
const std::string zone = std::to_string(i);
for (uint32_t j = 0; j < hosts[i]; ++j) {
const std::string url = fmt::format("tcp://host.{}.{}:80", i, j);
ret.push_back(newTestHost(cluster_, url, 1, zone));
}
}

return ret;
}

/**
* Generate hosts by zone.
* @param hosts number of hosts per zone.
*/
std::unordered_map<std::string, std::vector<HostPtr>>
generateHostsPerZone(const std::vector<uint32_t>& hosts) {
std::unordered_map<std::string, std::vector<HostPtr>> ret;
for (size_t i = 0; i < hosts.size(); ++i) {
const std::string zone = std::to_string(i);
std::vector<HostPtr> zone_hosts;

for (uint32_t j = 0; j < hosts[i]; ++j) {
const std::string url = fmt::format("tcp://host.{}.{}:80", i, j);
zone_hosts.push_back(newTestHost(cluster_, url, 1, zone));
}

ret.insert({zone, std::move(zone_hosts)});
}

return ret;
};

const uint32_t total_number_of_requests = 100000;

NiceMock<MockCluster> cluster_;
NiceMock<Runtime::MockLoader> runtime_;
Runtime::RandomGeneratorImpl random_;
Stats::IsolatedStoreImpl stats_store_;
ClusterStats stats_;
// TODO: make per originating host load balancer.
RandomLoadBalancer lb_{cluster_, stats_, runtime_, random_};
};

TEST_F(DISABLED_SimulationTest, strictlyEqualDistribution) {
run({1U, 1U, 1U}, {3U, 3U, 3U}, {3U, 3U, 3U});
}

TEST_F(DISABLED_SimulationTest, unequalZoneDistribution) {
run({1U, 1U, 1U}, {5U, 5U, 6U}, {5U, 5U, 6U});
}

} // Upstream

0 comments on commit 6f5ce8c

Please sign in to comment.