blob: a108bdbac9e70c37d8f1954062660a30eb455f24 [file] [log] [blame]
Willy Tarreauf89c1872009-10-01 11:19:37 +02001/*
2 * Map-based load-balancing (RR and HASH)
3 *
4 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <common/compat.h>
14#include <common/config.h>
15#include <common/debug.h>
Willy Tarreau45cb4fb2009-10-26 21:10:04 +010016#include <eb32tree.h>
Willy Tarreauf89c1872009-10-01 11:19:37 +020017
18#include <types/global.h>
19#include <types/server.h>
20
21#include <proto/backend.h>
22#include <proto/proto_http.h>
23#include <proto/proto_tcp.h>
24#include <proto/queue.h>
25
26/* this function updates the map according to server <srv>'s new state */
27static void map_set_server_status_down(struct server *srv)
28{
29 struct proxy *p = srv->proxy;
30
Willy Tarreauc5150da2014-05-13 19:27:31 +020031 if (!srv_lb_status_changed(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020032 return;
33
Willy Tarreau87eb1d62014-05-13 18:51:40 +020034 if (srv_is_usable(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020035 goto out_update_state;
36
37 /* FIXME: could be optimized since we know what changed */
38 recount_servers(p);
39 update_backend_weight(p);
Willy Tarreau5b4c2b52009-10-03 11:21:53 +020040 p->lbprm.map.state |= LB_MAP_RECALC;
Willy Tarreauf89c1872009-10-01 11:19:37 +020041 out_update_state:
Willy Tarreauc5150da2014-05-13 19:27:31 +020042 srv_lb_commit_status(srv);
Willy Tarreauf89c1872009-10-01 11:19:37 +020043}
44
45/* This function updates the map according to server <srv>'s new state */
46static void map_set_server_status_up(struct server *srv)
47{
48 struct proxy *p = srv->proxy;
49
Willy Tarreauc5150da2014-05-13 19:27:31 +020050 if (!srv_lb_status_changed(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020051 return;
52
Willy Tarreau87eb1d62014-05-13 18:51:40 +020053 if (!srv_is_usable(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020054 goto out_update_state;
55
56 /* FIXME: could be optimized since we know what changed */
57 recount_servers(p);
58 update_backend_weight(p);
Willy Tarreau5b4c2b52009-10-03 11:21:53 +020059 p->lbprm.map.state |= LB_MAP_RECALC;
Willy Tarreauf89c1872009-10-01 11:19:37 +020060 out_update_state:
Willy Tarreauc5150da2014-05-13 19:27:31 +020061 srv_lb_commit_status(srv);
Willy Tarreauf89c1872009-10-01 11:19:37 +020062}
63
64/* This function recomputes the server map for proxy px. It relies on
65 * px->lbprm.tot_wact, tot_wbck, tot_used, tot_weight, so it must be
66 * called after recount_servers(). It also expects px->lbprm.map.srv
67 * to be allocated with the largest size needed. It updates tot_weight.
68 */
69void recalc_server_map(struct proxy *px)
70{
71 int o, tot, flag;
72 struct server *cur, *best;
73
74 switch (px->lbprm.tot_used) {
75 case 0: /* no server */
Willy Tarreau5b4c2b52009-10-03 11:21:53 +020076 px->lbprm.map.state &= ~LB_MAP_RECALC;
Willy Tarreauf89c1872009-10-01 11:19:37 +020077 return;
Willy Tarreauf89c1872009-10-01 11:19:37 +020078 default:
79 tot = px->lbprm.tot_weight;
80 break;
81 }
82
83 /* here we *know* that we have some servers */
84 if (px->srv_act)
85 flag = SRV_RUNNING;
86 else
87 flag = SRV_RUNNING | SRV_BACKUP;
88
89 /* this algorithm gives priority to the first server, which means that
90 * it will respect the declaration order for equivalent weights, and
91 * that whatever the weights, the first server called will always be
92 * the first declared. This is an important asumption for the backup
93 * case, where we want the first server only.
94 */
95 for (cur = px->srv; cur; cur = cur->next)
96 cur->wscore = 0;
97
98 for (o = 0; o < tot; o++) {
99 int max = 0;
100 best = NULL;
101 for (cur = px->srv; cur; cur = cur->next) {
102 if (cur->eweight &&
103 flag == (cur->state &
104 (SRV_RUNNING | SRV_GOINGDOWN | SRV_BACKUP))) {
105 int v;
106
107 /* If we are forced to return only one server, we don't want to
108 * go further, because we would return the wrong one due to
109 * divide overflow.
110 */
111 if (tot == 1) {
112 best = cur;
113 /* note that best->wscore will be wrong but we don't care */
114 break;
115 }
116
117 cur->wscore += cur->eweight;
118 v = (cur->wscore + tot) / tot; /* result between 0 and 3 */
119 if (best == NULL || v > max) {
120 max = v;
121 best = cur;
122 }
123 }
124 }
125 px->lbprm.map.srv[o] = best;
126 best->wscore -= tot;
127 }
Willy Tarreau5b4c2b52009-10-03 11:21:53 +0200128 px->lbprm.map.state &= ~LB_MAP_RECALC;
Willy Tarreauf89c1872009-10-01 11:19:37 +0200129}
130
131/* This function is responsible of building the server MAP for map-based LB
132 * algorithms, allocating the map, and setting p->lbprm.wmult to the GCD of the
133 * weights if applicable. It should be called only once per proxy, at config
134 * time.
135 */
136void init_server_map(struct proxy *p)
137{
138 struct server *srv;
139 int pgcd;
140 int act, bck;
141
142 p->lbprm.set_server_status_up = map_set_server_status_up;
143 p->lbprm.set_server_status_down = map_set_server_status_down;
144 p->lbprm.update_server_eweight = NULL;
145
146 if (!p->srv)
147 return;
148
149 /* We will factor the weights to reduce the table,
150 * using Euclide's largest common divisor algorithm.
151 * Since we may have zero weights, we have to first
152 * find a non-zero weight server.
153 */
154 pgcd = 1;
155 srv = p->srv;
156 while (srv && !srv->uweight)
157 srv = srv->next;
158
159 if (srv) {
160 pgcd = srv->uweight; /* note: cannot be zero */
161 while (pgcd > 1 && (srv = srv->next)) {
162 int w = srv->uweight;
163 while (w) {
164 int t = pgcd % w;
165 pgcd = w;
166 w = t;
167 }
168 }
169 }
170
171 /* It is sometimes useful to know what factor to apply
172 * to the backend's effective weight to know its real
173 * weight.
174 */
175 p->lbprm.wmult = pgcd;
176
177 act = bck = 0;
178 for (srv = p->srv; srv; srv = srv->next) {
Willy Tarreau004e0452013-11-21 11:22:01 +0100179 srv->eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult;
Willy Tarreauc5150da2014-05-13 19:27:31 +0200180 srv_lb_commit_status(srv);
181
Willy Tarreauf89c1872009-10-01 11:19:37 +0200182 if (srv->state & SRV_BACKUP)
183 bck += srv->eweight;
184 else
185 act += srv->eweight;
186 }
187
188 /* this is the largest map we will ever need for this servers list */
189 if (act < bck)
190 act = bck;
191
192 if (!act)
193 act = 1;
194
195 p->lbprm.map.srv = (struct server **)calloc(act, sizeof(struct server *));
196 /* recounts servers and their weights */
Willy Tarreau5b4c2b52009-10-03 11:21:53 +0200197 p->lbprm.map.state = LB_MAP_RECALC;
Willy Tarreauf89c1872009-10-01 11:19:37 +0200198 recount_servers(p);
199 update_backend_weight(p);
200 recalc_server_map(p);
201}
202
203/*
204 * This function tries to find a running server with free connection slots for
205 * the proxy <px> following the round-robin method.
206 * If any server is found, it will be returned and px->lbprm.map.rr_idx will be updated
207 * to point to the next server. If no valid server is found, NULL is returned.
208 */
209struct server *map_get_server_rr(struct proxy *px, struct server *srvtoavoid)
210{
211 int newidx, avoididx;
212 struct server *srv, *avoided;
213
214 if (px->lbprm.tot_weight == 0)
215 return NULL;
216
Willy Tarreau5b4c2b52009-10-03 11:21:53 +0200217 if (px->lbprm.map.state & LB_MAP_RECALC)
Willy Tarreauf89c1872009-10-01 11:19:37 +0200218 recalc_server_map(px);
219
220 if (px->lbprm.map.rr_idx < 0 || px->lbprm.map.rr_idx >= px->lbprm.tot_weight)
221 px->lbprm.map.rr_idx = 0;
222 newidx = px->lbprm.map.rr_idx;
223
224 avoided = NULL;
225 avoididx = 0; /* shut a gcc warning */
226 do {
227 srv = px->lbprm.map.srv[newidx++];
Godbach8f9fd2f2013-08-07 09:48:23 +0800228 if (!srv->maxconn || (!srv->nbpend && srv->served < srv_dynamic_maxconn(srv))) {
Willy Tarreauf89c1872009-10-01 11:19:37 +0200229 /* make sure it is not the server we are try to exclude... */
230 if (srv != srvtoavoid) {
231 px->lbprm.map.rr_idx = newidx;
232 return srv;
233 }
234
235 avoided = srv; /* ...but remember that is was selected yet avoided */
236 avoididx = newidx;
237 }
238 if (newidx == px->lbprm.tot_weight)
239 newidx = 0;
240 } while (newidx != px->lbprm.map.rr_idx);
241
242 if (avoided)
243 px->lbprm.map.rr_idx = avoididx;
244
245 /* return NULL or srvtoavoid if found */
246 return avoided;
247}
248
Willy Tarreau39c9ba72009-10-01 21:11:15 +0200249/*
250 * This function returns the running server from the map at the location
251 * pointed to by the result of a modulo operation on <hash>. The server map may
252 * be recomputed if required before being looked up. If any server is found, it
253 * will be returned. If no valid server is found, NULL is returned.
254 */
255struct server *map_get_server_hash(struct proxy *px, unsigned int hash)
256{
257 if (px->lbprm.tot_weight == 0)
258 return NULL;
259
Willy Tarreau5b4c2b52009-10-03 11:21:53 +0200260 if (px->lbprm.map.state & LB_MAP_RECALC)
Willy Tarreau39c9ba72009-10-01 21:11:15 +0200261 recalc_server_map(px);
262
263 return px->lbprm.map.srv[hash % px->lbprm.tot_weight];
264}
265
Willy Tarreauf89c1872009-10-01 11:19:37 +0200266
267/*
268 * Local variables:
269 * c-indent-level: 8
270 * c-basic-offset: 8
271 * End:
272 */