Commit db7a9cf4 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Add another hash-method with better real-world survival chances: A classic

bucketed hash table of lists.  Hash is MD5.  Number of buckets and number
of mutexes can be configured at command line.



git-svn-id: http://www.varnish-cache.org/svn/trunk@279 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 19af9af7
...@@ -21,6 +21,7 @@ varnishd_SOURCES = \ ...@@ -21,6 +21,7 @@ varnishd_SOURCES = \
cache_vrt.c \ cache_vrt.c \
cli_event.c \ cli_event.c \
hash_simple_list.c \ hash_simple_list.c \
hash_classic.c \
mgt_child.c \ mgt_child.c \
rfc2616.c \ rfc2616.c \
storage_file.c \ storage_file.c \
......
...@@ -27,20 +27,7 @@ struct worker { ...@@ -27,20 +27,7 @@ struct worker {
struct worker; struct worker;
#endif #endif
/* Hashing -----------------------------------------------------------*/ #include "hash_slinger.h"
typedef void hash_init_f(void);
typedef struct objhead *hash_lookup_f(const char *key, struct objhead *nobj);
typedef int hash_deref_f(struct objhead *obj);
struct hash_slinger {
const char *name;
hash_init_f *init;
hash_lookup_f *lookup;
hash_deref_f *deref;
};
extern struct hash_slinger hsl_slinger;
/* Storage -----------------------------------------------------------*/ /* Storage -----------------------------------------------------------*/
......
...@@ -245,7 +245,7 @@ FetchSession(struct worker *w, struct sess *sp) ...@@ -245,7 +245,7 @@ FetchSession(struct worker *w, struct sess *sp)
sp->obj->xid = sp->xid; sp->obj->xid = sp->xid;
fd = VBE_GetFd(sp->backend, &fd_token, sp->xid); fd = VBE_GetFd(sp->backend, &fd_token, sp->xid);
assert(fd != -1); assert(fd != -1); /* XXX: handle this */
VSL(SLT_Backend, sp->fd, "%d %s", fd, sp->backend->vcl_name); VSL(SLT_Backend, sp->fd, "%d %s", fd, sp->backend->vcl_name);
hp = http_New(); hp = http_New();
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "libvarnish.h" #include "libvarnish.h"
#include "shmlog.h" #include "shmlog.h"
#include "heritage.h"
#include "cache.h" #include "cache.h"
static struct hash_slinger *hash; static struct hash_slinger *hash;
...@@ -129,7 +130,7 @@ void ...@@ -129,7 +130,7 @@ void
HSH_Init(void) HSH_Init(void)
{ {
hash = &hsl_slinger; hash = heritage.hash;
if (hash->init != NULL) if (hash->start != NULL)
hash->init(); hash->start();
} }
/* /*
* $Id$ * $Id$
*
* XXX: automatic thread-pool size adaptation.
*/ */
#include <stdio.h> #include <stdio.h>
...@@ -14,6 +16,7 @@ ...@@ -14,6 +16,7 @@
#include <event.h> #include <event.h>
#include "libvarnish.h" #include "libvarnish.h"
#include "heritage.h"
#include "shmlog.h" #include "shmlog.h"
#include "vcl.h" #include "vcl.h"
#include "cache.h" #include "cache.h"
...@@ -154,9 +157,10 @@ CacheInitPool(void) ...@@ -154,9 +157,10 @@ CacheInitPool(void)
AZ(pthread_cond_init(&shdcnd, NULL)); AZ(pthread_cond_init(&shdcnd, NULL));
for (i = 0; i < 5; i++) for (i = 0; i < heritage.wthread_min; i++) {
AZ(pthread_create(&tp, NULL, CacheWorker, NULL)); AZ(pthread_create(&tp, NULL, CacheWorker, NULL));
AZ(pthread_detach(tp)); AZ(pthread_detach(tp));
}
srandomdev(); srandomdev();
xids = random(); xids = random();
} }
/*
* $Id$
*
* A classic bucketed hash
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <queue.h>
#include <sys/types.h>
#include <md5.h>
#include <libvarnish.h>
#include <cache.h>
/*--------------------------------------------------------------------*/
struct hcl_entry {
TAILQ_ENTRY(hcl_entry) list;
char *key;
struct objhead *obj;
unsigned refcnt;
unsigned hash;
unsigned mtx;
};
TAILQ_HEAD(hcl_head, hcl_entry);
static struct hcl_head *hcl_head;
static unsigned hcl_nhash = 256;
static unsigned hcl_nmtx = 16;
static pthread_mutex_t *hcl_mutex;
/*--------------------------------------------------------------------
* The ->init method allows the management process to pass arguments
*/
static int
hcl_init(const char *p)
{
int i;
unsigned u1, u2;
i = sscanf(p, "%u,%u", &u1, &u2);
if (i == 0)
return (0);
if (u1 == 0 || (i == 2 && (u2 == 0 || u2 > u1))) {
fprintf(stderr, "Invallid parameters to hash \"classic\":\n");
fprintf(stderr,
"\t-h classic,<bucket count>[,<buckets per mutex>]\n");
return (1);
}
hcl_nhash = u1;
if (i == 1) {
hcl_nmtx = hcl_nhash / 16;
if (hcl_nmtx < 1)
hcl_nmtx = 1;
return(0);
} else {
hcl_nmtx = hcl_nhash / u2;
if (hcl_nmtx < 1)
hcl_nmtx = 1;
}
fprintf(stderr, "Classic hash: %u buckets %u mutexes\n",
hcl_nhash, hcl_nmtx);
return (0);
}
/*--------------------------------------------------------------------
* The ->start method is called during cache process start and allows
* initialization to happen before the first lookup.
*/
static void
hcl_start(void)
{
unsigned u;
hcl_head = calloc(sizeof *hcl_head, hcl_nhash);
assert(hcl_head != NULL);
hcl_mutex = calloc(sizeof *hcl_mutex, hcl_nmtx);
assert(hcl_mutex != NULL);
for (u = 0; u < hcl_nhash; u++)
TAILQ_INIT(&hcl_head[u]);
for (u = 0; u < hcl_nmtx; u++)
AZ(pthread_mutex_init(&hcl_mutex[u], NULL));
}
/*--------------------------------------------------------------------
* Lookup and possibly insert element.
* If nobj != NULL and the lookup does not find key, nobj is inserted.
* If nobj == NULL and the lookup does not find key, NULL is returned.
* A reference to the returned object is held.
*/
static struct objhead *
hcl_lookup(const char *key, struct objhead *nobj)
{
struct hcl_entry *he, *he2;
MD5_CTX c;
unsigned char md5[MD5_DIGEST_LENGTH];
unsigned u1, u2;
int i;
MD5Init(&c);
MD5Update(&c, key, strlen(key));
MD5Final(md5, &c);
memcpy(&u1, md5, sizeof u1);
u1 %= hcl_nhash;
memcpy(&u2, md5 + sizeof u1, sizeof u2);
u2 %= hcl_nmtx;
AZ(pthread_mutex_lock(&hcl_mutex[u2]));
TAILQ_FOREACH(he, &hcl_head[u1], list) {
i = strcmp(key, he->key);
if (i < 0)
continue;
if (i == 0) {
he->refcnt++;
nobj = he->obj;
nobj->hashpriv = he;
AZ(pthread_mutex_unlock(&hcl_mutex[u2]));
return (nobj);
}
if (nobj == NULL) {
AZ(pthread_mutex_unlock(&hcl_mutex[u2]));
return (NULL);
}
break;
}
he2 = calloc(sizeof *he2, 1);
assert(he2 != NULL);
he2->obj = nobj;
he2->refcnt = 1;
he2->hash = u1;
he2->mtx = u2;
he2->key = strdup(key);
assert(he2->key != NULL);
nobj->hashpriv = he2;
if (he != NULL)
TAILQ_INSERT_BEFORE(he, he2, list);
else
TAILQ_INSERT_TAIL(&hcl_head[u1], he2, list);
AZ(pthread_mutex_unlock(&hcl_mutex[u2]));
return (nobj);
}
/*--------------------------------------------------------------------
* Dereference and if no references are left, free.
*/
static int
hcl_deref(struct objhead *obj)
{
struct hcl_entry *he;
int ret;
unsigned mtx;
assert(obj->hashpriv != NULL);
he = obj->hashpriv;
mtx = he->mtx;
AZ(pthread_mutex_lock(&hcl_mutex[mtx]));
if (--he->refcnt == 0) {
free(he->key);
TAILQ_REMOVE(&hcl_head[he->hash], he, list);
free(he);
ret = 0;
} else
ret = 1;
AZ(pthread_mutex_unlock(&hcl_mutex[mtx]));
return (ret);
}
/*--------------------------------------------------------------------*/
struct hash_slinger hcl_slinger = {
"classic",
hcl_init,
hcl_start,
hcl_lookup,
hcl_deref,
};
...@@ -112,6 +112,7 @@ hsl_deref(struct objhead *obj) ...@@ -112,6 +112,7 @@ hsl_deref(struct objhead *obj)
struct hash_slinger hsl_slinger = { struct hash_slinger hsl_slinger = {
"simple_list", "simple_list",
NULL,
hsl_init, hsl_init,
hsl_lookup, hsl_lookup,
hsl_deref, hsl_deref,
......
/*
* $Id$
*/
typedef int hash_init_f(const char *);
typedef void hash_start_f(void);
typedef struct objhead *hash_lookup_f(const char *key, struct objhead *nobj);
typedef int hash_deref_f(struct objhead *obj);
struct hash_slinger {
const char *name;
hash_init_f *init;
hash_start_f *start;
hash_lookup_f *lookup;
hash_deref_f *deref;
};
...@@ -31,7 +31,13 @@ struct heritage { ...@@ -31,7 +31,13 @@ struct heritage {
/* Storage method */ /* Storage method */
struct stevedore *stevedore; struct stevedore *stevedore;
/* Hash method */
struct hash_slinger *hash;
unsigned default_ttl; unsigned default_ttl;
/* Worker threads */
unsigned wthread_min, wthread_max;
}; };
extern struct heritage heritage; extern struct heritage heritage;
......
...@@ -19,5 +19,10 @@ int open_tcp(const char *port); ...@@ -19,5 +19,10 @@ int open_tcp(const char *port);
extern struct stevedore sma_stevedore; extern struct stevedore sma_stevedore;
extern struct stevedore smf_stevedore; extern struct stevedore smf_stevedore;
#include "hash_slinger.h"
extern struct hash_slinger hsl_slinger;
extern struct hash_slinger hcl_slinger;
void VSL_MgtInit(const char *fn, unsigned size); void VSL_MgtInit(const char *fn, unsigned size);
extern struct varnish_stats *VSL_stats; extern struct varnish_stats *VSL_stats;
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "cache.h" #include "cache.h"
#include "libvarnish.h" #include "libvarnish.h"
#include "heritage.h" #include "heritage.h"
/*-------------------------------------------------------------------- /*--------------------------------------------------------------------
* From RFC2616, 13.2.3 Age Calculations * From RFC2616, 13.2.3 Age Calculations
* *
...@@ -106,6 +107,7 @@ RFC2616_cache_policy(struct sess *sp, struct http *hp) ...@@ -106,6 +107,7 @@ RFC2616_cache_policy(struct sess *sp, struct http *hp)
switch (http_GetStatus(hp)) { switch (http_GetStatus(hp)) {
case 200: /* OK */ case 200: /* OK */
sp->obj->valid = 1; sp->obj->valid = 1;
/* FALLTHROUGH */
case 203: /* Non-Authoritative Information */ case 203: /* Non-Authoritative Information */
case 300: /* Multiple Choices */ case 300: /* Multiple Choices */
case 301: /* Moved Permanently */ case 301: /* Moved Permanently */
......
...@@ -297,26 +297,47 @@ testme(void) ...@@ -297,26 +297,47 @@ testme(void)
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
static int
cmp_hash(struct hash_slinger *s, const char *p, const char *q)
{
if (strlen(s->name) != q - p)
return (1);
if (strncmp(s->name, p, q - p))
return (1);
return (0);
}
static void static void
usage(void) setup_hash(const char *sflag)
{ {
fprintf(stderr, "usage: varnishd [options]\n"); const char *p, *q;
fprintf(stderr, " %-28s # %s\n", "-b", "backend_IP_number"); struct hash_slinger *hp;
fprintf(stderr, " %-28s # %s\n", "-d", "debug");
fprintf(stderr, " %-28s # %s\n", "-f", "VCL_file"); p = strchr(sflag, ',');
fprintf(stderr, " %-28s # %s\n", "-p number", "TCP listen port"); if (p == NULL)
fprintf(stderr, " %-28s # %s\n", q = p = strchr(sflag, '\0');
"-s kind[,storageoptions]", "Backend storage specification"); else
fprintf(stderr, " %-28s # %s\n", "-t", "Default TTL"); q = p + 1;
#if 0 assert(p != NULL);
-c clusterid@cluster_controller assert(q != NULL);
-m memory_limit if (!cmp_hash(&hcl_slinger, sflag, p)) {
-s kind[,storage-options] hp = &hcl_slinger;
-l logfile,logsize } else if (!cmp_hash(&hsl_slinger, sflag, p)) {
-u uid hp = &hsl_slinger;
-a CLI_port } else {
#endif fprintf(stderr, "Unknown hash method \"%*.*s\"\n",
exit(1); p - sflag, p - sflag, sflag);
exit (2);
}
heritage.hash = hp;
if (hp->init != NULL) {
if (hp->init(q))
exit (1);
} else if (*q) {
fprintf(stderr, "Hash method \"%s\" takes no arguments\n",
hp->name);
exit (1);
}
} }
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
...@@ -359,6 +380,36 @@ setup_storage(const char *sflag) ...@@ -359,6 +380,36 @@ setup_storage(const char *sflag)
stp->init(heritage.stevedore, q); stp->init(heritage.stevedore, q);
} }
/*--------------------------------------------------------------------*/
static void
usage(void)
{
fprintf(stderr, "usage: varnishd [options]\n");
fprintf(stderr, " %-28s # %s\n", "-b backend",
"backend IP or hostname");
fprintf(stderr, " %-28s # %s\n", "-d", "debug");
fprintf(stderr, " %-28s # %s\n", "-f file", "VCL_file");
fprintf(stderr, " %-28s # %s\n",
"-h kind[,hashoptions]", "Hash specification");
fprintf(stderr, " %-28s # %s\n", "-p number", "TCP listen port");
fprintf(stderr, " %-28s # %s\n",
"-s kind[,storageoptions]", "Backend storage specification");
fprintf(stderr, " %-28s # %s\n", "-t", "Default TTL");
fprintf(stderr, " %-28s # %s\n", "-w int[,int]",
"Number of worker threads (fixed/{min,max})");
#if 0
-c clusterid@cluster_controller
-m memory_limit
-s kind[,storage-options]
-l logfile,logsize
-u uid
-a CLI_port
#endif
exit(1);
}
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
/* for development purposes */ /* for development purposes */
...@@ -368,20 +419,24 @@ setup_storage(const char *sflag) ...@@ -368,20 +419,24 @@ setup_storage(const char *sflag)
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int o; int o, i;
unsigned ua, ub;
const char *portnumber = "8080"; const char *portnumber = "8080";
unsigned dflag = 1; /* XXX: debug=on for now */ unsigned dflag = 1; /* XXX: debug=on for now */
const char *bflag = NULL; const char *bflag = NULL;
const char *fflag = NULL; const char *fflag = NULL;
const char *sflag = "file"; const char *sflag = "file";
const char *hflag = "classic";
register_printf_render_std((const unsigned char *)"HVQ"); register_printf_render_std((const unsigned char *)"HVQ");
VCC_InitCompile(); VCC_InitCompile();
heritage.default_ttl = 120; heritage.default_ttl = 120;
heritage.wthread_min = 5;
heritage.wthread_max = 5;
while ((o = getopt(argc, argv, "b:df:p:s:t:")) != -1) while ((o = getopt(argc, argv, "b:df:h:p:s:t:w:")) != -1)
switch (o) { switch (o) {
case 'b': case 'b':
bflag = optarg; bflag = optarg;
...@@ -392,6 +447,9 @@ main(int argc, char *argv[]) ...@@ -392,6 +447,9 @@ main(int argc, char *argv[])
case 'f': case 'f':
fflag = optarg; fflag = optarg;
break; break;
case 'h':
hflag = optarg;
break;
case 'p': case 'p':
portnumber = optarg; portnumber = optarg;
break; break;
...@@ -401,6 +459,15 @@ main(int argc, char *argv[]) ...@@ -401,6 +459,15 @@ main(int argc, char *argv[])
case 't': case 't':
heritage.default_ttl = strtoul(optarg, NULL, 0); heritage.default_ttl = strtoul(optarg, NULL, 0);
break; break;
case 'w':
i = sscanf(optarg, "%u,%u", &ua, &ub);
if (i == 0)
usage();
heritage.wthread_min = ua;
heritage.wthread_max = ua;
if (i == 2)
heritage.wthread_max = ub;
break;
default: default:
usage(); usage();
} }
...@@ -430,6 +497,7 @@ main(int argc, char *argv[]) ...@@ -430,6 +497,7 @@ main(int argc, char *argv[])
exit (1); exit (1);
setup_storage(sflag); setup_storage(sflag);
setup_hash(hflag);
/* /*
* XXX: Lacking the suspend/resume facility (due to the socket API * XXX: Lacking the suspend/resume facility (due to the socket API
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment