cache_acceptor_poll.c \
cache_acceptor_kqueue.c \
cache_backend.c \
- cache_backend_random.c \
- cache_backend_round_robin.c \
- cache_backend_simple.c \
cache_ban.c \
cache_center.c \
cache_cli.c \
+ cache_dir_random.c \
cache_dir_simple.c \
cache_expire.c \
cache_fetch.c \
+++ /dev/null
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2008 Linpro AS
- * All rights reserved.
- *
- * Author: Cecilie Fritzvold <cecilihf@linpro.no>
- *
- * 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$
- *
- */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netdb.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "shmlog.h"
-#include "cache.h"
-#include "vrt.h"
-
-
-#if 0
-struct ber {
- unsigned magic;
-#define BER_MAGIC 0x645b03f4
- struct brspec *blist;
- int count;
-#if 0
- /* Store a hash of the backend info given in
- * vcl for comparison when a new vcl file is
- * uploaded. Not in use yet.
- */
- unsigned hash;
-#endif
-};
-
-struct brspec {
- unsigned magic;
-#define BRSPEC_MAGIC 0x5aa072a7
- struct brspec *next;
- double limit;
- char *hostname;
- char *portname;
- struct addrinfo *addr;
- struct addrinfo *last_addr;
- double dnsttl;
- double dnstime;
- unsigned dnsseq;
- VTAILQ_HEAD(, vbe_conn) connlist;
- int health;
-};
-
-/*--------------------------------------------------------------------
- * Try to get a socket connected to one of the addresses on the list.
- * We start from the cached "last good" address and try all items on
- * the list exactly once.
- * If a new DNS lookup is made while we try, we start over and try the
- * new list exactly once.
- */
-
-static int
-ber_conn_try_list(const struct sess *sp, struct brspec *bs)
-{
- struct addrinfo *ai, *from;
- int s, loops;
- unsigned myseq;
-
- CHECK_OBJ_NOTNULL(bs, BRSPEC_MAGIC);
- if (bs->addr == NULL)
- return (-1);
- AN(bs->last_addr);
-
- /* Called with lock held */
- myseq = bs->dnsseq;
- loops = 0;
- ai = from = bs->last_addr;
- while (1) {
-
- /* NB: releases/acquires lock */
- s = VBE_TryConnect(sp, ai);
-
- if (s >= 0) {
- /* Update cached "last good" if still valid */
- if (myseq == bs->dnsseq)
- bs->last_addr = ai;
- return (s);
- }
-
- if (myseq != bs->dnsseq) {
- /* A DNS-lookup happended, try again from start */
- loops = 0;
- from = bs->last_addr;
- ai = from;
- } else {
- /* Try next one */
- ai = ai->ai_next;
- if (ai == NULL) {
- loops++;
- ai = bs->addr;
- }
- }
- if (loops == 1 && ai == from)
- return (-1);
- }
-}
-
-/*--------------------------------------------------------------------*/
-
-static const char *
-ber_dns_lookup(struct backend *bp, struct brspec *bs)
-{
- struct addrinfo *res, hint, *old;
- int error;
-
- CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
- CHECK_OBJ_NOTNULL(bs, BRSPEC_MAGIC);
-
- bs->dnstime = TIM_mono();
-
- /* Let go of lock while we do sleepable stuff */
- UNLOCK(&bp->mtx);
-
- memset(&hint, 0, sizeof hint);
- hint.ai_family = PF_UNSPEC;
- hint.ai_socktype = SOCK_STREAM;
- res = NULL;
- error = getaddrinfo(bs->hostname,
- bs->portname == NULL ? "http" : bs->portname,
- &hint, &res);
- LOCK(&bp->mtx);
- if (error) {
- if (res != NULL)
- freeaddrinfo(res);
- return(gai_strerror(error));
- }
- bs->dnsseq++;
- old = bs->addr;
- bs->last_addr = res;
- bs->addr = res;
- if (old != NULL)
- freeaddrinfo(old);
- return (NULL);
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-ber_conn_try(const struct sess *sp, struct backend *bp, struct brspec *bs)
-{
- int s;
-
- LOCK(&bp->mtx);
-
- s = ber_conn_try_list(sp, bs);
- if (s >= 0) {
- bp->refcount++;
- UNLOCK(&bp->mtx);
- return (s);
- }
-
- if (bs->dnstime + bs->dnsttl >= TIM_mono()) {
- UNLOCK(&bp->mtx);
- return (-1);
- }
-
- (void)ber_dns_lookup(bp, bs);
-
- /* And try the entire list */
- s = ber_conn_try_list(sp, bs);
- if (s >= 0) {
- bp->refcount++;
- UNLOCK(&bp->mtx);
- return (s);
- }
-
- UNLOCK(&bp->mtx);
- return (-1);
-
-}
-
-
-/* Get a backend connection ------------------------------------------
- *
- * Get the next backend in the round-robin list, and connect to this.
- *
- * 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 bermtx.
- */
-
-static struct vbe_conn *
-ber_nextfd(const struct sess *sp)
-{
- struct vbe_conn *vc;
- struct backend *bp;
- int reuse = 0;
- struct ber *ber;
- struct brspec *bs;
- int min_health = -10;
- int num = 0;
- double r = 0;
-
- CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
- CHECK_OBJ_NOTNULL(sp->backend, BACKEND_MAGIC);
- bp = sp->backend;
- CAST_OBJ_NOTNULL(ber, bp->priv, BER_MAGIC);
-
- r = (double)rand() / ((double)(RAND_MAX)+1.0);
- bs = ber->blist;
- CHECK_OBJ_NOTNULL(bs, BRSPEC_MAGIC);
- while (r > bs->limit) {
- bs = bs->next;
- CHECK_OBJ_NOTNULL(bs, BRSPEC_MAGIC);
- }
-
- /* If health is low (bad), use round-robin to find
- * a server with better health (if possible).
- */
- while (bs->health < min_health) {
- bs = bs->next;
- num++;
- if (num > ber->count) {
- min_health *= 10;
- num = 0;
- }
- }
-
- while (1) {
- LOCK(&bp->mtx);
- vc = VTAILQ_FIRST(&bs->connlist);
- if (vc != NULL) {
- bp->refcount++;
- assert(vc->backend == bp);
- assert(vc->fd >= 0);
- VTAILQ_REMOVE(&bs->connlist, vc, list);
- }
- UNLOCK(&bp->mtx);
- if (vc == NULL)
- break;
-
- if (VBE_CheckFd(vc->fd)) {
- /* XXX locking of stats */
- VSL_stats->backend_reuse += reuse;
- VSL_stats->backend_conn++;
- return (vc);
- }
- VBE_ClosedFd(sp->wrk, vc);
- }
-
- vc = VBE_NewConn();
- assert(vc->fd == -1);
- AZ(vc->backend);
- vc->fd = ber_conn_try(sp, bp, bs);
- if (vc->fd < 0) {
- VBE_ReleaseConn(vc);
- VSL_stats->backend_fail++;
- return (NULL);
- }
- vc->backend = bp;
- vc->priv = bs;
- VSL_stats->backend_conn++;
- return (vc);
-
-}
-
-static struct vbe_conn *
-ber_GetFd(const struct sess *sp)
-{
- struct vbe_conn *vc;
- unsigned n;
- for (n = 1; n < 5; n++) {
- vc = ber_nextfd(sp);
- if (vc == NULL) {
- AZ(usleep(100000 * n));
- continue;
- }
- assert(vc->fd >= 0);
- assert(vc->backend == sp->backend);
- WSL(sp->wrk, SLT_BackendXID, vc->fd, "%u", sp->xid);
- WSP(sp, SLT_Backend, "%d %s", vc->fd, sp->backend->vcl_name);
- return (vc);
- }
- return (NULL);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-ber_ClosedFd(struct worker *w, struct vbe_conn *vc)
-{
- CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
- CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
- assert(vc->fd >= 0);
- WSL(w, SLT_BackendClose, vc->fd, "%s", vc->backend->vcl_name);
- AZ(close(vc->fd));
- vc->fd = -1;
- VBE_DropRef(vc->backend);
- vc->backend = NULL;
- VBE_ReleaseConn(vc);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-ber_RecycleFd(struct worker *w, struct vbe_conn *vc)
-{
- struct brspec *bs;
-
- CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
- CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
- CAST_OBJ_NOTNULL(bs, vc->priv, BRSPEC_MAGIC);
-
- assert(vc->fd >= 0);
- WSL(w, SLT_BackendReuse, vc->fd, "%s", vc->backend->vcl_name);
- LOCK(&vc->backend->mtx);
- VSL_stats->backend_recycle++;
- VTAILQ_INSERT_HEAD(&bs->connlist, vc, list);
- VBE_DropRefLocked(vc->backend);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-ber_Cleanup(const struct backend *b)
-{
- struct ber *ber;
- struct vbe_conn *vbe;
- struct brspec *bs, *bstmp;
-
- assert(b->refcount == 0);
- CAST_OBJ_NOTNULL(ber, b->priv, BER_MAGIC);
-
- bs = ber->blist;
-
- do {
- free(bs->portname);
- free(bs->hostname);
- freeaddrinfo(bs->addr);
- while (1) {
- vbe = VTAILQ_FIRST(&bs->connlist);
- if (vbe == NULL)
- break;
- VTAILQ_REMOVE(&bs->connlist, vbe, list);
- if (vbe->fd >= 0)
- AZ(close(vbe->fd));
- free(vbe);
- }
- bstmp = bs;
- bs = bs->next;
- free(bstmp);
- } while (bs != ber->blist);
-
- free(ber);
-}
-
-/*--------------------------------------------------------------------*/
-
-/* Will return the hostname of the first backend in the list */
-static const char *
-ber_GetHostname(const struct backend *b)
-{
- struct ber *ber;
-
- CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
- CAST_OBJ_NOTNULL(ber, b->priv, BER_MAGIC);
- return (ber->blist->hostname);
-}
-
-/*--------------------------------------------------------------------*/
-
-/* This should maybe be divided into two separate functions. One for
- * increasing/decreasing health, and one for "pulling" the health
- * towards neutral (0) as time passes
- */
-static void
-ber_UpdateHealth(const struct sess *sp, const struct vbe_conn *vc, int add)
-{
- struct brspec *bs, *first;
- struct ber *ber;
-
- if (vc != NULL) {
- CAST_OBJ_NOTNULL(bs, vc->priv, BRSPEC_MAGIC);
-
- if (bs->health + add >= -10000 || bs->health + add <= 10000)
- bs->health += add;
- } else {
- CAST_OBJ_NOTNULL(ber, sp->backend->priv, BRSPEC_MAGIC);
- first = ber->blist;
- bs = first;
- do {
- bs = bs->next;
- bs->health = (int)((double)bs->health / 2);
- } while (bs != first);
- }
-}
-
-/*--------------------------------------------------------------------*/
-
-struct backend_method backend_method_random = {
- .name = "random",
- .getfd = ber_GetFd,
- .close = ber_ClosedFd,
- .recycle = ber_RecycleFd,
- .gethostname = ber_GetHostname,
- .updatehealth = ber_UpdateHealth,
- .cleanup = ber_Cleanup,
-};
-
-#endif
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_init_dir_random(struct cli *cli, struct director **bp, const struct vrt_dir_random *t)
-{
- (void)cli;
- (void)bp;
- (void)t;
-
-
-#if 0
- struct backend *b;
-
- if (VBE_AddBackend(&backend_method_random, t->ident, bp))
- return; /* reuse existing backend */
-
- bp = *bp;
- AN(t->name);
- REPLACE(b->vcl_name, t->name);
- struct backend *b;
- struct ber *ber;
- struct vrt_backend_entry *be;
- struct brspec *bs = NULL;
- struct brspec *bs_prev = NULL;
- struct brspec *bs_first = NULL;
- double limit = 0;
- double default_weight;
-
- /*
- * Scan existing backends to see if we can recycle one of them.
- */
-
- /*
- * XXX: Do this by comparing a hash generated from this new
- * XXX: backend with the earlier computed hashes from existing
- * XXX: backends ? Should the hash be a parameter to this function,
- * XXX: or computed here?
- */
-
- b = VBE_NewBackend(&backend_method_random);
-
- ber = calloc(sizeof *ber, 1);
- XXXAN(ber);
- ber->magic = BER_MAGIC;
- ber->count = t->count;
-
- b->priv = ber;
-
- AN(t->name);
- b->vcl_name = strdup(t->name);
- XXXAN(b->vcl_name);
-
- default_weight = 1.0 / (double)t->count;
-
- be = t->bentry;
- while (be != NULL) {
- bs = calloc(sizeof *bs, 1);
- XXXAN(bs);
- bs->magic = BRSPEC_MAGIC;
- AN(be->port);
- bs->portname = strdup(be->port);
- XXXAN(bs->portname);
-
- AN(be->host);
- bs->hostname = strdup(be->host);
- XXXAN(bs->hostname);
-
- if (!(t->weighted))
- be->weight = default_weight;
-
- limit += be->weight;
- bs->limit = limit;
-
- bs->dnsttl = 300;
- bs->health = 0;
-
- if (bs_first == NULL)
- bs_first = bs;
-
- bs->next = bs_prev;
- bs_prev = bs;
- be = be->next;
- }
- XXXAN(bs_first);
- bs_first->next = bs;
- ber->blist = bs;
-
- *bp = b;
-#endif
-}
-
+++ /dev/null
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2008 Linpro AS
- * All rights reserved.
- *
- * Author: Cecilie Fritzvold <cecilihf@linpro.no>
- *
- * 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$
- *
- */
-
-#if 0
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netdb.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "shmlog.h"
-#include "cache.h"
-#include "vrt.h"
-
-
-struct brr {
- unsigned magic;
-#define BRR_MAGIC 0x66f05894
- struct bspec *blist;
- int count;
-#if 0
- /* Store a hash of the backend info given in
- * vcl for comparison when a new vcl file is
- * uploaded. Not in use yet.
- */
- unsigned hash;
-#endif
-};
-
-struct bspec {
- unsigned magic;
-#define BSPEC_MAGIC 0x761d69c2
- struct bspec *next;
- char *hostname;
- char *portname;
- struct addrinfo *addr;
- struct addrinfo *last_addr;
- double dnsttl;
- double dnstime;
- unsigned dnsseq;
- VTAILQ_HEAD(, vbe_conn) connlist;
- int health;
-};
-
-/*--------------------------------------------------------------------
- * Try to get a socket connected to one of the addresses on the list.
- * We start from the cached "last good" address and try all items on
- * the list exactly once.
- * If a new DNS lookup is made while we try, we start over and try the
- * new list exactly once.
- */
-
-static int
-brr_conn_try_list(const struct sess *sp, struct bspec *bs)
-{
- struct addrinfo *ai, *from;
- int s, loops;
- unsigned myseq;
-
- CHECK_OBJ_NOTNULL(bs, BSPEC_MAGIC);
- if (bs->addr == NULL)
- return (-1);
- AN(bs->last_addr);
-
- /* Called with lock held */
- myseq = bs->dnsseq;
- loops = 0;
- ai = from = bs->last_addr;
- while (1) {
-
- /* NB: releases/acquires lock */
- s = VBE_TryConnect(sp, ai);
-
- if (s >= 0) {
- /* Update cached "last good" if still valid */
- if (myseq == bs->dnsseq)
- bs->last_addr = ai;
- return (s);
- }
-
- if (myseq != bs->dnsseq) {
- /* A DNS-lookup happended, try again from start */
- loops = 0;
- from = bs->last_addr;
- ai = from;
- } else {
- /* Try next one */
- ai = ai->ai_next;
- if (ai == NULL) {
- loops++;
- ai = bs->addr;
- }
- }
- if (loops == 1 && ai == from)
- return (-1);
- }
-}
-
-/*--------------------------------------------------------------------*/
-
-static const char *
-brr_dns_lookup(struct backend *bp, struct bspec *bs)
-{
- struct addrinfo *res, hint, *old;
- int error;
-
- CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
- CHECK_OBJ_NOTNULL(bs, BSPEC_MAGIC);
-
- bs->dnstime = TIM_mono();
-
- /* Let go of lock while we do sleepable stuff */
- UNLOCK(&bp->mtx);
-
- memset(&hint, 0, sizeof hint);
- hint.ai_family = PF_UNSPEC;
- hint.ai_socktype = SOCK_STREAM;
- res = NULL;
- error = getaddrinfo(bs->hostname,
- bs->portname == NULL ? "http" : bs->portname,
- &hint, &res);
- LOCK(&bp->mtx);
- if (error) {
- if (res != NULL)
- freeaddrinfo(res);
- return(gai_strerror(error));
- }
- bs->dnsseq++;
- old = bs->addr;
- bs->last_addr = res;
- bs->addr = res;
- if (old != NULL)
- freeaddrinfo(old);
- return (NULL);
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-brr_conn_try(const struct sess *sp, struct backend *bp, struct bspec *bs)
-{
- int s;
-
- LOCK(&bp->mtx);
-
- s = brr_conn_try_list(sp, bs);
- if (s >= 0) {
- bp->refcount++;
- UNLOCK(&bp->mtx);
- return (s);
- }
-
- if (bs->dnstime + bs->dnsttl >= TIM_mono()) {
- UNLOCK(&bp->mtx);
- return (-1);
- }
-
- (void)brr_dns_lookup(bp, bs);
-
- /* And try the entire list */
- s = brr_conn_try_list(sp, bs);
- if (s >= 0) {
- bp->refcount++;
- UNLOCK(&bp->mtx);
- return (s);
- }
-
- UNLOCK(&bp->mtx);
- return (-1);
-}
-
-
-/* Get a backend connection ------------------------------------------
- *
- * Get the next backend in the round-robin list, and connect to this.
- *
- * 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 brrmtx.
- */
-
-static struct vbe_conn *
-brr_nextfd(const struct sess *sp)
-{
- struct vbe_conn *vc;
- struct backend *bp;
- int reuse = 0;
- struct brr *brr;
- struct bspec *bs;
- int min_health = -10;
- int num = 0;
-
- CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
- CHECK_OBJ_NOTNULL(sp->backend, BACKEND_MAGIC);
- bp = sp->backend;
- CAST_OBJ_NOTNULL(brr, bp->priv, BRR_MAGIC);
-
- do {
- bs = brr->blist = brr->blist->next;
- num++;
- if (num > brr->count) {
- min_health *= 10;
- num = 0;
- }
- } while (bs->health < min_health);
-
- while (1) {
- LOCK(&bp->mtx);
- vc = VTAILQ_FIRST(&bs->connlist);
- if (vc != NULL) {
- bp->refcount++;
- assert(vc->backend == bp);
- assert(vc->fd >= 0);
- VTAILQ_REMOVE(&bs->connlist, vc, list);
- }
- UNLOCK(&bp->mtx);
- if (vc == NULL)
- break;
-
- if (VBE_CheckFd(vc->fd)) {
- /* XXX locking of stats */
- VSL_stats->backend_reuse += reuse;
- VSL_stats->backend_conn++;
- return (vc);
- }
- VBE_ClosedFd(sp->wrk, vc);
- }
-
- vc = VBE_NewConn();
- assert(vc->fd == -1);
- AZ(vc->backend);
- vc->fd = brr_conn_try(sp, bp, bs);
- if (vc->fd < 0) {
- VBE_ReleaseConn(vc);
- VSL_stats->backend_fail++;
- return (NULL);
- }
- vc->backend = bp;
- vc->priv = bs;
- VSL_stats->backend_conn++;
- return (vc);
-}
-
-static struct vbe_conn *
-brr_GetFd(const struct sess *sp)
-{
- struct vbe_conn *vc;
- unsigned n;
- for (n = 1; n < 5; n++) {
- vc = brr_nextfd(sp);
- if (vc == NULL) {
- AZ(usleep(100000 * n));
- continue;
- }
- assert(vc->fd >= 0);
- assert(vc->backend == sp->backend);
- WSL(sp->wrk, SLT_BackendXID, vc->fd, "%u", sp->xid);
- WSP(sp, SLT_Backend, "%d %s", vc->fd, sp->backend->vcl_name);
- return (vc);
- }
- return (NULL);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-brr_ClosedFd(struct worker *w, struct vbe_conn *vc)
-{
- CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
- CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
- assert(vc->fd >= 0);
- WSL(w, SLT_BackendClose, vc->fd, "%s", vc->backend->vcl_name);
- AZ(close(vc->fd));
- vc->fd = -1;
- VBE_DropRef(vc->backend);
- vc->backend = NULL;
- VBE_ReleaseConn(vc);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-brr_RecycleFd(struct worker *w, struct vbe_conn *vc)
-{
- struct bspec *bs;
-
- CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
- CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
- CAST_OBJ_NOTNULL(bs, vc->priv, BSPEC_MAGIC);
-
- assert(vc->fd >= 0);
- WSL(w, SLT_BackendReuse, vc->fd, "%s", vc->backend->vcl_name);
- LOCK(&vc->backend->mtx);
- VSL_stats->backend_recycle++;
- VTAILQ_INSERT_HEAD(&bs->connlist, vc, list);
- VBE_DropRefLocked(vc->backend);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-brr_Cleanup(const struct backend *b)
-{
- struct brr *brr;
- struct vbe_conn *vbe;
- struct bspec *bs, *bstmp;
-
- assert(b->refcount == 0);
- CAST_OBJ_NOTNULL(brr, b->priv, BRR_MAGIC);
-
- bs = brr->blist;
-
- do {
- free(bs->portname);
- free(bs->hostname);
- freeaddrinfo(bs->addr);
- while (1) {
- vbe = VTAILQ_FIRST(&bs->connlist);
- if (vbe == NULL)
- break;
- VTAILQ_REMOVE(&bs->connlist, vbe, list);
- if (vbe->fd >= 0)
- AZ(close(vbe->fd));
- free(vbe);
- }
- bstmp = bs;
- bs = bs->next;
- free(bstmp);
- } while (bs != brr->blist);
-
- free(brr);
-}
-
-/*--------------------------------------------------------------------*/
-
-/* Will return the hostname of the first backend in the list */
-static const char *
-brr_GetHostname(const struct backend *b)
-{
- struct brr *brr;
-
- CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
- CAST_OBJ_NOTNULL(brr, b->priv, BRR_MAGIC);
- return (brr->blist->hostname);
-}
-
-/*--------------------------------------------------------------------*/
-
-/* This should maybe be divided into two separate functions. One for
- * increasing/decreasing health, and one for "pulling" the health
- * towards neutral (0) as time passes
- */
-static void
-brr_UpdateHealth(const struct sess *sp, const struct vbe_conn *vc, int add)
-{
- struct bspec *bs, *first;
- struct brr *brr;
-
- if (vc != NULL) {
-
- CAST_OBJ_NOTNULL(bs, vc->priv, BSPEC_MAGIC);
-
- if (bs->health + add >= -10000 || bs->health + add <= 10000)
- bs->health += add;
- } else {
- CAST_OBJ_NOTNULL(brr, sp->backend->priv, BSPEC_MAGIC);
- first = brr->blist;
- bs = first;
- do {
- bs = bs->next;
- bs->health = (int)((double)bs->health / 2);
- } while (bs != first);
- }
-}
-
-/*--------------------------------------------------------------------*/
-
-struct backend_method backend_method_round_robin = {
- .name = "round_robin",
- .getfd = brr_GetFd,
- .close = brr_ClosedFd,
- .recycle = brr_RecycleFd,
- .gethostname = brr_GetHostname,
- .updatehealth = brr_UpdateHealth,
- .cleanup = brr_Cleanup,
-};
-
-/*--------------------------------------------------------------------*/
-
-void
-VRT_init_round_robin_backend(struct backend **bp, const struct vrt_round_robin_backend *t)
-{
- struct backend *b;
- struct brr *brr;
- struct vrt_backend_entry *be;
- struct bspec *bs = NULL;
- struct bspec *bs_prev = NULL;
- struct bspec *bs_first = NULL;
-
- /*
- * Scan existing backends to see if we can recycle one of them.
- */
- /*
- * XXX: Do this by comparing a hash generated from this new
- * XXX: backend with the earlier computed hashes from existing
- * XXX: backends ? Should the hash be a parameter to this function,
- * XXX: or computed here?
- */
-
-
- b = VBE_NewBackend(&backend_method_round_robin);
-
- brr = calloc(sizeof *brr, 1);
- XXXAN(brr);
- brr->magic = BRR_MAGIC;
- brr->count = t->count;
-
- b->priv = brr;
-
- AN(t->name);
- b->vcl_name = strdup(t->name);
- XXXAN(b->vcl_name);
-
- be = t->bentry;
- while (be != NULL) {
- bs = calloc(sizeof *bs, 1);
- XXXAN(bs);
- bs->magic = BSPEC_MAGIC;
- AN(be->port);
- bs->portname = strdup(be->port);
- XXXAN(bs->portname);
-
- AN(be->host);
- bs->hostname = strdup(be->host);
- XXXAN(bs->hostname);
-
- bs->dnsttl = 300;
- bs->health = 0;
-
- if (bs_first == NULL)
- bs_first = bs;
-
- bs->next = bs_prev;
- bs_prev = bs;
- be = be->next;
- }
-
- XXXAN(bs_first);
- bs_first->next = bs;
- brr->blist = bs;
-
- *bp = b;
-}
-
-#endif
+++ /dev/null
-/*-
- * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2008 Linpro AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
- *
- * 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$
- *
- */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-
-#include <netdb.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "shmlog.h"
-#include "cache.h"
-#include "vrt.h"
-
-#if 0
-
-struct bes {
- unsigned magic;
-#define BES_MAGIC 0x015e17ac
- char *hostname;
- char *portname;
- struct addrinfo *addr;
- struct addrinfo *last_addr;
- double dnsttl;
- double dnstime;
- unsigned dnsseq;
- VTAILQ_HEAD(, vbe_conn) connlist;
-};
-
-/*--------------------------------------------------------------------
- * Try to get a socket connected to one of the addresses on the list.
- * We start from the cached "last good" address and try all items on
- * the list exactly once.
- * If a new DNS lookup is made while we try, we start over and try the
- * new list exactly once.
- */
-
-static int
-bes_conn_try_list(const struct sess *sp, struct bes *bes)
-{
- struct addrinfo *ai, *from;
- int s, loops;
- unsigned myseq;
-
- CHECK_OBJ_NOTNULL(bes, BES_MAGIC);
- if (bes->addr == NULL)
- return (-1);
- AN(bes->last_addr);
-
- /* Called with lock held */
- myseq = bes->dnsseq;
- loops = 0;
- ai = from = bes->last_addr;
- while (1) {
-
- /* NB: releases/acquires lock */
- s = VBE_TryConnect(sp, ai);
-
- if (s >= 0) {
- /* Update cached "last good" if still valid */
- if (myseq == bes->dnsseq)
- bes->last_addr = ai;
- return (s);
- }
-
- if (myseq != bes->dnsseq) {
- /* A DNS-lookup happended, try again from start */
- loops = 0;
- from = bes->last_addr;
- ai = from;
- } else {
- /* Try next one */
- ai = ai->ai_next;
- if (ai == NULL) {
- loops++;
- ai = bes->addr;
- }
- }
- if (loops == 1 && ai == from)
- return (-1);
- }
-}
-
-/*--------------------------------------------------------------------*/
-
-static const char *
-bes_dns_lookup(struct backend *bp)
-{
- struct addrinfo *res, hint, *old;
- struct bes *bes;
- int error;
-
- CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
- CAST_OBJ_NOTNULL(bes, bp->priv, BES_MAGIC);
-
- bes->dnstime = TIM_mono();
-
- /* Let go of lock while we do sleepable stuff */
- UNLOCK(&bp->mtx);
-
- memset(&hint, 0, sizeof hint);
- hint.ai_family = PF_UNSPEC;
- hint.ai_socktype = SOCK_STREAM;
- res = NULL;
- error = getaddrinfo(bes->hostname,
- bes->portname == NULL ? "http" : bes->portname,
- &hint, &res);
- LOCK(&bp->mtx);
- if (error) {
- if (res != NULL)
- freeaddrinfo(res);
- return(gai_strerror(error));
- }
- bes->dnsseq++;
- old = bes->addr;
- bes->last_addr = res;
- bes->addr = res;
- if (old != NULL)
- freeaddrinfo(old);
- return (NULL);
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-bes_conn_try(const struct sess *sp, struct backend *bp)
-{
- int s;
- struct bes *bes;
-
- CAST_OBJ_NOTNULL(bes, bp->priv, BES_MAGIC);
-
- LOCK(&bp->mtx);
-
- s = bes_conn_try_list(sp, bes);
- if (s >= 0) {
- bp->refcount++;
- UNLOCK(&bp->mtx);
- return (s);
- }
-
- if (bes->dnstime + bes->dnsttl >= TIM_mono()) {
- UNLOCK(&bp->mtx);
- return (-1);
- }
-
- (void)bes_dns_lookup(bp);
-
- /* And try the entire list */
- s = bes_conn_try_list(sp, bes);
- if (s >= 0) {
- bp->refcount++;
- UNLOCK(&bp->mtx);
- return (s);
- }
-
- UNLOCK(&bp->mtx);
- return (-1);
-}
-
-/* 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 besmtx.
- */
-
-static struct vbe_conn *
-bes_nextfd(const struct sess *sp)
-{
- struct vbe_conn *vc;
- struct backend *bp;
- int reuse = 0;
- struct bes *bes;
-
- CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
- CHECK_OBJ_NOTNULL(sp->backend, BACKEND_MAGIC);
- bp = sp->backend;
- CAST_OBJ_NOTNULL(bes, bp->priv, BES_MAGIC);
- while (1) {
- LOCK(&bp->mtx);
- vc = VTAILQ_FIRST(&bes->connlist);
- if (vc != NULL) {
- bp->refcount++;
- assert(vc->backend == bp);
- assert(vc->fd >= 0);
- VTAILQ_REMOVE(&bes->connlist, vc, list);
- }
- UNLOCK(&bp->mtx);
- if (vc == NULL)
- break;
-
- if (VBE_CheckFd(vc->fd)) {
- /* XXX locking of stats */
- VSL_stats->backend_reuse += reuse;
- VSL_stats->backend_conn++;
- return (vc);
- }
- VBE_ClosedFd(sp->wrk, vc);
- }
-
- vc = VBE_NewConn();
- assert(vc->fd == -1);
- AZ(vc->backend);
- vc->fd = bes_conn_try(sp, bp);
- if (vc->fd < 0) {
- VBE_ReleaseConn(vc);
- VSL_stats->backend_fail++;
- return (NULL);
- }
- vc->backend = bp;
- VSL_stats->backend_conn++;
- return (vc);
-}
-
-/*--------------------------------------------------------------------*/
-
-static struct vbe_conn *
-bes_GetFd(const struct sess *sp)
-{
- struct vbe_conn *vc;
- unsigned n;
- for (n = 1; n < 5; n++) {
- vc = bes_nextfd(sp);
- if (vc == NULL) {
- AZ(usleep(100000 * n));
- continue;
- }
- assert(vc->fd >= 0);
- assert(vc->backend == sp->backend);
- WSL(sp->wrk, SLT_BackendXID, vc->fd, "%u", sp->xid);
- WSP(sp, SLT_Backend, "%d %s", vc->fd, sp->backend->vcl_name);
- return (vc);
- }
- return (NULL);
-}
-
-/* Close a connection ------------------------------------------------*/
-
-static void
-bes_ClosedFd(struct worker *w, struct vbe_conn *vc)
-{
- int i;
-
- CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
- CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
- assert(vc->fd >= 0);
- WSL(w, SLT_BackendClose, vc->fd, "%s", vc->backend->vcl_name);
- i = close(vc->fd);
- assert(i == 0 || errno == ECONNRESET || errno == ENOTCONN);
- vc->fd = -1;
- VBE_DropRef(vc->backend);
- vc->backend = NULL;
- VBE_ReleaseConn(vc);
-}
-
-/* Recycle a connection ----------------------------------------------*/
-
-static void
-bes_RecycleFd(struct worker *w, struct vbe_conn *vc)
-{
- struct bes *bes;
-
- CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
- CHECK_OBJ_NOTNULL(vc->backend, BACKEND_MAGIC);
- CAST_OBJ_NOTNULL(bes, vc->backend->priv, BES_MAGIC);
-
- assert(vc->fd >= 0);
- WSL(w, SLT_BackendReuse, vc->fd, "%s", vc->backend->vcl_name);
- LOCK(&vc->backend->mtx);
- VSL_stats->backend_recycle++;
- VTAILQ_INSERT_HEAD(&bes->connlist, vc, list);
- VBE_DropRefLocked(vc->backend);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-bes_Cleanup(const struct backend *b)
-{
- struct bes *bes;
- struct vbe_conn *vbe;
-
- assert(b->refcount == 0);
- CAST_OBJ_NOTNULL(bes, b->priv, BES_MAGIC);
- free(bes->portname);
- free(bes->hostname);
- freeaddrinfo(bes->addr);
- while (1) {
- vbe = VTAILQ_FIRST(&bes->connlist);
- if (vbe == NULL)
- break;
- VTAILQ_REMOVE(&bes->connlist, vbe, list);
- if (vbe->fd >= 0)
- AZ(close(vbe->fd));
- FREE_OBJ(vbe);
- }
- FREE_OBJ(bes);
-}
-
-/*--------------------------------------------------------------------*/
-
-static const char *
-bes_GetHostname(const struct backend *b)
-{
- struct bes *bes;
-
- CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
- CAST_OBJ_NOTNULL(bes, b->priv, BES_MAGIC);
- return (bes->hostname);
-}
-
-
-#endif
--- /dev/null
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2008 Linpro AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
+ *
+ * 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$
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "shmlog.h"
+#include "cache.h"
+#include "vrt.h"
+
+/*--------------------------------------------------------------------*/
+
+struct vdi_random_host {
+ struct backend *backend;
+ unsigned weight;
+};
+
+struct vdi_random {
+ unsigned magic;
+#define VDI_RANDOM_MAGIC 0x476d25b7
+ struct director dir;
+ struct backend *backend;
+ unsigned nhosts;
+ struct vdi_random_host *hosts;
+};
+
+
+static struct backend *
+vdi_random_choose(struct sess *sp)
+{
+ struct vdi_random *vs;
+ uint32_t r;
+ struct vdi_random_host *vh;
+
+ CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC);
+ CAST_OBJ_NOTNULL(vs, sp->director->priv, VDI_RANDOM_MAGIC);
+ r = random();
+ r &= 0x7fffffff;
+
+ for (vh = vs->hosts; ; vh++)
+ if (r < vh->weight)
+ return (vh->backend);
+ assert(0 != __LINE__);
+ return (NULL);
+}
+
+static void
+vdi_random_fini(struct director *d)
+{
+ struct vdi_random *vs;
+
+ CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
+ CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
+
+ VBE_DropRef(vs->backend);
+ free(vs->hosts);
+ free(vs);
+}
+
+void
+VRT_init_dir_random(struct cli *cli, struct director **bp, const struct vrt_dir_random *t)
+{
+ struct vdi_random *vs;
+ const struct vrt_dir_random_entry *te;
+ struct vdi_random_host *vh;
+ double s, a, b;
+ unsigned v;
+ int i;
+
+ (void)cli;
+
+ vs = calloc(sizeof *vs, 1);
+ XXXAN(vs);
+ vs->hosts = calloc(sizeof *vh, t->nmember);
+ XXXAN(vs->hosts);
+
+ vs->magic = VDI_RANDOM_MAGIC;
+ vs->dir.magic = DIRECTOR_MAGIC;
+ vs->dir.priv = vs;
+ vs->dir.name = "random";
+ vs->dir.choose = vdi_random_choose;
+ vs->dir.fini = vdi_random_fini;
+
+ s = 0;
+ vh = vs->hosts;
+ te = t->members;
+ for (i = 0; i < t->nmember; i++, vh++, te++) {
+ s += te->weight;
+ vh->backend = VBE_AddBackend(cli, te->host);
+ }
+
+ /* Normalize weights */
+ i = 0;
+ a = 0.0;
+ for (te = t->members; te->host != NULL; te++, i++) {
+ /* First normalize the specified weight in FP */
+ b = te->weight / s;
+ /* Then accumulate to eliminate rounding errors */
+ a += b;
+ /* Convert to unsigned in random() range */
+ v = (unsigned)((1U<<31) * a);
+ vs->hosts[i].weight = v;
+ }
+ *bp = &vs->dir;
+}