Hi,

We found a problem with list requests_inflight while using evdns on highly loaded system (about 2000 DNS requests per second). First of all linear search is slow and second is that this list is not big enough to handle all requests, so many requests were in requests_waiting list, that has no limits on length. Due to this application grows in size constantly and is not able to serve requests. I've replaced linear list with hashed list, that improves performance. The one thing that is not logical (but I don't know how to make it better) is that I'm using bzero of hash nests array in reading resolv conf function. The second part of patch allows using evhttp module for listening on unix sockets. This is our project specific requirement, but it may be useful for others.
--- a/evdns.c   Wed Feb 13 15:31:39 2008 +0300
+++ b/evdns.c   Wed Feb 13 16:24:17 2008 +0300
@@ -156,6 +156,7 @@ typedef unsigned int uint;
 #endif
 
 #define MAX_ADDRS 32  /* maximum number of addresses from a single packet */
+#define MAX_NESTS 1024 /* numbers of nests in hashed list of pending requests 
*/
 /* which we bother recording */
 
 #define TYPE_A         EVDNS_TYPE_A
@@ -231,7 +232,7 @@ struct nameserver {
        char write_waiting;  /* true if we are waiting for EV_WRITE events */
 };
 
-static struct request *req_head = NULL, *req_waiting_head = NULL;
+static struct request *req_head[MAX_NESTS], *req_waiting_head = NULL;
 static struct nameserver *server_head = NULL;
 
 /* Represents a local port where we're listening for DNS requests. Right now, 
*/
@@ -310,7 +311,7 @@ static int global_requests_inflight = 0;
 /* and are counted here */
 static int global_requests_waiting = 0;
 
