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 20
VPFX(lookup)(VRT_CTX, VCL_STRING name)
50
{
51 20
        if ((ctx->method & VCL_MET_TASK_H) == 0) {
52 10
                VRT_fail(ctx,
53
                    "lookup() may only be called from vcl_init / vcl_fini");
54 10
                return (NULL);
55
        }
56
57 10
        return (VRT_LookupDirector(ctx, name));
58 20
}
59
60
static void
61 300
vdir_expand(struct vdir *vd, unsigned n)
62
{
63 300
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
64
65 300
        vd->backend = realloc(vd->backend, n * sizeof *vd->backend);
66 300
        AN(vd->backend);
67 300
        vd->weight = realloc(vd->weight, n * sizeof *vd->weight);
68 300
        AN(vd->weight);
69 300
        if (n > vd->healthy->nbits)
70 0
                vbit_expand(vd->healthy, n);
71 300
        AN(vd->healthy);
72 300
        vd->l_backend = n;
73 300
}
74
75
void
76 330
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 330
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
82 330
        CHECK_OBJ_NOTNULL(m, VDI_METHODS_MAGIC);
83 330
        AN(vcl_name);
84 330
        AN(vdp);
85 330
        AZ(*vdp);
86 330
        ALLOC_OBJ(vd, VDIR_MAGIC);
87 330
        AN(vd);
88 330
        *vdp = vd;
89 330
        AZ(pthread_rwlock_init(&vd->mtx, NULL));
90 330
        vd->dir = VRT_AddDirector(ctx, m, priv, "%s", vcl_name);
91 330
        vd->healthy = vbit_new(8);
92 330
        AN(vd->healthy);
93 330
}
94
95
void
96 60
vdir_delete(struct vdir **vdp)
97
{
98
        struct vdir *vd;
99
        unsigned u;
100
101 60
        TAKE_OBJ_NOTNULL(vd, vdp, VDIR_MAGIC);
102
103 60
        AZ(vd->dir);
104 120
        for (u = 0; u < vd->n_backend; u++)
105 60
                VRT_Assign_Backend(&vd->backend[u], NULL);
106 60
        free(vd->backend);
107 60
        free(vd->weight);
108 60
        AZ(pthread_rwlock_destroy(&vd->mtx));
109 60
        vbit_destroy(vd->healthy);
110 60
        FREE_OBJ(vd);
111 60
}
112
113
void
114 1560
vdir_rdlock(struct vdir *vd)
115
{
116 1560
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
117 1560
        AZ(pthread_rwlock_rdlock(&vd->mtx));
118 1560
}
119
120
void
121 1590
vdir_wrlock(struct vdir *vd)
122
{
123 1590
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
124 1590
        AZ(pthread_rwlock_wrlock(&vd->mtx));
125 1590
}
126
127
void
128 3150
vdir_unlock(struct vdir *vd)
129
{
130 3150
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
131 3150
        AZ(pthread_rwlock_unlock(&vd->mtx));
132 3150
}
133
134
135
void
136 800
vdir_add_backend(VRT_CTX, struct vdir *vd, VCL_BACKEND be, double weight)
137
{
138
        unsigned u;
139
140 800
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
141 800
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
142 800
        if (be == NULL) {
143 60
                VRT_fail(ctx, "%s: None backend cannot be added",
144 30
                    VRT_BACKEND_string(vd->dir));
145 30
                return;
146
        }
147 770
        AN(be);
148 770
        vdir_wrlock(vd);
149 770
        if (vd->n_backend >= vd->l_backend)
150 300
                vdir_expand(vd, vd->l_backend + 16);
151 770
        assert(vd->n_backend < vd->l_backend);
152 770
        u = vd->n_backend++;
153 770
        vd->backend[u] = NULL;
154 770
        VRT_Assign_Backend(&vd->backend[u], be);
155 770
        vd->weight[u] = weight;
156 770
        vdir_unlock(vd);
157 800
}
158
159
void
160 120
vdir_remove_backend(VRT_CTX, struct vdir *vd, VCL_BACKEND be, unsigned *cur)
161
{
162
        unsigned u, n;
163
164 120
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
165 120
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
166 120
        if (be == NULL) {
167 0
                VRT_fail(ctx, "%s: None backend cannot be removed",
168 0
                    VRT_BACKEND_string(vd->dir));
169 0
                return;
170
        }
171 120
        CHECK_OBJ(be, DIRECTOR_MAGIC);
172 120
        vdir_wrlock(vd);
173 170
        for (u = 0; u < vd->n_backend; u++)
174 170
                if (vd->backend[u] == be)
175 120
                        break;
176 120
        if (u == vd->n_backend) {
177 0
                vdir_unlock(vd);
178 0
                return;
179
        }
180 120
        VRT_Assign_Backend(&vd->backend[u], NULL);
181 120
        n = (vd->n_backend - u) - 1;
182 120
        memmove(&vd->backend[u], &vd->backend[u+1], n * sizeof(vd->backend[0]));
183 120
        memmove(&vd->weight[u], &vd->weight[u+1], n * sizeof(vd->weight[0]));
184 120
        vd->n_backend--;
185
186 120
        if (cur) {
187 40
                assert(*cur <= vd->n_backend);
188 40
                if (u < *cur)
189 10
                        (*cur)--;
190 30
                else if (*cur == vd->n_backend)
191 10
                        *cur = 0;
192 40
        }
193 120
        vdir_unlock(vd);
194 120
}
195
196
VCL_BOOL
197 490
vdir_any_healthy(VRT_CTX, struct vdir *vd, VCL_TIME *changed)
198
{
199 490
        unsigned retval = 0;
200
        VCL_BACKEND be;
201
        unsigned u;
202
        vtim_real c;
203
204 490
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
205 490
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
206 490
        vdir_rdlock(vd);
207 490
        if (changed != NULL)
208 100
                *changed = 0;
209 700
        for (u = 0; u < vd->n_backend; u++) {
210 630
                be = vd->backend[u];
211 630
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
212 630
                retval = VRT_Healthy(ctx, be, &c);
213 630
                if (changed != NULL && c > *changed)
214 130
                        *changed = c;
215 630
                if (retval)
216 420
                        break;
217 210
        }
218 490
        vdir_unlock(vd);
219 490
        return (retval);
220
}
221
222
void
223 510
vdir_list(VRT_CTX, struct vdir *vd, struct vsb *vsb, int pflag, int jflag,
224
    int weight)
