varnish-cache/vmod/vmod_directors_shard.c
1
/*-
2
 * Copyright 2009-2018 UPLEX - Nils Goroll Systemoptimierung
3
 * All rights reserved.
4
 *
5
 * Authors: Julian Wiesener <jw@uplex.de>
6
 *          Nils Goroll <slink@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 <string.h>
36
37
#include "cache/cache.h"
38
#include "vcl.h"
39
40
#include "vend.h"
41
42
#include "vcc_directors_if.h"
43
#include "vmod_directors_shard_dir.h"
44
#include "vmod_directors_shard_cfg.h"
45
#include "vsb.h"
46
47
/* -------------------------------------------------------------------------
48
 *  shard director: LAZY mode (vdi resolve function), parameter objects
49
 *
50
 *  By associating a parameter object with a shard director, we enable LAZY
51
 *  lookups as with the other directors. Parameter objects are defined with VCL
52
 *  scope (normal vmod objects), but can be overridden per backend request using
53
 *  a task priv.
54
 *
55
 *  We use the same concept to carry shard.backend() parameters to vdi resolve
56
 *  for LAZY mode: They get saved in a per-director task scope parameter object.
57
 *
58
 *  Each object points to another object providing defaults for values which are
59
 *  not defined.
60
 *
61
 *  Actual resolution of the various parameter objects does not happen before
62
 *  they are used, which enables changing them independently (ie, shard
63
 *  .backend() parameters have precedence over an associated parameter object,
64
 *  which by itself can be overridden).
65
 *
66
 *  Overview of parameter objects (pointers are alternatives)
67
 *
68
 *  shard() director        shard_param() object    default praram
69
 *
70
 *               --------------------------------->   vmod static
71
 *    VCL obj   /                                ->
72
 *    .param  -+--------->    VCL obj           /  _
73
 *                            .default  --------   /|
74
 *                                                /
75
 *                               ^               /
76
 *                               |              /
77
 *                                             /
78
 *                            .default        /
79
 *          ------------->    TASK priv      /
80
 *         /                                /
81
 *    .default -----------------------------
82
 *    TASK priv
83
 */
84
85
/* -------------------------------------------------------------------------
86
 * method arguments and set parameters bitmask in vmod_directors_shard_param
87
 */
88
89
#define arg_by          ((uint32_t)1)
90
#define arg_key         ((uint32_t)1 << 1)
91
#define arg_key_blob    ((uint32_t)1 << 2)
92
#define arg_alt         ((uint32_t)1 << 3)
93
#define arg_warmup      ((uint32_t)1 << 4)
94
#define arg_rampup      ((uint32_t)1 << 5)
95
#define arg_healthy     ((uint32_t)1 << 6)
96
#define arg_param       ((uint32_t)1 << 7)
97
#define arg_resolve     ((uint32_t)1 << 8)
98
#define arg_mask_       ((arg_resolve << 1) - 1)
99
/* allowed in shard_param.set */
100
#define arg_mask_set_   (arg_param - 1)
101
/* allowed in shard_param */
102
#define arg_mask_param_ ( arg_mask_set_         \
103
                          & ~arg_key                    \
104
                          & ~arg_key_blob )
105
106
/* -------------------------------------------------------------------------
107
 * shard parameters - declaration & defaults
108
 */
109
enum vmod_directors_shard_param_scope {
110
        _SCOPE_INVALID = 0,
111
        SCOPE_VMOD,
112
        SCOPE_VCL,
113
        SCOPE_TASK,
114
        SCOPE_STACK
115
};
116
117
struct vmod_directors_shard_param;
118
119
#define VMOD_SHARD_SHARD_PARAM_BLOB             0xdf5ca116
120
121
struct vmod_directors_shard_param {
122
        unsigned                                magic;
123
#define VMOD_SHARD_SHARD_PARAM_MAGIC            0xdf5ca117
124
125
        /* internals */
126
        uint32_t                                key;
127
        const char                              *vcl_name;
128
        const struct vmod_directors_shard_param *defaults;
129
        enum vmod_directors_shard_param_scope   scope;
130
131
        /* parameters */
132
        VCL_ENUM                                by;
133
        VCL_ENUM                                healthy;
134
        uint32_t                                mask;
135
        VCL_BOOL                                rampup;
136
        VCL_INT                                 alt;
137
        VCL_REAL                                warmup;
138
};
139
140
static const struct vmod_directors_shard_param shard_param_default = {
141
        .magic          = VMOD_SHARD_SHARD_PARAM_MAGIC,
142
143
        .key            = 0,
144
        .vcl_name       = "builtin defaults",
145
        .defaults       = NULL,
146
        .scope          = SCOPE_VMOD,
147
148
        .mask           = arg_mask_param_,
149
        .rampup = 1,
150
        .alt            = 0,
151
        .warmup         = -1,
152
};
153
154
#define default_by(ptr) (ptr == NULL ? VENUM(HASH) : ptr)
155
#define default_healthy(ptr) (ptr == NULL ? VENUM(CHOSEN) : ptr)
156
157
static struct vmod_directors_shard_param *
158
shard_param_stack(struct vmod_directors_shard_param *p,
159
    const struct vmod_directors_shard_param *pa, const char *who);
160
161
static const struct vmod_directors_shard_param *
162
shard_param_task_r(VRT_CTX, const void *id, const char *who,
163
    const struct vmod_directors_shard_param *pa);
164
165
static struct vmod_directors_shard_param *
166
shard_param_task_l(VRT_CTX, const void *id, const char *who,
167
    const struct vmod_directors_shard_param *pa);
168
169
static const struct vmod_directors_shard_param *
170
shard_param_blob(VCL_BLOB blob);
171
172
static const struct vmod_directors_shard_param *
173
vmod_shard_param_read(VRT_CTX, const void *id, const char *who,
174
    const struct vmod_directors_shard_param *p,
175
    struct vmod_directors_shard_param *pstk);
