blob: 8b983f4afbf21e2b33979fc8798d45c20ae168e8 [file] [log] [blame]
Willy Tarreau18baa9d2007-11-26 01:24:50 +01001/*
2 * experimental weighted round robin scheduler - (c) 2007 willy tarreau.
3 *
4 * This filling algorithm is excellent at spreading the servers, as it also
5 * takes care of keeping the most uniform distance between occurences of each
6 * server, by maximizing this distance. It reduces the number of variables
7 * and expensive operations.
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include "eb32tree.h"
13
14struct srv {
15 struct eb32_node node;
16 struct eb_root *tree; // we want to know where the server is
17 int num;
18 int w; /* weight */
19 int next, last;
20 int rem;
21} *srv;
22
23/* those trees represent a sliding window of 3 time frames */
24struct eb_root tree_0 = EB_ROOT;
25struct eb_root tree_1 = EB_ROOT;
26struct eb_root tree_2 = EB_ROOT;
27
28struct eb_root *init_tree; /* receives positions 0..sw-1 */
29struct eb_root *next_tree; /* receives positions >= 2sw */
30
31int nsrv; /* # of servers */
32int nsw, sw; /* sum of weights */
33int p; /* current position, between sw..2sw-1 */
34
35/* queue a server in the weights tree */
36void queue_by_weight(struct eb_root *root, struct srv *s) {
37 s->node.key = 255 - s->w;
38 eb32i_insert(root, &s->node);
39 s->tree = root;
40}
41
42/* queue a server in the weight tree <root>, except if its weight is 0 */
43void queue_by_weight_0(struct eb_root *root, struct srv *s) {
44 if (s->w) {
45 s->node.key = 255 - s->w;
46 eb32i_insert(root, &s->node);
47 s->tree = root;
48 } else {
49 s->tree = NULL;
50 }
51}
52
53static inline void dequeue_srv(struct srv *s) {
54 eb32_delete(&s->node);
55}
56
57/* queues a server into the correct tree depending on ->next */
58void put_srv(struct srv *s) {
59 if (s->w <= 0 ||
60 s->next >= 2*sw || /* delay everything which does not fit into the window */
61 s->next >= sw+nsw) { /* and everything which does not fit into the theorical new window */
62 /* put into next tree */
63 s->next -= sw; // readjust next in case we could finally take this back to current.
64 queue_by_weight_0(next_tree, s);
65 } else { /* FIXME: sw can be smaller than s->rem! */
66 //s->node.key = sw * s->next + s->rem - s->w;
67 //s->node.key = 65536 * s->next + s->rem - s->w;
68 //s->node.key = 256*s->next + (s->rem / 256);
69 //s->node.key = 256*s->next + ((s->rem - s->w) / 256);
70 s->node.key = 16*s->next + ((s->rem - s->w) / 4096);
71 eb32i_insert(&tree_0, &s->node);
72 s->tree = &tree_0;
73 }
74}
75
76/* prepares a server when extracting it from the init tree */
77static inline void get_srv_init(struct srv *s) {
78 s->next = s->rem = 0;
79}
80
81/* prepares a server when extracting it from the next tree */
82static inline void get_srv_next(struct srv *s) {
83 s->next += sw;
84}
85
86/* prepares a server when extracting it from the next tree */
87static inline void get_srv_down(struct srv *s) {
88 s->next = p;
89}
90
91/* prepares a server when extracting it from its tree */
92void get_srv(struct srv *s) {
93 if (s->tree == init_tree) {
94 get_srv_init(s);
95 }
96 else if (s->tree == next_tree) {
97 get_srv_next(s);
98 }
99 else if (s->tree == NULL) {
100 get_srv_down(s);
101 }
102}
103
104
105/* return next server from the current tree, or a server from the init tree
106 * if appropriate. If both trees are empty, return NULL.
107 */
108struct srv *get_next_server() {
109 struct eb32_node *node;
110 struct srv *s;
111
112 node = eb32_first(&tree_0);
113 s = eb32_entry(node, struct srv, node);
114
115 if (!node || s->next > p) {
116 /* either we have no server left, or we have a hole */
117 struct eb32_node *node2;
118 node2 = eb32_first(init_tree);
119 if (node2) {
120 node = node2;
121 s = eb32_entry(node, struct srv, node);
122 get_srv_init(s);
123 if (s->w == 0)
124 node = NULL;
125 }
126 }
127 if (node)
128 return s;
129 else
130 return NULL;
131}
132
133void update_position(struct srv *s) {
134 //if (s->tree == init_tree) {
135 if (!s->next) {
136 // first time ever for this server
137 s->last = p;
138 s->next = p + nsw / s->w;
139 s->rem += nsw % s->w;
140
141 if (s->rem >= s->w) {
142 s->rem -= s->w;
143 s->next++;
144 }
145 } else {
146 s->last = s->next; // or p ?
147 //s->next += sw / s->w;
148 //s->rem += sw % s->w;
149 s->next += nsw / s->w;
150 s->rem += nsw % s->w;
151
152 if (s->rem >= s->w) {
153 s->rem -= s->w;
154 s->next++;
155 }
156 }
157}
158
159
160/* switches trees init_tree and next_tree. init_tree should be empty when
161 * this happens, and next_tree filled with servers sorted by weights.
162 */
163void switch_trees() {
164 struct eb_root *swap;
165 swap = init_tree;
166 init_tree = next_tree;
167 next_tree = swap;
168 sw = nsw;
169 p = sw;
170}
171
172main(int argc, char **argv) {
173 int conns;
174 int i;
175
176 struct srv *s;
177
178 argc--; argv++;
179 nsrv = argc;
180
181 if (!nsrv)
182 exit(1);
183
184 srv = (struct srv *)calloc(nsrv, sizeof(struct srv));
185
186 sw = 0;
187 for (i = 0; i < nsrv; i++) {
188 s = &srv[i];
189 s->num = i;
190 s->w = atol(argv[i]);
191 sw += s->w;
192 }
193
194 nsw = sw;
195
196 init_tree = &tree_1;
197 next_tree = &tree_2;
198
199 /* and insert all the servers in the PREV tree */
200 /* note that it is required to insert them according to
201 * the reverse order of their weights.
202 */
203 printf("---------------:");
204 for (i = 0; i < nsrv; i++) {
205 s = &srv[i];
206 queue_by_weight_0(init_tree, s);
207 printf("%2d", s->w);
208 }
209 printf("\n");
210
211 p = sw; // time base of current tree
212 conns = 0;
213 while (1) {
214 struct eb32_node *node;
215
216 printf("%08d|%06d: ", conns, p);
217
218 /* if we have en empty tree, let's first try to collect weights
219 * which might have changed.
220 */
221 if (!sw) {
222 if (nsw) {
223 sw = nsw;
224 p = sw;
225 /* do not switch trees, otherwise new servers (from init)
226 * would end up in next.
227 */
228 //switch_trees();
229 //printf("bla\n");
230 }
231 else
232 goto next_iteration;
233 }
234
235 s = get_next_server();
236 if (!s) {
237 printf("----------- switch (empty) -- sw=%d -> %d ---------\n", sw, nsw);
238 switch_trees();
239 s = get_next_server();
240 printf("%08d|%06d: ", conns, p);
241
242 if (!s)
243 goto next_iteration;
244 }
245 else if (s->next >= 2*sw) {
246 printf("ARGGGGG! s[%d].next=%d, max=%d\n", s->num, s->next, 2*sw-1);
247 }
248
249 /* now we have THE server we want to put at this position */
250 for (i = 0; i < s->num; i++) {
251 if (srv[i].w > 0)
252 printf(". ");
253 else
254 printf("_ ");
255 }
256 printf("# ");
257 for (i = s->num + 1; i < nsrv; i++) {
258 if (srv[i].w > 0)
259 printf(". ");
260 else
261 printf("_ ");
262 }
263 printf(" : ");
264
265 printf("s=%02d v=%04d w=%03d n=%03d r=%03d ",
266 s->num, s->node.key, s->w, s->next, s->rem);
267
268 update_position(s);
269 printf(" | next=%03d, rem=%03d ", s->next, s->rem);
270
271 if (s->next >= sw * 2) {
272 dequeue_srv(s);
273 //queue_by_weight(next_tree, s);
274 put_srv(s);
275 printf(" => next (w=%d, n=%d) ", s->w, s->next);
276 }
277 else {
278 printf(" => curr ");
279
280 //s->node.key = s->next;
281 /* we want to ensure that in case of conflicts, servers with
282 * the highest weights will get served first. Also, we still
283 * have the remainder to see where the entry expected to be
284 * inserted.
285 */
286 //s->node.key = 256 * s->next + 255 - s->w;
287 //s->node.key = sw * s->next + sw / s->w;
288 //s->node.key = sw * s->next + s->rem; /// seems best (check with filltab15) !
289
290 //s->node.key = (2 * sw * s->next) + s->rem + sw / s->w;
291
292 /* FIXME: must be optimized */
293 dequeue_srv(s);
294 put_srv(s);
295 //eb32i_insert(&tree_0, &s->node);
296 //s->tree = &tree_0;
297 }
298
299 next_iteration:
300 p++;
301 conns++;
302 if (/*conns == 30*/ /**/random()%1000 == 0/**/) {
303 int w = /*20*//**/random()%10000/**/;
304 int num = /*1*//**/random()%nsrv/**/;
305 struct srv *s = &srv[num];
306
307 nsw = nsw - s->w + w;
308 //sw=nsw;
309
310 if (s->tree == init_tree) {
311 printf(" -- chgwght1(%d): %d->%d, n=%d --", s->num, s->w, w, s->next);
312 printf("(init)");
313 s->w = w;
314 dequeue_srv(s);
315 queue_by_weight_0(s->tree, s);
316 }
317 else if (s->tree == NULL) {
318 printf(" -- chgwght2(%d): %d->%d, n=%d --", s->num, s->w, w, s->next);
319 printf("(down)");
320 s->w = w;
321 dequeue_srv(s);
322 //queue_by_weight_0(init_tree, s);
323 get_srv(s);
324 s->next = p + (nsw + sw - p) / s->w;
325 put_srv(s);
326 }
327 else {
328 int oldnext;
329
330 /* the server is either active or in the next queue */
331 get_srv(s);
332 printf(" -- chgwght3(%d): %d->%d, n=%d, sw=%d, nsw=%d --", s->num, s->w, w, s->next, sw, nsw);
333
334 oldnext = s->next;
335 s->w = w;
336
337 /* we must measure how far we are from the end of the current window
338 * and try to fit their as many entries as should theorically be.
339 */
340
341 //s->w = s->w * (2*sw - p) / sw;
342 if (s->w > 0) {
343 int step = (nsw /*+ sw - p*/) / s->w;
344 s->next = s->last + step;
345 s->rem = 0;
346 if (s->next > oldnext) {
347 s->next = oldnext;
348 printf(" aaaaaaa ");
349 }
350
351 if (s->next < p + 2) {
352 s->next = p + step;
353 printf(" bbbbbb ");
354 }
355 } else {
356 printf(" push -- ");
357 /* push it into the next tree */
358 s->w = 0;
359 s->next = p + sw;
360 }
361
362
363 dequeue_srv(s);
364 printf(" n=%d", s->next);
365 put_srv(s);
366 }
367 }
368
369 printf("\n");
370
371 if (0 && conns % 50000 == 0) {
372 printf("-------- %-5d : changing all weights ----\n", conns);
373
374 for (i = 0; i < nsrv; i++) {
375 int w = i + 1;
376 s = &srv[i];
377 nsw = nsw - s->w + w;
378 s->w = w;
379 dequeue_srv(s);
380 queue_by_weight_0(next_tree, s); // or init_tree ?
381 }
382 }
383
384 }
385}
386