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