176
177
// XXX #3329 #3330 revisit - for now, treat pipe like backend
178
#define SHARD_VCL_TASK_REQ (VCL_MET_TASK_C & ~VCL_MET_PIPE)
179
#define SHARD_VCL_TASK_BEREQ (VCL_MET_TASK_B | VCL_MET_PIPE)
180
/* -------------------------------------------------------------------------
181
 * shard vmod interface
182
 */
183
static vdi_healthy_f vmod_shard_healthy;
184
static vdi_resolve_f vmod_shard_resolve;
185
static vdi_list_f vmod_shard_list;
186
187
struct vmod_directors_shard {
188
        unsigned                                magic;
189
#define VMOD_SHARD_SHARD_MAGIC                  0x6e63e1bf
190
        struct sharddir                         *shardd;
191
        VCL_BACKEND                             dir;
192
};
193
194
static void
195 250
shard__assert(void)
196
{
197
        VCL_INT t1;
198
        uint32_t t2a, t2b;
199
200
        /* we put our uint32 key in a VCL_INT container */
201 250
        assert(sizeof(VCL_INT) >= sizeof(uint32_t));
202 250
        t2a = UINT32_MAX;
203 250
        t1 = (VCL_INT)t2a;
204 250
        t2b = (uint32_t)t1;
205 250
        assert(t2a == t2b);
206 250
}
207
208
static void v_matchproto_(vdi_destroy_f)
209 50
vmod_shard_destroy(VCL_BACKEND dir)
210
{
211
        struct sharddir *shardd;
212
213 50
        CAST_OBJ_NOTNULL(shardd, dir->priv, SHARDDIR_MAGIC);
214 50
        sharddir_delete(&shardd);
215 50
}
216
217
static const struct vdi_methods vmod_shard_methods[1] = {{
218
        .magic =        VDI_METHODS_MAGIC,
219
        .type =         "shard",
220
        .resolve =      vmod_shard_resolve,
221
        .healthy =      vmod_shard_healthy,
222
        .destroy =      vmod_shard_destroy,
223
        .list =         vmod_shard_list
224
}};
225
226
227
VCL_VOID v_matchproto_(td_directors_shard__init)
228 250
vmod_shard__init(VRT_CTX, struct vmod_directors_shard **vshardp,
229
    const char *vcl_name)
230
{
231
        struct vmod_directors_shard *vshard;
232
233 250
        shard__assert();
234
235 250
        AN(vshardp);
236 250
        AZ(*vshardp);
237 250
        ALLOC_OBJ(vshard, VMOD_SHARD_SHARD_MAGIC);
238 250
        AN(vshard);
239
240 250
        *vshardp = vshard;
241 250
        sharddir_new(&vshard->shardd, vcl_name, &shard_param_default);
242
243 500
        vshard->dir = VRT_AddDirector(ctx, vmod_shard_methods, vshard->shardd,
244 250
            "%s", vcl_name);
245 250
}
246
247
VCL_VOID v_matchproto_(td_directors_shard__fini)
248 50
vmod_shard__fini(struct vmod_directors_shard **vshardp)
249
{
250
        struct vmod_directors_shard *vshard;
251
252 50
        TAKE_OBJ_NOTNULL(vshard, vshardp, VMOD_SHARD_SHARD_MAGIC);
253 50
        VRT_DelDirector(&vshard->dir);
254 50
        FREE_OBJ(vshard);
255 50
}
256
257
VCL_INT v_matchproto_(td_directors_shard_key)
258 110
vmod_shard_key(VRT_CTX, struct vmod_directors_shard *vshard, VCL_STRANDS s)
259
{
260
261 110
        (void)ctx;
262 110
        (void)vshard;
263
264 110
        return ((VCL_INT)VRT_HashStrands32(s));
265
}
266
267
VCL_VOID v_matchproto_(td_directors_set_warmup)
268 40
vmod_shard_set_warmup(VRT_CTX, struct vmod_directors_shard *vshard,
269
    VCL_REAL probability)
270
{
271 40
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
272 40
        if (probability < 0 || probability >= 1) {
273 20
                shard_notice(ctx->vsl, vshard->shardd->name,
274
                    ".set_warmup(%f) ignored", probability);
275 20
                return;
276
        }
277 20
        shardcfg_set_warmup(vshard->shardd, probability);
278 40
}
279
280
VCL_VOID v_matchproto_(td_directors_set_rampup)
281 20
vmod_shard_set_rampup(VRT_CTX, struct vmod_directors_shard *vshard,
282
    VCL_DURATION duration)
283
{
284 20
        (void)ctx;
285 20
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
286 20
        shardcfg_set_rampup(vshard->shardd, duration);
287 20
}
288
289
VCL_VOID v_matchproto_(td_directors_shard_associate)
290 40
vmod_shard_associate(VRT_CTX,
291
    struct vmod_directors_shard *vshard, VCL_BLOB b)
292
{
293
        const struct vmod_directors_shard_param *ppt;
294 40
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
295
296 40
        if (b == NULL) {
297 10
                sharddir_set_param(vshard->shardd, &shard_param_default);
298 10
                return;
299
        }
300
301 30
        ppt = shard_param_blob(b);
302
303 30
        if (ppt == NULL) {
304 10
                shard_fail(ctx, vshard->shardd->name, "%s",
305
                    "shard .associate param invalid");
306 10
                return;
307
        }
308
309 20
        sharddir_set_param(vshard->shardd, ppt);
310 40
}
311
312
VCL_BOOL v_matchproto_(td_directors_shard_add_backend)
313 1140
vmod_shard_add_backend(VRT_CTX, struct vmod_directors_shard *vshard,
314
    struct VARGS(shard_add_backend) *args)