225
{
226
        VCL_BACKEND be;
227
        VCL_BOOL h;
228
        unsigned u, nh;
229
        double w;
230
231 510
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
232 510
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
233
234 510
        if (pflag) {
235 80
                if (jflag) {
236 40
                        VSB_cat(vsb, "{\n");
237 40
                        VSB_indent(vsb, 2);
238 40
                        if (weight)
239 20
                                VSB_printf(vsb, "\"total_weight\": %f,\n",
240 10
                                    vd->total_weight);
241 40
                        VSB_cat(vsb, "\"backends\": {\n");
242 40
                        VSB_indent(vsb, 2);
243 40
                } else {
244 40
                        VSB_cat(vsb, "\n\n\tBackend\tWeight\tHealth\n");
245
                }
246 80
        }
247
248 510
        vdir_rdlock(vd);
249 510
        vdir_update_health(ctx, vd);
250 730
        for (u = 0; pflag && u < vd->n_backend; u++) {
251 220
                be = vd->backend[u];
252 220
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
253
254 220
                h = vbit_test(vd->healthy, u);
255
256 220
                w = h ? vd->weight[u] : 0.0;
257
258 220
                if (jflag) {
259 110
                        if (u)
260 70
                                VSB_cat(vsb, ",\n");
261 110
                        VSB_printf(vsb, "\"%s\": {\n", be->vcl_name);
262 110
                        VSB_indent(vsb, 2);
263
264 110
                        if (weight)
265 40
                                VSB_printf(vsb, "\"weight\": %f,\n", w);
266
267 110
                        if (h)
268 80
                                VSB_cat(vsb, "\"health\": \"healthy\"\n");
269
                        else
270 30
                                VSB_cat(vsb, "\"health\": \"sick\"\n");
271
272 110
                        VSB_indent(vsb, -2);
273 110
                        VSB_cat(vsb, "}");
274 110
                } else {
275 110
                        VSB_cat(vsb, "\t");
276 110
                        VSB_cat(vsb, be->vcl_name);
277 110
                        if (weight)
278 80
                                VSB_printf(vsb, "\t%6.2f%%\t",
279 40
                                    100 * w / vd->total_weight);
280
                        else
281 70
                                VSB_cat(vsb, "\t-\t");
282 110
                        VSB_cat(vsb, h ? "healthy" : "sick");
283 110
                        VSB_cat(vsb, "\n");
284
                }
285 220
        }
286 510
        nh = vd->n_healthy;
287 510
        u = vd->n_backend;
288 510
        vdir_unlock(vd);
289
290 510
        if (jflag && (pflag)) {
291 40
                VSB_cat(vsb, "\n");
292 40
                VSB_indent(vsb, -2);
293 40
                VSB_cat(vsb, "}\n");
294 40
                VSB_indent(vsb, -2);
295 40
                VSB_cat(vsb, "},\n");
296 40
        }
297
298 510
        if (pflag)
299 80
                return;
300
301 430
        if (jflag)
302 200
                VSB_printf(vsb, "[%u, %u, \"%s\"]", nh, u,
303 100
                    nh ? "healthy" : "sick");
304
        else
305 330
                VSB_printf(vsb, "%u/%u\t%s", nh, u, nh ? "healthy" : "sick");
306 510
}
307
308
/*
309
 * iterate backends and update
310
 * - healthy bitmap
311
 * - number of healthy backends
312
 * - total_weight
313
 * - last change time of the VCL_BACKEND
314
 *
315
 * must be called under the vdir lock (read or write).
316
 *
317
 * A write lock is required if consistency between the individual attributes is
318
 * a must, e.g. when total_weight is required to be the exact sum of the weights
319
 *
320
 * The read lock is safe because add_backend expands the healthy bitmap and all
321
 * other members are atomic and may be used if consistency is not required.
322
 */
