blob: 592df91cceb8bbfa0a2a574aac7270fb08642115 [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
Willy Tarreau1e56f922020-06-04 23:20:13 +020013#include <import/eb32tree.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020014#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020015#include <haproxy/backend.h>
Willy Tarreau28671592020-06-04 20:22:59 +020016#include <haproxy/lb_map.h>
Willy Tarreaua55c4542020-06-04 22:59:39 +020017#include <haproxy/queue.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020018#include <haproxy/server-t.h>
Willy Tarreauf89c1872009-10-01 11:19:37 +020019
Willy Tarreau1b877482018-08-21 19:44:53 +020020/* this function updates the map according to server <srv>'s new state.
21 *
22 * The server's lock must be held. The lbprm's lock will be used.
23 */
Willy Tarreauf89c1872009-10-01 11:19:37 +020024static void map_set_server_status_down(struct server *srv)
25{
26 struct proxy *p = srv->proxy;
27
Willy Tarreauc5150da2014-05-13 19:27:31 +020028 if (!srv_lb_status_changed(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020029 return;
30
Emeric Brun52a91d32017-08-31 14:41:55 +020031 if (srv_willbe_usable(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020032 goto out_update_state;
33
34 /* FIXME: could be optimized since we know what changed */
Willy Tarreaucd10def2020-10-17 18:48:47 +020035 HA_RWLOCK_WRLOCK(LBPRM_LOCK, &p->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +020036 recount_servers(p);
37 update_backend_weight(p);
Christopher Faulet5b517552017-06-09 14:17:53 +020038 recalc_server_map(p);
Willy Tarreaucd10def2020-10-17 18:48:47 +020039 HA_RWLOCK_WRUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +020040 out_update_state:
Willy Tarreauc5150da2014-05-13 19:27:31 +020041 srv_lb_commit_status(srv);
Willy Tarreauf89c1872009-10-01 11:19:37 +020042}
43
Willy Tarreau1b877482018-08-21 19:44:53 +020044/* This function updates the map according to server <srv>'s new state.
45 *
46 * The server's lock must be held. The lbprm's lock will be used.
47 */
Willy Tarreauf89c1872009-10-01 11:19:37 +020048static void map_set_server_status_up(struct server *srv)
49{
50 struct proxy *p = srv->proxy;
51
Willy Tarreauc5150da2014-05-13 19:27:31 +020052 if (!srv_lb_status_changed(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020053 return;
54
Emeric Brun52a91d32017-08-31 14:41:55 +020055 if (!srv_willbe_usable(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020056 goto out_update_state;
57
58 /* FIXME: could be optimized since we know what changed */
Willy Tarreaucd10def2020-10-17 18:48:47 +020059 HA_RWLOCK_WRLOCK(LBPRM_LOCK, &p->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +020060 recount_servers(p);
61 update_backend_weight(p);
Christopher Faulet5b517552017-06-09 14:17:53 +020062 recalc_server_map(p);
Willy Tarreaucd10def2020-10-17 18:48:47 +020063 HA_RWLOCK_WRUNLOCK(LBPRM_LOCK, &p->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +020064 out_update_state:
Willy Tarreauc5150da2014-05-13 19:27:31 +020065 srv_lb_commit_status(srv);
Willy Tarreauf89c1872009-10-01 11:19:37 +020066}
67
68/* This function recomputes the server map for proxy px. It relies on
69 * px->lbprm.tot_wact, tot_wbck, tot_used, tot_weight, so it must be
70 * called after recount_servers(). It also expects px->lbprm.map.srv
71 * to be allocated with the largest size needed. It updates tot_weight.
Willy Tarreau1b877482018-08-21 19:44:53 +020072 *
73 * The lbprm's lock must be held.
Willy Tarreauf89c1872009-10-01 11:19:37 +020074 */
75void recalc_server_map(struct proxy *px)
76{
77 int o, tot, flag;
78 struct server *cur, *best;
79
80 switch (px->lbprm.tot_used) {
81 case 0: /* no server */
Willy Tarreauf89c1872009-10-01 11:19:37 +020082 return;
Willy Tarreauf89c1872009-10-01 11:19:37 +020083 default:
84 tot = px->lbprm.tot_weight;
85 break;
86 }
87
88 /* here we *know* that we have some servers */
89 if (px->srv_act)
Willy Tarreauc93cd162014-05-13 15:54:22 +020090 flag = 0;
Willy Tarreauf89c1872009-10-01 11:19:37 +020091 else
Willy Tarreauc93cd162014-05-13 15:54:22 +020092 flag = SRV_F_BACKUP;
Willy Tarreauf89c1872009-10-01 11:19:37 +020093
94 /* this algorithm gives priority to the first server, which means that
95 * it will respect the declaration order for equivalent weights, and
96 * that whatever the weights, the first server called will always be
Ilya Shipitsin6fb0f212020-04-02 15:25:26 +050097 * the first declared. This is an important assumption for the backup
Willy Tarreauf89c1872009-10-01 11:19:37 +020098 * case, where we want the first server only.
99 */
100 for (cur = px->srv; cur; cur = cur->next)
101 cur->wscore = 0;
102
103 for (o = 0; o < tot; o++) {
104 int max = 0;
105 best = NULL;
106 for (cur = px->srv; cur; cur = cur->next) {
Willy Tarreau9943d312014-05-22 16:20:59 +0200107 if ((cur->flags & SRV_F_BACKUP) == flag &&
Emeric Brun52a91d32017-08-31 14:41:55 +0200108 srv_willbe_usable(cur)) {
Willy Tarreauf89c1872009-10-01 11:19:37 +0200109 int v;
110
111 /* If we are forced to return only one server, we don't want to
112 * go further, because we would return the wrong one due to
113 * divide overflow.
114 */
115 if (tot == 1) {
116 best = cur;
117 /* note that best->wscore will be wrong but we don't care */
118 break;
119 }
120
Olivier Houchard36a8e6f2019-03-08 18:52:46 +0100121 _HA_ATOMIC_ADD(&cur->wscore, cur->next_eweight);
Willy Tarreauf89c1872009-10-01 11:19:37 +0200122 v = (cur->wscore + tot) / tot; /* result between 0 and 3 */
123 if (best == NULL || v > max) {
124 max = v;
125 best = cur;
126 }
127 }
128 }
129 px->lbprm.map.srv[o] = best;
Willy Tarreauc8b476d2018-12-02 19:22:55 +0100130 if (best)
Olivier Houchard36a8e6f2019-03-08 18:52:46 +0100131 _HA_ATOMIC_SUB(&best->wscore, tot);
Willy Tarreauf89c1872009-10-01 11:19:37 +0200132 }
Willy Tarreauf89c1872009-10-01 11:19:37 +0200133}
134
135/* This function is responsible of building the server MAP for map-based LB
136 * algorithms, allocating the map, and setting p->lbprm.wmult to the GCD of the
137 * weights if applicable. It should be called only once per proxy, at config
138 * time.
139 */
140void init_server_map(struct proxy *p)
141{
142 struct server *srv;
143 int pgcd;
144 int act, bck;
145
146 p->lbprm.set_server_status_up = map_set_server_status_up;
147 p->lbprm.set_server_status_down = map_set_server_status_down;
148 p->lbprm.update_server_eweight = NULL;
149
150 if (!p->srv)
151 return;
152
153 /* We will factor the weights to reduce the table,
154 * using Euclide's largest common divisor algorithm.
155 * Since we may have zero weights, we have to first
156 * find a non-zero weight server.
157 */
158 pgcd = 1;
159 srv = p->srv;
160 while (srv && !srv->uweight)
161 srv = srv->next;
162
163 if (srv) {
164 pgcd = srv->uweight; /* note: cannot be zero */
165 while (pgcd > 1 && (srv = srv->next)) {
166 int w = srv->uweight;
167 while (w) {
168 int t = pgcd % w;
169 pgcd = w;
170 w = t;
171 }
172 }
173 }
174
175 /* It is sometimes useful to know what factor to apply
176 * to the backend's effective weight to know its real
177 * weight.
178 */
179 p->lbprm.wmult = pgcd;
180
181 act = bck = 0;
182 for (srv = p->srv; srv; srv = srv->next) {
Emeric Brun52a91d32017-08-31 14:41:55 +0200183 srv->next_eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult;
Willy Tarreauc5150da2014-05-13 19:27:31 +0200184
Willy Tarreauc93cd162014-05-13 15:54:22 +0200185 if (srv->flags & SRV_F_BACKUP)
Emeric Brun52a91d32017-08-31 14:41:55 +0200186 bck += srv->next_eweight;
Willy Tarreauf89c1872009-10-01 11:19:37 +0200187 else
Emeric Brun52a91d32017-08-31 14:41:55 +0200188 act += srv->next_eweight;
189 srv_lb_commit_status(srv);
Willy Tarreauf89c1872009-10-01 11:19:37 +0200190 }
191
192 /* this is the largest map we will ever need for this servers list */
193 if (act < bck)
194 act = bck;
195
196 if (!act)
197 act = 1;
198
Tim Duesterhuse52b6e52020-09-12 20:26:43 +0200199 p->lbprm.map.srv = calloc(act, sizeof(*p->lbprm.map.srv));
Willy Tarreauf89c1872009-10-01 11:19:37 +0200200 /* recounts servers and their weights */
Willy Tarreauf89c1872009-10-01 11:19:37 +0200201 recount_servers(p);
202 update_backend_weight(p);
203 recalc_server_map(p);
204}
205
206/*
207 * This function tries to find a running server with free connection slots for
208 * the proxy <px> following the round-robin method.
209 * If any server is found, it will be returned and px->lbprm.map.rr_idx will be updated
210 * to point to the next server. If no valid server is found, NULL is returned.
Willy Tarreau1b877482018-08-21 19:44:53 +0200211 *
212 * The lbprm's lock will be used.
Willy Tarreauf89c1872009-10-01 11:19:37 +0200213 */
214struct server *map_get_server_rr(struct proxy *px, struct server *srvtoavoid)
215{
216 int newidx, avoididx;
217 struct server *srv, *avoided;
218
Willy Tarreauae99aeb2020-10-17 18:55:18 +0200219 HA_RWLOCK_SKLOCK(LBPRM_LOCK, &px->lbprm.lock);
Christopher Faulet5b517552017-06-09 14:17:53 +0200220 if (px->lbprm.tot_weight == 0) {
221 avoided = NULL;
222 goto out;
223 }
Willy Tarreauf89c1872009-10-01 11:19:37 +0200224
225 if (px->lbprm.map.rr_idx < 0 || px->lbprm.map.rr_idx >= px->lbprm.tot_weight)
226 px->lbprm.map.rr_idx = 0;
227 newidx = px->lbprm.map.rr_idx;
228
229 avoided = NULL;
230 avoididx = 0; /* shut a gcc warning */
231 do {
232 srv = px->lbprm.map.srv[newidx++];
Willy Tarreaua0570452021-06-18 09:30:30 +0200233 if (!srv->maxconn || (!srv->queue.length && srv->served < srv_dynamic_maxconn(srv))) {
Willy Tarreauf89c1872009-10-01 11:19:37 +0200234 /* make sure it is not the server we are try to exclude... */
Willy Tarreau03071f62017-11-05 10:59:12 +0100235 /* ...but remember that is was selected yet avoided */
236 avoided = srv;
237 avoididx = newidx;
Willy Tarreauf89c1872009-10-01 11:19:37 +0200238 if (srv != srvtoavoid) {
239 px->lbprm.map.rr_idx = newidx;
Willy Tarreau03071f62017-11-05 10:59:12 +0100240 goto out;
Willy Tarreauf89c1872009-10-01 11:19:37 +0200241 }
Willy Tarreauf89c1872009-10-01 11:19:37 +0200242 }
243 if (newidx == px->lbprm.tot_weight)
244 newidx = 0;
245 } while (newidx != px->lbprm.map.rr_idx);
246
247 if (avoided)
248 px->lbprm.map.rr_idx = avoididx;
249
Christopher Faulet5b517552017-06-09 14:17:53 +0200250 out:
Willy Tarreauae99aeb2020-10-17 18:55:18 +0200251 HA_RWLOCK_SKUNLOCK(LBPRM_LOCK, &px->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +0200252 /* return NULL or srvtoavoid if found */
253 return avoided;
254}
255
Willy Tarreau39c9ba72009-10-01 21:11:15 +0200256/*
257 * This function returns the running server from the map at the location
258 * pointed to by the result of a modulo operation on <hash>. The server map may
259 * be recomputed if required before being looked up. If any server is found, it
260 * will be returned. If no valid server is found, NULL is returned.
Willy Tarreau1b877482018-08-21 19:44:53 +0200261 *
262 * The lbprm's lock will be used.
Willy Tarreau39c9ba72009-10-01 21:11:15 +0200263 */
264struct server *map_get_server_hash(struct proxy *px, unsigned int hash)
265{
Willy Tarreau1b877482018-08-21 19:44:53 +0200266 struct server *srv = NULL;
267
Willy Tarreauae99aeb2020-10-17 18:55:18 +0200268 HA_RWLOCK_RDLOCK(LBPRM_LOCK, &px->lbprm.lock);
Willy Tarreau1b877482018-08-21 19:44:53 +0200269 if (px->lbprm.tot_weight)
270 srv = px->lbprm.map.srv[hash % px->lbprm.tot_weight];
Willy Tarreauae99aeb2020-10-17 18:55:18 +0200271 HA_RWLOCK_RDUNLOCK(LBPRM_LOCK, &px->lbprm.lock);
Willy Tarreau1b877482018-08-21 19:44:53 +0200272 return srv;
Willy Tarreau39c9ba72009-10-01 21:11:15 +0200273}
274
Willy Tarreauf89c1872009-10-01 11:19:37 +0200275
276/*
277 * Local variables:
278 * c-indent-level: 8
279 * c-basic-offset: 8
280 * End:
281 */