[MAJOR] implemented the 'minconn' server parameter for dynamic load regulation
When 'minconn' is set, the number of simultaneous sessions sent to the server
will be limited by a dynamic value depending on the global load on the
instance itself. The principle is to fix the maximal concurrency on the server
proportionnally to the instance's usage relative to its maxconn, with a minimum
fixed to <minconn>. The formula for the number of simultaneous sessions sent
to the server is then max(srv_minconn, srv_maxconn*px_conn/px_maxconn). This
helps unloading the servers when the load is very low.
diff --git a/ROADMAP b/ROADMAP
index 8dae1ab..b70024e 100644
--- a/ROADMAP
+++ b/ROADMAP
@@ -30,7 +30,7 @@
    and might cause little trouble to some very specific clients used to
    close immediately after sending the request (no support for KA, which ones?)
 
- - minconn : makes the server's maxconn dynamic, which will be computed as a
+ + minconn : makes the server's maxconn dynamic, which will be computed as a
    ratio of the proxy's sessions :
      srv->effective_maxconn =
           max(srv->maxconn * px->nbsess / px->maxconn, srv->minconn)
diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt
index 2f9660e..191f136 100644
--- a/doc/haproxy-en.txt
+++ b/doc/haproxy-en.txt
@@ -1216,6 +1216,41 @@
         server web-backup1 192.168.2.1:80 cookie s4 check maxconn 200 backup
         server web-excuse 192.168.3.1:80 check backup
 
+
+This was so much efficient at reducing the server's response time that some
+users wanted to use low values to improve their server's performance. However,
+they were not able anymore to handle very large loads because it was not
+possible anymore to saturate the servers. For this reason, version 1.2.14 has
+brought dynamic limitation with the addition of the parameter 'minconn'. When
+this parameter is set along with maxconn, it will enable dynamic limitation
+based on the instance's load. The maximum number of concurrent sessions on a
+server will be proportionnal to the number of sessions on the instance relative
+to its maxconn. A minimum of <minconn> will be allowed whatever the load. This
+will ensure that servers will perform at their best level under normal loads,
+while still handling surges when needed. The dynamic limit is computed like
+this :
+
+    srv.dyn_limit = max(srv.minconn, srv.maxconn * inst.sess / inst.maxconn)
+
+Example :
+---------
+    # be nice with P3 which only has 256 MB of RAM.
+    listen web_appl 0.0.0.0:80
+        maxconn 10000
+        mode http
+        cookie SERVERID insert nocache indirect
+        balance roundrobin
+        server pentium3-800 192.168.1.1:80 cookie s1 weight  8 minconn 10 maxconn 100 check
+        server opteron-2.0G 192.168.1.2:80 cookie s2 weight 20 minconn 30 maxconn 300 check
+        server opteron-2.4G 192.168.1.3:80 cookie s3 weight 24 minconn 30 maxconn 300 check
+        server web-backup1 192.168.2.1:80 cookie s4 check maxconn 200 backup
+        server web-excuse 192.168.3.1:80 check backup
+
+In the example above, the server 'pentium3-800' will receive at most 100
+simultaneous sessions when the proxy instance will reach 10000 sessions, and
+will receive only 10 simultaneous sessions when the proxy will be under 1000
+sessions.
+
 Notes :
 -------
   - The requests will not stay indefinitely in the queue, they follow the
@@ -1223,6 +1258,8 @@
     timeout because the server is saturated or because the queue is filled,
     the session will expire with a 503 error.
 
+  - if only <minconn> is specified, it has the same effect as <maxconn>
+
   - setting too low values for maxconn might improve performance but might also
     allow slow users to block access to the server for other users.
 
diff --git a/haproxy.c b/haproxy.c
index c6a0312..586978a 100644
--- a/haproxy.c
+++ b/haproxy.c
@@ -587,7 +587,7 @@
     unsigned int wscore;		/* weight score, used during srv map computation */
     int cur_sess, cur_sess_max;		/* number of currently active sessions (including syn_sent) */
     unsigned int cum_sess;		/* cumulated number of sessions really sent to this server */
-    unsigned int maxconn;		/* max # of active sessions. 0 = unlimited. */
+    unsigned int maxconn, minconn;	/* max # of active sessions (0 = unlimited), min# for dynamic limit. */
     unsigned failed_checks, down_trans;	/* failed checks and up-down transitions */
     unsigned failed_conns, failed_resp;	/* failed connect() and responses */
     unsigned failed_secu;		/* blocked responses because of security concerns */
@@ -695,9 +695,9 @@
     struct list pendconns;		/* pending connections with no server assigned yet */
     int nbpend, nbpend_max;		/* number of pending connections with no server assigned yet */
     int totpend;			/* total number of pending connections on this instance (for stats) */
