This patch for haproxy-1.2.14 was submitted by Oleksandr Krailo. It implements a basic least connection algorithm. I'm not really tempted to merge it into 1.2 right now, in order to limit the number of changes, but it might get merged early into 1.3. Please note that this is not very useful now that the maxconn can be set per server, but it might be helpful to some people. There are a few caveats though (not tested, just reading the code) : - the search always starts from the first server in the table. This means that when the load is low or when responses are very fast, and we never go above one connection at a time, it's always the first server which will receive the traffic. To fix this, the index "i" below should be replaced with "px->srv_rr_idx" (see how it's done in get_server_rr_with_conns()). With this change, the load will round-robin between the servers when their respective loads are equal. - the weight is ignored. This is not trivial in this case. In theory, it should be enough to replace the two following lines : if (s > srv->cur_sess || t == NULL) { s = srv->cur_sess; by this : if (s > srv->cur_sess * px->srv_map_sz / (srv->eweight+1) || t == NULL){ s = srv->cur_sess * px->srv_map_sz / (srv->eweight+1); There is the same performance problem as in get_server_rr_with_conns(), where we have to scan all the srv_map and see the same server multiple times if weights are very different. Another approach would be to consider two lists, one for the usable servers, and the other one for the servers map. The servers map would link to the (ordered) usable server, and the usable server list would link to the first occurence of each server in the server map. This way, we would only have to go back and forth to find the next potential server when searching a non-saturated one, and stop when the server list would have been scanned. --- haproxy-1.2.14.orig/haproxy.c +++ haproxy-1.2.14/haproxy.c @@ -378,8 +378,9 @@ #define PR_O_USE_ALL_BK 0x00100000 /* load-balance between backup servers */ #define PR_O_FORCE_CLO 0x00200000 /* enforce the connection close immediately after server response */ #define PR_O_BALANCE_SH 0x00400000 /* balance on source IP hash */ -#define PR_O_BALANCE (PR_O_BALANCE_RR | PR_O_BALANCE_SH) #define PR_O_ABRT_CLOSE 0x00800000 /* immediately abort request when client closes */ +#define PR_O_BALANCE_LC 0x01000000 /* balance on least connection (fewer active jobs) server */ +#define PR_O_BALANCE (PR_O_BALANCE_RR | PR_O_BALANCE_SH | PR_O_BALANCE_LC) /* various session flags, bits values 0x01 to 0x20 (shift 0) */ #define SN_DIRECT 0x00000001 /* connection made on the server matching the client cookie */ @@ -2221,6 +2222,34 @@ /* + * This function tries to find a running server for the proxy following + * the least load method. If no valid server is found, NULL is returned. + */ +static inline struct server *get_server_lc(struct proxy *px) { + int s, i; + struct server *srv, *t; + + if (px->srv_map_sz == 0) + return NULL; + + i = 0; + t = NULL; + s = 0; + + while (i < px->srv_map_sz) { + srv = px->srv_map[i++]; + if (!srv->maxconn || srv->cur_sess < srv_dynamic_maxconn(srv)) { + if (s > srv->cur_sess || t == NULL) { + t = srv; + s = srv->cur_sess; + } + } + } + return t; +} + + +/* * This function marks the session as 'assigned' in direct or dispatch modes, * or tries to assign one in balance mode, according to the algorithm. It does * nothing if the session had already been assigned a server. @@ -2270,6 +2299,11 @@ (void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr, len); } + else if (s->proxy->options & PR_O_BALANCE_LC) { + s->srv = get_server_lc(s->proxy); + if (!s->srv) + return SRV_STATUS_FULL; + } else /* unknown balancing algorithm */ return SRV_STATUS_INTERNAL; } @@ -8461,8 +8495,11 @@ else if (!strcmp(args[1], "source")) { curproxy->options |= PR_O_BALANCE_SH; } + else if (!strcmp(args[1], "leastconn")) { + curproxy->options |= PR_O_BALANCE_LC; + } else { - Alert("parsing [%s:%d] : '%s' only supports 'roundrobin' and 'source' options.\n", file, linenum, args[0]); + Alert("parsing [%s:%d] : '%s' only supports 'roundrobin', 'source' and 'leastconn' options.\n", file, linenum, args[0]); return -1; } }