315
{
316 1140
        VCL_REAL weight = 1;
317
318 1140
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
319
320 1140
        if (args->backend == NULL) {
321 10
                shard_fail(ctx, vshard->shardd->name, "%s",
322
                    "None backend cannot be added");
323 10
                return (0);
324
        }
325
326 1130
        if (args->valid_weight) {
327 30
                if (args->weight >= 1)
328 20
                        weight = args->weight;
329
                else
330 10
                        shard_notice(ctx->vsl, vshard->shardd->name,
331
                            ".add_backend(weight=%f) ignored", args->weight);
332 30
        }
333
334 2260
        return (shardcfg_add_backend(ctx, vshard->shardd, args->backend,
335 1130
            args->valid_ident ? args->ident : NULL,
336 1130
            args->valid_rampup ? args->rampup : nan(""),
337 1130
            weight));
338 1140
}
339
340
VCL_BOOL v_matchproto_(td_directors_shard_remove_backend)
341 300
vmod_shard_remove_backend(VRT_CTX, struct vmod_directors_shard *vshard,
342
    struct VARGS(shard_remove_backend) *args)
343
{
344 300
        VCL_BACKEND be = args->valid_backend ? args->backend : NULL;
345 300
        VCL_STRING ident = args->valid_ident ? args->ident : NULL;
346
347 300
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
348
349 300
        if (be == NULL && ident == NULL) {
350 10
                shard_fail(ctx, vshard->shardd->name, "%s",
351
                    ".remove_backend(): either backend or ident are required");
352 10
                return (0);
353
        }
354
355 290
        return (shardcfg_remove_backend(ctx, vshard->shardd, be, ident));
356 300
}
357
358
VCL_BOOL v_matchproto_(td_directors_shard_clear)
359 150
vmod_shard_clear(VRT_CTX, struct vmod_directors_shard *vshard)
360
{
361 150
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
362 150
        return (shardcfg_clear(ctx, vshard->shardd));
363
}
364
365
VCL_BOOL v_matchproto_(td_directors_shard_reconfigure)
366 420
vmod_shard_reconfigure(VRT_CTX, struct vmod_directors_shard *vshard,
367
    VCL_INT replicas)
368
{
369 420
        return (shardcfg_reconfigure(ctx, vshard->shardd, replicas));
370
}
371
372
static inline uint32_t
373 1290
shard_get_key(VRT_CTX, const struct vmod_directors_shard_param *p)
374
{
375
        struct http *http;
376 1290
        VCL_ENUM by = default_by(p->by);
377
378 1290
        if (by == VENUM(KEY) || by == VENUM(BLOB))
379 1020
                return (p->key);
380 270
        if (by == VENUM(HASH) && ctx->bo != NULL) {
381 70
                CHECK_OBJ(ctx->bo, BUSYOBJ_MAGIC);
382 70
                return (vbe32dec(ctx->bo->digest));
383
        }
384 200
        if (by == VENUM(HASH) || by == VENUM(URL)) {
385 200
                if (ctx->http_req) {
386 140
                        AN(http = ctx->http_req);
387 140
                } else {
388 60
                        AN(ctx->http_bereq);
389 60
                        AN(http = ctx->http_bereq);
390
                }
391 200
                return (VRT_HashStrands32(TOSTRAND(http->hd[HTTP_HDR_URL].b)));
392
        }
393 0
        WRONG("by enum");
394 1290
}
395
396
/*
397
 * merge parameters to resolve all undef values
398
 * key is to be calculated after merging
399
 */
400
static void
401 5760
shard_param_merge(struct vmod_directors_shard_param *to,
402
                  const struct vmod_directors_shard_param *from)
