From 0eae6323bf8a3bb8e6a399f4f09971e388a4ced5 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 20 Dec 2019 11:18:54 +0100 Subject: [PATCH] MEDIUM: dns: implement synchronous send 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 | 47 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/dns.c b/src/dns.c index c131f08f1..bc68a81c0 100644 --- 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++;