[MAJOR] proxy: finally get rid of maintain_proxies()
This function is finally not needed anymore, as it has been replaced with
a per-proxy task that is scheduled when some limits are encountered on
incoming connections or when the process is stopping. The savings should
be noticeable on configs with a large number of proxies. The most important
point is that the rate limiting is now enforced in a clean and solid way.
diff --git a/include/proto/proxy.h b/include/proto/proxy.h
index c9bfc02..afcfa0a 100644
--- a/include/proto/proxy.h
+++ b/include/proto/proxy.h
@@ -2,7 +2,7 @@
* include/proto/proxy.h
* This file defines function prototypes for proxy management.
*
- * Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
+ * Copyright (C) 2000-2011 Willy Tarreau - w@1wt.eu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -29,7 +29,7 @@
#include <proto/freq_ctr.h>
int start_proxies(int verbose);
-void maintain_proxies(int *next);
+struct task *manage_proxy(struct task *t);
void soft_stop(void);
void pause_proxy(struct proxy *p);
void stop_proxy(struct proxy *p);
diff --git a/include/proto/task.h b/include/proto/task.h
index adcae4c..4690670 100644
--- a/include/proto/task.h
+++ b/include/proto/task.h
@@ -242,10 +242,9 @@
}
/*
- * This does 4 things :
+ * This does 3 things :
* - wake up all expired tasks
* - call all runnable tasks
- * - call maintain_proxies() to enable/disable the listeners
* - return the date of next event in <next> or eternity.
*/
diff --git a/include/types/proxy.h b/include/types/proxy.h
index e5d49ed..82c3458 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -306,6 +306,7 @@
struct list listener_queue; /* list of the temporarily limited listeners because of lack of a proxy resource */
struct stktable table; /* table for storing sticking sessions */
+ struct task *task; /* the associated task, mandatory to manage rate limiting, stopping and resource shortage */
int grace; /* grace time after stop request */
char *check_req; /* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */
int check_len; /* Length of the HTTP or SSL3 request */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 21ef3d9..fd81a20 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -6412,7 +6412,22 @@
}
}
}
-
+
+ /* create the task associated with the proxy */
+ curproxy->task = task_new();
+ if (curproxy->task) {
+ curproxy->task->context = curproxy;
+ curproxy->task->process = manage_proxy;
+ /* no need to queue, it will be done automatically if some
+ * listener gets limited.
+ */
+ curproxy->task->expire = TICK_ETERNITY;
+ } else {
+ Alert("Proxy '%s': no more memory when trying to allocate the management task\n",
+ curproxy->id);
+ cfgerr++;
+ }
+
curproxy = curproxy->next;
}
diff --git a/src/haproxy.c b/src/haproxy.c
index c3841dd..26fd021 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -918,6 +918,7 @@
free(p->fwdfor_hdr_name);
free_http_req_rules(&p->http_req_rules);
+ free(p->task);
pool_destroy2(p->req_cap_pool);
pool_destroy2(p->rsp_cap_pool);
@@ -992,13 +993,7 @@
return ret;
}
-/*
- * Runs the polling loop
- *
- * FIXME:
- * - we still use 'listeners' to check whether we want to stop or not.
- *
- */
+/* Runs the polling loop */
void run_poll_loop()
{
int next;
@@ -1014,11 +1009,6 @@
/* Process a few tasks */
process_runnable_tasks(&next);
- /* maintain all proxies in a consistent state. This should quickly
- * become a task because it becomes expensive when there are huge
- * numbers of proxies. */
- maintain_proxies(&next);
-
/* stop when there's nothing left to do */
if (jobs == 0)
break;
diff --git a/src/proxy.c b/src/proxy.c
index 9b88274..7c22a75 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -40,7 +40,7 @@
#include <proto/task.h>
-int listeners; /* # of proxy listeners, set by cfgparse, unset by maintain_proxies */
+int listeners; /* # of proxy listeners, set by cfgparse */
struct proxy *proxy = NULL; /* list of all existing proxies */
struct eb_root used_proxy_id = EB_ROOT; /* list of proxy IDs in use */
unsigned int error_snapshot_id = 0; /* global ID assigned to each error then incremented */
@@ -467,74 +467,72 @@
/*
- * this function enables proxies when there are enough free sessions,
- * or stops them when the table is full. It is designed to be called from the
- * select_loop(). It adjusts the date of next expiration event during stop
- * time if appropriate.
+ * This is the proxy management task. It enables proxies when there are enough
+ * free sessions, or stops them when the table is full. It is designed to be
+ * called as a task which is woken up upon stopping or when rate limiting must
+ * be enforced.
*/
-void maintain_proxies(int *next)
+struct task *manage_proxy(struct task *t)
{
- struct proxy *p;
+ struct proxy *p = t->context;
+ int next = TICK_ETERNITY;
unsigned int wait;
- p = proxy;
+ /* We should periodically try to enable listeners waiting for a
+ * global resource here.
+ */
- /* if there are enough free sessions, we'll activate proxies */
- if (actconn < global.maxconn) {
- /* We should periodically try to enable listeners waiting for a
- * global resource here.
- */
-
- for (; p; p = p->next) {
- /* first, let's check if we need to stop the proxy */
- if (unlikely(stopping && p->state != PR_STSTOPPED)) {
- int t;
- t = tick_remain(now_ms, p->stop_time);
- if (t == 0) {
- Warning("Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
- p->id, p->fe_counters.cum_conn, p->be_counters.cum_conn);
- send_log(p, LOG_WARNING, "Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
- p->id, p->fe_counters.cum_conn, p->be_counters.cum_conn);
- stop_proxy(p);
- /* try to free more memory */
- pool_gc2();
- }
- else {
- *next = tick_first(*next, p->stop_time);
- }
- }
-
- /* the rest below is just for frontends */
- if (!(p->cap & PR_CAP_FE))
- continue;
+ /* first, let's check if we need to stop the proxy */
+ if (unlikely(stopping && p->state != PR_STSTOPPED)) {
+ int t;
+ t = tick_remain(now_ms, p->stop_time);
+ if (t == 0) {
+ Warning("Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
+ p->id, p->fe_counters.cum_conn, p->be_counters.cum_conn);
+ send_log(p, LOG_WARNING, "Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
+ p->id, p->fe_counters.cum_conn, p->be_counters.cum_conn);
+ stop_proxy(p);
+ /* try to free more memory */
+ pool_gc2();
+ }
+ else {
+ next = tick_first(next, p->stop_time);
+ }
+ }
- /* check the various reasons we may find to block the frontend */
- if (unlikely(p->feconn >= p->maxconn)) {
- if (p->state == PR_STREADY)
- p->state = PR_STFULL;
- continue;
- }
+ /* the rest below is just for frontends */
+ if (!(p->cap & PR_CAP_FE))
+ goto out;
- /* OK we have no reason to block, so let's unblock if we were blocking */
- if (p->state == PR_STFULL)
- p->state = PR_STREADY;
+ /* check the various reasons we may find to block the frontend */
+ if (unlikely(p->feconn >= p->maxconn)) {
+ if (p->state == PR_STREADY)
+ p->state = PR_STFULL;
+ goto out;
+ }
- if (p->fe_sps_lim &&
- (wait = next_event_delay(&p->fe_sess_per_sec, p->fe_sps_lim, 0))) {
- /* we're blocking because a limit was reached on the number of
- * requests/s on the frontend. We want to re-check ASAP, which
- * means in 1 ms before estimated expiration date, because the
- * timer will have settled down.
- */
- *next = tick_first(*next, tick_add(now_ms, wait));
- continue;
- }
+ /* OK we have no reason to block, so let's unblock if we were blocking */
+ if (p->state == PR_STFULL)
+ p->state = PR_STREADY;
- /* The proxy is not limited so we can re-enable any waiting listener */
- if (!LIST_ISEMPTY(&p->listener_queue))
- dequeue_all_listeners(&p->listener_queue);
- }
+ if (p->fe_sps_lim &&
+ (wait = next_event_delay(&p->fe_sess_per_sec, p->fe_sps_lim, 0))) {
+ /* we're blocking because a limit was reached on the number of
+ * requests/s on the frontend. We want to re-check ASAP, which
+ * means in 1 ms before estimated expiration date, because the
+ * timer will have settled down.
+ */
+ next = tick_first(next, tick_add(now_ms, wait));
+ goto out;
}
+
+ /* The proxy is not limited so we can re-enable any waiting listener */
+ if (!LIST_ISEMPTY(&p->listener_queue))
+ dequeue_all_listeners(&p->listener_queue);
+ out:
+ t->expire = next;
+ task_queue(t);
+ return t;
}
@@ -560,6 +558,8 @@
if (p->table.size && p->table.sync_task)
task_wakeup(p->table.sync_task, TASK_WOKEN_MSG);
+ /* wake every proxy task up so that they can handle the stopping */
+ task_wakeup(p->task, TASK_WOKEN_MSG);
p = p->next;
}
diff --git a/src/stream_sock.c b/src/stream_sock.c
index a0b2c11..d69c153 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -1204,6 +1204,7 @@
if (unlikely(!max)) {
/* frontend accept rate limit was reached */
limit_listener(l, &p->listener_queue);
+ task_schedule(p->task, tick_add(now_ms, next_event_delay(&p->fe_sess_per_sec, p->fe_sps_lim, 0)));
return 0;
}