varnish-cache/vmod/vmod_directors.c
1
/*-
2
 * Copyright (c) 2013-2015 Varnish Software AS
3
 * Copyright 2019 UPLEX - Nils Goroll Systemoptimierung
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@FreeBSD.org>
7
 * Author: Nils Goroll <nils.goroll@uplex.de>
8
 *
9
 * SPDX-License-Identifier: BSD-2-Clause
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include "config.h"
34
35
#include <stdlib.h>
36
#include <stdio.h>
37
38
#include "cache/cache.h"
39
40
#include "vbm.h"
41
#include "vcl.h"
42
#include "vsb.h"
43
44
#include "vcc_directors_if.h"
45
46
#include "vmod_directors.h"
47
48
VCL_BACKEND
49 40
VPFX(lookup)(VRT_CTX, VCL_STRING name)
50
{
51 40
        if ((ctx->method & VCL_MET_TASK_H) == 0) {
52 20
                VRT_fail(ctx,
53
                    "lookup() may only be called from vcl_init / vcl_fini");
54 20
                return (NULL);
55
        }
56
57 20
        return (VRT_LookupDirector(ctx, name));
58 40
}
59
60
static void
61 620
vdir_expand(struct vdir *vd, unsigned n)
62
{
63 620
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
64
65 620
        vd->backend = realloc(vd->backend, n * sizeof *vd->backend);
66 620
        AN(vd->backend);
67 620
        vd->weight = realloc(vd->weight, n * sizeof *vd->weight);
68 620
        AN(vd->weight);
69 620
        if (n > vd->healthy->nbits)
70 0
                vbit_expand(vd->healthy, n);
71 620
        AN(vd->healthy);
72 620
        vd->l_backend = n;
73 620
}
74
75
void
76 680
vdir_new(VRT_CTX, struct vdir **vdp, const char *vcl_name,
77
    const struct vdi_methods *m, void *priv)
78
{
79
        struct vdir *vd;
80
81 680
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
82 680
        CHECK_OBJ_NOTNULL(m, VDI_METHODS_MAGIC);
83 680
        AN(vcl_name);
84 680
        AN(vdp);
85 680
        AZ(*vdp);
86 680
        ALLOC_OBJ(vd, VDIR_MAGIC);
87 680
        AN(vd);
88 680
        *vdp = vd;
89 680
        AZ(pthread_rwlock_init(&vd->mtx, NULL));
90 680
        vd->dir = VRT_AddDirector(ctx, m, priv, "%s", vcl_name);
91 680
        vd->healthy = vbit_new(8);
92 680
        AN(vd->healthy);
93 680
}
94
95
void
96 120
vdir_delete(struct vdir **vdp)
97
{
98
        struct vdir *vd;
99
100 120
        TAKE_OBJ_NOTNULL(vd, vdp, VDIR_MAGIC);
101
102 120
        AZ(vd->dir);
103 120
        free(vd->backend);
104 120
        free(vd->weight);
105 120
        AZ(pthread_rwlock_destroy(&vd->mtx));
106 120
        vbit_destroy(vd->healthy);
107 120
        FREE_OBJ(vd);
108 120
}
109
110
void
111 3140
vdir_rdlock(struct vdir *vd)
112
{
113 3140
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
114 3140
        AZ(pthread_rwlock_rdlock(&vd->mtx));
115 3140
}
116
117
void
118 3240
vdir_wrlock(struct vdir *vd)
119
{
120 3240
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
121 3240
        AZ(pthread_rwlock_wrlock(&vd->mtx));
122 3240
}
123
124
void
125 6380
vdir_unlock(struct vdir *vd)
126
{
127 6380
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
128 6380
        AZ(pthread_rwlock_unlock(&vd->mtx));
129 6380
}
130
131
132
void
133 1660
vdir_add_backend(VRT_CTX, struct vdir *vd, VCL_BACKEND be, double weight)
134
{
135
        unsigned u;
136
137 1660
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
138 1660
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
139 1660
        if (be == NULL) {
140 120
                VRT_fail(ctx, "%s: None backend cannot be added",
141 60
                    VRT_BACKEND_string(vd->dir));
142 60
                return;
143
        }
144 1600
        AN(be);
145 1600
        vdir_wrlock(vd);
146 1600
        if (vd->n_backend >= vd->l_backend)
147 620
                vdir_expand(vd, vd->l_backend + 16);
148 1600
        assert(vd->n_backend < vd->l_backend);
149 1600
        u = vd->n_backend++;
150 1600
        vd->backend[u] = be;
151 1600
        vd->weight[u] = weight;
152 1600
        vdir_unlock(vd);
153 1660
}
154
155
void
156 240
vdir_remove_backend(VRT_CTX, struct vdir *vd, VCL_BACKEND be, unsigned *cur)
157
{
158
        unsigned u, n;
159
160 240
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
161 240
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
162 240
        if (be == NULL) {
163 0
                VRT_fail(ctx, "%s: None backend cannot be removed",
164 0
                    VRT_BACKEND_string(vd->dir));
165 0
                return;
166
        }
167 240
        CHECK_OBJ(be, DIRECTOR_MAGIC);
168 240
        vdir_wrlock(vd);
169 340
        for (u = 0; u < vd->n_backend; u++)
170 340
                if (vd->backend[u] == be)
171 240
                        break;
172 240
        if (u == vd->n_backend) {
173 0
                vdir_unlock(vd);
174 0
                return;
175
        }
176 240
        n = (vd->n_backend - u) - 1;
177 240
        memmove(&vd->backend[u], &vd->backend[u+1], n * sizeof(vd->backend[0]));
178 240
        memmove(&vd->weight[u], &vd->weight[u+1], n * sizeof(vd->weight[0]));
179 240
        vd->n_backend--;
180
181 240
        if (cur) {
182 80
                assert(*cur <= vd->n_backend);
183 80
                if (u < *cur)
184 20
                        (*cur)--;
185 60
                else if (*cur == vd->n_backend)
186 20
                        *cur = 0;
187 80
        }
188 240
        vdir_unlock(vd);
189 240
}
190
191
VCL_BOOL
192 980
vdir_any_healthy(VRT_CTX, struct vdir *vd, VCL_TIME *changed)
193
{
194 980
        unsigned retval = 0;
195
        VCL_BACKEND be;
196
        unsigned u;
197
        vtim_real c;
198
199 980
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
200 980
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
201 980
        vdir_rdlock(vd);
202 980
        if (changed != NULL)
203 200
                *changed = 0;
204 1400
        for (u = 0; u < vd->n_backend; u++) {
205 1260
                be = vd->backend[u];
206 1260
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
207 1260
                retval = VRT_Healthy(ctx, be, &c);
208 1260
                if (changed != NULL && c > *changed)
209 260
                        *changed = c;
210 1260
                if (retval)
211 840
                        break;
212 420
        }
213 980
        vdir_unlock(vd);
214 980
        return (retval);
215
}
216
217
void
218 1040
vdir_list(VRT_CTX, struct vdir *vd, struct vsb *vsb, int pflag, int jflag,
219
    int weight)
220
{
221
        VCL_BACKEND be;
222
        VCL_BOOL h;
223
        unsigned u, nh;
224
        double w;
225
226 1040
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
227 1040
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
228
229 1040
        if (pflag) {
230 160
                if (jflag) {
231 80
                        VSB_cat(vsb, "{\n");
232 80
                        VSB_indent(vsb, 2);
233 80
                        if (weight)
234 40
                                VSB_printf(vsb, "\"total_weight\": %f,\n",
235 20
                                    vd->total_weight);
236 80
                        VSB_cat(vsb, "\"backends\": {\n");
237 80
                        VSB_indent(vsb, 2);
238 80
                } else {
239 80
                        VSB_cat(vsb, "\n\n\tBackend\tWeight\tHealth\n");
240
                }
241 160
        }
242
243 1040
        vdir_rdlock(vd);
244 1040
        vdir_update_health(ctx, vd);
245 1480
        for (u = 0; pflag && u < vd->n_backend; u++) {
246 440
                be = vd->backend[u];
247 440
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
248
249 440
                h = vbit_test(vd->healthy, u);
250
251 440
                w = h ? vd->weight[u] : 0.0;
252
253 440
                if (jflag) {
254 220
                        if (u)
255 140
                                VSB_cat(vsb, ",\n");
256 220
                        VSB_printf(vsb, "\"%s\": {\n", be->vcl_name);
257 220
                        VSB_indent(vsb, 2);
258
259 220
                        if (weight)
260 80
                                VSB_printf(vsb, "\"weight\": %f,\n", w);
261
262 220
                        if (h)
263 160
                                VSB_cat(vsb, "\"health\": \"healthy\"\n");
264
                        else
265 60
                                VSB_cat(vsb, "\"health\": \"sick\"\n");
266
267 220
                        VSB_indent(vsb, -2);
268 220
                        VSB_cat(vsb, "}");
269 220
                } else {
270 220
                        VSB_cat(vsb, "\t");
271 220
                        VSB_cat(vsb, be->vcl_name);
272 220
                        if (weight)
273 160
                                VSB_printf(vsb, "\t%6.2f%%\t",
274 80
                                    100 * w / vd->total_weight);
275
                        else
276 140
                                VSB_cat(vsb, "\t-\t");
277 220
                        VSB_cat(vsb, h ? "healthy" : "sick");
278 220
                        VSB_cat(vsb, "\n");
279
                }
280 440
        }
281 1040
        nh = vd->n_healthy;
282 1040
        u = vd->n_backend;
283 1040
        vdir_unlock(vd);
284
285 1040
        if (jflag && (pflag)) {
286 80
                VSB_cat(vsb, "\n");
287 80
                VSB_indent(vsb, -2);
288 80
                VSB_cat(vsb, "}\n");
289 80
                VSB_indent(vsb, -2);
290 80
                VSB_cat(vsb, "},\n");
291 80
        }
292
293 1040
        if (pflag)
294 160
                return;
295
296 880
        if (jflag)
297 400
                VSB_printf(vsb, "[%u, %u, \"%s\"]", nh, u,
298 200
                    nh ? "healthy" : "sick");
299
        else
300 680
                VSB_printf(vsb, "%u/%u\t%s", nh, u, nh ? "healthy" : "sick");
301 1040
}
302
303
/*
304
 * iterate backends and update
305
 * - healthy bitmap
306
 * - number of healthy backends
307
 * - total_weight
308
 * - last change time of the VCL_BACKEND
309
 *
310
 * must be called under the vdir lock (read or write).
311
 *
312
 * A write lock is required if consistency between the individual attributes is
313
 * a must, e.g. when total_weight is required to be the exact sum of the weights
314
 *
315
 * The read lock is safe because add_backend expands the healthy bitmap and all
316
 * other members are atomic and may be used if consistency is not required.
317
 */