403
{
404 5760
        CHECK_OBJ_NOTNULL(to, VMOD_SHARD_SHARD_PARAM_MAGIC);
405 5760
        assert((to->mask & ~arg_mask_param_) == 0);
406
407 5760
        if (to->mask == arg_mask_param_)
408 0
                return;
409
410 5760
        CHECK_OBJ_NOTNULL(from, VMOD_SHARD_SHARD_PARAM_MAGIC);
411 5760
        assert((from->mask & ~arg_mask_param_) == 0);
412
413 5760
        if ((to->mask & arg_by) == 0 && (from->mask & arg_by) != 0) {
414 2090
                to->by = from->by;
415 2090
                if (from->by == VENUM(KEY) || from->by == VENUM(BLOB))
416 1020
                        to->key = from->key;
417 2090
        }
418
419
#define mrg(to, from, field) do {                                       \
420
                if (((to)->mask & arg_ ## field) == 0 &&                \
421
                    ((from)->mask & arg_ ## field) != 0)                \
422
                        (to)->field = (from)->field;                    \
423
        } while(0)
424
425 5760
        mrg(to, from, healthy);
426 5760
        mrg(to, from, rampup);
427 5760
        mrg(to, from, alt);
428 5760
        mrg(to, from, warmup);
429
#undef mrg
430
431 5760
        to->mask |= from->mask;
432
433 5760
        if (to->mask == arg_mask_param_)
434 2540
                return;
435
436 3220
        AN(from->defaults);
437 3220
        shard_param_merge(to, from->defaults);
438 5760
}
439
440
static uint32_t
441 100
shard_blob_key(VCL_BLOB key_blob)
442
{
443 100
        uint8_t k[4] = { 0 };
444
        const uint8_t *b;
445
        size_t i, ki;
446
447 100
        AN(key_blob);
448 100
        AN(key_blob->blob);
449 100
        assert(key_blob->len > 0);
450
451 100
        if (key_blob->len >= 4)
452 100
                ki = 0;
453
        else
454 0
                ki = 4 - key_blob->len;
455
456 100
        b = key_blob->blob;
457 500
        for (i = 0; ki < 4; i++, ki++)
458 400
                k[ki] = b[i];
459 100
        assert(i <= key_blob->len);
460
461 100
        return (vbe32dec(k));
462
}
463
464
/*
465
 * convert vmod interface valid_* to our bitmask
466
 */
467
468
#define tobit(args, name) ((args)->valid_##name ? arg_##name : 0)
469
470
static uint32_t
471 1070
shard_backendarg_mask_(const struct VARGS(shard_backend) * const a)
472
{
473 3210
        return (tobit(a, by)            |
474 2140
                tobit(a, key)           |
475 2140
                tobit(a, key_blob)      |
476 2140
                tobit(a, alt)           |
477 2140
                tobit(a, warmup)        |
478 2140
                tobit(a, rampup)        |
479 2140
                tobit(a, healthy)       |
480 2140
                tobit(a, param)         |
481 1070
                tobit(a, resolve));
482
}
483
static uint32_t
484 990
shard_param_set_mask(const struct VARGS(shard_param_set) * const a)
485
{
486 2970
        return (tobit(a, by)            |
487 1980
                tobit(a, key)           |
488 1980
                tobit(a, key_blob)      |
489 1980
                tobit(a, alt)           |
490 1980
                tobit(a, warmup)        |
491 1980
                tobit(a, rampup)        |
492 990
                tobit(a, healthy));
493
}
494
#undef tobit
495
496
/*
497
 * check arguments and return in a struct param
498
 */
499
static struct vmod_directors_shard_param *
500 1950
shard_param_args(VRT_CTX,
501
    struct vmod_directors_shard_param *p, const char *func,
502
    uint32_t args, VCL_ENUM by_s, VCL_INT key_int, VCL_BLOB key_blob,
503
    VCL_INT alt, VCL_REAL warmup, VCL_BOOL rampup, VCL_ENUM healthy_s)
504
{
505
506 1950
        CHECK_OBJ_NOTNULL(p, VMOD_SHARD_SHARD_PARAM_MAGIC);
507 1950
        AN(p->vcl_name);
508
509 1950
        assert((args & ~arg_mask_set_) == 0);
510
511 1950
        if (!(args & arg_by))
512 500
                by_s = NULL;
513 1950
        by_s = default_by(by_s);
514
515
        /* by_s / key_int / key_blob */
516 1950
        if (by_s == VENUM(KEY)) {
517 800
                if ((args & arg_key) == 0) {
518 10
                        shard_fail(ctx, p->vcl_name,
519
                            "%s missing key argument with by=%s",
520
                            func, by_s);
521 10
                        return (NULL);
522
                }
523 790
                if (key_int < 0 || key_int > UINT32_MAX) {
524 10
                        shard_fail(ctx, p->vcl_name,
525
                            "%s invalid key argument %jd with by=%s",
526
                            func, (intmax_t)key_int, by_s);
527 10
                        return (NULL);
528
                }
529 780
                assert(key_int >= 0);
530 780
                assert(key_int <= UINT32_MAX);
531 780
                p->key = (uint32_t)key_int;
532 1930
        } else if (by_s == VENUM(BLOB)) {
533 120
                if ((args & arg_key_blob) == 0) {
534 10
                        shard_fail(ctx, p->vcl_name,
535
                            "%s missing key_blob argument with by=%s",
536
                            func, by_s);
537 10
                        return (NULL);
538
                }
539 110
                if (key_blob == NULL || key_blob->len == 0 ||
540 100
                    key_blob->blob == NULL) {
541 10
                        shard_err(ctx->vsl, p->vcl_name,
542
                            "%s by=BLOB but no or empty key_blob - using key 0",
543
                            func);
544 10
                        p->key = 0;
545 10
                } else
546 100
                        p->key = shard_blob_key(key_blob);
547 1140
        } else if (by_s == VENUM(HASH) || by_s == VENUM(URL)) {
548 1030
                if (args & (arg_key|arg_key_blob)) {
549 20
                        shard_fail(ctx, p->vcl_name,
550
                            "%s key and key_blob arguments are "
551
                            "invalid with by=%s", func, by_s);
552 20
                        return (NULL);
553
                }
554 1010
        } else {
555 0
                WRONG("by enum");
556
        }
557 1900
        p->by = by_s;
558
559 1900
        if (args & arg_alt) {
560 630
                if (alt < 0) {
561 10
                        shard_fail(ctx, p->vcl_name,
562
                            "%s invalid alt argument %jd",
563
                            func, (intmax_t)alt);
564 10
                        return (NULL);
565
                }
566 620
                p->alt = alt;
567 620
        }
568
569 1890
        if (args & arg_warmup) {
570 190
                if ((warmup < 0 && warmup != -1) || warmup > 1) {
571 20
                        shard_fail(ctx, p->vcl_name,
572
                            "%s invalid warmup argument %f",
573
                            func, warmup);
574 20
                        return (NULL);
575
                }
576 170
                p->warmup = warmup;
577 170
        }
578
579 1870
        if (args & arg_rampup)
580 60
                p->rampup = !!rampup;
581
582 1870
        if (args & arg_healthy)
583 330
                p->healthy = healthy_s;
584
585 1870
        p->mask = args & arg_mask_param_;
586 1870
        return (p);
587 1950
}
588
589
VCL_BACKEND v_matchproto_(td_directors_shard_backend)
590 1070
vmod_shard_backend(VRT_CTX, struct vmod_directors_shard *vshard,
591
                   struct VARGS(shard_backend) *a)
592
{
593
        struct sharddir *shardd;
594
        struct vmod_directors_shard_param pstk;
595 1070
        struct vmod_directors_shard_param *pp = NULL;
596
        const struct vmod_directors_shard_param *ppt;
597
        VCL_ENUM resolve;
598 1070
        uint32_t args = shard_backendarg_mask_(a);
599
600 1070
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
601 1070
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
602 1070
        shardd = vshard->shardd;
603 1070
        CHECK_OBJ_NOTNULL(shardd, SHARDDIR_MAGIC);
604 1070
        assert((args & ~arg_mask_) == 0);
605
606 1070
        if (args & arg_resolve)
607 490
                resolve = a->resolve;
608 580
        else if (ctx->method & VCL_MET_TASK_H)
609 10
                resolve = VENUM(LAZY);
610
        else
611 570
                resolve = VENUM(NOW);
612
613 1070
        if (resolve == VENUM(LAZY)) {
614 160
                if ((args & ~arg_resolve) == 0) {
615 70
                        AN(vshard->dir);
616 70
                        return (vshard->dir);
617
                }
618
619 90
                if ((ctx->method & SHARD_VCL_TASK_BEREQ) == 0) {
620 10
                        shard_fail(ctx, shardd->name, "%s",
621
                            ".backend(resolve=LAZY) with other "
622
                            "parameters can only be used in backend/pipe "
623
                            "context");
624 10
                        return (NULL);
625
                }
626
627 160
                pp = shard_param_task_l(ctx, shardd, shardd->name,
628 80
                    shardd->param);
629 80
                if (pp == NULL)
630 0
                        return (NULL);
631 990
        } else if (resolve == VENUM(NOW)) {
632 910
                if (ctx->method & VCL_MET_TASK_H) {
633 10
                        shard_fail(ctx, shardd->name, "%s",
634
                            ".backend(resolve=NOW) can not be "
635
                            "used in vcl_init{}/vcl_fini{}");
636 10
                        return (NULL);
637
                }
638 1800
                ppt = shard_param_task_r(ctx, shardd, shardd->name,
639 900
                    shardd->param);
640 900
                AN(ppt);
641 900
                pp = shard_param_stack(&pstk, ppt, shardd->name);
642 900
        } else {
643 0
                WRONG("resolve enum");
644
        }
645
646 980
        AN(pp);
647
648 980
        if (args & arg_param) {
649 30
                ppt = shard_param_blob(a->param);
650 30
                if (ppt == NULL) {
651 10
                        shard_fail(ctx, shardd->name, "%s",
652
                            ".backend(key_blob) param invalid");
653 10
                        return (NULL);
654
                }
655 20
                pp->defaults = ppt;
656 20
        }
657
658 1940
        pp = shard_param_args(ctx, pp, "shard.backend()",
659 970
                              args & arg_mask_set_,
660 970
                              a->by, a->key, a->key_blob, a->alt, a->warmup,
661 970
                              a->rampup, a->healthy);
662 970
        if (pp == NULL)
663 0
                return (NULL);
664
665 970
        if (resolve == VENUM(LAZY))
666 80
                return (vshard->dir);
667
668 890
        assert(resolve == VENUM(NOW));
669 890
        shard_param_merge(pp, pp->defaults);
670 1780
        return (sharddir_pick_be(ctx, shardd, shard_get_key(ctx, pp),
671 890
            pp->alt, pp->warmup, pp->rampup, pp->healthy));
672 1070
}
673
674
static VCL_BOOL v_matchproto_(vdi_healthy)
675 160
vmod_shard_healthy(VRT_CTX, VCL_BACKEND dir, VCL_TIME *changed)
676
{
677
        struct sharddir *shardd;
678
679 160
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
680 160
        CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC);
681 160
        CAST_OBJ_NOTNULL(shardd, dir->priv, SHARDDIR_MAGIC);
682 160
        return (sharddir_any_healthy(ctx, shardd, changed));
683
}
684
685
static VCL_BACKEND v_matchproto_(vdi_resolve_f)
686 150
vmod_shard_resolve(VRT_CTX, VCL_BACKEND dir)
687
{
688
        struct sharddir *shardd;
689
        struct vmod_directors_shard_param pstk[1];
690
        const struct vmod_directors_shard_param *pp;
691
692 150
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
693 150
        CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC);
694 150
        CAST_OBJ_NOTNULL(shardd, dir->priv, SHARDDIR_MAGIC);
695
696 300
        pp = vmod_shard_param_read(ctx, shardd, shardd->name,
697 150
            shardd->param, pstk);
698 150
        CHECK_OBJ_NOTNULL(pp, VMOD_SHARD_SHARD_PARAM_MAGIC);
699
700 300
        return (sharddir_pick_be(ctx, shardd,
701 150
                                 shard_get_key(ctx, pp), pp->alt, pp->warmup,
702 150
                                 pp->rampup, pp->healthy));
703
}
704
705
static void v_matchproto_(vdi_list_f)
706 300
vmod_shard_list(VRT_CTX, VCL_BACKEND dir, struct vsb *vsb, int pflag, int jflag)
707
{
708
        struct sharddir *shardd;
709
        struct shard_backend *sbe;
710 300
        VCL_TIME c, changed = 0;
711
        VCL_DURATION rampup_d, d;
712
        VCL_BACKEND be;
713
        VCL_BOOL h;
714 300
        unsigned i, nh = 0;
715
        double rampup_p;
716
717 300
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
718 300
        CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC);
