From d624aceaef5ddf09cc8869990ffb4033ad93a452 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 10 Sep 2025 16:37:40 +0200 Subject: [PATCH] MEDIUM: dns: bind the nameserver sockets to the initiating thread There's still a big architectural limitation in the dns/resolvers code regarding threads: resolvers run as a task that is scheduled to run anywhere, and each NS dgram socket is bound to any thread of the same thread group as the initiating thread. This becomes a big problem when dealing with multiple nameservers because responses arrive on any thread, start by locking the resolvers section, and other threads dealing with responses are just stuck waiting for the lock to disappear. This means that most of the time is exclusively spent causing contention. The process_resolvers() function also also suffers from this contention but apparently less often. It turns out that the nameserver sockets are created during emission of the first packet, triggered from the resolvers task. The present patch exploits this to stick all sockets to the calling thread instead of any thread. This way there is no longer any contention between multiple nameservers of a same resolvers section. Tests with a section having 10 name servers showed that the CPU usage dropped from 38 to about 10%, or almost by a factor of 4. Note that TCP resolvers do not offer this possibility because the tasks that manage the applets are created earlier to run anywhere during config parsing. This might possibly be refined later, e.g. by changing the task's affinity when it first runs. The change was kept fairly minimal to permit a backport once enough testing is conducted on it. It could address a significant part of the trouble reported by Felipe in GH issue #3101. --- src/dns.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/dns.c b/src/dns.c index ed10b5cae..6257bdd15 100644 --- a/src/dns.c +++ b/src/dns.c @@ -121,7 +121,13 @@ static int dns_connect_nameserver(struct dns_nameserver *ns) /* Add the fd in the fd list and update its parameters */ dgram->t.sock.fd = fd; - fd_insert(fd, dgram, dgram_fd_handler, tgid, tg->threads_enabled); + + /* let's stick the FD to the initiator thread, this will ensure that + * most of the time, a resolver will not try to access its structure + * at the same time as a response is processed, and will eliminate + * locking contention. + */ + fd_insert(fd, dgram, dgram_fd_handler, tgid, ti->ltid_bit); fd_want_recv(fd); return 0; }