-static int global_max_requests_inflight = 64;
+static int global_max_requests_inflight = 2048;
 
 static struct timeval global_timeout = {5, 0};  /* 5 seconds */
 static int global_max_reissues = 1;  /* a reissue occurs when we get some 
errors from the server */
@@ -443,7 +444,7 @@ _evdns_log(int warn, const char *fmt, ..
 /* failure */
 static struct request *
 request_find_from_trans_id(u16 trans_id) {
-       struct request *req = req_head, *const started_at = req_head;
+       struct request *req = req_head[trans_id % MAX_NESTS], *const started_at 
= req_head[trans_id % MAX_NESTS];
 
        if (req) {
                do {
@@ -498,6 +499,7 @@ static void
 static void
 nameserver_failed(struct nameserver *const ns, const char *msg) {
        struct request *req, *started_at;
+       int i;
        /* if this nameserver has already been marked as failed */
        /* then don't do anything */
        if (!ns->state) return;
@@ -529,17 +531,19 @@ nameserver_failed(struct nameserver *con
        /* trying to reassign requests to one */
        if (!global_good_nameservers) return;
 
-       req = req_head;
-       started_at = req_head;
-       if (req) {
-               do {
-                       if (req->tx_count == 0 && req->ns == ns) {
-                               /* still waiting to go out, can be moved */
-                               /* to another server */
-                               req->ns = nameserver_pick();
-                       }
-                       req = req->next;
-               } while (req != started_at);
+       for (i = 0; i < MAX_NESTS; i ++) {
+               req = req_head[i];
+               started_at = req_head[i];
+               if (req) {
+                       do {
+                               if (req->tx_count == 0 && req->ns == ns) {
+                                       /* still waiting to go out, can be 
moved */
+                                       /* to another server */
+                                       req->ns = nameserver_pick();
+                               }
+                               req = req->next;
+                       } while (req != started_at);
+               }
        }
 }
 
@@ -650,7 +654,7 @@ evdns_requests_pump_waiting_queue(void) 
                req->ns = nameserver_pick();
                request_trans_id_set(req, transaction_id_pick());
 
-               evdns_request_insert(req, &req_head);
+               evdns_request_insert(req, &req_head[req->trans_id % MAX_NESTS]);
                evdns_request_transmit(req);
                evdns_transmit();
        }
@@ -742,19 +746,19 @@ reply_handle(struct request *const req, 
                                /* a new request was issued so this request is 
finished and */
                                /* the user callback will be made when that 
request (or a */
                                /* child of it) finishes. */
-                               request_finished(req, &req_head);
+                               request_finished(req, &req_head[req->trans_id % 
MAX_NESTS]);
                                return;
                        }
                }
 
                /* all else failed. Pass the failure up */
                reply_callback(req, 0, error, NULL);
-               request_finished(req, &req_head);
+               request_finished(req, &req_head[req->trans_id % MAX_NESTS]);
        } else {
                /* all ok, tell the user */
                reply_callback(req, ttl, 0, reply);
                nameserver_up(req->ns);
-               request_finished(req, &req_head);
+               request_finished(req, &req_head[req->trans_id % MAX_NESTS]);
        }
 }
 
@@ -1090,12 +1094,13 @@ static u16
 static u16
 transaction_id_pick(void) {
        for (;;) {
-               const struct request *req = req_head, *started_at;
+               const struct request *req, *started_at;
                u16 trans_id = trans_id_function();
+               req = req_head[trans_id % MAX_NESTS];
 
                if (trans_id == 0xffff) continue;
                /* now check to see if that id is already inflight */
-               req = started_at = req_head;
+               req = started_at = req_head[trans_id % MAX_NESTS];
                if (req) {
                        do {
                                if (req->trans_id == trans_id) break;
@@ -1869,7 +1874,7 @@ evdns_request_timeout_callback(int fd, s
        if (req->tx_count >= global_max_retransmits) {
                /* this request has failed */
                reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL);
-               request_finished(req, &req_head);
+               request_finished(req, &req_head[req->trans_id % MAX_NESTS]);
        } else {
                /* retransmit it */
                evdns_request_transmit(req);
@@ -1981,19 +1986,22 @@ nameserver_send_probe(struct nameserver 
 /*   1 tried to transmit something */
 static int
 evdns_transmit(void) {
+       int i;
        char did_try_to_transmit = 0;
-
-       if (req_head) {
-               struct request *const started_at = req_head, *req = req_head;
-               /* first transmit all the requests which are currently waiting 
*/
-               do {
-                       if (req->transmit_me) {
-                               did_try_to_transmit = 1;
-                               evdns_request_transmit(req);
-                       }
-
-                       req = req->next;
-               } while (req != started_at);
+       
+       for (i = 0; i < MAX_NESTS; i ++) { 
+               if (req_head[i]) {
+                       struct request *const started_at = req_head[i], *req = 
req_head[i];
+                       /* first transmit all the requests which are currently 
waiting */
+                       do {
+                               if (req->transmit_me) {
+                                       did_try_to_transmit = 1;
+                                       evdns_request_transmit(req);
+                               }
+
+                               req = req->next;
+                       } while (req != started_at);
+               }
        }
 
        return did_try_to_transmit;
@@ -2019,7 +2027,8 @@ evdns_clear_nameservers_and_suspend(void
 evdns_clear_nameservers_and_suspend(void)
 {
        struct nameserver *server = server_head, *started_at = server_head;
-       struct request *req = req_head, *req_started_at = req_head;
+       struct request *req, *req_started_at;
+       int i;
 
        if (!server)
                return 0;
@@ -2036,29 +2045,35 @@ evdns_clear_nameservers_and_suspend(void
        }
        server_head = NULL;
        global_good_nameservers = 0;
-
-       while (req) {
-               struct request *next = req->next;
-               req->tx_count = req->reissue_count = 0;
-               req->ns = NULL;
-               /* ???? What to do about searches? */
-               (void) evtimer_del(&req->timeout_event);
-               req->trans_id = 0;
-               req->transmit_me = 0;
-
-               global_requests_waiting++;
-               evdns_request_insert(req, &req_waiting_head);
-               /* We want to insert these suspended elements at the front of
-                * the waiting queue, since they were pending before any of
-                * the waiting entries were added.  This is a circular list,
-                * so we can just shift the start back by one.*/
-               req_waiting_head = req_waiting_head->prev;
-
-               if (next == req_started_at)
-                       break;
-               req = next;
-       }
-       req_head = NULL;
+       
+       for (i = 0; i < MAX_NESTS; i ++) {
+               req = req_head[i];
+               req_started_at = req;
+
+               while (req) {
+                       struct request *next = req->next;
+                       req->tx_count = req->reissue_count = 0;
+                       req->ns = NULL;
+                       /* ???? What to do about searches? */
+                       (void) evtimer_del(&req->timeout_event);
+                       req->trans_id = 0;
+                       req->transmit_me = 0;
+
+                       global_requests_waiting++;
+                       evdns_request_insert(req, &req_waiting_head);
+                       /* We want to insert these suspended elements at the 
front of
+                        * the waiting queue, since they were pending before 
any of
+                        * the waiting entries were added.  This is a circular 
list,
+                        * so we can just shift the start back by one.*/
+                       req_waiting_head = req_waiting_head->prev;
+
+                       if (next == req_started_at)
+                               break;
+                       req = next;
+               }
+               req_head[i] = NULL;
+       }
+
        global_requests_inflight = 0;
 
        return 0;
@@ -2245,7 +2260,7 @@ request_submit(struct request *const req
        if (req->ns) {
                /* if it has a nameserver assigned then this is going */
                /* straight into the inflight queue */
-               evdns_request_insert(req, &req_head);
+               evdns_request_insert(req, &req_head[req->trans_id % MAX_NESTS]);
                global_requests_inflight++;
                evdns_request_transmit(req);
        } else {
@@ -2714,6 +2729,9 @@ evdns_resolv_conf_parse(int flags, const
        u8 *resolv;
        char *start;
        int err = 0;
+       
+       /* XXX: remove this hack from here */
+       bzero (req_head, MAX_NESTS * sizeof (req_head[0]));
 
        log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename);
 
@@ -3002,11 +3020,14 @@ evdns_shutdown(int fail_requests)
 {
        struct nameserver *server, *server_next;
        struct search_domain *dom, *dom_next;
-
-       while (req_head) {
-               if (fail_requests)
-                       reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL);
-               request_finished(req_head, &req_head);
+       int i;
+       
+       for (i = 0; i < MAX_NESTS; i ++) {
+               while (req_head[i]) {
+                       if (fail_requests)
+                               reply_callback(req_head[i], 0, 
DNS_ERR_SHUTDOWN, NULL);
+                       request_finished(req_head[i], &req_head[i]);
+               }
        }
        while (req_waiting_head) {
                if (fail_requests)
--- a/http.c    Wed Feb 13 15:31:39 2008 +0300
+++ b/http.c    Wed Feb 13 16:24:17 2008 +0300
@@ -78,6 +78,9 @@
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
+#ifndef WIN32
+#include <sys/un.h>
+#endif
 
 #undef timeout_pending
 #undef timeout_initialized
@@ -2349,18 +2352,25 @@ name_from_addr(struct sockaddr *sa, sock
        static char ntop[NI_MAXHOST];
        static char strport[NI_MAXSERV];
        int ni_result;
-
-       if ((ni_result = getnameinfo(sa, salen,
-               ntop, sizeof(ntop), strport, sizeof(strport),
-               NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
-               if (ni_result == EAI_SYSTEM)
-                       event_err(1, "getnameinfo failed");
-               else
-                       event_errx(1, "getnameinfo failed: %s", 
gai_strerror(ni_result));
-       }
- 
+       
+       if (sa->sa_family == PF_UNIX) {
+               strncpy (ntop, ((struct sockaddr_un *)sa)->sun_path, NI_MAXHOST 
- 1);
+               strport[0] = '0';
+               strport[1] = '\0';
+       }
+       else {
+               if ((ni_result = getnameinfo(sa, salen,
+                       ntop, sizeof(ntop), strport, sizeof(strport),
+                       NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
+                       if (ni_result == EAI_SYSTEM)
+                               event_err(1, "getnameinfo failed");
+                       else
+                               event_errx(1, "getnameinfo failed: %s", 
gai_strerror(ni_result));
+               }
+       }
        *phost = ntop;
        *pport = strport;
+ 
 #else
        /* XXXX */
 #endif

_______________________________________________
Libevent-users mailing list
Libevent-users@monkey.org
http://monkeymail.org/mailman/listinfo/libevent-users

Reply via email to