From: phk Date: Mon, 20 Aug 2007 11:46:26 +0000 (+0000) Subject: Separate the generic backend handling from the backend "method". X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=569399aed4239932d75b68b80e283f8cea082937;p=varnish Separate the generic backend handling from the backend "method". For now all we have is a "simple" method, but we want more complex methods later on, round-robin, least-busy and so on. For now, fall into the other ditch and move everything to cache_backend_simple.c, we will move bits back as the gain generality. git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@1892 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- diff --git a/varnish-cache/bin/varnishd/Makefile.am b/varnish-cache/bin/varnishd/Makefile.am index 1685ac3c..b3dba993 100644 --- a/varnish-cache/bin/varnishd/Makefile.am +++ b/varnish-cache/bin/varnishd/Makefile.am @@ -12,6 +12,7 @@ varnishd_SOURCES = \ cache_acceptor_poll.c \ cache_acceptor_kqueue.c \ cache_backend.c \ + cache_backend_simple.c \ cache_ban.c \ cache_center.c \ cache_cli.c \ diff --git a/varnish-cache/bin/varnishd/cache.h b/varnish-cache/bin/varnishd/cache.h index 19160ebc..158b69d6 100644 --- a/varnish-cache/bin/varnishd/cache.h +++ b/varnish-cache/bin/varnishd/cache.h @@ -178,7 +178,7 @@ struct workreq { #include "hash_slinger.h" -/* Backend Connection ------------------------------------------------*/ +/* Backend Request ---------------------------------------------------*/ struct bereq { unsigned magic; @@ -189,14 +189,6 @@ struct bereq { struct http http[1]; }; -struct vbe_conn { - unsigned magic; -#define VBE_CONN_MAGIC 0x0c5e6592 - TAILQ_ENTRY(vbe_conn) list; - struct backend *backend; - int fd; -}; - /* Storage -----------------------------------------------------------*/ struct storage { @@ -325,10 +317,40 @@ struct sess { const char **hashptr; }; +/* -------------------------------------------------------------------*/ + +/* Backend connection */ +struct vbe_conn { + unsigned magic; +#define VBE_CONN_MAGIC 0x0c5e6592 + TAILQ_ENTRY(vbe_conn) list; + struct backend *backend; + int fd; +}; + + +/* Backend method */ +typedef struct vbe_conn *vbe_getfd_f(struct sess *sp); +typedef void vbe_close_f(struct worker *w, struct vbe_conn *vc); +typedef void vbe_recycle_f(struct worker *w, struct vbe_conn *vc); +typedef void vbe_init_f(void); + +struct backend_method { + const char *name; + vbe_getfd_f *getfd; + vbe_close_f *close; + vbe_recycle_f *recycle; + vbe_init_f *init; +}; + +/* Backend indstance */ struct backend { unsigned magic; #define BACKEND_MAGIC 0x64c4c7c6 const char *vcl_name; + + struct backend_method *method; + const char *hostname; const char *portname; @@ -364,6 +386,7 @@ void VCA_Init(void); extern int vca_pipes[2]; /* cache_backend.c */ + void VBE_Init(void); struct vbe_conn *VBE_GetFd(struct sess *sp); void VBE_ClosedFd(struct worker *w, struct vbe_conn *vc); @@ -371,6 +394,9 @@ void VBE_RecycleFd(struct worker *w, struct vbe_conn *vc); struct bereq * VBE_new_bereq(void); void VBE_free_bereq(struct bereq *bereq); +/* cache_backend_simple.c */ +extern struct backend_method backend_method_simple; + /* cache_ban.c */ void AddBan(const char *, int hash); void BAN_Init(void); diff --git a/varnish-cache/bin/varnishd/cache_backend.c b/varnish-cache/bin/varnishd/cache_backend.c index 2e46b6e1..1170d481 100644 --- a/varnish-cache/bin/varnishd/cache_backend.c +++ b/varnish-cache/bin/varnishd/cache_backend.c @@ -28,44 +28,24 @@ * * $Id$ * - * NB: This file is in transition: - * In the future it will contain the central part of the backend handling, - * refcounting, functions for DNS lookup and connection revalidation etc. - * The actual policies will be put in separate files, cache_backend_simple.c, - * cache_backend_round_robin.c etc etc. + * Manage backend connections and requests. * - * Manage backend connections. - * - * XXX: When we switch VCL we can have vbe_conn's dangling from - * XXX: the backends no longer used. When the VCL's refcount - * XXX: drops to zero we should zap them. */ -#include -#include - -#include -#include -#include #include -#include #include -#include -#include -#include #include "heritage.h" #include "shmlog.h" #include "cache.h" -/* A backend IP */ - -static TAILQ_HEAD(,vbe_conn) vbe_head = TAILQ_HEAD_INITIALIZER(vbe_head); static TAILQ_HEAD(,bereq) bereq_head = TAILQ_HEAD_INITIALIZER(bereq_head); static MTX vbemtx; -/*--------------------------------------------------------------------*/ +/*-------------------------------------------------------------------- + * Get a http structure for talking to the backend. + */ struct bereq * VBE_new_bereq(void) @@ -108,245 +88,11 @@ VBE_free_bereq(struct bereq *bereq) /*--------------------------------------------------------------------*/ -static struct vbe_conn * -vbe_new_conn(void) -{ - struct vbe_conn *vbc; - - vbc = calloc(sizeof *vbc, 1); - if (vbc == NULL) - return (NULL); - VSL_stats->n_vbe_conn++; - vbc->magic = VBE_CONN_MAGIC; - vbc->fd = -1; - return (vbc); -} - -/*-------------------------------------------------------------------- - * XXX: There is a race here, we need to lock the replacement of the - * XXX: resolved addresses, or some other thread might try to access - * XXX: them while/during/after we changed them. - * XXX: preferably, we should make a copy to the vbe while we hold a - * XXX: lock anyway. - */ - -static void -vbe_lookup(struct backend *bp) -{ - struct addrinfo *res, hint, *old; - int error; - - memset(&hint, 0, sizeof hint); - hint.ai_family = PF_UNSPEC; - hint.ai_socktype = SOCK_STREAM; - res = NULL; - error = getaddrinfo(bp->hostname, - bp->portname == NULL ? "http" : bp->portname, - &hint, &res); - bp->dnstime = TIM_mono(); - if (error) { - if (res != NULL) - freeaddrinfo(res); - printf("getaddrinfo: %s\n", gai_strerror(error)); /* XXX */ - return; - } - old = bp->addr; - bp->last_addr = res; - bp->addr = res; - if (old != NULL) - freeaddrinfo(old); -} - -/*--------------------------------------------------------------------*/ - -static int -vbe_sock_conn(const struct addrinfo *ai) -{ - int s; - - s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (s >= 0 && connect(s, ai->ai_addr, ai->ai_addrlen)) { - AZ(close(s)); - s = -1; - } - return (s); -} - -/*--------------------------------------------------------------------*/ - -static int -vbe_conn_try(struct backend *bp, struct addrinfo **pai) -{ - struct addrinfo *ai; - int s; - - /* First try the cached good address, and any following it */ - for (ai = bp->last_addr; ai != NULL; ai = ai->ai_next) { - s = vbe_sock_conn(ai); - if (s >= 0) { - bp->last_addr = ai; - *pai = ai; - return (s); - } - } - - /* Then try the list until the cached last good address */ - for (ai = bp->addr; ai != bp->last_addr; ai = ai->ai_next) { - s = vbe_sock_conn(ai); - if (s >= 0) { - bp->last_addr = ai; - *pai = ai; - return (s); - } - } - - if (bp->dnstime + bp->dnsttl >= TIM_mono()) - return (-1); - - /* Then do another lookup to catch DNS changes */ - vbe_lookup(bp); - - /* And try the entire list */ - for (ai = bp->addr; ai != NULL; ai = ai->ai_next) { - s = vbe_sock_conn(ai); - if (s >= 0) { - bp->last_addr = ai; - *pai = ai; - return (s); - } - } - - return (-1); -} - -static int -vbe_connect(struct sess *sp, struct backend *bp) -{ - int s; - char abuf1[TCP_ADDRBUFSIZE], abuf2[TCP_ADDRBUFSIZE]; - char pbuf1[TCP_PORTBUFSIZE], pbuf2[TCP_PORTBUFSIZE]; - struct addrinfo *ai; - - CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC); - AN(bp->hostname); - - s = vbe_conn_try(bp, &ai); - if (s < 0) - return (s); - - TCP_myname(s, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1); - TCP_name(ai->ai_addr, ai->ai_addrlen, - abuf2, sizeof abuf2, pbuf2, sizeof pbuf2); - WSL(sp->wrk, SLT_BackendOpen, s, "%s %s %s %s %s", - bp->vcl_name, abuf1, pbuf1, abuf2, pbuf2); - return (s); -} - -/* Get a backend connection ------------------------------------------ - * - * Try all cached backend connections for this backend, and use the - * first one that is looks like it is still connected. - * If that fails to get us a connection, create a new one, reusing a - * connection from the freelist, if possible. - * - * This function is slightly complicated by optimizations on vbemtx. - */ - -static struct vbe_conn * -vbe_nextfd(struct sess *sp) -{ - struct vbe_conn *vc, *vc2; - struct pollfd pfd; - struct backend *bp; - int reuse = 0; - - CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); - bp = sp->backend; - CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC); - vc2 = NULL; - while (1) { - LOCK(&vbemtx); - vc = TAILQ_FIRST(&bp->connlist); - if (vc != NULL) { - assert(vc->backend == bp); - assert(vc->fd >= 0); - TAILQ_REMOVE(&bp->connlist, vc, list); - } else { - vc2 = TAILQ_FIRST(&vbe_head); - if (vc2 != NULL) { - VSL_stats->backend_unused--; - TAILQ_REMOVE(&vbe_head, vc2, list); - } - } - UNLOCK(&vbemtx); - if (vc == NULL) - break; - - /* Test the connection for remote close before we use it */ - pfd.fd = vc->fd; - pfd.events = POLLIN; - pfd.revents = 0; - if (!poll(&pfd, 1, 0)) { - reuse = 1; - break; - } - VBE_ClosedFd(sp->wrk, vc); - } - - if (vc == NULL) { - if (vc2 == NULL) - vc = vbe_new_conn(); - else - vc = vc2; - if (vc != NULL) { - assert(vc->fd == -1); - AZ(vc->backend); - vc->fd = vbe_connect(sp, bp); - if (vc->fd < 0) { - LOCK(&vbemtx); - TAILQ_INSERT_HEAD(&vbe_head, vc, list); - VSL_stats->backend_unused++; - UNLOCK(&vbemtx); - vc = NULL; - } else { - vc->backend = bp; - } - } - } - LOCK(&vbemtx); - if (vc != NULL ) { - VSL_stats->backend_reuse += reuse; - VSL_stats->backend_conn++; - } else { - VSL_stats->backend_fail++; - } - UNLOCK(&vbemtx); - if (vc != NULL ) { - WSL(sp->wrk, SLT_BackendXID, vc->fd, "%u", sp->xid); - assert(vc->fd >= 0); - assert(vc->backend == bp); - } - return (vc); -} - -/*--------------------------------------------------------------------*/ - struct vbe_conn * VBE_GetFd(struct sess *sp) { - struct vbe_conn *vc; - unsigned n; - for (n = 1; n < 5; n++) { - vc = vbe_nextfd(sp); - if (vc != NULL) { - WSL(sp->wrk, SLT_Backend, sp->fd, "%d %s", vc->fd, - sp->backend->vcl_name); - return (vc); - } - usleep(100000 * n); - } - return (NULL); + return(sp->backend->method->getfd(sp)); } /* Close a connection ------------------------------------------------*/ @@ -355,17 +101,7 @@ void VBE_ClosedFd(struct worker *w, struct vbe_conn *vc) { - CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC); - assert(vc->fd >= 0); - AN(vc->backend); - WSL(w, SLT_BackendClose, vc->fd, "%s", vc->backend->vcl_name); - AZ(close(vc->fd)); - vc->fd = -1; - vc->backend = NULL; - LOCK(&vbemtx); - TAILQ_INSERT_HEAD(&vbe_head, vc, list); - VSL_stats->backend_unused++; - UNLOCK(&vbemtx); + vc->backend->method->close(w, vc); } /* Recycle a connection ----------------------------------------------*/ @@ -374,14 +110,7 @@ void VBE_RecycleFd(struct worker *w, struct vbe_conn *vc) { - CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC); - assert(vc->fd >= 0); - AN(vc->backend); - WSL(w, SLT_BackendReuse, vc->fd, "%s", vc->backend->vcl_name); - LOCK(&vbemtx); - VSL_stats->backend_recycle++; - TAILQ_INSERT_HEAD(&vc->backend->connlist, vc, list); - UNLOCK(&vbemtx); + vc->backend->method->recycle(w, vc); } /*--------------------------------------------------------------------*/ @@ -391,4 +120,5 @@ VBE_Init(void) { MTX_INIT(&vbemtx); + backend_method_simple.init(); } diff --git a/varnish-cache/bin/varnishd/cache_backend_simple.c b/varnish-cache/bin/varnishd/cache_backend_simple.c new file mode 100644 index 00000000..59bb7389 --- /dev/null +++ b/varnish-cache/bin/varnishd/cache_backend_simple.c @@ -0,0 +1,352 @@ +/*- + * Copyright (c) 2006 Verdens Gang AS + * Copyright (c) 2006-2007 Linpro AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + * + * + * XXX: When we switch VCL we can have vbe_conn's dangling from + * XXX: the backends no longer used. When the VCL's refcount + * XXX: drops to zero we should zap them. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "shmlog.h" +#include "cache.h" + +static TAILQ_HEAD(,vbe_conn) vbe_head = TAILQ_HEAD_INITIALIZER(vbe_head); + +static MTX vbemtx; + +/*--------------------------------------------------------------------*/ + +static struct vbe_conn * +vbe_new_conn(void) +{ + struct vbe_conn *vbc; + + vbc = calloc(sizeof *vbc, 1); + if (vbc == NULL) + return (NULL); + VSL_stats->n_vbe_conn++; + vbc->magic = VBE_CONN_MAGIC; + vbc->fd = -1; + return (vbc); +} + +/*-------------------------------------------------------------------- + * XXX: There is a race here, we need to lock the replacement of the + * XXX: resolved addresses, or some other thread might try to access + * XXX: them while/during/after we changed them. + * XXX: preferably, we should make a copy to the vbe while we hold a + * XXX: lock anyway. + */ + +static void +vbe_lookup(struct backend *bp) +{ + struct addrinfo *res, hint, *old; + int error; + + memset(&hint, 0, sizeof hint); + hint.ai_family = PF_UNSPEC; + hint.ai_socktype = SOCK_STREAM; + res = NULL; + error = getaddrinfo(bp->hostname, + bp->portname == NULL ? "http" : bp->portname, + &hint, &res); + bp->dnstime = TIM_mono(); + if (error) { + if (res != NULL) + freeaddrinfo(res); + printf("getaddrinfo: %s\n", gai_strerror(error)); /* XXX */ + return; + } + old = bp->addr; + bp->last_addr = res; + bp->addr = res; + if (old != NULL) + freeaddrinfo(old); +} + +/*--------------------------------------------------------------------*/ + +static int +vbe_sock_conn(const struct addrinfo *ai) +{ + int s; + + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s >= 0 && connect(s, ai->ai_addr, ai->ai_addrlen)) { + AZ(close(s)); + s = -1; + } + return (s); +} + +/*--------------------------------------------------------------------*/ + +static int +vbe_conn_try(struct backend *bp, struct addrinfo **pai) +{ + struct addrinfo *ai; + int s; + + /* First try the cached good address, and any following it */ + for (ai = bp->last_addr; ai != NULL; ai = ai->ai_next) { + s = vbe_sock_conn(ai); + if (s >= 0) { + bp->last_addr = ai; + *pai = ai; + return (s); + } + } + + /* Then try the list until the cached last good address */ + for (ai = bp->addr; ai != bp->last_addr; ai = ai->ai_next) { + s = vbe_sock_conn(ai); + if (s >= 0) { + bp->last_addr = ai; + *pai = ai; + return (s); + } + } + + if (bp->dnstime + bp->dnsttl >= TIM_mono()) + return (-1); + + /* Then do another lookup to catch DNS changes */ + vbe_lookup(bp); + + /* And try the entire list */ + for (ai = bp->addr; ai != NULL; ai = ai->ai_next) { + s = vbe_sock_conn(ai); + if (s >= 0) { + bp->last_addr = ai; + *pai = ai; + return (s); + } + } + + return (-1); +} + +static int +vbe_connect(struct sess *sp, struct backend *bp) +{ + int s; + char abuf1[TCP_ADDRBUFSIZE], abuf2[TCP_ADDRBUFSIZE]; + char pbuf1[TCP_PORTBUFSIZE], pbuf2[TCP_PORTBUFSIZE]; + struct addrinfo *ai; + + CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC); + AN(bp->hostname); + + s = vbe_conn_try(bp, &ai); + if (s < 0) + return (s); + + TCP_myname(s, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1); + TCP_name(ai->ai_addr, ai->ai_addrlen, + abuf2, sizeof abuf2, pbuf2, sizeof pbuf2); + WSL(sp->wrk, SLT_BackendOpen, s, "%s %s %s %s %s", + bp->vcl_name, abuf1, pbuf1, abuf2, pbuf2); + return (s); +} + +/* Get a backend connection ------------------------------------------ + * + * Try all cached backend connections for this backend, and use the + * first one that is looks like it is still connected. + * If that fails to get us a connection, create a new one, reusing a + * connection from the freelist, if possible. + * + * This function is slightly complicated by optimizations on vbemtx. + */ + +static struct vbe_conn * +vbe_nextfd(struct sess *sp) +{ + struct vbe_conn *vc, *vc2; + struct pollfd pfd; + struct backend *bp; + int reuse = 0; + + CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); + bp = sp->backend; + CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC); + vc2 = NULL; + while (1) { + LOCK(&vbemtx); + vc = TAILQ_FIRST(&bp->connlist); + if (vc != NULL) { + assert(vc->backend == bp); + assert(vc->fd >= 0); + TAILQ_REMOVE(&bp->connlist, vc, list); + } else { + vc2 = TAILQ_FIRST(&vbe_head); + if (vc2 != NULL) { + VSL_stats->backend_unused--; + TAILQ_REMOVE(&vbe_head, vc2, list); + } + } + UNLOCK(&vbemtx); + if (vc == NULL) + break; + + /* Test the connection for remote close before we use it */ + pfd.fd = vc->fd; + pfd.events = POLLIN; + pfd.revents = 0; + if (!poll(&pfd, 1, 0)) { + reuse = 1; + break; + } + VBE_ClosedFd(sp->wrk, vc); + } + + if (vc == NULL) { + if (vc2 == NULL) + vc = vbe_new_conn(); + else + vc = vc2; + if (vc != NULL) { + assert(vc->fd == -1); + AZ(vc->backend); + vc->fd = vbe_connect(sp, bp); + if (vc->fd < 0) { + LOCK(&vbemtx); + TAILQ_INSERT_HEAD(&vbe_head, vc, list); + VSL_stats->backend_unused++; + UNLOCK(&vbemtx); + vc = NULL; + } else { + vc->backend = bp; + } + } + } + LOCK(&vbemtx); + if (vc != NULL ) { + VSL_stats->backend_reuse += reuse; + VSL_stats->backend_conn++; + } else { + VSL_stats->backend_fail++; + } + UNLOCK(&vbemtx); + if (vc != NULL ) { + WSL(sp->wrk, SLT_BackendXID, vc->fd, "%u", sp->xid); + assert(vc->fd >= 0); + assert(vc->backend == bp); + } + return (vc); +} + +/*--------------------------------------------------------------------*/ + +static struct vbe_conn * +bes_GetFd(struct sess *sp) +{ + struct vbe_conn *vc; + unsigned n; + + for (n = 1; n < 5; n++) { + vc = vbe_nextfd(sp); + if (vc != NULL) { + WSL(sp->wrk, SLT_Backend, sp->fd, "%d %s", vc->fd, + sp->backend->vcl_name); + return (vc); + } + usleep(100000 * n); + } + return (NULL); +} + +/* Close a connection ------------------------------------------------*/ + +static void +bes_ClosedFd(struct worker *w, struct vbe_conn *vc) +{ + + CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC); + assert(vc->fd >= 0); + AN(vc->backend); + WSL(w, SLT_BackendClose, vc->fd, "%s", vc->backend->vcl_name); + AZ(close(vc->fd)); + vc->fd = -1; + vc->backend = NULL; + LOCK(&vbemtx); + TAILQ_INSERT_HEAD(&vbe_head, vc, list); + VSL_stats->backend_unused++; + UNLOCK(&vbemtx); +} + +/* Recycle a connection ----------------------------------------------*/ + +static void +bes_RecycleFd(struct worker *w, struct vbe_conn *vc) +{ + + CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC); + assert(vc->fd >= 0); + AN(vc->backend); + WSL(w, SLT_BackendReuse, vc->fd, "%s", vc->backend->vcl_name); + LOCK(&vbemtx); + VSL_stats->backend_recycle++; + TAILQ_INSERT_HEAD(&vc->backend->connlist, vc, list); + UNLOCK(&vbemtx); +} + +/*--------------------------------------------------------------------*/ + +static void +bes_Init(void) +{ + + MTX_INIT(&vbemtx); +} + + +/*--------------------------------------------------------------------*/ + + +struct backend_method backend_method_simple = { + .name = "simple", + .getfd = bes_GetFd, + .close = bes_ClosedFd, + .recycle = bes_RecycleFd, + .init = bes_Init +}; diff --git a/varnish-cache/bin/varnishd/cache_vrt.c b/varnish-cache/bin/varnishd/cache_vrt.c index ca3ee0f6..b6876422 100644 --- a/varnish-cache/bin/varnishd/cache_vrt.c +++ b/varnish-cache/bin/varnishd/cache_vrt.c @@ -540,6 +540,8 @@ VRT_init_simple_backend(struct backend **bp, struct vrt_simple_backend *t) b->hostname = strdup(t->host); XXXAN(b->hostname); + b->method = &backend_method_simple; + *bp = b; }