318
void
319 2240
vdir_update_health(VRT_CTX, struct vdir *vd)
320
{
321 2240
        VCL_TIME c, changed = 0;
322
        VCL_BOOL h;
323
        VCL_BACKEND be;
324 2240
        unsigned u, nh = 0;
325 2240
        double tw = 0.0;
326
        struct vbitmap *healthy;
327
328 2240
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
329 2240
        healthy = vd->healthy;
330 7460
        for (u = 0; u < vd->n_backend; u++) {
331 5220
                be = vd->backend[u];
332 5220
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
333 5220
                c = 0;
334 5220
                h = VRT_Healthy(ctx, vd->backend[u], &c);
335 5220
                if (h) {
336 4040
                        nh++;
337 4040
                        tw += vd->weight[u];
338 4040
                }
339 5220
                if (c > changed)
340 5020
                        changed = c;
341 5220
                if (h != vbit_test(healthy, u)) {
342 2660
                        if (h)
343 2620
                                vbit_set(healthy, u);
344
                        else
345 40
                                vbit_clr(healthy, u);
346 2660
                }
347 5220
        }
348 2240
        VRT_SetChanged(vd->dir, changed);
349 2240
        vd->total_weight = tw;
350 2240
        vd->n_healthy = nh;
351 2240
}
352
353
static unsigned
354 920
vdir_pick_by_weight(const struct vdir *vd, double w)
355
{
356 920
        const struct vbitmap *healthy = vd->healthy;
357 920
        double a = 0.0;
358
        unsigned u;
359
360 920
        AN(healthy);
361 1396
        for (u = 0; u < vd->n_backend; u++) {
362 1396
                if (! vbit_test(healthy, u))
363 80
                        continue;
364 1316
                a += vd->weight[u];
365 1316
                if (w < a)
366 920
                        return (u);
367 396
        }
368 0
        WRONG("");
369
}
370
371
VCL_BACKEND
372 940
vdir_pick_be(VRT_CTX, struct vdir *vd, double w)
373
{
374
        unsigned u;
375 940
        VCL_BACKEND be = NULL;
376
377 940
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
378 940
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
379 940
        vdir_wrlock(vd);
380 940
        vdir_update_health(ctx, vd);
381 940
        if (vd->total_weight > 0.0) {
382 920
                u = vdir_pick_by_weight(vd, w * vd->total_weight);
383 920
                assert(u < vd->n_backend);
384 920
                be = vd->backend[u];
385 920
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
386 920
        }
387 940
        vdir_unlock(vd);
388 940
        return (be);
389
}