719 300
        CAST_OBJ_NOTNULL(shardd, dir->priv, SHARDDIR_MAGIC);
720
721 300
        if (pflag) {
722 40
                if (jflag) {
723 10
                        VSB_cat(vsb, "{\n");
724 10
                        VSB_indent(vsb, 2);
725 10
                        VSB_printf(vsb, "\"warmup\": %f,\n", shardd->warmup);
726 20
                        VSB_printf(vsb, "\"rampup_duration\": %f,\n",
727 10
                            shardd->rampup_duration);
728 10
                        VSB_cat(vsb, "\"backends\": {\n");
729 10
                        VSB_indent(vsb, 2);
730 10
                } else {
731 30
                        VSB_cat(vsb, "\n\n\tBackend\tIdent\tHealth\t"
732
                            "Rampup  Remaining\n");
733
                }
734 40
        }
735
736 300
        sharddir_rdlock(shardd);
737 1210
        for (i = 0; i < shardd->n_backend; i++) {
738 910
                sbe = &shardd->backend[i];
739 910
                AN(sbe);
740 910
                be = sbe->backend;
741 910
                CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC);
742
743 910
                c = 0;
744 910
                h = VRT_Healthy(ctx, be, &c);
745 910
                if (h)
746 820
                        nh++;
747 910
                if (c > changed)
