]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MINOR] listeners: add support for queueing resource limited listeners
authorWilly Tarreau <w@1wt.eu>
Sun, 24 Jul 2011 20:03:52 +0000 (22:03 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 24 Jul 2011 20:03:52 +0000 (22:03 +0200)
When a listeners encounters a resource shortage, it currently stops until
one re-enables it. This is far from being perfect as it does not yet handle
the case where the single connection from the listener is rejected (eg: the
stats page).

Now we'll have a special status for resource limited listeners and we'll
queue them into one or multiple lists. That way, each time we have to stop
a listener because of a resource shortage, we can enqueue it and change its
state, so that it is dequeued once more resources are available.

This patch currently does not change any existing behaviour, it only adds
the basic building blocks for doing that.

include/proto/protocols.h
include/types/protocols.h
src/protocols.c

index 354a4e971a5ae942fea1a0334a084ce1b0ef6e2f..05567ea689fec777d23b0b020d6dceb9cd4ad7fc 100644 (file)
@@ -71,6 +71,14 @@ int enable_all_listeners(struct protocol *proto);
  */
 int disable_all_listeners(struct protocol *proto);
 
+/* Marks a ready listener as limited so that we only try to re-enable it when
+ * resources are free again. It will be queued into the specified queue.
+ */
+void limit_listener(struct listener *l, struct list *list);
+
+/* Dequeues all of the listeners waiting for a resource in wait queue <queue>. */
+void dequeue_all_listeners(struct list *list);
+
 /* This function closes the listening socket for the specified listener,
  * provided that it's already in a listening state. The listener enters the
  * LI_ASSIGNED state. It always returns ERR_NONE. This function is intended
index a333ea2fc6b5aa17ebb681d1b29ca75eb3b91ca9..80c1a6e02374ae37e56de516dab4db41b2193310 100644 (file)
@@ -46,6 +46,7 @@ enum {
        LI_LISTEN,      /* started, listening but not enabled */
        LI_READY,       /* started, listening and enabled */
        LI_FULL,        /* reached its connection limit */
+       LI_LIMITED,     /* transient state: limits have been reached, listener is queued */
 };
 
 /* Listener transitions
@@ -67,6 +68,11 @@ enum {
  *   +-----------------
  *            disable()
  *
+ * The LIMITED state my be used when a limit has been detected just before
+ * using a listener. In this case, the listener MUST be queued into the
+ * appropriate wait queue (either the proxy's or the global one). It may be
+ * set back to the READY state at any instant and for any reason, so one must
+ * not rely on this state.
  */
 
 /* listener socket options */
@@ -101,6 +107,7 @@ struct listener {
        struct task * (*handler)(struct task *t); /* protocol handler. It is a task */
        int  *timeout;                  /* pointer to client-side timeout */
        struct proxy *frontend;         /* the frontend this listener belongs to, or NULL */
+       struct list wait_queue;         /* link element to make the listener wait for something (LI_LIMITED)  */
        unsigned int analysers;         /* bitmap of required protocol analysers */
        int nice;                       /* nice value to assign to the instanciated tasks */
        union {                         /* protocol-dependant access restrictions */
index 7d8913d6cce0f5282b9c6f169c9214264b61e918..46c41f6078cb3b14ea093b51f8aa2d7676bcd03e 100644 (file)
@@ -50,6 +50,8 @@ void disable_listener(struct listener *listener)
                return;
        if (listener->state == LI_READY)
                EV_FD_CLR(listener->fd, DIR_RD);
+       if (listener->state == LI_LIMITED)
+               LIST_DEL(&listener->wait_queue);
        listener->state = LI_LISTEN;
 }
 
@@ -74,15 +76,18 @@ int pause_listener(struct listener *l)
        if (shutdown(l->fd, SHUT_RD) != 0)
                return 0; /* should always be OK */
 
+       if (l->state == LI_LIMITED)
+               LIST_DEL(&l->wait_queue);
+
        EV_FD_CLR(l->fd, DIR_RD);
        l->state = LI_PAUSED;
        return 1;
 }
 
-/* This function tries to resume a temporarily disabled listener. Paused, full
- * and disabled listeners are handled, which means that this function may
- * replace enable_listener(). The resulting state will either be LI_READY or
- * LI_FULL. 0 is returned in case of failure to resume (eg: dead socket).
+/* This function tries to resume a temporarily disabled listener. Paused, full,
+ * limited and disabled listeners are handled, which means that this function
+ * may replace enable_listener(). The resulting state will either be LI_READY
+ * or LI_FULL. 0 is returned in case of failure to resume (eg: dead socket).
  */
 int resume_listener(struct listener *l)
 {
@@ -96,6 +101,9 @@ int resume_listener(struct listener *l)
        if (l->state == LI_READY)
                return 1;
 
+       if (l->state == LI_LIMITED)
+               LIST_DEL(&l->wait_queue);
+
        if (l->nbconn >= l->maxconn) {
                l->state = LI_FULL;
                return 1;
@@ -112,11 +120,26 @@ int resume_listener(struct listener *l)
 void listener_full(struct listener *l)
 {
        if (l->state >= LI_READY) {
+               if (l->state == LI_LIMITED)
+                       LIST_DEL(&l->wait_queue);
+
                EV_FD_CLR(l->fd, DIR_RD);
                l->state = LI_FULL;
        }
 }
 
+/* Marks a ready listener as limited so that we only try to re-enable it when
+ * resources are free again. It will be queued into the specified queue.
+ */
+void limit_listener(struct listener *l, struct list *list)
+{
+       if (l->state == LI_READY) {
+               LIST_ADDQ(list, &l->wait_queue);
+               EV_FD_CLR(l->fd, DIR_RD);
+               l->state = LI_LIMITED;
+       }
+}
+
 /* This function adds all of the protocol's listener's file descriptors to the
  * polling lists when they are in the LI_LISTEN state. It is intended to be
  * used as a protocol's generic enable_all() primitive, for use after the
@@ -146,6 +169,20 @@ int disable_all_listeners(struct protocol *proto)
        return ERR_NONE;
 }
 
+/* Dequeues all of the listeners waiting for a resource in wait queue <queue>. */
+void dequeue_all_listeners(struct list *list)
+{
+       struct listener *listener, *l_back;
+
+       list_for_each_entry_safe(listener, l_back, list, wait_queue) {
+               /* This cannot fail because the listeners are by definition in
+                * the LI_LIMITED state. The function also removes the entry
+                * from the queue.
+                */
+               resume_listener(listener);
+       }
+}
+
 /* This function closes the listening socket for the specified listener,
  * provided that it's already in a listening state. The listener enters the
  * LI_ASSIGNED state. It always returns ERR_NONE. This function is intended
@@ -156,6 +193,9 @@ int unbind_listener(struct listener *listener)
        if (listener->state == LI_READY)
                EV_FD_CLR(listener->fd, DIR_RD);
 
+       if (listener->state == LI_LIMITED)
+               LIST_DEL(&listener->wait_queue);
+
        if (listener->state >= LI_PAUSED) {
                fd_delete(listener->fd);
                listener->state = LI_ASSIGNED;