]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: dns: implement synchronous send
authorWilly Tarreau <w@1wt.eu>
Fri, 20 Dec 2019 10:18:54 +0000 (11:18 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 8 Jan 2020 05:10:38 +0000 (06:10 +0100)
In dns_send_query(), there's no point in first waking up the FD, to get
called back by the poller to send the request and sleep. Instead let's
simply send the request as soon as it's known and only subscribe to the
poller when the socket buffers are full and it's required to poll (i.e.
almost never).

This significantly reduces the number of calls to the poller. A large
config sees the number of epoll_ctl() calls reduced from 577 to 7 over
10 seconds, the number of recvfrom() from 1533 to 582 and the number of
sendto() from 369 to 162.

It also has the extra benefit of building each requests only once per
resolution and sending it to multiple resolvers instead of rebuilding
it for each and every resolver.

This will reduce the risk of seeing situations similar to bug #416 in
the future.

src/dns.c

index c131f08f1be9e213b552efbb2e83827a3f2090db..bc68a81c064345d4b562535ab1ea043ab275914b 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -272,22 +272,49 @@ static int dns_send_query(struct dns_resolution *resolution)
 {
        struct dns_resolvers  *resolvers = resolution->resolvers;
        struct dns_nameserver *ns;
+       int len;
+
+       /* Update resolution */
+       resolution->nb_queries   = 0;
+       resolution->nb_responses = 0;
+       resolution->last_query   = now_ms;
+
+       len = dns_build_query(resolution->query_id, resolution->query_type,
+                             resolvers->accepted_payload_size,
+                             resolution->hostname_dn, resolution->hostname_dn_len,
+                             trash.area, trash.size);
 
        list_for_each_entry(ns, &resolvers->nameservers, list) {
                int fd = ns->dgram->t.sock.fd;
+               int ret;
+
                if (fd == -1) {
                        if (dns_connect_namesaver(ns) == -1)
                                continue;
                        fd = ns->dgram->t.sock.fd;
                        resolvers->nb_nameservers++;
                }
-               fd_want_send(fd);
-       }
 
-       /* Update resolution */
-       resolution->nb_queries   = 0;
-       resolution->nb_responses = 0;
-       resolution->last_query   = now_ms;
+               if (len < 0)
+                       goto snd_error;
+
+               ret = send(fd, trash.area, len, 0);
+               if (ret == len) {
+                       ns->counters.sent++;
+                       resolution->nb_queries++;
+                       continue;
+               }
+
+               if (ret == -1 && errno == EAGAIN) {
+                       /* retry once the socket is ready */
+                       fd_cant_send(fd);
+                       continue;
+               }
+
+       snd_error:
+               ns->counters.snd_error++;
+               resolution->nb_queries++;
+       }
 
        /* Push the resolution at the end of the active list */
        LIST_DEL(&resolution->list);
@@ -1751,8 +1778,14 @@ static void dns_resolve_send(struct dgram_conn *dgram)
                        goto snd_error;
 
                ret = send(fd, trash.area, len, 0);
-               if (ret != len)
+               if (ret != len) {
+                       if (ret == -1 && errno == EAGAIN) {
+                               /* retry once the socket is ready */
+                               fd_cant_send(fd);
+                               continue;
+                       }
                        goto snd_error;
+               }
 
                ns->counters.sent++;
                res->nb_queries++;