748 700
                        changed = c;
749 910
                if ((pflag) == 0)
750 750
                        continue;
751
752 160
                d = ctx->now - c;
753 160
                rampup_d = shardcfg_get_rampup(shardd, i);
754 160
                if (! h) {
755 20
                        rampup_p = 0.0;
756 20
                        rampup_d = 0.0;
757 160
                } else if (d < rampup_d) {
758 60
                        rampup_p = d / rampup_d;
759 60
                        rampup_d -= d;
760 60
                } else {
761 80
                        rampup_p = 1.0;
762 80
                        rampup_d = 0.0;
763
                }
764
765 160
                if (jflag) {
766 40
                        if (i)
767 30
                                VSB_cat(vsb, ",\n");
768 80
                        VSB_printf(vsb, "\"%s\": {\n",
769 40
                            be->vcl_name);
770 40
                        VSB_indent(vsb, 2);
771 80
                        VSB_printf(vsb, "\"ident\": \"%s\",\n",
772 40
                            sbe->ident ? sbe->ident : be->vcl_name);
773 80
                        VSB_printf(vsb, "\"health\": \"%s\",\n",
774 40
                            h ? "healthy" : "sick");
775 40
                        VSB_printf(vsb, "\"rampup\": %f,\n", rampup_p);
776 80
                        VSB_printf(vsb, "\"rampup_remaining\": %.3f\n",
777 40
                            rampup_d);
778 40
                        VSB_indent(vsb, -2);
779 40
                        VSB_cat(vsb, "}");
780 40
                } else {
781 240
                        VSB_printf(vsb, "\t%s\t%s\t%s\t%6.2f%% %8.3fs\n",
782 120
                            be->vcl_name,
783 120
                            sbe->ident ? sbe->ident : be->vcl_name,
784 120
                            h ? "healthy" : "sick",
785 120
                            rampup_p * 100, rampup_d);
786
                }
787 160
        }
788 300
        sharddir_unlock(shardd);
789
790 300
        if (jflag && (pflag)) {
791 10
                VSB_cat(vsb, "\n");
792 10
                VSB_indent(vsb, -2);
793 10
                VSB_cat(vsb, "}\n");
794 10
                VSB_indent(vsb, -2);
795 10
                VSB_cat(vsb, "},\n");
796 10
        }
797
798 300
        if (pflag)
799 40
                return;
800
801 260
        if (jflag)
802 40
                VSB_printf(vsb, "[%u, %u, \"%s\"]", nh, i,
803 20
                    nh ? "healthy" : "sick");
804
        else
805 240
                VSB_printf(vsb, "%u/%u\t%s", nh, i, nh ? "healthy" : "sick");
806 300
}
807
808
VCL_VOID v_matchproto_(td_directors_shard_backend)
809 70
vmod_shard_debug(VRT_CTX, struct vmod_directors_shard *vshard,
810
    VCL_INT i)
811
{
812 70
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
813
814 70
        (void)ctx;
815 70
        sharddir_debug(vshard->shardd, i & UINT32_MAX);
816 70
}
817
818
/* =============================================================
819
 * shard_param
820
 */
821
822
VCL_VOID v_matchproto_(td_directors_shard_param__init)
823 170
vmod_shard_param__init(VRT_CTX,
824
    struct vmod_directors_shard_param **pp, const char *vcl_name)
825
{
826
        struct vmod_directors_shard_param *p;
827
828 170
        (void) ctx;
829 170
        AN(pp);
830 170
        AZ(*pp);
831 170
        ALLOC_OBJ(p, VMOD_SHARD_SHARD_PARAM_MAGIC);
832 170
        AN(p);
833 170
        p->vcl_name = vcl_name;
834 170
        p->scope = SCOPE_VCL;
835 170
        p->defaults = &shard_param_default;
836
837 170
        *pp = p;
838 170
}
839
840
VCL_VOID v_matchproto_(td_directors_shard_param__fini)
841 80
vmod_shard_param__fini(struct vmod_directors_shard_param **pp)
842
{
843
        struct vmod_directors_shard_param *p;
844
845 80
        TAKE_OBJ_NOTNULL(p, pp, VMOD_SHARD_SHARD_PARAM_MAGIC);
846 80
        FREE_OBJ(p);
847 80
}
848
849
/*
850
 * init a stack param struct defaulting to pa with the given name
851
 */
852
static struct vmod_directors_shard_param *
853 2550
shard_param_stack(struct vmod_directors_shard_param *p,
854
    const struct vmod_directors_shard_param *pa, const char *who)
855
{
856 2550
        CHECK_OBJ_NOTNULL(pa, VMOD_SHARD_SHARD_PARAM_MAGIC);
857 2550
        assert(pa->scope > _SCOPE_INVALID);
858
859 2550
        AN(p);
860 2550
        INIT_OBJ(p, VMOD_SHARD_SHARD_PARAM_MAGIC);
861 2550
        p->vcl_name = who;
862 2550
        p->scope = SCOPE_STACK;
863 2550
        p->defaults = pa;
864
865 2550
        return (p);
866
}
867
868
static const struct vmod_directors_shard_param *
869 2590
shard_param_task_r(VRT_CTX, const void *id, const char *who,
870
   const struct vmod_directors_shard_param *pa)
