varnish-cache/lib/libvmod_directors/vmod_shard.c
1
/*-
2
 * Copyright 2009-2016 UPLEX - Nils Goroll Systemoptimierung
3
 * All rights reserved.
4
 *
5
 * Authors: Julian Wiesener <jw@uplex.de>
6
 *          Nils Goroll <slink@uplex.de>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
#include "config.h"
31
32
#include <stdlib.h>
33
#include <string.h>
34
35
#include "cache/cache.h"
36
#include "vcl.h"
37
38
#include "vend.h"
39
40
#include "vcc_if.h"
41
#include "shard_dir.h"
42
#include "shard_cfg.h"
43
#include "shard_hash.h"
44
45
struct vmod_directors_shard {
46
        unsigned                magic;
47
#define VMOD_SHARD_SHARD_MAGIC  0x6e63e1bf
48
        struct sharddir *shardd;
49
};
50
51
VCL_VOID v_matchproto_(td_directors_shard__init)
52 30
vmod_shard__init(VRT_CTX, struct vmod_directors_shard **vshardp,
53
    const char *vcl_name)
54
{
55
        struct vmod_directors_shard *vshard;
56
        VCL_INT t1;
57
        uint32_t t2a, t2b;
58
59
        /* see vmod_key comment */
60
        assert(sizeof(VCL_INT) >= sizeof(uint32_t));
61 30
        t2a = UINT32_MAX;
62 30
        t1 = (VCL_INT)t2a;
63 30
        t2b = (uint32_t)t1;
64 30
        assert(t2a == t2b);
65
66
        (void)ctx;
67 30
        AN(vshardp);
68 30
        AZ(*vshardp);
69 30
        ALLOC_OBJ(vshard, VMOD_SHARD_SHARD_MAGIC);
70 30
        AN(vshard);
71
72 30
        *vshardp = vshard;
73 30
        sharddir_new(&vshard->shardd, vcl_name);
74 30
}
75
76
VCL_VOID v_matchproto_(td_directors_shard__fini)
77 0
vmod_shard__fini(struct vmod_directors_shard **vshardp)
78
{
79 0
        struct vmod_directors_shard *vshard = *vshardp;
80
81 0
        *vshardp = NULL;
82 0
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
83 0
        sharddir_delete(&vshard->shardd);
84 0
        FREE_OBJ(vshard);
85 0
}
86
87
/*
88
 * our key is a uint32_t, but VCL_INT is a (signed) long.  We cast back and
89
 * forth, asserting in vmod_shard__init() that VCL_INT is a large enough
90
 * container
91
 */
92
VCL_INT v_matchproto_(td_directors_shard_key)
93 50
    vmod_shard_key(VRT_CTX, struct vmod_directors_shard *vshard,
94
    VCL_STRING s, VCL_ENUM alg_s)
95
{
96 50
        enum alg_e alg = parse_alg_e(alg_s);
97 50
        hash_func hash_fp = shard_hash_f[alg];
98
99
        (void)ctx;
100
        (void)vshard;;
101
102 50
        return (VCL_INT)hash_fp(s ? s : "");
103
}
104
105
VCL_VOID v_matchproto_(td_directors_set_warmup)
106 0
vmod_shard_set_warmup(VRT_CTX, struct vmod_directors_shard *vshard,
107
    VCL_REAL probability)
108
{
109 0
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
110 0
        if (probability < 0 || probability >= 1) {
111 0
                shard_err(ctx, vshard->shardd,
112
                    ".set_warmup(%f) ignored", probability);
113 0
                return;
114
        }
115 0
        shardcfg_set_warmup(vshard->shardd, probability);
116
}
117
118
VCL_VOID v_matchproto_(td_directors_set_rampup)
119 2
vmod_shard_set_rampup(VRT_CTX, struct vmod_directors_shard *vshard,
120
    VCL_DURATION duration)
121
{
122
        (void)ctx;
123 2
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
124 2
        shardcfg_set_rampup(vshard->shardd, duration);
125 2
}
126
127
VCL_BOOL v_matchproto_(td_directors_shard_add_backend)
128 158
vmod_shard_add_backend(VRT_CTX, struct vmod_directors_shard *vshard,
129
    struct vmod_priv *priv,
130
    VCL_BACKEND be, VCL_STRING ident, VCL_DURATION rampup)
131
{
132 158
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
133
134 158
        if (be == NULL) {
135 0
                shard_err0(ctx, vshard->shardd,
136
                    ".backend_add() NULL backend given");
137 0
                return 0;
138
        }
139
140 158
        return shardcfg_add_backend(ctx, priv, vshard->shardd,
141
            be, ident, rampup);
142
}
143
144
VCL_BOOL v_matchproto_(td_directors_shard_remove_backend)
145 16
vmod_shard_remove_backend(VRT_CTX, struct vmod_directors_shard *vshard,
146
    struct vmod_priv *priv,
147
    VCL_BACKEND be, VCL_STRING ident)