323
void
324 1110
vdir_update_health(VRT_CTX, struct vdir *vd)
325
{
326 1110
        VCL_TIME c, changed = 0;
327
        VCL_BOOL h;
328
        VCL_BACKEND be;
329 1110
        unsigned u, nh = 0;
330 1110
        double tw = 0.0;
331
        struct vbitmap *healthy;
332
333 1110
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
334 1110
        healthy = vd->healthy;
335 3690
        for (u = 0; u < vd->n_backend; u++) {
336 2580
                be = vd->backend[u];
337 2580
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
338 2580
                c = 0;
339 2580
                h = VRT_Healthy(ctx, vd->backend[u], &c);
340 2580
                if (h) {
341 1990
                        nh++;
342 1990
                        tw += vd->weight[u];
343 1990
                }
344 2580
                if (c > changed)
345 2480
                        changed = c;
346 2580
                if (h != vbit_test(healthy, u)) {
347 1300
                        if (h)
348 1280
                                vbit_set(healthy, u);
349
                        else
350 20
                                vbit_clr(healthy, u);
351 1300
                }
352 2580
        }
353 1110
        VRT_SetChanged(vd->dir, changed);
354 1110
        vd->total_weight = tw;
355 1110
        vd->n_healthy = nh;
356 1110
}
357
358
static unsigned
359 460
vdir_pick_by_weight(const struct vdir *vd, double w)
360
{
361 460
        const struct vbitmap *healthy = vd->healthy;
362 460
        double a = 0.0;
363
        unsigned u;
364
365 460
        AN(healthy);
366 702
        for (u = 0; u < vd->n_backend; u++) {
367 702
                if (! vbit_test(healthy, u))
368 40
                        continue;
369 662
                a += vd->weight[u];
370 662
                if (w < a)
371 460
                        return (u);
372 202
        }
373 0
        WRONG("");
374
}
375
376
VCL_BACKEND
377 470
vdir_pick_be(VRT_CTX, struct vdir *vd, double w)
378
{
379
        unsigned u;
380 470
        VCL_BACKEND be = NULL;
381
382 470
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
383 470
        CHECK_OBJ_NOTNULL(vd, VDIR_MAGIC);
384 470
        vdir_wrlock(vd);
385 470
        vdir_update_health(ctx, vd);
386 470
        if (vd->total_weight > 0.0) {
387 460
                u = vdir_pick_by_weight(vd, w * vd->total_weight);
388 460
                assert(u < vd->n_backend);
389 460
                be = vd->backend[u];
390 460
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
391 460
        }
392 470
        vdir_unlock(vd);
393 470
        return (be);
394
}