From: Remi Gacogne Date: Wed, 20 Feb 2019 16:47:30 +0000 (+0100) Subject: rec: Use a bounded load-balancing algo to distribute queries X-Git-Tag: rec-4.1.12~1^2~4 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4f4a0ce2fc8a8f21daac3cc9308cfb654090b0de;p=pdns rec: Use a bounded load-balancing algo to distribute queries (cherry picked from commit 144040bef0b1f65abfb4634f65b1445a84393a1b) --- diff --git a/pdns/pdns_recursor.cc b/pdns/pdns_recursor.cc index ce5394e98..a9f317208 100644 --- a/pdns/pdns_recursor.cc +++ b/pdns/pdns_recursor.cc @@ -124,6 +124,12 @@ struct ThreadPipeSet int readQueriesToThread; }; +struct RecWorkerThreadInfo +{ + MT_t* mt{nullptr}; + uint64_t numberOfDistributedQueries{0}; +}; + /* the TID of the thread handling the web server, carbon, statistics and the control channel */ static const int s_handlerThreadID = -1; /* when pdns-distributes-queries is set, the TID of the thread handling, hashing and distributing new queries @@ -137,6 +143,7 @@ typedef vector > > deferredAdd_t; static const ComboAddress g_local4("0.0.0.0"), g_local6("::"); static vector g_pipes; // effectively readonly after startup +static vector s_threadInfos; static tcpListenSockets_t g_tcpListenSockets; // shared across threads, but this is fine, never written to from a thread. All threads listen on all sockets static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now static std::unordered_map deferredAdds; @@ -166,6 +173,7 @@ static bool g_gettagNeedsEDNSOptions{false}; static time_t g_statisticsInterval; static bool g_useIncomingECS; std::atomic g_maxCacheEntries, g_maxPacketCacheEntries; +static double s_balancingFactor; RecursorControlChannel s_rcc; // only active in thread 0 RecursorStats g_stats; @@ -2164,6 +2172,14 @@ static void doStats(void) L<(pleaseGetPacketCacheSize) << " packet cache entries, "<<(int)(100.0*broadcastAccFunction(pleaseGetPacketCacheHits)/SyncRes::s_queries) << "% packet cache hits"<(s_distributorThreadID)) { L<numProcesses(); + } + return 0; +} + +static unsigned int selectWorker(unsigned int hash) +{ + if (s_balancingFactor == 0) { + return 1 + (hash % (g_pipes.size()-1)); + } + + /* we start with one, representing the query we are currently handling */ + double currentLoad = 1; + std::vector load(g_numWorkerThreads); + for (size_t idx = 0; idx < g_numWorkerThreads; idx++) { + load[idx] = getWorkerLoad(idx); + currentLoad += load[idx]; + // cerr<<"load for worker "< targetLoad) { + // cerr<<"worker "<func = func; @@ -2371,7 +2427,7 @@ void distributeAsyncFunction(const string& packet, const pipefunc_t& func) was full, let's try another one */ unsigned int newTarget = 0; do { - newTarget = 1 + dns_random(g_pipes.size()-1); + newTarget = /* skip distributor */ 1 + dns_random(g_pipes.size()-1); } while (newTarget == target); if (!trySendingQueryToWorker(newTarget, tmsg)) { @@ -3139,10 +3195,14 @@ static int serviceMain(int argc, char*argv[]) g_statisticsInterval = ::arg().asNum("statistics-interval"); + s_balancingFactor = ::arg().asDouble("distribution-load-factor"); + #ifdef SO_REUSEPORT g_reusePort = ::arg().mustDo("reuseport"); #endif + s_threadInfos.resize(/* distributor */ 1 + g_numWorkerThreads); + g_useOneSocketPerThread = (!g_weDistributeQueries && g_reusePort); if (g_useOneSocketPerThread) { @@ -3322,6 +3382,10 @@ try } MT=std::unique_ptr >(new MTasker(::arg().asNum("stack-size"))); + if (worker) { + auto& threadInfo = s_threadInfos.at(t_id); + threadInfo.mt = MT.get(); + } PacketID pident; @@ -3572,6 +3636,8 @@ int main(int argc, char **argv) ::arg().setSwitch("log-rpz-changes", "Log additions and removals to RPZ zones at Info level")="no"; + ::arg().set("distribution-load-factor", "The load factor used when PowerDNS is distributing queries to worker threads")="0.0"; + ::arg().setCmd("help","Provide a helpful message"); ::arg().setCmd("version","Print version string"); ::arg().setCmd("config","Output blank configuration"); diff --git a/pdns/recursordist/docs/settings.rst b/pdns/recursordist/docs/settings.rst index 67033f8ad..59ddc1b00 100644 --- a/pdns/recursordist/docs/settings.rst +++ b/pdns/recursordist/docs/settings.rst @@ -263,6 +263,25 @@ Do not log to syslog, only to stdout. Use this setting when running inside a supervisor that handles logging (like systemd). **Note**: do not use this setting in combination with `daemon`_ as all logging will disappear. +.. _setting-distribution-load-factor: + +``distribution-load-factor`` +---------------------------- +.. versionadded:: 4.1.12 + +- Double +- Default: 0.0 + +If `pdns-distributes-queries`_ is set and this setting is set to another value +than 0, the distributor thread will use a bounded load-balancing algorithm while +distributing queries to worker threads, making sure that no thread is assigned +more queries than distribution-load-factor times the average number of queries +currently processed by all the workers. +For example, with a value of 1.25, no server should get more than 125 % of the +average load. This helps making sure that all the workers have roughly the same +share of queries, even if the incoming traffic is very skewed, with a larger +number of requests asking for the same qname. + .. _setting-dnssec: ``dnssec``