148
{
149 16
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
150
151 16
        if (be == NULL && ident == NULL) {
152 0
                shard_err0(ctx, vshard->shardd,
153
                    ".backend_remove() at least one of backend "
154
                    "and ident must be given");
155 0
                return 0;
156
        }
157
158 16
        return shardcfg_remove_backend(ctx, priv, vshard->shardd,
159
            be, ident);
160
}
161
162
VCL_BOOL v_matchproto_(td_directors_shard_clear)
163 28
vmod_shard_clear(VRT_CTX, struct vmod_directors_shard *vshard,
164
    struct vmod_priv *priv)
165
{
166 28
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
167 28
        return shardcfg_clear(ctx, priv, vshard->shardd);
168
}
169
170
VCL_BOOL v_matchproto_(td_directors_shard_reconfigure)
171 74
vmod_shard_reconfigure(VRT_CTX, struct vmod_directors_shard *vshard,
172
    struct vmod_priv *priv, VCL_INT replicas, VCL_ENUM alg_s)
173
{
174 74
        enum alg_e alg = parse_alg_e(alg_s);
175
176 74
        return shardcfg_reconfigure(ctx, priv, vshard->shardd, replicas, alg);
177
}
178
179
static inline uint32_t
180 74
get_key(VRT_CTX, enum by_e by, VCL_INT key_int, VCL_BLOB key_blob)
181
{
182
        struct http *http;
183 74
        uint8_t k[4] = { 0 };
184
        uint8_t *b;
185
        int i, ki;
186
187 74
        switch (by) {
188
        case BY_HASH:
189 18
                if (ctx->bo) {
190 0
                        CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC);
191 0
                        return (vbe32dec(ctx->bo->digest));
192
                }
193
                /* FALLTHROUGH */
194
        case BY_URL:
195 18
                if (ctx->http_req) {
196 18
                        AN(http = ctx->http_req);
197
                } else {
198 0
                        AN(ctx->http_bereq);
199 0
                        AN(http = ctx->http_bereq);
200
                }
201 18
                return (shard_hash_f[SHA256](http->hd[HTTP_HDR_URL].b));
202
        case BY_KEY:
203 56
                return ((uint32_t)key_int);
204
        case BY_BLOB:
205 0
                assert(key_blob);
206 0
                assert(key_blob->len > 0);
207 0
                assert(key_blob->priv != NULL);
208
209 0
                if (key_blob->len >= 4)
210 0
                        ki = 0;
211
                else
212 0
                        ki = 4 - key_blob->len;
213
214 0
                b = key_blob->priv;
215 0
                for (i = 0; ki < 4; i++, ki++)
216 0
                        k[ki] = b[i];
217 0
                assert(i <= key_blob->len);
218
219 0
                return (vbe32dec(k));
220
        default:
221 0
                WRONG("by value");
222
        }
223
}
224
225
VCL_BACKEND v_matchproto_(td_directors_shard_backend)
226 74
vmod_shard_backend(VRT_CTX, struct vmod_directors_shard *vshard,
227
    VCL_ENUM by_s, VCL_INT key_int, VCL_BLOB key_blob, VCL_INT alt,
228
    VCL_REAL warmup, VCL_BOOL rampup, VCL_ENUM healthy_s)
229
{
230 74
        enum by_e       by      = parse_by_e(by_s);
231 74
        enum healthy_e  healthy = parse_healthy_e(healthy_s);
232
233
        uint32_t        key;
234
235 74
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
236 74
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
237
238
        /* TODO #2500 */
239 74
        if ((ctx->method & (VCL_MET_TASK_C | VCL_MET_TASK_B)) == 0) {
240 0
                VRT_fail(ctx, "shard .backend() method may only be used "
241
                         "in client and backend context");
242 0
                return NULL;
243
        }
244
245 74
        if (key_int && by != BY_KEY) {
246 0
                shard_err(ctx, vshard->shardd,
247
                    "by=%s but key argument used", by_s);
248 0
                return NULL;
249
        }
250
251 74
        if (key_blob && by != BY_BLOB) {
252 0
                shard_err(ctx, vshard->shardd,
253
                    "by=%s but key_blob argument used", by_s);
254 0
                return NULL;
255
        }
256
257 74
        if (by == BY_BLOB) {
258 0
                if (key_blob == NULL ||
259 0
                    key_blob->len <= 0 ||
260 0
                    key_blob->priv == NULL) {
261 0
                        shard_err0(ctx, vshard->shardd,
262
                            "by=BLOB but no or empty key_blob "
263
                            "- using key 0");
264 0
                        by = BY_KEY;
265 0
                        key_int = 0;
266
                }
267
        }
268
269 74
        key = get_key(ctx, by, key_int, key_blob);
270
271 74
        return (sharddir_pick_be(ctx, vshard->shardd,
272
                key, alt, warmup, rampup, healthy));
273
}
274
275
VCL_VOID v_matchproto_(td_directors_shard_backend)
276 10
vmod_shard_debug(VRT_CTX, struct vmod_directors_shard *vshard,
277
    VCL_INT i)
278
{
279 10
        CHECK_OBJ_NOTNULL(vshard, VMOD_SHARD_SHARD_MAGIC);
280
281
        (void)ctx;
282 10
        sharddir_debug(vshard->shardd, i & UINT32_MAX);
283 10
}