blob: a6d1cf8641abafaaaf248b5f15854ca2f7cddf3b [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 Tarreau49801602020-06-04 22:50:02 +020013#include <haproxy/backend.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020014#include <haproxy/api.h>
Willy Tarreau28671592020-06-04 20:22:59 +020015#include <haproxy/lb_map.h>
Willy Tarreau8d2b7772020-05-27 10:58:19 +020016#include <import/eb32tree.h>
Willy Tarreauf89c1872009-10-01 11:19:37 +020017
Willy Tarreauf89c1872009-10-01 11:19:37 +020018#include <types/server.h>
Willy Tarreauf89c1872009-10-01 11:19:37 +020019#include <proto/queue.h>
20
Willy Tarreau1b877482018-08-21 19:44:53 +020021/* this function updates the map according to server <srv>'s new state.
22 *
23 * The server's lock must be held. The lbprm's lock will be used.
24 */
Willy Tarreauf89c1872009-10-01 11:19:37 +020025static void map_set_server_status_down(struct server *srv)
26{
27 struct proxy *p = srv->proxy;
28
Willy Tarreauc5150da2014-05-13 19:27:31 +020029 if (!srv_lb_status_changed(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020030 return;
31
Emeric Brun52a91d32017-08-31 14:41:55 +020032 if (srv_willbe_usable(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020033 goto out_update_state;
34
35 /* FIXME: could be optimized since we know what changed */
Willy Tarreau1b877482018-08-21 19:44:53 +020036 HA_SPIN_LOCK(LBPRM_LOCK, &p->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +020037 recount_servers(p);
38 update_backend_weight(p);
Christopher Faulet5b517552017-06-09 14:17:53 +020039 recalc_server_map(p);
Willy Tarreau1b877482018-08-21 19:44:53 +020040 HA_SPIN_UNLOCK(LBPRM_LOCK, &p->lbprm.lock);
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
Willy Tarreau1b877482018-08-21 19:44:53 +020045/* This function updates the map according to server <srv>'s new state.
46 *
47 * The server's lock must be held. The lbprm's lock will be used.
48 */
Willy Tarreauf89c1872009-10-01 11:19:37 +020049static void map_set_server_status_up(struct server *srv)
50{
51 struct proxy *p = srv->proxy;
52
Willy Tarreauc5150da2014-05-13 19:27:31 +020053 if (!srv_lb_status_changed(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020054 return;
55
Emeric Brun52a91d32017-08-31 14:41:55 +020056 if (!srv_willbe_usable(srv))
Willy Tarreauf89c1872009-10-01 11:19:37 +020057 goto out_update_state;
58
59 /* FIXME: could be optimized since we know what changed */
Willy Tarreau1b877482018-08-21 19:44:53 +020060 HA_SPIN_LOCK(LBPRM_LOCK, &p->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +020061 recount_servers(p);
62 update_backend_weight(p);
Christopher Faulet5b517552017-06-09 14:17:53 +020063 recalc_server_map(p);
Willy Tarreau1b877482018-08-21 19:44:53 +020064 HA_SPIN_UNLOCK(LBPRM_LOCK, &p->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +020065 out_update_state:
Willy Tarreauc5150da2014-05-13 19:27:31 +020066 srv_lb_commit_status(srv);
Willy Tarreauf89c1872009-10-01 11:19:37 +020067}
68
69/* This function recomputes the server map for proxy px. It relies on
70 * px->lbprm.tot_wact, tot_wbck, tot_used, tot_weight, so it must be
71 * called after recount_servers(). It also expects px->lbprm.map.srv
72 * to be allocated with the largest size needed. It updates tot_weight.
Willy Tarreau1b877482018-08-21 19:44:53 +020073 *
74 * The lbprm's lock must be held.
Willy Tarreauf89c1872009-10-01 11:19:37 +020075 */
76void recalc_server_map(struct proxy *px)
77{
78 int o, tot, flag;
79 struct server *cur, *best;
80
81 switch (px->lbprm.tot_used) {
82 case 0: /* no server */
Willy Tarreauf89c1872009-10-01 11:19:37 +020083 return;
Willy Tarreauf89c1872009-10-01 11:19:37 +020084 default:
85 tot = px->lbprm.tot_weight;
86 break;
87 }
88
89 /* here we *know* that we have some servers */
90 if (px->srv_act)
Willy Tarreauc93cd162014-05-13 15:54:22 +020091 flag = 0;
Willy Tarreauf89c1872009-10-01 11:19:37 +020092 else
Willy Tarreauc93cd162014-05-13 15:54:22 +020093 flag = SRV_F_BACKUP;
Willy Tarreauf89c1872009-10-01 11:19:37 +020094
95 /* this algorithm gives priority to the first server, which means that
96 * it will respect the declaration order for equivalent weights, and
97 * that whatever the weights, the first server called will always be
Ilya Shipitsin6fb0f212020-04-02 15:25:26 +050098 * the first declared. This is an important assumption for the backup
Willy Tarreauf89c1872009-10-01 11:19:37 +020099 * case, where we want the first server only.
100 */
101 for (cur = px->srv; cur; cur = cur->next)
102 cur->wscore = 0;
103
104 for (o = 0; o < tot; o++) {
105 int max = 0;
106 best = NULL;
107 for (cur = px->srv; cur; cur = cur->next) {
Willy Tarreau9943d312014-05-22 16:20:59 +0200108 if ((cur->flags & SRV_F_BACKUP) == flag &&
Emeric Brun52a91d32017-08-31 14:41:55 +0200109 srv_willbe_usable(cur)) {
Willy Tarreauf89c1872009-10-01 11:19:37 +0200110 int v;
111
112 /* If we are forced to return only one server, we don't want to
113 * go further, because we would return the wrong one due to
114 * divide overflow.
115 */
116 if (tot == 1) {
117 best = cur;
118 /* note that best->wscore will be wrong but we don't care */
119 break;
120 }
121
Olivier Houchard36a8e6f2019-03-08 18:52:46 +0100122 _HA_ATOMIC_ADD(&cur->wscore, cur->next_eweight);
Willy Tarreauf89c1872009-10-01 11:19:37 +0200123 v = (cur->wscore + tot) / tot; /* result between 0 and 3 */
124 if (best == NULL || v > max) {
125 max = v;
126 best = cur;
127 }
128 }
129 }
130 px->lbprm.map.srv[o] = best;
Willy Tarreauc8b476d2018-12-02 19:22:55 +0100131 if (best)
Olivier Houchard36a8e6f2019-03-08 18:52:46 +0100132 _HA_ATOMIC_SUB(&best->wscore, tot);
Willy Tarreauf89c1872009-10-01 11:19:37 +0200133 }
Willy Tarreauf89c1872009-10-01 11:19:37 +0200134}
135
136/* This function is responsible of building the server MAP for map-based LB
137 * algorithms, allocating the map, and setting p->lbprm.wmult to the GCD of the
138 * weights if applicable. It should be called only once per proxy, at config
139 * time.
140 */
141void init_server_map(struct proxy *p)
142{
143 struct server *srv;
144 int pgcd;
145 int act, bck;
146
147 p->lbprm.set_server_status_up = map_set_server_status_up;
148 p->lbprm.set_server_status_down = map_set_server_status_down;
149 p->lbprm.update_server_eweight = NULL;
150
151 if (!p->srv)
152 return;
153
154 /* We will factor the weights to reduce the table,
155 * using Euclide's largest common divisor algorithm.
156 * Since we may have zero weights, we have to first
157 * find a non-zero weight server.
158 */
159 pgcd = 1;
160 srv = p->srv;
161 while (srv && !srv->uweight)
162 srv = srv->next;
163
164 if (srv) {
165 pgcd = srv->uweight; /* note: cannot be zero */
166 while (pgcd > 1 && (srv = srv->next)) {
167 int w = srv->uweight;
168 while (w) {
169 int t = pgcd % w;
170 pgcd = w;
171 w = t;
172 }
173 }
174 }
175
176 /* It is sometimes useful to know what factor to apply
177 * to the backend's effective weight to know its real
178 * weight.
179 */
180 p->lbprm.wmult = pgcd;
181
182 act = bck = 0;
183 for (srv = p->srv; srv; srv = srv->next) {
Emeric Brun52a91d32017-08-31 14:41:55 +0200184 srv->next_eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult;
Willy Tarreauc5150da2014-05-13 19:27:31 +0200185
Willy Tarreauc93cd162014-05-13 15:54:22 +0200186 if (srv->flags & SRV_F_BACKUP)
Emeric Brun52a91d32017-08-31 14:41:55 +0200187 bck += srv->next_eweight;
Willy Tarreauf89c1872009-10-01 11:19:37 +0200188 else
Emeric Brun52a91d32017-08-31 14:41:55 +0200189 act += srv->next_eweight;
190 srv_lb_commit_status(srv);
Willy Tarreauf89c1872009-10-01 11:19:37 +0200191 }
192
193 /* this is the largest map we will ever need for this servers list */
194 if (act < bck)
195 act = bck;
196
197 if (!act)
198 act = 1;
199
Vincent Bernat3c2f2f22016-04-03 13:48:42 +0200200 p->lbprm.map.srv = calloc(act, sizeof(struct server *));
Willy Tarreauf89c1872009-10-01 11:19:37 +0200201 /* recounts servers and their weights */
Willy Tarreauf89c1872009-10-01 11:19:37 +0200202 recount_servers(p);
203 update_backend_weight(p);
204 recalc_server_map(p);
205}
206
207/*
208 * This function tries to find a running server with free connection slots for
209 * the proxy <px> following the round-robin method.
210 * If any server is found, it will be returned and px->lbprm.map.rr_idx will be updated
211 * to point to the next server. If no valid server is found, NULL is returned.
Willy Tarreau1b877482018-08-21 19:44:53 +0200212 *
213 * The lbprm's lock will be used.
Willy Tarreauf89c1872009-10-01 11:19:37 +0200214 */
215struct server *map_get_server_rr(struct proxy *px, struct server *srvtoavoid)
216{
217 int newidx, avoididx;
218 struct server *srv, *avoided;
219
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100220 HA_SPIN_LOCK(LBPRM_LOCK, &px->lbprm.lock);
Christopher Faulet5b517552017-06-09 14:17:53 +0200221 if (px->lbprm.tot_weight == 0) {
222 avoided = NULL;
223 goto out;
224 }
Willy Tarreauf89c1872009-10-01 11:19:37 +0200225
226 if (px->lbprm.map.rr_idx < 0 || px->lbprm.map.rr_idx >= px->lbprm.tot_weight)
227 px->lbprm.map.rr_idx = 0;
228 newidx = px->lbprm.map.rr_idx;
229
230 avoided = NULL;
231 avoididx = 0; /* shut a gcc warning */
232 do {
233 srv = px->lbprm.map.srv[newidx++];
Godbach8f9fd2f2013-08-07 09:48:23 +0800234 if (!srv->maxconn || (!srv->nbpend && srv->served < srv_dynamic_maxconn(srv))) {
Willy Tarreauf89c1872009-10-01 11:19:37 +0200235 /* make sure it is not the server we are try to exclude... */
Willy Tarreau03071f62017-11-05 10:59:12 +0100236 /* ...but remember that is was selected yet avoided */
237 avoided = srv;
238 avoididx = newidx;
Willy Tarreauf89c1872009-10-01 11:19:37 +0200239 if (srv != srvtoavoid) {
240 px->lbprm.map.rr_idx = newidx;
Willy Tarreau03071f62017-11-05 10:59:12 +0100241 goto out;
Willy Tarreauf89c1872009-10-01 11:19:37 +0200242 }
Willy Tarreauf89c1872009-10-01 11:19:37 +0200243 }
244 if (newidx == px->lbprm.tot_weight)
245 newidx = 0;
246 } while (newidx != px->lbprm.map.rr_idx);
247
248 if (avoided)
249 px->lbprm.map.rr_idx = avoididx;
250
Christopher Faulet5b517552017-06-09 14:17:53 +0200251 out:
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100252 HA_SPIN_UNLOCK(LBPRM_LOCK, &px->lbprm.lock);
Willy Tarreauf89c1872009-10-01 11:19:37 +0200253 /* return NULL or srvtoavoid if found */
254 return avoided;
255}
256
Willy Tarreau39c9ba72009-10-01 21:11:15 +0200257/*
258 * This function returns the running server from the map at the location
259 * pointed to by the result of a modulo operation on <hash>. The server map may
260 * be recomputed if required before being looked up. If any server is found, it
261 * will be returned. If no valid server is found, NULL is returned.
Willy Tarreau1b877482018-08-21 19:44:53 +0200262 *
263 * The lbprm's lock will be used.
Willy Tarreau39c9ba72009-10-01 21:11:15 +0200264 */
265struct server *map_get_server_hash(struct proxy *px, unsigned int hash)
266{
Willy Tarreau1b877482018-08-21 19:44:53 +0200267 struct server *srv = NULL;
268
269 HA_SPIN_LOCK(LBPRM_LOCK, &px->lbprm.lock);
270 if (px->lbprm.tot_weight)
271 srv = px->lbprm.map.srv[hash % px->lbprm.tot_weight];
272 HA_SPIN_UNLOCK(LBPRM_LOCK, &px->lbprm.lock);
273 return srv;
Willy Tarreau39c9ba72009-10-01 21:11:15 +0200274}
275
Willy Tarreauf89c1872009-10-01 11:19:37 +0200276
277/*
278 * Local variables:
279 * c-indent-level: 8
280 * c-basic-offset: 8
281 * End:
282 */