871
{
872
        const struct vmod_directors_shard_param *p;
873
        const struct vmod_priv *task;
874
        const void *task_id;
875
876 2590
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
877 2590
        CHECK_OBJ_NOTNULL(pa, VMOD_SHARD_SHARD_PARAM_MAGIC);
878 2590
        assert(pa->scope > _SCOPE_INVALID);
879
880 2590
        task_id = (const char *)id + task_off_param;
881 2590
        task = VRT_priv_task_get(ctx, task_id);
882
883 2590
        if (task) {
884 1360
                CAST_OBJ_NOTNULL(p, task->priv, VMOD_SHARD_SHARD_PARAM_MAGIC);
885 1360
                assert(p->scope == SCOPE_TASK);
886 1360
                assert(who == p->vcl_name);
887 1360
                return (p);
888
        }
889
890 1230
        if (id == pa || pa->scope != SCOPE_VCL)
891 890
                return (pa);
892
893 340
        return (shard_param_task_r(ctx, pa, pa->vcl_name, pa));
894 2590
}
895
896
/*
897
 * get a task scoped param struct for id defaulting to pa
898
 * if id != pa and pa has VCL scope, also get a task scoped param struct for pa
899
 */
900
static struct vmod_directors_shard_param *
901 980
shard_param_task_l(VRT_CTX, const void *id, const char *who,
902
   const struct vmod_directors_shard_param *pa)
903
{
904
        struct vmod_directors_shard_param *p;
905
        struct vmod_priv *task;
906
        const void *task_id;
907
908 980
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
909 980
        CHECK_OBJ_NOTNULL(pa, VMOD_SHARD_SHARD_PARAM_MAGIC);
910 980
        assert(pa->scope > _SCOPE_INVALID);
911
912 980
        task_id = (const char *)id + task_off_param;
913 980
        task = VRT_priv_task(ctx, task_id);
914
915 980
        if (task == NULL) {
916 0
                shard_fail(ctx, who, "%s", "no priv_task");
917 0
                return (NULL);
918
        }
919
920 980
        if (task->priv) {
921 80
                CAST_OBJ_NOTNULL(p, task->priv, VMOD_SHARD_SHARD_PARAM_MAGIC);
922 80
                assert(p->scope == SCOPE_TASK);
923 80
                assert(who == p->vcl_name);
924 80
                return (p);
925
        }
926
927 900
        p = WS_Alloc(ctx->ws, sizeof *p);
928 900
        if (p == NULL) {
929 0
                shard_fail(ctx, who, "%s", "WS_Alloc failed");
930 0
                return (NULL);
931
        }
932 900
        task->priv = p;
933 900
        INIT_OBJ(p, VMOD_SHARD_SHARD_PARAM_MAGIC);
934 900
        p->vcl_name = who;
935 900
        p->scope = SCOPE_TASK;
936
937 900
        if (id == pa || pa->scope != SCOPE_VCL)
938 880
                p->defaults = pa;
939
        else
940 20
                p->defaults = shard_param_task_l(ctx, pa, pa->vcl_name, pa);
941
942 900
        if (p->defaults == NULL)
943 0
                return (NULL);
944
945 900
        return (p);
946 980
}
947
948
static struct vmod_directors_shard_param *
949 1040
shard_param_prep(VRT_CTX, struct vmod_directors_shard_param *p,
950
    const char *who)
951
{
952 1040
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
953 1040
        CHECK_OBJ_NOTNULL(p, VMOD_SHARD_SHARD_PARAM_MAGIC);
954
955 1040
        if (ctx->method & SHARD_VCL_TASK_REQ) {
956 10
                shard_fail(ctx, p->vcl_name, "%s may only be used "
957
                    "in vcl_init and in backend/pipe context", who);
958 10
                return (NULL);
959 1030
        } else if (ctx->method & SHARD_VCL_TASK_BEREQ)
960 880
                p = shard_param_task_l(ctx, p, p->vcl_name, p);
961
        else
962 150
                assert(ctx->method & VCL_MET_TASK_H);
963
964 1030
        return (p);
965 1040
}
966
967
VCL_VOID v_matchproto_(td_directors_shard_param_set)
968 990
vmod_shard_param_set(VRT_CTX, struct vmod_directors_shard_param *p,
969
                     struct VARGS(shard_param_set) *a)
970
{
971 990
        uint32_t args = shard_param_set_mask(a);
972
973 990
        assert((args & ~arg_mask_set_) == 0);
974
975 990
        p = shard_param_prep(ctx, p, "shard_param.set()");
976 990
        if (p == NULL)
977 10
                return;
978 1960
        (void) shard_param_args(ctx, p, "shard_param.set()", args,
979 980
                                a->by, a->key, a->key_blob, a->alt, a->warmup,
980 980
                                a->rampup, a->healthy);
981 990
}
982
983
VCL_VOID v_matchproto_(td_directors_shard_param_clear)
984 50
vmod_shard_param_clear(VRT_CTX,
985
    struct vmod_directors_shard_param *p)
986
{
987 50
        p = shard_param_prep(ctx, p, "shard_param.clear()");
988 50
        if (p == NULL)
989 0
                return;
990 50
        p->mask = 0;
991 50
}
992
993
static const struct vmod_directors_shard_param *
994 1650
vmod_shard_param_read(VRT_CTX, const void *id, const char *who,
995
    const struct vmod_directors_shard_param *p,
996
    struct vmod_directors_shard_param *pstk)
