From 6f5ce8c19d56935e99dfa038a16b2d9afaa55096 Mon Sep 17 00:00:00 2001 From: Roman Dzhabarov Date: Fri, 7 Oct 2016 09:50:44 -0700 Subject: [PATCH] Simulation for load balancing logic. (#127) --- test/CMakeLists.txt | 1 + .../upstream/load_balancer_simulation_test.cc | 133 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 test/common/upstream/load_balancer_simulation_test.cc diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1cab2bfd5ad2..a6d9a00b9a86 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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 diff --git a/test/common/upstream/load_balancer_simulation_test.cc b/test/common/upstream/load_balancer_simulation_test.cc new file mode 100644 index 000000000000..0648312a2ffd --- /dev/null +++ b/test/common/upstream/load_balancer_simulation_test.cc @@ -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 originating_cluster, std::vector all_destination_cluster, + std::vector healthy_destination_cluster) { + stats_.upstream_zone_count_.set(all_destination_cluster.size()); + + std::unordered_map> healthy_map = + generateHostsPerZone(healthy_destination_cluster); + std::unordered_map> all_map = + generateHostsPerZone(all_destination_cluster); + + std::vector originating_hosts = generateHostList(originating_cluster); + cluster_.healthy_hosts_ = generateHostList(healthy_destination_cluster); + cluster_.hosts_ = generateHostList(all_destination_cluster); + + std::map 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& 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 generateHostList(const std::vector& hosts) { + std::vector 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> + generateHostsPerZone(const std::vector& hosts) { + std::unordered_map> ret; + for (size_t i = 0; i < hosts.size(); ++i) { + const std::string zone = std::to_string(i); + std::vector 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 cluster_; + NiceMock 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 \ No newline at end of file