-    int nbconn, nbconn_max;		/* # of active sessions */
+    unsigned int nbconn, nbconn_max;	/* # of active sessions */
     unsigned int cum_conn;		/* cumulated number of processed sessions */
-    int maxconn;			/* max # of active sessions */
+    unsigned int maxconn;		/* max # of active sessions */
     unsigned failed_conns, failed_resp;	/* failed connect() and responses */
     unsigned failed_secu;		/* blocked responses because of security concerns */
     int conn_retries;			/* maximum number of connect retries */
@@ -1983,12 +1983,22 @@
     return p;
 }
 
+/* returns the effective dynamic maxconn for a server, considering the minconn
+ * and the proxy's usage relative to its saturation.
+ */
+static unsigned int srv_dynamic_maxconn(struct server *s) {
+    return s->minconn ? 
+	((s->maxconn * s->proxy->nbconn / s->proxy->maxconn) < s->minconn) ? s->minconn :
+	(s->maxconn * s->proxy->nbconn / s->proxy->maxconn) : s->maxconn;
+}
+
 /* returns 0 if nothing has to be done for server <s> regarding queued connections,
  * and non-zero otherwise. Suited for and if/else usage.
  */
 static inline int may_dequeue_tasks(struct server *s, struct proxy *p) {
     return (s && (s->nbpend || p->nbpend) &&
-	    s->maxconn && s->cur_sess < s->maxconn && s->queue_mgt);
+	    (!s->maxconn || s->cur_sess < srv_dynamic_maxconn(s)) &&
+	    s->queue_mgt);
 }
 
 
@@ -2157,7 +2167,7 @@
 
     do {
 	srv = px->srv_map[newidx++];
-	if (!srv->maxconn || srv->cur_sess < srv->maxconn) {
+	if (!srv->maxconn || srv->cur_sess < srv_dynamic_maxconn(srv)) {
 	    px->srv_rr_idx = newidx;
 	    return srv;
 	}
@@ -2349,7 +2359,7 @@
 	 * is not needed.
 	 */
 	if (s->srv &&
-	    s->srv->maxconn && s->srv->cur_sess >= s->srv->maxconn) {
+	    s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) {
 	    p = pendconn_add(s);
 	    if (p)
 		return SRV_STATUS_QUEUED;
@@ -2364,8 +2374,8 @@
     switch (err) {
     case SRV_STATUS_OK:
 	/* in balance mode, we might have servers with connection limits */
-	if (s->srv != NULL &&
-	    s->srv->maxconn && s->srv->cur_sess >= s->srv->maxconn) {
+	if (s->srv &&
+	    s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) {
 	    p = pendconn_add(s);
 	    if (p)
 		return SRV_STATUS_QUEUED;
@@ -6737,7 +6747,7 @@
 		    /* check if we can handle some connections queued at the proxy. We
 		     * will take as many as we can handle.
 		     */
-		    for (xferred = 0; !s->maxconn || xferred < s->maxconn; xferred++) {
+		    for (xferred = 0; !s->maxconn || xferred < srv_dynamic_maxconn(s); xferred++) {
 			struct session *sess;
 			struct pendconn *p;
 
@@ -6810,7 +6820,7 @@
     /* First, check if we can handle some connections queued at the proxy. We
      * will take as many as we can handle.
      */
-    for (xferred = 0; s->cur_sess + xferred < s->maxconn; xferred++) {
+    for (xferred = 0; s->cur_sess + xferred < srv_dynamic_maxconn(s); xferred++) {
 	struct session *sess;
 
 	sess = pendconn_get_next_sess(s, p);
@@ -8556,6 +8566,10 @@
 		newsrv->uweight = w - 1;
 		cur_arg += 2;
 	    }
+	    else if (!strcmp(args[cur_arg], "minconn")) {
+		newsrv->minconn = atol(args[cur_arg + 1]);
+		cur_arg += 2;
+	    }
 	    else if (!strcmp(args[cur_arg], "maxconn")) {
 		newsrv->maxconn = atol(args[cur_arg + 1]);
 		cur_arg += 2;
@@ -8576,7 +8590,7 @@
 		cur_arg += 2;
 	    }
 	    else {
-		Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'port', 'source', and 'weight'.\n",
+		Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'port', 'source', 'minconn', 'maxconn' and 'weight'.\n",
 		      file, linenum, newsrv->id);
 		return -1;
 	    }
@@ -9451,6 +9465,12 @@
 	 */
 	newsrv = curproxy->srv;
 	while (newsrv != NULL) {
+	    if (newsrv->minconn && !newsrv->maxconn) {
+		/* only 'minconn' was specified. Let's turn this into maxconn */
+		newsrv->maxconn = newsrv->minconn;
+		newsrv->minconn = 0;
+	    }
+
 	    if (newsrv->maxconn > 0) {
 		struct task *t;