]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add bounded loads to the consistent hashing policy
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 21 Nov 2019 13:49:30 +0000 (14:49 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 21 Nov 2019 13:49:30 +0000 (14:49 +0100)
pdns/dnsdist-console.cc
pdns/dnsdist-lua.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
pdns/dnsdistdist/docs/guides/serverselection.rst

index c6a9b8132abac0a0607d7dcd44f7309ba2713fa7..e424bb3316862fd09f7c4ef4182caaf175e8502e 100644 (file)
@@ -496,6 +496,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "setAPIWritable", true, "bool, dir", "allow modifications via the API. if `dir` is set, it must be a valid directory where the configuration files will be written by the API" },
   { "setCacheCleaningDelay", true, "num", "Set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries" },
   { "setCacheCleaningPercentage", true, "num", "Set the percentage of the cache that the cache cleaning algorithm will try to free by removing expired entries. By default (100), all expired entries are remove" },
+  { "setConsistentHashingBalancingFactor", true, "factor", "Set the balancing factor for bounded-load consistent hashing" },
   { "setConsoleACL", true, "{netmask, netmask}", "replace the console ACL set with these netmasks" },
   { "setConsoleConnectionsLogging", true, "enabled", "whether to log the opening and closing of console connections" },
   { "setConsoleOutputMaxMsgSize", true, "messageSize", "set console message maximum size in bytes, default is 10 MB" },
index 8908a2f8b9291fd8fa618e6640cf20e80053f799..251a071f598721711ccc48d34b102bb36bed53a7 100644 (file)
@@ -1591,6 +1591,11 @@ void setupLuaConfig(bool client)
       g_roundrobinFailOnNoServer = fail;
     });
 
+  g_lua.writeFunction("setConsistentHashingBalancingFactor", [](double factor) {
+      setLuaSideEffect();
+      g_consistentHashBalancingFactor = factor;
+    });
+
   g_lua.writeFunction("setRingBuffersSize", [](size_t capacity, boost::optional<size_t> numberOfShards) {
       setLuaSideEffect();
       if (g_configurationDone) {
index 1674e9fc853d730a1bed7537eae336ece0341942..4c3bb2cc529fb1210d4c72986a1a5534a7e66164 100644 (file)
@@ -882,6 +882,7 @@ shared_ptr<DownstreamState> wrandom(const NumberedServerVector& servers, const D
 }
 
 uint32_t g_hashperturb;
+double g_consistentHashBalancingFactor = 0;
 shared_ptr<DownstreamState> whashed(const NumberedServerVector& servers, const DNSQuestion* dq)
 {
   return valrandom(dq->qname->hash(g_hashperturb), servers, dq);
@@ -894,8 +895,18 @@ shared_ptr<DownstreamState> chashed(const NumberedServerVector& servers, const D
   unsigned int min = std::numeric_limits<unsigned int>::max();
   shared_ptr<DownstreamState> ret = nullptr, first = nullptr;
 
+  double targetLoad = std::numeric_limits<double>::max();
+  if (g_consistentHashBalancingFactor != 0) {
+    /* we start with one, representing the query we are currently handling */
+    double currentLoad = 1;
+    for (const auto& pair : servers) {
+      currentLoad += pair.second->outstanding;
+    }
+    targetLoad = (currentLoad / servers.size()) * g_consistentHashBalancingFactor;
+  }
+
   for (const auto& d: servers) {
-    if (d.second->isUp()) {
+    if (d.second->isUp() && d.second->outstanding <= targetLoad) {
       // make sure hashes have been computed
       if (d.second->hashes.empty()) {
         d.second->hash();
index 69bc7833627e162db964debde7fd4d04d2ac0200..5ee8792886a95ccb26d2cd585e733f37a89d71bd 100644 (file)
@@ -1161,6 +1161,7 @@ extern size_t g_udpVectorSize;
 extern bool g_preserveTrailingData;
 extern bool g_allowEmptyResponse;
 extern bool g_roundrobinFailOnNoServer;
+extern double g_consistentHashBalancingFactor;
 
 #ifdef HAVE_EBPF
 extern shared_ptr<BPFFilter> g_defaultBPFFilter;
index 7e7a4495d27b374cd6127beb847c4d90769059ba..919ebedc964aeb06ddf3b2823f70a8e00fcbf84e 100644 (file)
@@ -46,10 +46,19 @@ The current hash algorithm is based on the qname of the query.
 ``chashed``
 ~~~~~~~~~~~
 
+.. versionadded: 1.3.3
+
 ``chashed`` is a consistent hashing distribution policy. Identical questions with identical hashes will be distributed to the same servers. But unlike the ``whashed`` policy, this distribution will keep consistent over time. Adding or removing servers will only remap a small part of the queries.
 
+Increasing the weight of servers to a value larger than the default is required to get a good distribution of queries. Small values like 100 or 1000 should be enough to get a correct distribution.
+This is a side-effect of the internal implementation of the consistent hashing algorithm, which assigns as many points on a circle to a server than its weight, and distributes a query to the server who has the closest point on the circle from the hash of the query's qname. Therefore having very few points, as is the case with the default weight of 1, leads to a poor distribution of queries.
+
 You can also set the hash perturbation value, see :func:`setWHashedPertubation`. To achieve consistent distribution over :program:`dnsdist` restarts, you will also need to explicitly set the backend's UUIDs with the ``id`` option of :func:`newServer`. You can get the current UUIDs of your backends by calling :func:`showServers` with the ``showUUIDs=true`` option.
 
+Since 1.5.0, a bounded-load version is also supported, preventing one server from receiving much more queries than the others, even if the distribution of queries is not perfect. This "consistent hashing with bounded loads" algorithm is enabled by setting func:`setConsistentHashingBalancingFactor` to a value other than 0, which is the default. This value is the maximum number of outstanding queries that a given server can have at a given time, as a ratio of the average number of outstanding queries for all the active servers in the pool.
+For example, setting func:`setConsistentHashingBalancingFactor` to 1.5 means that no server will be allowed to have more outstanding queries than 1.5 times the average of all outstanding queries in the pool. The algorithm will try to select a server based on the hash of the qname, as is done when no bounded-load is set, but will disqualify all servers that have more outstanding queries than the average times the factor, until a suitable server is found.
+The higher the factor, the more imbalance between the servers is allowed.
+
 ``roundrobin``
 ~~~~~~~~~~~~~~
 
@@ -115,6 +124,14 @@ Functions
   :param string name: Name of the policy
   :param string function: The function to call for this policy
 
+.. function:: setConsistentHashingBalancingFactor(factor)
+
+  .. versionadded: 1.5.0
+
+  Set the maximum imbalance between the number of outstanding queries for a given server relative to the average number of outstanding queries for all servers in the pool,
+  when using the ``chashed`` consistent hashing load-balancing policy.
+  Default is 0, which disables the bounded-load algorithm.
+
 .. function:: setServerPolicy(policy)
 
   Set server selection policy to ``policy``.