Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulation for load balancing logic. #127

Merged
merged 7 commits into from
Oct 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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