997
{
998
        struct vmod_directors_shard_param *pp;
999
1000 1650
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
1001 1650
        CHECK_OBJ_NOTNULL(p, VMOD_SHARD_SHARD_PARAM_MAGIC);
1002
1003 1650
        if (ctx->method == 0 || (ctx->method & SHARD_VCL_TASK_BEREQ))
1004 1350
                p = shard_param_task_r(ctx, id, who, p);
1005
1006 1650
        CHECK_OBJ_NOTNULL(p, VMOD_SHARD_SHARD_PARAM_MAGIC);
1007 1650
        pp = shard_param_stack(pstk, p, p->vcl_name);
1008 1650
        shard_param_merge(pp, p);
1009 1650
        return (pp);
1010
}
1011
1012
VCL_STRING v_matchproto_(td_directors_shard_param_get_by)
1013 250
vmod_shard_param_get_by(VRT_CTX,
1014
    struct vmod_directors_shard_param *p)
1015
{
1016
        struct vmod_directors_shard_param pstk;
1017
        const struct vmod_directors_shard_param *pp;
1018
1019 250
        pp = vmod_shard_param_read(ctx, p, p->vcl_name, p, &pstk);
1020 250
        CHECK_OBJ_NOTNULL(pp, VMOD_SHARD_SHARD_PARAM_MAGIC);
1021 250
        return (default_by(pp->by));
1022
}
1023
1024
VCL_INT v_matchproto_(td_directors_shard_param_get_key)
1025 250
vmod_shard_param_get_key(VRT_CTX,
1026
    struct vmod_directors_shard_param *p)
1027
{
1028
        struct vmod_directors_shard_param pstk;
1029
        const struct vmod_directors_shard_param *pp;
1030
1031 250
        pp = vmod_shard_param_read(ctx, p, p->vcl_name, p, &pstk);
1032 250
        CHECK_OBJ_NOTNULL(pp, VMOD_SHARD_SHARD_PARAM_MAGIC);
1033 250
        return ((VCL_INT)shard_get_key(ctx, pp));
1034
}
1035
VCL_INT v_matchproto_(td_directors_shard_param_get_alt)
1036 250
vmod_shard_param_get_alt(VRT_CTX,
1037
    struct vmod_directors_shard_param *p)
1038
{
1039
        struct vmod_directors_shard_param pstk;
1040
        const struct vmod_directors_shard_param *pp;
1041
1042 250
        pp = vmod_shard_param_read(ctx, p, p->vcl_name, p, &pstk);
1043 250
        CHECK_OBJ_NOTNULL(pp, VMOD_SHARD_SHARD_PARAM_MAGIC);
1044 250
        return (pp->alt);
1045
}
1046
1047
VCL_REAL v_matchproto_(td_directors_shard_param_get_warmup)
1048 250
vmod_shard_param_get_warmup(VRT_CTX,
1049
    struct vmod_directors_shard_param *p)
1050
{
1051
        struct vmod_directors_shard_param pstk;
1052
        const struct vmod_directors_shard_param *pp;
1053
1054 250
        pp = vmod_shard_param_read(ctx, p, p->vcl_name, p, &pstk);
1055 250
        CHECK_OBJ_NOTNULL(pp, VMOD_SHARD_SHARD_PARAM_MAGIC);
1056 250
        return (pp->warmup);
1057
}
1058
1059
VCL_BOOL v_matchproto_(td_directors_shard_param_get_rampup)
1060 250
vmod_shard_param_get_rampup(VRT_CTX,
1061
    struct vmod_directors_shard_param *p)
1062
{
1063
        struct vmod_directors_shard_param pstk;
1064
        const struct vmod_directors_shard_param *pp;
1065
1066 250
        pp = vmod_shard_param_read(ctx, p, p->vcl_name, p, &pstk);
1067 250
        CHECK_OBJ_NOTNULL(pp, VMOD_SHARD_SHARD_PARAM_MAGIC);
1068 250
        return (pp->rampup);
1069
}
1070
1071
VCL_STRING v_matchproto_(td_directors_shard_param_get_healthy)
1072 250
vmod_shard_param_get_healthy(VRT_CTX,
1073
    struct vmod_directors_shard_param *p)
1074
{
1075
        struct vmod_directors_shard_param pstk;
1076
        const struct vmod_directors_shard_param *pp;
1077
1078 250
        pp = vmod_shard_param_read(ctx, p, p->vcl_name, p, &pstk);
1079 250
        CHECK_OBJ_NOTNULL(pp, VMOD_SHARD_SHARD_PARAM_MAGIC);
1080 250
        return (default_healthy(pp->healthy));
1081
}
1082
1083
static const struct vmod_directors_shard_param *
1084 60
shard_param_blob(VCL_BLOB blob)
1085
{
1086
        const struct vmod_directors_shard_param *p;
1087
1088 100
        if (blob && blob->type == VMOD_SHARD_SHARD_PARAM_BLOB &&
1089 40
            blob->blob != NULL &&
1090 40
            blob->len == sizeof(struct vmod_directors_shard_param)) {
1091 40
                CAST_OBJ_NOTNULL(p, blob->blob, VMOD_SHARD_SHARD_PARAM_MAGIC);
1092 40
                return (p);
1093
        }
1094
1095 20
        return (NULL);
1096 60
}
1097
1098
VCL_BLOB v_matchproto_(td_directors_shard_param_use)
1099 40
vmod_shard_param_use(VRT_CTX,
1100
    struct vmod_directors_shard_param *p)
1101
{
1102 40
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
1103 40
        CHECK_OBJ_NOTNULL(p, VMOD_SHARD_SHARD_PARAM_MAGIC);
1104
1105 40
        return (VRT_blob(ctx, "xshard_param.use()", p, sizeof *p,
1106
            VMOD_SHARD_SHARD_PARAM_BLOB));
1107
}