source: bin/varnishd/cache_backend_poll.c @ b37c01

Revision b37c01, 12.9 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Since we're cleaning up, rename TIM_ to VTIM_ and take out of libvarnish.h

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2011 Varnish Software AS
4 * All rights reserved.
5 *
6 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
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 * Poll backends for collection of health statistics
30 *
31 * We co-opt threads from the worker pool for probing the backends,
32 * but we want to avoid a potentially messy cleanup operation when we
33 * retire the backend, so the thread owns the health information, which
34 * the backend references, rather than the other way around.
35 *
36 */
37
38#include "config.h"
39
40#include <stdio.h>
41#include <math.h>
42#include <stdlib.h>
43#include <poll.h>
44
45#include "cli_priv.h"
46#include "cache.h"
47#include "vrt.h"
48#include "vtcp.h"
49#include "vtim.h"
50#include "cache_backend.h"
51
52/* Default averaging rate, we want something pretty responsive */
53#define AVG_RATE                        4
54
55struct vbp_vcl {
56        unsigned                        magic;
57#define VBP_VCL_MAGIC                   0x70829764
58
59        VTAILQ_ENTRY(vbp_vcl)           list;
60        const struct vrt_backend_probe  *probep;
61        struct vrt_backend_probe        probe;
62        const char                      *hosthdr;
63};
64
65struct vbp_target {
66        unsigned                        magic;
67#define VBP_TARGET_MAGIC                0x6b7cb656
68
69        struct backend                  *backend;
70        VTAILQ_HEAD( ,vbp_vcl)          vcls;
71
72        struct vrt_backend_probe        probe;
73        int                             stop;
74        struct vsb                      *vsb;
75        char                            *req;
76        int                             req_len;
77
78        char                            resp_buf[128];
79        unsigned                        good;
80
81        /* Collected statistics */
82#define BITMAP(n, c, t, b)      uint64_t        n;
83#include "tbl/backend_poll.h"
84#undef BITMAP
85
86        double                          last;
87        double                          avg;
88        double                          rate;
89
90        VTAILQ_ENTRY(vbp_target)        list;
91        pthread_t                       thread;
92};
93
94static VTAILQ_HEAD(, vbp_target)        vbp_list =
95    VTAILQ_HEAD_INITIALIZER(vbp_list);
96
97static struct lock                      vbp_mtx;
98
99/*--------------------------------------------------------------------
100 * Poke one backend, once, but possibly at both IPv4 and IPv6 addresses.
101 *
102 * We do deliberately not use the stuff in cache_backend.c, because we
103 * want to measure the backends response without local distractions.
104 */
105
106static int
107vbp_connect(int pf, const struct sockaddr_storage *sa, socklen_t salen, int tmo)
108{
109        int s, i;
110
111        s = socket(pf, SOCK_STREAM, 0);
112        if (s < 0)
113                return (s);
114
115        i = VTCP_connect(s, sa, salen, tmo);
116        if (i == 0)
117                return (s);
118        VTCP_close(&s);
119        return (-1);
120}
121
122static void
123vbp_poke(struct vbp_target *vt)
124{
125        int s, tmo, i;
126        double t_start, t_now, t_end;
127        unsigned rlen, resp;
128        struct backend *bp;
129        char buf[8192], *p;
130        struct pollfd pfda[1], *pfd = pfda;
131
132        bp = vt->backend;
133        CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
134
135        t_start = t_now = VTIM_real();
136        t_end = t_start + vt->probe.timeout;
137        tmo = (int)round((t_end - t_now) * 1e3);
138
139        s = -1;
140        if (params->prefer_ipv6 && bp->ipv6 != NULL) {
141                s = vbp_connect(PF_INET6, bp->ipv6, bp->ipv6len, tmo);
142                t_now = VTIM_real();
143                tmo = (int)round((t_end - t_now) * 1e3);
144                if (s >= 0)
145                        vt->good_ipv6 |= 1;
146        }
147        if (tmo > 0 && s < 0 && bp->ipv4 != NULL) {
148                s = vbp_connect(PF_INET, bp->ipv4, bp->ipv4len, tmo);
149                t_now = VTIM_real();
150                tmo = (int)round((t_end - t_now) * 1e3);
151                if (s >= 0)
152                        vt->good_ipv4 |= 1;
153        }
154        if (tmo > 0 && s < 0 && bp->ipv6 != NULL) {
155                s = vbp_connect(PF_INET6, bp->ipv6, bp->ipv6len, tmo);
156                t_now = VTIM_real();
157                tmo = (int)round((t_end - t_now) * 1e3);
158                if (s >= 0)
159                        vt->good_ipv6 |= 1;
160        }
161        if (s < 0) {
162                /* Got no connection: failed */
163                return;
164        }
165        if (tmo <= 0) {
166                /* Spent too long time getting it */
167                VTCP_close(&s);
168                return;
169        }
170
171        /* Send the request */
172        i = write(s, vt->req, vt->req_len);
173        if (i != vt->req_len) {
174                if (i < 0)
175                        vt->err_xmit |= 1;
176                VTCP_close(&s);
177                return;
178        }
179        vt->good_xmit |= 1;
180
181        pfd->fd = s;
182        rlen = 0;
183        do {
184                pfd->events = POLLIN;
185                pfd->revents = 0;
186                tmo = (int)round((t_end - t_now) * 1e3);
187                if (tmo > 0)
188                        i = poll(pfd, 1, tmo);
189                if (i == 0 || tmo <= 0) {
190                        VTCP_close(&s);
191                        return;
192                }
193                if (rlen < sizeof vt->resp_buf)
194                        i = read(s, vt->resp_buf + rlen,
195                            sizeof vt->resp_buf - rlen);
196                else
197                        i = read(s, buf, sizeof buf);
198                rlen += i;
199        } while (i > 0);
200
201        VTCP_close(&s);
202
203        if (i < 0) {
204                vt->err_recv |= 1;
205                return;
206        }
207
208        if (rlen == 0)
209                return;
210
211        /* So we have a good receive ... */
212        t_now = VTIM_real();
213        vt->last = t_now - t_start;
214        vt->good_recv |= 1;
215
216        /* Now find out if we like the response */
217        vt->resp_buf[sizeof vt->resp_buf - 1] = '\0';
218        p = strchr(vt->resp_buf, '\r');
219        if (p != NULL)
220                *p = '\0';
221        p = strchr(vt->resp_buf, '\n');
222        if (p != NULL)
223                *p = '\0';
224
225        i = sscanf(vt->resp_buf, "HTTP/%*f %u %s", &resp, buf);
226
227        if (i == 2 && resp == vt->probe.exp_status)
228                vt->happy |= 1;
229}
230
231/*--------------------------------------------------------------------
232 * Record pokings...
233 */
234
235static void
236vbp_start_poke(struct vbp_target *vt)
237{
238        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
239
240#define BITMAP(n, c, t, b)      vt->n <<= 1;
241#include "tbl/backend_poll.h"
242#undef BITMAP
243
244        vt->last = 0;
245        vt->resp_buf[0] = '\0';
246}
247
248static void
249vbp_has_poked(struct vbp_target *vt)
250{
251        unsigned i, j;
252        uint64_t u;
253        const char *logmsg;
254        char bits[10];
255
256        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
257
258        /* Calculate exponential average */
259        if (vt->happy & 1) {
260                if (vt->rate < AVG_RATE)
261                        vt->rate += 1.0;
262                vt->avg += (vt->last - vt->avg) / vt->rate;
263        }
264
265        i = 0;
266#define BITMAP(n, c, t, b)      bits[i++] = (vt->n & 1) ? c : '-';
267#include "tbl/backend_poll.h"
268#undef BITMAP
269        bits[i] = '\0';
270
271        u = vt->happy;
272        for (i = j = 0; i < vt->probe.window; i++) {
273                if (u & 1)
274                        j++;
275                u >>= 1;
276        }
277        vt->good = j;
278
279        if (vt->good >= vt->probe.threshold) {
280                if (vt->backend->healthy)
281                        logmsg = "Still healthy";
282                else
283                        logmsg = "Back healthy";
284                vt->backend->healthy = 1;
285        } else {
286                if (vt->backend->healthy)
287                        logmsg = "Went sick";
288                else
289                        logmsg = "Still sick";
290                vt->backend->healthy = 0;
291        }
292        VSL(SLT_Backend_health, 0, "%s %s %s %u %u %u %.6f %.6f %s",
293            vt->backend->vcl_name, logmsg, bits,
294            vt->good, vt->probe.threshold, vt->probe.window,
295            vt->last, vt->avg, vt->resp_buf);
296        vt->backend->vsc->happy = vt->happy;
297}
298
299/*--------------------------------------------------------------------
300 * Build request from probe spec
301 */
302
303static void
304vbp_build_req(struct vsb *vsb, const struct vbp_vcl *vcl)
305{
306
307        XXXAN(vsb);
308        XXXAN(vcl);
309        VSB_clear(vsb);
310        if(vcl->probe.request != NULL) {
311                VSB_cat(vsb, vcl->probe.request);
312        } else {
313                VSB_printf(vsb, "GET %s HTTP/1.1\r\n",
314                    vcl->probe.url != NULL ?  vcl->probe.url : "/");
315                if (vcl->hosthdr != NULL)
316                        VSB_printf(vsb, "Host: %s\r\n", vcl->hosthdr);
317                VSB_printf(vsb, "Connection: close\r\n");
318                VSB_printf(vsb, "\r\n");
319        }
320        AZ(VSB_finish(vsb));
321}
322
323/*--------------------------------------------------------------------
324 * One thread per backend to be poked.
325 */
326
327static void *
328vbp_wrk_poll_backend(void *priv)
329{
330        struct vbp_target *vt;
331        struct vbp_vcl *vcl = NULL;
332
333        THR_SetName("backend poll");
334
335        CAST_OBJ_NOTNULL(vt, priv, VBP_TARGET_MAGIC);
336
337        while (!vt->stop) {
338                Lck_Lock(&vbp_mtx);
339                if (VTAILQ_FIRST(&vt->vcls) != vcl) {
340                        vcl = VTAILQ_FIRST(&vt->vcls);
341                        vbp_build_req(vt->vsb, vcl);
342                        vt->probe = vcl->probe;
343                }
344                Lck_Unlock(&vbp_mtx);
345
346                vt->req = VSB_data(vt->vsb);
347                vt->req_len = VSB_len(vt->vsb);
348
349                vbp_start_poke(vt);
350                vbp_poke(vt);
351                vbp_has_poked(vt);
352
353                if (!vt->stop)
354                        VTIM_sleep(vt->probe.interval);
355        }
356        return (NULL);
357}
358
359/*--------------------------------------------------------------------
360 * Cli functions
361 */
362
363static void
364vbp_bitmap(struct cli *cli, char c, uint64_t map, const char *lbl)
365{
366        int i;
367        uint64_t u = (1ULL << 63);
368
369        for (i = 0; i < 64; i++) {
370                if (map & u)
371                        VCLI_Out(cli, "%c", c);
372                else
373                        VCLI_Out(cli, "-");
374                map <<= 1;
375        }
376        VCLI_Out(cli, " %s\n", lbl);
377}
378
379/*lint -e{506} constant value boolean */
380/*lint -e{774} constant value boolean */
381static void
382vbp_health_one(struct cli *cli, const struct vbp_target *vt)
383{
384
385        VCLI_Out(cli, "Backend %s is %s\n",
386            vt->backend->vcl_name,
387            vt->backend->healthy ? "Healthy" : "Sick");
388        VCLI_Out(cli, "Current states  good: %2u threshold: %2u window: %2u\n",
389            vt->good, vt->probe.threshold, vt->probe.window);
390        VCLI_Out(cli, "Average responsetime of good probes: %.6f\n", vt->avg);
391        VCLI_Out(cli,
392            "Oldest                       "
393            "                             Newest\n");
394        VCLI_Out(cli,
395            "============================="
396            "===================================\n");
397
398#define BITMAP(n, c, t, b)                                      \
399                if ((vt->n != 0) || (b))                        \
400                        vbp_bitmap(cli, (c), vt->n, (t));
401#include "tbl/backend_poll.h"
402#undef BITMAP
403}
404
405static void
406vbp_health(struct cli *cli, const char * const *av, void *priv)
407{
408        struct vbp_target *vt;
409
410        ASSERT_CLI();
411        (void)av;
412        (void)priv;
413
414        VTAILQ_FOREACH(vt, &vbp_list, list)
415                vbp_health_one(cli, vt);
416}
417
418static struct cli_proto debug_cmds[] = {
419        { "debug.health", "debug.health",
420                "\tDump backend health stuff\n",
421                0, 0, "d", vbp_health },
422        { NULL }
423};
424
425/*--------------------------------------------------------------------
426 * A new VCL wants to probe this backend,
427 */
428
429static struct vbp_vcl *
430vbp_new_vcl(const struct vrt_backend_probe *p, const char *hosthdr)
431{
432        struct vbp_vcl *vcl;
433
434        ALLOC_OBJ(vcl, VBP_VCL_MAGIC);
435        XXXAN(vcl);
436        vcl->probep = p;
437        vcl->probe = *p;
438        vcl->hosthdr = hosthdr;
439
440        /*
441         * Sanitize and insert defaults
442         * XXX: we could make these defaults parameters
443         */
444        if (vcl->probe.timeout == 0.0)
445                vcl->probe.timeout = 2.0;
446        if (vcl->probe.interval == 0.0)
447                vcl->probe.interval = 5.0;
448        if (vcl->probe.window == 0)
449                vcl->probe.window = 8;
450        if (vcl->probe.threshold == 0)
451                vcl->probe.threshold = 3;
452        if (vcl->probe.exp_status == 0)
453                vcl->probe.exp_status = 200;
454
455        if (vcl->probe.threshold == ~0U)
456                vcl->probe.initial = vcl->probe.threshold - 1;
457
458        if (vcl->probe.initial > vcl->probe.threshold)
459                vcl->probe.initial = vcl->probe.threshold;
460        return (vcl);
461}
462
463/*--------------------------------------------------------------------
464 * Insert/Remove/Use called from cache_backend.c
465 */
466
467void
468VBP_Insert(struct backend *b, const struct vrt_backend_probe *p, const char *hosthdr)
469{
470        struct vbp_target *vt;
471        struct vbp_vcl *vcl;
472        int startthread = 0;
473        unsigned u;
474
475        ASSERT_CLI();
476        AN(p);
477
478        if (b->probe == NULL) {
479                ALLOC_OBJ(vt, VBP_TARGET_MAGIC);
480                XXXAN(vt);
481                VTAILQ_INIT(&vt->vcls);
482                vt->backend = b;
483                vt->vsb = VSB_new_auto();
484                XXXAN(vt->vsb);
485                b->probe = vt;
486                startthread = 1;
487                VTAILQ_INSERT_TAIL(&vbp_list, vt, list);
488        } else {
489                vt = b->probe;
490        }
491
492        VTAILQ_FOREACH(vcl, &vt->vcls, list)
493                assert (vcl->probep != p);
494
495        vcl = vbp_new_vcl(p, hosthdr);
496        Lck_Lock(&vbp_mtx);
497        VTAILQ_INSERT_TAIL(&vt->vcls, vcl, list);
498        Lck_Unlock(&vbp_mtx);
499
500        if (startthread) {
501                for (u = 0; u < vcl->probe.initial; u++) {
502                        vbp_start_poke(vt);
503                        vt->happy |= 1;
504                        vbp_has_poked(vt);
505                }
506                AZ(pthread_create(&vt->thread, NULL, vbp_wrk_poll_backend, vt));
507        }
508}
509
510void
511VBP_Use(const struct backend *b, const struct vrt_backend_probe *p)
512{
513        struct vbp_target *vt;
514        struct vbp_vcl *vcl;
515
516        ASSERT_CLI();
517        AN(p);
518        CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
519        AN(b->probe);
520        vt = b->probe;
521
522        VTAILQ_FOREACH(vcl, &vt->vcls, list) {
523                if (vcl->probep != p)
524                        continue;
525
526                Lck_Lock(&vbp_mtx);
527                VTAILQ_REMOVE(&vt->vcls, vcl, list);
528                VTAILQ_INSERT_HEAD(&vt->vcls, vcl, list);
529                Lck_Unlock(&vbp_mtx);
530                return;
531        }
532}
533
534void
535VBP_Remove(struct backend *b, struct vrt_backend_probe const *p)
536{
537        struct vbp_target *vt;
538        struct vbp_vcl *vcl;
539        void *ret;
540
541        ASSERT_CLI();
542        AN(p);
543        CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
544        AN(b->probe);
545        vt = b->probe;
546
547        VTAILQ_FOREACH(vcl, &vt->vcls, list)
548                if (vcl->probep == p)
549                        break;
550
551        AN(vcl);
552
553        Lck_Lock(&vbp_mtx);
554        VTAILQ_REMOVE(&vt->vcls, vcl, list);
555        Lck_Unlock(&vbp_mtx);
556
557        FREE_OBJ(vcl);
558
559        if (!VTAILQ_EMPTY(&vt->vcls))
560                return;
561
562        /* No more polling for this backend */
563
564        b->healthy = 1;
565
566        vt->stop = 1;
567        AZ(pthread_cancel(vt->thread));
568        AZ(pthread_join(vt->thread, &ret));
569
570        b->healthy = 1;
571
572        VTAILQ_REMOVE(&vbp_list, vt, list);
573        b->probe = NULL;
574        VSB_delete(vt->vsb);
575        FREE_OBJ(vt);
576}
577
578/*--------------------------------------------------------------------
579 * Initialize the backend probe subsystem
580 */
581
582void
583VBP_Init(void)
584{
585
586        Lck_New(&vbp_mtx, lck_vbp);
587        CLI_AddFuncs(debug_cmds);
588}
Note: See TracBrowser for help on using the repository browser.