From 54003230fe90a10180ca39deee254e63fde7642e Mon Sep 17 00:00:00 2001 From: dormando Date: Sun, 29 Jun 2008 15:35:45 -0700 Subject: [PATCH 01/24] Don't leak reference on OOM error. --- memcached.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/memcached.c b/memcached.c index ba1d1d016b..21ecdc8a49 100644 --- a/memcached.c +++ b/memcached.c @@ -859,6 +859,9 @@ int do_store_item(item *it, int comm) { if (new_it == NULL) { /* SERVER_ERROR out of memory */ + if (old_it != NULL) + do_item_remove(old_it); + return 0; } From 7a7cdfe10df10e05ee7a4a30c9aa0ed025e552b1 Mon Sep 17 00:00:00 2001 From: dormando Date: Sun, 29 Jun 2008 15:51:32 -0700 Subject: [PATCH 02/24] Don't leak references in process_get_command. --- memcached.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/memcached.c b/memcached.c index 21ecdc8a49..0f8c69c9a2 100644 --- a/memcached.c +++ b/memcached.c @@ -1252,7 +1252,10 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, if (new_list) { c->isize *= 2; c->ilist = new_list; - } else break; + } else { + item_remove(it); + break; + } } /* @@ -1272,7 +1275,10 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, if (new_suffix_list) { c->suffixsize *= 2; c->suffixlist = new_suffix_list; - } else break; + } else { + item_remove(it); + break; + } } suffix = suffix_from_freelist(); @@ -1283,6 +1289,7 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, stats.get_misses += stats_get_misses; STATS_UNLOCK(); out_string(c, "SERVER_ERROR out of memory making CAS suffix"); + item_remove(it); return; } *(c->suffixlist + i) = suffix; @@ -1293,6 +1300,7 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, add_iov(c, suffix, strlen(suffix)) != 0 || add_iov(c, ITEM_data(it), it->nbytes) != 0) { + item_remove(it); break; } } @@ -1302,6 +1310,7 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, add_iov(c, ITEM_key(it), it->nkey) != 0 || add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0) { + item_remove(it); break; } } From c27f9c9c8f6e2665efd3d70330956830f74e4778 Mon Sep 17 00:00:00 2001 From: dormando Date: Sun, 29 Jun 2008 16:45:59 -0700 Subject: [PATCH 03/24] Don't leave stale data on failed set attempt. This is mildly less confusing for folks with OOM errors or who store items for long periods of time, but occasionally attempt setting a very large item. While I don't believe this actually fixes a class of bugs we've seen due to refcount leaks, I make this change for consistency. --- memcached.c | 11 +++++++++++ t/getset.t | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/memcached.c b/memcached.c index 0f8c69c9a2..93a8f67f6d 100644 --- a/memcached.c +++ b/memcached.c @@ -1439,6 +1439,17 @@ static void process_update_command(conn *c, token_t *tokens, const size_t ntoken /* swallow the data line */ c->write_and_go = conn_swallow; c->sbytes = vlen + 2; + + /* Avoid stale data persisting in cache because we failed alloc. + * Unacceptable for SET. Anywhere else too? */ + if (comm == NREAD_SET) { + it = item_get(key, nkey); + if (it) { + item_unlink(it); + item_remove(it); + } + } + return; } if(handle_cas) diff --git a/t/getset.t b/t/getset.t index 7b8b7651e2..1438411e5e 100755 --- a/t/getset.t +++ b/t/getset.t @@ -1,7 +1,7 @@ #!/usr/bin/perl use strict; -use Test::More tests => 535; +use Test::More tests => 539; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; @@ -83,10 +83,15 @@ is(scalar <$sock>, "DELETED\r\n", "pipeline delete"); my $len = 1024; while ($len < 1024*1028) { my $val = "B"x$len; - print $sock "set foo_$len 0 0 $len\r\n$val\r\n"; if ($len > (1024*1024)) { + # Ensure causing a memory overflow doesn't leave stale data. + print $sock "set foo_$len 0 0 3\r\nMOO\r\n"; + is(scalar <$sock>, "STORED\r\n"); + print $sock "set foo_$len 0 0 $len\r\n$val\r\n"; is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "failed to store size $len"); + mem_get_is($sock, "foo_$len"); } else { + print $sock "set foo_$len 0 0 $len\r\n$val\r\n"; is(scalar <$sock>, "STORED\r\n", "stored size $len"); } $len += 2048; From 8468fd232fbbd25924b193a30c007b308d4301da Mon Sep 17 00:00:00 2001 From: dormando Date: Sun, 6 Jul 2008 17:00:15 -0700 Subject: [PATCH 04/24] Storage of values with negative lengths was undefined, potential crash. Reported on the mailing list by Marcus Hunger --- memcached.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/memcached.c b/memcached.c index 93a8f67f6d..77895e3a1d 100644 --- a/memcached.c +++ b/memcached.c @@ -1407,7 +1407,8 @@ static void process_update_command(conn *c, token_t *tokens, const size_t ntoken req_cas_id = strtoull(tokens[5].value, NULL, 10); } - if(errno == ERANGE || ((flags == 0 || exptime == 0) && errno == EINVAL)) { + if(errno == ERANGE || ((flags == 0 || exptime == 0) && errno == EINVAL) + || vlen < 0) { out_string(c, "CLIENT_ERROR bad command line format"); return; } From 846ddb6a311f25c3f1ef17207eef6b338f0d7c79 Mon Sep 17 00:00:00 2001 From: Steve Yen Date: Wed, 23 Apr 2008 12:40:13 -0700 Subject: [PATCH 05/24] lru tests --- t/lru.t | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/t/lru.t b/t/lru.t index 99e07d5c9d..a198b0f436 100755 --- a/t/lru.t +++ b/t/lru.t @@ -1,11 +1,61 @@ #!/usr/bin/perl use strict; -use Test::More skip_all => "Tests not written."; # tests => 1 +use Test::More tests => 149; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; +# assuming max slab is 1M and default mem is 64M my $server = new_memcached(); my $sock = $server->sock; +# create a big value for the largest slab +my $max = 1024 * 1024; +my $big = "a big value that's > .5M and < 1M. "; +while (length($big) * 2 < $max) { + $big = $big . $big; +} + +ok(length($big) > 512 * 1024); +ok(length($big) < 1024 * 1024); + +# test that an even bigger value is rejected while we're here +my $too_big = $big . $big . $big; +my $len = length($too_big); +print $sock "set too_big 0 0 $len\r\n$too_big\r\n"; +is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "too_big not stored"); + +# set the big value +my $len = length($big); +print $sock "set big 0 0 $len\r\n$big\r\n"; +is(scalar <$sock>, "STORED\r\n", "stored big"); +mem_get_is($sock, "big", $big); + +# no evictions yet +my $stats = mem_stats($sock); +is($stats->{"evictions"}, "0", "no evictions to start"); + +# set many big items, enough to get evictions +for (my $i = 0; $i < 100; $i++) { + print $sock "set item_$i 0 0 $len\r\n$big\r\n"; + is(scalar <$sock>, "STORED\r\n", "stored item_$i"); +} + +# some evictions should have happened +my $stats = mem_stats($sock); +my $evictions = int($stats->{"evictions"}); +ok($evictions == 37, "some evictions happened"); + +# the first big value should be gone +mem_get_is($sock, "big", undef); + +# the earliest items should be gone too +for (my $i = 0; $i < $evictions - 1; $i++) { + mem_get_is($sock, "item_$i", undef); +} + +# check that the non-evicted are the right ones +for (my $i = $evictions - 1; $i < $evictions + 4; $i++) { + mem_get_is($sock, "item_$i", $big); +} From e4534c8552cc53ad4fd2bf431ee169b3667970aa Mon Sep 17 00:00:00 2001 From: Trond Norbye Date: Wed, 23 Jul 2008 11:38:29 -0700 Subject: [PATCH 06/24] Add DTrace probes for Solaris/etc. --- Makefile.am | 18 ++- assoc.c | 22 +++- configure.ac | 22 +++- items.c | 6 + memcached.c | 68 ++++++++--- memcached.h | 12 +- memcached_dtrace.d | 274 ++++++++++++++++++++++++++++++++++++++++++++ memcached_dtrace.h | 276 +++++++++++++++++++++++++++++++++++++++++++++ slabs.c | 58 ++++++---- thread.c | 7 +- 10 files changed, 713 insertions(+), 50 deletions(-) create mode 100644 memcached_dtrace.d create mode 100644 memcached_dtrace.h diff --git a/Makefile.am b/Makefile.am index bec67d3823..3b2b990765 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,8 +3,22 @@ bin_PROGRAMS = memcached memcached-debug memcached_SOURCES = memcached.c slabs.c slabs.h items.c items.h assoc.c assoc.h memcached.h thread.c stats.c stats.h memcached_debug_SOURCES = $(memcached_SOURCES) memcached_CPPFLAGS = -DNDEBUG -memcached_LDADD = @LIBOBJS@ -memcached_debug_LDADD = $(memcached_LDADD) +memcached_LDADD = @DTRACE_OBJ@ @DAEMON_OBJ@ +memcached_debug_LDADD = @DTRACE_DEBUG_OBJ@ @DAEMON_OBJ@ +memcached_DEPENDENCIES = @DTRACE_OBJ@ @DAEMON_OBJ@ +memcached_debug_DEPENDENCIES = @DTRACE_DEBUG_OBJ@ @DAEMON_OBJ@ + +memcached_dtrace.h: + ${DTRACE} -h -s memcached_dtrace.d + sed -e s,_DTRACE_VERSION,ENABLE_DTRACE,g memcached_dtrace.h | \ + tr '\t' ' ' | grep -v unistd.h > memcached_dtrace.tmp + mv memcached_dtrace.tmp memcached_dtrace.h + +memcached_dtrace.o: $(memcached_OBJECTS) + $(DTRACE) $(DTRACEFLAGS) -G -o memcached_dtrace.o -s ${srcdir}/memcached_dtrace.d $(memcached_OBJECTS) + +memcached_debug_dtrace.o: $(memcached_debug_OBJECTS) + $(DTRACE) $(DTRACEFLAGS) -G -o memcached_debug_dtrace.o -s ${srcdir}/memcached_dtrace.d $(memcached_debug_OBJECTS) SUBDIRS = doc DIST_DIRS = scripts diff --git a/assoc.c b/assoc.c index 5170a8a228..09d12017df 100644 --- a/assoc.c +++ b/assoc.c @@ -496,14 +496,18 @@ item *assoc_find(const char *key, const size_t nkey) { it = primary_hashtable[hv & hashmask(hashpower)]; } + item *ret = NULL; + int depth = 0; while (it) { - if ((nkey == it->nkey) && - (memcmp(key, ITEM_key(it), nkey) == 0)) { - return it; + if ((nkey == it->nkey) && (memcmp(key, ITEM_key(it), nkey) == 0)) { + ret = it; + break; } it = it->h_next; + ++depth; } - return 0; + MEMCACHED_ASSOC_FIND(key, depth); + return ret; } /* returns the address of the item pointer before the key. if *item == 0, @@ -595,6 +599,7 @@ int assoc_insert(item *it) { assoc_expand(); } + MEMCACHED_ASSOC_INSERT(ITEM_key(it), hash_items); return 1; } @@ -602,10 +607,15 @@ void assoc_delete(const char *key, const size_t nkey) { item **before = _hashitem_before(key, nkey); if (*before) { - item *nxt = (*before)->h_next; + item *nxt; + hash_items--; + /* The DTrace probe cannot be triggered as the last instruction + * due to possible tail-optimization by the compiler + */ + MEMCACHED_ASSOC_DELETE(key, hash_items); + nxt = (*before)->h_next; (*before)->h_next = 0; /* probably pointless, but whatever. */ *before = nxt; - hash_items--; return; } /* Note: we never actually get here. the callers don't delete things diff --git a/configure.ac b/configure.ac index a304dc6aa6..e7110db87e 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,25 @@ AC_PROG_CC AM_PROG_CC_C_O AC_PROG_INSTALL +AC_ARG_ENABLE(dtrace, + [AS_HELP_STRING([--enable-dtrace],[Enable dtrace probes])]) +if test "x$enable_dtrace" == "xyes"; then + AC_PATH_PROG([DTRACE], [dtrace], "no", [/usr/sbin:$PATH]) + if test "x$DTRACE" != "xno"; then + AC_DEFINE([ENABLE_DTRACE],1,[Set to nonzero if you want to include DTRACE]) + DTRACE_OBJ=memcached_dtrace.o + DTRACE_DEBUG_OBJ=memcached_debug_dtrace.o + else + AC_MSG_ERROR([Need dtrace binary and OS support.]) + fi +else + AC_DEFINE([ENABLE_DTRACE],0,[Set to nonzero if you want to include DTRACE]) +fi +AC_SUBST(DTRACE) +AC_SUBST(DTRACE_OBJ) +AC_SUBST(DTRACE_DEBUG_OBJ) +AC_SUBST(DTRACEFLAGS) + AC_ARG_ENABLE(64bit, [AS_HELP_STRING([--enable-64bit],[build 64bit verison])]) if test "x$enable_64bit" == "xyes" @@ -113,7 +132,8 @@ AC_SEARCH_LIBS(socket, socket) AC_SEARCH_LIBS(gethostbyname, nsl) AC_SEARCH_LIBS(mallinfo, malloc) -AC_CHECK_FUNC(daemon,AC_DEFINE([HAVE_DAEMON],,[Define this if you have daemon()]),[AC_LIBOBJ(daemon)]) +AC_CHECK_FUNC(daemon,AC_DEFINE([HAVE_DAEMON],,[Define this if you have daemon()]),[DAEMON_OBJ=daemon.o]) +AC_SUBST(DAEMON_OBJ) AC_HEADER_STDBOOL AC_C_CONST diff --git a/items.c b/items.c index 88c92f6cbf..d9e779312d 100644 --- a/items.c +++ b/items.c @@ -229,6 +229,7 @@ static void item_unlink_q(item *it) { } int do_item_link(item *it) { + MEMCACHED_ITEM_LINK(ITEM_key(it), it->nbytes); assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0); assert(it->nbytes < (1024 * 1024)); /* 1MB max size */ it->it_flags |= ITEM_LINKED; @@ -250,6 +251,7 @@ int do_item_link(item *it) { } void do_item_unlink(item *it) { + MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nbytes); if ((it->it_flags & ITEM_LINKED) != 0) { it->it_flags &= ~ITEM_LINKED; STATS_LOCK(); @@ -263,6 +265,7 @@ void do_item_unlink(item *it) { } void do_item_remove(item *it) { + MEMCACHED_ITEM_REMOVE(ITEM_key(it), it->nbytes); assert((it->it_flags & ITEM_SLABBED) == 0); if (it->refcount != 0) { it->refcount--; @@ -275,6 +278,7 @@ void do_item_remove(item *it) { } void do_item_update(item *it) { + MEMCACHED_ITEM_UPDATE(ITEM_key(it), it->nbytes); if (it->time < current_time - ITEM_UPDATE_INTERVAL) { assert((it->it_flags & ITEM_SLABBED) == 0); @@ -287,6 +291,8 @@ void do_item_update(item *it) { } int do_item_replace(item *it, item *new_it) { + MEMCACHED_ITEM_REPLACE(ITEM_key(it), it->nbytes, + ITEM_key(new_it), new_it->nbytes); assert((it->it_flags & ITEM_SLABBED) == 0); do_item_unlink(it); diff --git a/memcached.c b/memcached.c index 77895e3a1d..3b7a187f72 100644 --- a/memcached.c +++ b/memcached.c @@ -301,6 +301,8 @@ conn *conn_new(const int sfd, const int init_state, const int event_flags, fprintf(stderr, "calloc()\n"); return NULL; } + MEMCACHED_CONN_CREATE(c); + c->rbuf = c->wbuf = 0; c->ilist = 0; c->suffixlist = 0; @@ -325,13 +327,7 @@ conn *conn_new(const int sfd, const int init_state, const int event_flags, if (c->rbuf == 0 || c->wbuf == 0 || c->ilist == 0 || c->iov == 0 || c->msglist == 0 || c->suffixlist == 0) { - if (c->rbuf != 0) free(c->rbuf); - if (c->wbuf != 0) free(c->wbuf); - if (c->ilist !=0) free(c->ilist); - if (c->suffixlist != 0) free(c->suffixlist); - if (c->iov != 0) free(c->iov); - if (c->msglist != 0) free(c->msglist); - free(c); + conn_free(c); fprintf(stderr, "malloc()\n"); return NULL; } @@ -391,6 +387,8 @@ conn *conn_new(const int sfd, const int init_state, const int event_flags, stats.total_conns++; STATS_UNLOCK(); + MEMCACHED_CONN_ALLOCATE(c->sfd); + return c; } @@ -427,6 +425,7 @@ static void conn_cleanup(conn *c) { */ void conn_free(conn *c) { if (c) { + MEMCACHED_CONN_DESTROY(c); if (c->hdrbuf) free(c->hdrbuf); if (c->msglist) @@ -454,6 +453,7 @@ static void conn_close(conn *c) { if (settings.verbose > 1) fprintf(stderr, "<%d connection closed.\n", c->sfd); + MEMCACHED_CONN_RELEASE(c->sfd); close(c->sfd); accept_new_conns(true); conn_cleanup(c); @@ -543,6 +543,10 @@ static void conn_set_state(conn *c, int state) { assoc_move_next_bucket(); } c->state = state; + + if (state == conn_write) { + MEMCACHED_PROCESS_COMMAND_END(c->sfd, c->wbuf, c->wbytes); + } } } @@ -784,9 +788,32 @@ static void complete_nread(conn *c) { out_string(c, "CLIENT_ERROR bad data chunk"); } else { ret = store_item(it, comm); - if (ret == 1) + if (ret == 1) { out_string(c, "STORED"); - else if(ret == 2) +#ifdef HAVE_DTRACE + switch (comm) { + case NREAD_ADD: + MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nbytes); + break; + case NREAD_REPLACE: + MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nbytes); + break; + case NREAD_APPEND: + MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nbytes); + break; + case NREAD_PREPEND: + MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nbytes); + break; + case NREAD_SET: + MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nbytes); + break; + case NREAD_CAS: + MEMCACHED_COMMAND_CAS(c->sfd, ITEM_key(it), it->nbytes, + it->cas_id); + break; + } +#endif + } else if(ret == 2) out_string(c, "EXISTS"); else if(ret == 3) out_string(c, "NOT_FOUND"); @@ -1268,6 +1295,8 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, if(return_cas == true) { + MEMCACHED_COMMAND_GETS(c->sfd, ITEM_key(it), it->nbytes, + it->cas_id); /* Goofy mid-flight realloc. */ if (i >= c->suffixsize) { char **new_suffix_list = realloc(c->suffixlist, @@ -1306,6 +1335,8 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, } else { + MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nbytes); + if (add_iov(c, "VALUE ", 6) != 0 || add_iov(c, ITEM_key(it), it->nkey) != 0 || add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0) @@ -1327,6 +1358,11 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, } else { stats_get_misses++; + if (return_cas) { + MEMCACHED_COMMAND_GETS(c->sfd, key, -1, 0); + } else { + MEMCACHED_COMMAND_GET(c->sfd, key, -1); + } } key_token++; @@ -1508,13 +1544,14 @@ static void process_arithmetic_command(conn *c, token_t *tokens, const size_t nt return; } - out_string(c, add_delta(it, incr, delta, temp)); + out_string(c, add_delta(c, it, incr, delta, temp)); item_remove(it); /* release our reference */ } /* * adds a delta value to a numeric item. * + * c connection requesting the operation * it item to adjust * incr true to increment value, false to decrement * delta amount to adjust value by @@ -1522,7 +1559,7 @@ static void process_arithmetic_command(conn *c, token_t *tokens, const size_t nt * * returns a response string to send back to the client. */ -char *do_add_delta(item *it, const bool incr, const int64_t delta, char *buf) { +char *do_add_delta(conn *c, item *it, const bool incr, const int64_t delta, char *buf) { char *ptr; int64_t value; int res; @@ -1536,11 +1573,13 @@ char *do_add_delta(item *it, const bool incr, const int64_t delta, char *buf) { return "CLIENT_ERROR cannot increment or decrement non-numeric value"; } - if (incr) + if (incr) { value += delta; - else { + MEMCACHED_COMMAND_INCR(c->sfd, ITEM_key(it), value); + } else { if (delta >= value) value = 0; else value -= delta; + MEMCACHED_COMMAND_DECR(c->sfd, ITEM_key(it), value); } sprintf(buf, "%llu", value); res = strlen(buf); @@ -1608,6 +1647,7 @@ static void process_delete_command(conn *c, token_t *tokens, const size_t ntoken it = item_get(key, nkey); if (it) { + MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), exptime); if (exptime == 0) { item_unlink(it); item_remove(it); /* release our reference */ @@ -1672,6 +1712,8 @@ static void process_command(conn *c, char *command) { assert(c != NULL); + MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes); + if (settings.verbose > 1) fprintf(stderr, "<%d %s\n", c->sfd, command); diff --git a/memcached.h b/memcached.h index ffbe880d61..fcef0b7be2 100644 --- a/memcached.h +++ b/memcached.h @@ -240,7 +240,8 @@ char *do_suffix_from_freelist(); bool do_suffix_add_to_freelist(char *s); char *do_defer_delete(item *item, time_t exptime); void do_run_deferred_deletes(void); -char *do_add_delta(item *item, const bool incr, const int64_t delta, char *buf); +char *do_add_delta(conn *c, item *item, const bool incr, const int64_t delta, + char *buf); int do_store_item(item *item, int comm); conn *conn_new(const int sfd, const int init_state, const int event_flags, const int read_buffer_size, const bool is_udp, struct event_base *base); @@ -249,7 +250,7 @@ conn *conn_new(const int sfd, const int init_state, const int event_flags, const #include "slabs.h" #include "assoc.h" #include "items.h" - +#include "memcached_dtrace.h" /* * In multithreaded mode, we wrap certain functions with lock management and @@ -271,7 +272,8 @@ int dispatch_event_add(int thread, conn *c); void dispatch_conn_new(int sfd, int init_state, int event_flags, int read_buffer_size, int is_udp); /* Lock wrappers for cache functions that are called from main loop. */ -char *mt_add_delta(item *item, const int incr, const int64_t delta, char *buf); +char *mt_add_delta(conn *c, item *item, const int incr, const int64_t delta, + char *buf); void mt_assoc_move_next_bucket(void); conn *mt_conn_from_freelist(void); bool mt_conn_add_to_freelist(conn *c); @@ -300,7 +302,7 @@ void mt_stats_unlock(void); int mt_store_item(item *item, int comm); -# define add_delta(x,y,z,a) mt_add_delta(x,y,z,a) +# define add_delta(c,x,y,z,a) mt_add_delta(c,x,y,z,a) # define assoc_move_next_bucket() mt_assoc_move_next_bucket() # define conn_from_freelist() mt_conn_from_freelist() # define conn_add_to_freelist(x) mt_conn_add_to_freelist(x) @@ -331,7 +333,7 @@ int mt_store_item(item *item, int comm); #else /* !USE_THREADS */ -# define add_delta(x,y,z,a) do_add_delta(x,y,z,a) +# define add_delta(c,x,y,z,a) do_add_delta(c,x,y,z,a) # define assoc_move_next_bucket() do_assoc_move_next_bucket() # define conn_from_freelist() do_conn_from_freelist() # define conn_add_to_freelist(x) do_conn_add_to_freelist(x) diff --git a/memcached_dtrace.d b/memcached_dtrace.d new file mode 100644 index 0000000000..9119dd41aa --- /dev/null +++ b/memcached_dtrace.d @@ -0,0 +1,274 @@ +/* + * Copyright (c) <2008>, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``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 SUN MICROSYSTEMS, INC. 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. + */ +provider memcached { + /** + * Fired when a connection object is allocated from the connection pool + * @param connid the connection id + */ + probe conn__allocate(int connid); + + /** + * Fired when a connection object is released back to the connection pool + * @param connid the connection id + */ + probe conn__release(int connid); + + /** + * Fired when a new connection object is being created (there is no more + * connection objects in the connection pool) + * @param ptr pointer to the connection object + */ + probe conn__create(void *ptr); + + /** + * Fired when a connection object is being destroyed ("released back to + * the memory subsystem") + * @param ptr pointer to the connection object + */ + probe conn__destroy(void *ptr); + + /** + * Fired when a connection is dispatched from the "main thread" to a + * worker thread. + * @param connid the connection id + * @param threadid the thread id + */ + probe conn__dispatch(int connid, int threadid); + + /** + * Allocate memory from the slab allocator + * @param size the requested size + * @param slabclass the allocation will be fulfilled in this class + * @param slabsize the size of each item in this class + * @param ptr pointer to allocated memory + */ + probe slabs__allocate(int size, int slabclass, int slabsize, void* ptr); + + /** + * Failed to allocate memory (out of memory) + * @param size the requested size + * @param slabclass the class that failed to fulfill the request + */ + probe slabs__allocate__failed(int size, int slabclass); + + /** + * Fired when a slab class needs more space + * @param slabclass class that needs more memory + */ + probe slabs__slabclass__allocate(int slabclass); + + /** + * Failed to allocate memory (out of memory) + * @param slabclass the class that failed grab more memory + */ + probe slabs__slabclass__allocate__failed(int slabclass); + + /** + * Release memory + * @param size the size of the memory + * @param slabclass the class the memory belongs to + * @param ptr pointer to the memory to release + */ + probe slabs__free(int size, int slabclass, void* ptr); + + /** + * Fired when the when we have searched the hash table for a named key. + * These two elements provide an insight in how well the hash function + * functions. Long traversals are a sign of a less optimal function, + * wasting cpu capacity. + * + * @param key the key searched for + * @param depth the depth in the list of hash table + */ + probe assoc__find(const char *key, int depth); + + /** + * Fired when a new item has been inserted. + * @param key the key just inserted + * @param nokeys the total number of keys currently being stored, + * including the key for which insert was called. + */ + probe assoc__insert(const char *key, int nokeys); + + /** + * Fired when a new item has been removed. + * @param key the key just deleted + * @param nokeys the total number of keys currently being stored, + * excluding the key for which delete was called. + */ + probe assoc__delete(const char *key, int nokeys); + + /** + * Fired when an item is being linked in the cache + * @param key the items key + * @param size the size of the data + */ + probe item__link(const char *key, int size); + + /** + * Fired when an item is being deleted + * @param key the items key + * @param size the size of the data + */ + probe item__unlink(const char *key, int size); + + /** + * Fired when the refcount for an item is reduced + * @param key the items key + * @param size the size of the data + */ + probe item__remove(const char *key, int size); + + /** + * Fired when the "last refenced" time is updated + * @param key the items key + * @param size the size of the data + */ + probe item__update(const char *key, int size); + + /** + * Fired when an item is bein replaced with another item + * @param oldkey the key of the item to replace + * @param oldsize the size of the old item + * @param newkey the key of the new item + * @param newsize the size of the new item + */ + probe item__replace(const char *oldkey, int oldsize, const char *newkey, int newsize); + + /** + * Fired when the processing of a command starts + * @param connid the connection id + * @param request the incomming request + * @param size the size of the request + */ + probe process__command__start(int connid, const void *request, int size); + + /** + * Fired when the processing of a command is done + * @param connid the connection id + * @param respnse the response to send back to the client + * @param size the size of the response + */ + probe process__command__end(int connid, const void *response, int size); + + /** + * Fired for a get-command + * @param connid connection id + * @param key requested key + * @param size size of the key's data (or -1 if not found) + */ + probe command__get(int connid, const char *key, int size); + + /** + * Fired for a gets command + * @param connid connection id + * @param key requested key + * @param size size of the key's data (or -1 if not found) + * @param casid the casid for the item + */ + probe command__gets(int connid, const char *key, int size, int64_t casid); + + /** + * Fired for a add-command + * @param connid connection id + * @param key requested key + * @param size the new size of the key's data (or -1 if not found) + */ + probe command__add(int connid, const char *key, int size); + + /** + * Fired for a set-command + * @param connid connection id + * @param key requested key + * @param size the new size of the key's data (or -1 if not found) + */ + probe command__set(int connid, const char *key, int size); + + /** + * Fired for a replace-command + * @param connid connection id + * @param key requested key + * @param size the new size of the key's data (or -1 if not found) + */ + probe command__replace(int connid, const char *key, int size); + + /** + * Fired for a prepend-command + * @param connid connection id + * @param key requested key + * @param size the new size of the key's data (or -1 if not found) + */ + probe command__prepend(int connid, const char *key, int size); + + /** + * Fired for a append-command + * @param connid connection id + * @param key requested key + * @param size the new size of the key's data (or -1 if not found) + */ + probe command__append(int connid, const char *key, int size); + + /** + * Fired for a cas-command + * @param connid connection id + * @param key requested key + * @param size size of the key's data (or -1 if not found) + * @param casid the cas id requested + */ + probe command__cas(int connid, const char *key, int size, int64_t casid); + + /** + * Fired for incr command + * @param connid connection id + * @param key the requested key + * @param val the new value + */ + probe command__incr(int connid, const char *key, int64_t val); + + /** + * Fired for decr command + * @param connid connection id + * @param key the requested key + * @param val the new value + */ + probe command__decr(int connid, const char *key, int64_t val); + + /** + * Fired for a delete command + * @param connid connection id + * @param key the requested key + * @param exptime the expiry time + */ + probe command__delete(int connid, const char *key, long exptime); + +}; + +#pragma D attributes Unstable/Unstable/Common provider memcached provider +#pragma D attributes Private/Private/Common provider memcached module +#pragma D attributes Private/Private/Common provider memcached function +#pragma D attributes Unstable/Unstable/Common provider memcached name +#pragma D attributes Unstable/Unstable/Common provider memcached args diff --git a/memcached_dtrace.h b/memcached_dtrace.h new file mode 100644 index 0000000000..c296d063ee --- /dev/null +++ b/memcached_dtrace.h @@ -0,0 +1,276 @@ +/* + * Generated by dtrace(1M). + */ + +#ifndef _MEMCACHED_DTRACE_H +#define _MEMCACHED_DTRACE_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#if ENABLE_DTRACE + +#define MEMCACHED_ASSOC_DELETE(arg0, arg1) \ + __dtrace_memcached___assoc__delete(arg0, arg1) +#define MEMCACHED_ASSOC_DELETE_ENABLED() \ + __dtraceenabled_memcached___assoc__delete() +#define MEMCACHED_ASSOC_FIND(arg0, arg1) \ + __dtrace_memcached___assoc__find(arg0, arg1) +#define MEMCACHED_ASSOC_FIND_ENABLED() \ + __dtraceenabled_memcached___assoc__find() +#define MEMCACHED_ASSOC_INSERT(arg0, arg1) \ + __dtrace_memcached___assoc__insert(arg0, arg1) +#define MEMCACHED_ASSOC_INSERT_ENABLED() \ + __dtraceenabled_memcached___assoc__insert() +#define MEMCACHED_COMMAND_ADD(arg0, arg1, arg2) \ + __dtrace_memcached___command__add(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_ADD_ENABLED() \ + __dtraceenabled_memcached___command__add() +#define MEMCACHED_COMMAND_APPEND(arg0, arg1, arg2) \ + __dtrace_memcached___command__append(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_APPEND_ENABLED() \ + __dtraceenabled_memcached___command__append() +#define MEMCACHED_COMMAND_CAS(arg0, arg1, arg2, arg3) \ + __dtrace_memcached___command__cas(arg0, arg1, arg2, arg3) +#define MEMCACHED_COMMAND_CAS_ENABLED() \ + __dtraceenabled_memcached___command__cas() +#define MEMCACHED_COMMAND_DECR(arg0, arg1, arg2) \ + __dtrace_memcached___command__decr(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_DECR_ENABLED() \ + __dtraceenabled_memcached___command__decr() +#define MEMCACHED_COMMAND_DELETE(arg0, arg1, arg2) \ + __dtrace_memcached___command__delete(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_DELETE_ENABLED() \ + __dtraceenabled_memcached___command__delete() +#define MEMCACHED_COMMAND_GET(arg0, arg1, arg2) \ + __dtrace_memcached___command__get(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_GET_ENABLED() \ + __dtraceenabled_memcached___command__get() +#define MEMCACHED_COMMAND_GETS(arg0, arg1, arg2, arg3) \ + __dtrace_memcached___command__gets(arg0, arg1, arg2, arg3) +#define MEMCACHED_COMMAND_GETS_ENABLED() \ + __dtraceenabled_memcached___command__gets() +#define MEMCACHED_COMMAND_INCR(arg0, arg1, arg2) \ + __dtrace_memcached___command__incr(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_INCR_ENABLED() \ + __dtraceenabled_memcached___command__incr() +#define MEMCACHED_COMMAND_PREPEND(arg0, arg1, arg2) \ + __dtrace_memcached___command__prepend(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_PREPEND_ENABLED() \ + __dtraceenabled_memcached___command__prepend() +#define MEMCACHED_COMMAND_REPLACE(arg0, arg1, arg2) \ + __dtrace_memcached___command__replace(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_REPLACE_ENABLED() \ + __dtraceenabled_memcached___command__replace() +#define MEMCACHED_COMMAND_SET(arg0, arg1, arg2) \ + __dtrace_memcached___command__set(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_SET_ENABLED() \ + __dtraceenabled_memcached___command__set() +#define MEMCACHED_CONN_ALLOCATE(arg0) \ + __dtrace_memcached___conn__allocate(arg0) +#define MEMCACHED_CONN_ALLOCATE_ENABLED() \ + __dtraceenabled_memcached___conn__allocate() +#define MEMCACHED_CONN_CREATE(arg0) \ + __dtrace_memcached___conn__create(arg0) +#define MEMCACHED_CONN_CREATE_ENABLED() \ + __dtraceenabled_memcached___conn__create() +#define MEMCACHED_CONN_DESTROY(arg0) \ + __dtrace_memcached___conn__destroy(arg0) +#define MEMCACHED_CONN_DESTROY_ENABLED() \ + __dtraceenabled_memcached___conn__destroy() +#define MEMCACHED_CONN_DISPATCH(arg0, arg1) \ + __dtrace_memcached___conn__dispatch(arg0, arg1) +#define MEMCACHED_CONN_DISPATCH_ENABLED() \ + __dtraceenabled_memcached___conn__dispatch() +#define MEMCACHED_CONN_RELEASE(arg0) \ + __dtrace_memcached___conn__release(arg0) +#define MEMCACHED_CONN_RELEASE_ENABLED() \ + __dtraceenabled_memcached___conn__release() +#define MEMCACHED_ITEM_LINK(arg0, arg1) \ + __dtrace_memcached___item__link(arg0, arg1) +#define MEMCACHED_ITEM_LINK_ENABLED() \ + __dtraceenabled_memcached___item__link() +#define MEMCACHED_ITEM_REMOVE(arg0, arg1) \ + __dtrace_memcached___item__remove(arg0, arg1) +#define MEMCACHED_ITEM_REMOVE_ENABLED() \ + __dtraceenabled_memcached___item__remove() +#define MEMCACHED_ITEM_REPLACE(arg0, arg1, arg2, arg3) \ + __dtrace_memcached___item__replace(arg0, arg1, arg2, arg3) +#define MEMCACHED_ITEM_REPLACE_ENABLED() \ + __dtraceenabled_memcached___item__replace() +#define MEMCACHED_ITEM_UNLINK(arg0, arg1) \ + __dtrace_memcached___item__unlink(arg0, arg1) +#define MEMCACHED_ITEM_UNLINK_ENABLED() \ + __dtraceenabled_memcached___item__unlink() +#define MEMCACHED_ITEM_UPDATE(arg0, arg1) \ + __dtrace_memcached___item__update(arg0, arg1) +#define MEMCACHED_ITEM_UPDATE_ENABLED() \ + __dtraceenabled_memcached___item__update() +#define MEMCACHED_PROCESS_COMMAND_END(arg0, arg1, arg2) \ + __dtrace_memcached___process__command__end(arg0, arg1, arg2) +#define MEMCACHED_PROCESS_COMMAND_END_ENABLED() \ + __dtraceenabled_memcached___process__command__end() +#define MEMCACHED_PROCESS_COMMAND_START(arg0, arg1, arg2) \ + __dtrace_memcached___process__command__start(arg0, arg1, arg2) +#define MEMCACHED_PROCESS_COMMAND_START_ENABLED() \ + __dtraceenabled_memcached___process__command__start() +#define MEMCACHED_SLABS_ALLOCATE(arg0, arg1, arg2, arg3) \ + __dtrace_memcached___slabs__allocate(arg0, arg1, arg2, arg3) +#define MEMCACHED_SLABS_ALLOCATE_ENABLED() \ + __dtraceenabled_memcached___slabs__allocate() +#define MEMCACHED_SLABS_ALLOCATE_FAILED(arg0, arg1) \ + __dtrace_memcached___slabs__allocate__failed(arg0, arg1) +#define MEMCACHED_SLABS_ALLOCATE_FAILED_ENABLED() \ + __dtraceenabled_memcached___slabs__allocate__failed() +#define MEMCACHED_SLABS_FREE(arg0, arg1, arg2) \ + __dtrace_memcached___slabs__free(arg0, arg1, arg2) +#define MEMCACHED_SLABS_FREE_ENABLED() \ + __dtraceenabled_memcached___slabs__free() +#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE(arg0) \ + __dtrace_memcached___slabs__slabclass__allocate(arg0) +#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_ENABLED() \ + __dtraceenabled_memcached___slabs__slabclass__allocate() +#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(arg0) \ + __dtrace_memcached___slabs__slabclass__allocate__failed(arg0) +#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED_ENABLED() \ + __dtraceenabled_memcached___slabs__slabclass__allocate__failed() + + +extern void __dtrace_memcached___assoc__delete(char *, int); +extern int __dtraceenabled_memcached___assoc__delete(void); +extern void __dtrace_memcached___assoc__find(char *, int); +extern int __dtraceenabled_memcached___assoc__find(void); +extern void __dtrace_memcached___assoc__insert(char *, int); +extern int __dtraceenabled_memcached___assoc__insert(void); +extern void __dtrace_memcached___command__add(int, char *, int); +extern int __dtraceenabled_memcached___command__add(void); +extern void __dtrace_memcached___command__append(int, char *, int); +extern int __dtraceenabled_memcached___command__append(void); +extern void __dtrace_memcached___command__cas(int, char *, int, int64_t); +extern int __dtraceenabled_memcached___command__cas(void); +extern void __dtrace_memcached___command__decr(int, char *, int64_t); +extern int __dtraceenabled_memcached___command__decr(void); +extern void __dtrace_memcached___command__delete(int, char *, long); +extern int __dtraceenabled_memcached___command__delete(void); +extern void __dtrace_memcached___command__get(int, char *, int); +extern int __dtraceenabled_memcached___command__get(void); +extern void __dtrace_memcached___command__gets(int, char *, int, int64_t); +extern int __dtraceenabled_memcached___command__gets(void); +extern void __dtrace_memcached___command__incr(int, char *, int64_t); +extern int __dtraceenabled_memcached___command__incr(void); +extern void __dtrace_memcached___command__prepend(int, char *, int); +extern int __dtraceenabled_memcached___command__prepend(void); +extern void __dtrace_memcached___command__replace(int, char *, int); +extern int __dtraceenabled_memcached___command__replace(void); +extern void __dtrace_memcached___command__set(int, char *, int); +extern int __dtraceenabled_memcached___command__set(void); +extern void __dtrace_memcached___conn__allocate(int); +extern int __dtraceenabled_memcached___conn__allocate(void); +extern void __dtrace_memcached___conn__create(void *); +extern int __dtraceenabled_memcached___conn__create(void); +extern void __dtrace_memcached___conn__destroy(void *); +extern int __dtraceenabled_memcached___conn__destroy(void); +extern void __dtrace_memcached___conn__dispatch(int, int); +extern int __dtraceenabled_memcached___conn__dispatch(void); +extern void __dtrace_memcached___conn__release(int); +extern int __dtraceenabled_memcached___conn__release(void); +extern void __dtrace_memcached___item__link(char *, int); +extern int __dtraceenabled_memcached___item__link(void); +extern void __dtrace_memcached___item__remove(char *, int); +extern int __dtraceenabled_memcached___item__remove(void); +extern void __dtrace_memcached___item__replace(char *, int, char *, int); +extern int __dtraceenabled_memcached___item__replace(void); +extern void __dtrace_memcached___item__unlink(char *, int); +extern int __dtraceenabled_memcached___item__unlink(void); +extern void __dtrace_memcached___item__update(char *, int); +extern int __dtraceenabled_memcached___item__update(void); +extern void __dtrace_memcached___process__command__end(int, void *, int); +extern int __dtraceenabled_memcached___process__command__end(void); +extern void __dtrace_memcached___process__command__start(int, void *, int); +extern int __dtraceenabled_memcached___process__command__start(void); +extern void __dtrace_memcached___slabs__allocate(int, int, int, void *); +extern int __dtraceenabled_memcached___slabs__allocate(void); +extern void __dtrace_memcached___slabs__allocate__failed(int, int); +extern int __dtraceenabled_memcached___slabs__allocate__failed(void); +extern void __dtrace_memcached___slabs__free(int, int, void *); +extern int __dtraceenabled_memcached___slabs__free(void); +extern void __dtrace_memcached___slabs__slabclass__allocate(int); +extern int __dtraceenabled_memcached___slabs__slabclass__allocate(void); +extern void __dtrace_memcached___slabs__slabclass__allocate__failed(int); +extern int __dtraceenabled_memcached___slabs__slabclass__allocate__failed(void); + +#else + +#define MEMCACHED_ASSOC_DELETE(arg0, arg1) +#define MEMCACHED_ASSOC_DELETE_ENABLED() (0) +#define MEMCACHED_ASSOC_FIND(arg0, arg1) +#define MEMCACHED_ASSOC_FIND_ENABLED() (0) +#define MEMCACHED_ASSOC_INSERT(arg0, arg1) +#define MEMCACHED_ASSOC_INSERT_ENABLED() (0) +#define MEMCACHED_COMMAND_ADD(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_ADD_ENABLED() (0) +#define MEMCACHED_COMMAND_APPEND(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_APPEND_ENABLED() (0) +#define MEMCACHED_COMMAND_CAS(arg0, arg1, arg2, arg3) +#define MEMCACHED_COMMAND_CAS_ENABLED() (0) +#define MEMCACHED_COMMAND_DECR(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_DECR_ENABLED() (0) +#define MEMCACHED_COMMAND_DELETE(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_DELETE_ENABLED() (0) +#define MEMCACHED_COMMAND_GET(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_GET_ENABLED() (0) +#define MEMCACHED_COMMAND_GETS(arg0, arg1, arg2, arg3) +#define MEMCACHED_COMMAND_GETS_ENABLED() (0) +#define MEMCACHED_COMMAND_INCR(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_INCR_ENABLED() (0) +#define MEMCACHED_COMMAND_PREPEND(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_PREPEND_ENABLED() (0) +#define MEMCACHED_COMMAND_REPLACE(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_REPLACE_ENABLED() (0) +#define MEMCACHED_COMMAND_SET(arg0, arg1, arg2) +#define MEMCACHED_COMMAND_SET_ENABLED() (0) +#define MEMCACHED_CONN_ALLOCATE(arg0) +#define MEMCACHED_CONN_ALLOCATE_ENABLED() (0) +#define MEMCACHED_CONN_CREATE(arg0) +#define MEMCACHED_CONN_CREATE_ENABLED() (0) +#define MEMCACHED_CONN_DESTROY(arg0) +#define MEMCACHED_CONN_DESTROY_ENABLED() (0) +#define MEMCACHED_CONN_DISPATCH(arg0, arg1) +#define MEMCACHED_CONN_DISPATCH_ENABLED() (0) +#define MEMCACHED_CONN_RELEASE(arg0) +#define MEMCACHED_CONN_RELEASE_ENABLED() (0) +#define MEMCACHED_ITEM_LINK(arg0, arg1) +#define MEMCACHED_ITEM_LINK_ENABLED() (0) +#define MEMCACHED_ITEM_REMOVE(arg0, arg1) +#define MEMCACHED_ITEM_REMOVE_ENABLED() (0) +#define MEMCACHED_ITEM_REPLACE(arg0, arg1, arg2, arg3) +#define MEMCACHED_ITEM_REPLACE_ENABLED() (0) +#define MEMCACHED_ITEM_UNLINK(arg0, arg1) +#define MEMCACHED_ITEM_UNLINK_ENABLED() (0) +#define MEMCACHED_ITEM_UPDATE(arg0, arg1) +#define MEMCACHED_ITEM_UPDATE_ENABLED() (0) +#define MEMCACHED_PROCESS_COMMAND_END(arg0, arg1, arg2) +#define MEMCACHED_PROCESS_COMMAND_END_ENABLED() (0) +#define MEMCACHED_PROCESS_COMMAND_START(arg0, arg1, arg2) +#define MEMCACHED_PROCESS_COMMAND_START_ENABLED() (0) +#define MEMCACHED_SLABS_ALLOCATE(arg0, arg1, arg2, arg3) +#define MEMCACHED_SLABS_ALLOCATE_ENABLED() (0) +#define MEMCACHED_SLABS_ALLOCATE_FAILED(arg0, arg1) +#define MEMCACHED_SLABS_ALLOCATE_FAILED_ENABLED() (0) +#define MEMCACHED_SLABS_FREE(arg0, arg1, arg2) +#define MEMCACHED_SLABS_FREE_ENABLED() (0) +#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE(arg0) +#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_ENABLED() (0) +#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(arg0) +#define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED_ENABLED() (0) + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _MEMCACHED_DTRACE_H */ diff --git a/slabs.c b/slabs.c index dbd9740606..fab39b0faf 100644 --- a/slabs.c +++ b/slabs.c @@ -200,13 +200,13 @@ static int do_slabs_newslab(const unsigned int id) { #endif char *ptr; - if (mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) - return 0; - - if (grow_slab_list(id) == 0) return 0; + if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) || + (grow_slab_list(id) == 0) || + ((ptr = memory_allocate((size_t)len)) == 0)) { - ptr = memory_allocate((size_t)len); - if (ptr == 0) return 0; + MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id); + return 0; + } memset(ptr, 0, (size_t)len); p->end_page_ptr = ptr; @@ -214,47 +214,62 @@ static int do_slabs_newslab(const unsigned int id) { p->slab_list[p->slabs++] = ptr; mem_malloced += len; + + MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id); return 1; } /*@null@*/ void *do_slabs_alloc(const size_t size, unsigned int id) { slabclass_t *p; + void *ret = NULL; - if (id < POWER_SMALLEST || id > power_largest) + if (id < POWER_SMALLEST || id > power_largest) { + MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0); return NULL; + } p = &slabclass[id]; assert(p->sl_curr == 0 || ((item *)p->slots[p->sl_curr - 1])->slabs_clsid == 0); #ifdef USE_SYSTEM_MALLOC - if (mem_limit && mem_malloced + size > mem_limit) + if (mem_limit && mem_malloced + size > mem_limit) { + MEMCACHED_SLABS_ALLOCATE_FAILED(size, id); return 0; + } mem_malloced += size; - return malloc(size); + ret = malloc(size); + MEMCACHED_SLABS_ALLOCATE(size, id, 0, ret); + return ret; #endif /* fail unless we have space at the end of a recently allocated page, we have something on our freelist, or we could allocate a new page */ - if (! (p->end_page_ptr != 0 || p->sl_curr != 0 || do_slabs_newslab(id) != 0)) - return 0; - - /* return off our freelist, if we have one */ - if (p->sl_curr != 0) - return p->slots[--p->sl_curr]; - - /* if we recently allocated a whole page, return from that */ - if (p->end_page_ptr) { - void *ptr = p->end_page_ptr; + if (! (p->end_page_ptr != 0 || p->sl_curr != 0 || + do_slabs_newslab(id) != 0)) { + /* We don't have more memory available */ + ret = NULL; + } else if (p->sl_curr != 0) { + /* return off our freelist */ + ret = p->slots[--p->sl_curr]; + } else { + /* if we recently allocated a whole page, return from that */ + assert(p->end_page_ptr != NULL); + ret = p->end_page_ptr; if (--p->end_page_free != 0) { p->end_page_ptr += p->size; } else { p->end_page_ptr = 0; } - return ptr; } - return NULL; /* shouldn't ever get here */ + if (ret) { + MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret); + } else { + MEMCACHED_SLABS_ALLOCATE_FAILED(size, id); + } + + return ret; } void do_slabs_free(void *ptr, const size_t size, unsigned int id) { @@ -265,6 +280,7 @@ void do_slabs_free(void *ptr, const size_t size, unsigned int id) { if (id < POWER_SMALLEST || id > power_largest) return; + MEMCACHED_SLABS_FREE(size, id, ptr); p = &slabclass[id]; #ifdef USE_SYSTEM_MALLOC diff --git a/thread.c b/thread.c index e0303b01e0..3bacb7ecdd 100644 --- a/thread.c +++ b/thread.c @@ -381,6 +381,8 @@ void dispatch_conn_new(int sfd, int init_state, int event_flags, item->is_udp = is_udp; cq_push(&threads[thread].new_conn_queue, item); + + MEMCACHED_CONN_DISPATCH(sfd, threads[thread].thread_id); if (write(threads[thread].notify_send_fd, "", 1) != 1) { perror("Writing to thread notify pipe"); } @@ -495,11 +497,12 @@ char *mt_defer_delete(item *item, time_t exptime) { /* * Does arithmetic on a numeric item value. */ -char *mt_add_delta(item *item, int incr, const int64_t delta, char *buf) { +char *mt_add_delta(conn *c, item *item, int incr, const int64_t delta, + char *buf) { char *ret; pthread_mutex_lock(&cache_lock); - ret = do_add_delta(item, incr, delta, buf); + ret = do_add_delta(c, item, incr, delta, buf); pthread_mutex_unlock(&cache_lock); return ret; } From a19b04b18c07ddd71eced7a89f037b760d8e7386 Mon Sep 17 00:00:00 2001 From: dormando Date: Thu, 24 Jul 2008 10:57:09 -0700 Subject: [PATCH 07/24] Add support for newer automake's. Patch provided by facebook, attribution is to one or more of: Marc, Steve, Haiping Zhao, and Paul Saab, Steven Grimm, etc. --- autogen.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index 46cbeba68a..873f0a4589 100755 --- a/autogen.sh +++ b/autogen.sh @@ -8,7 +8,7 @@ # echo "aclocal..." -ACLOCAL=`which aclocal-1.9 || which aclocal19 || which aclocal-1.7 || which aclocal17 || which aclocal-1.5 || which aclocal15 || which aclocal || exit 1` +ACLOCAL=`which aclocal-1.10 || which aclocal-1.9 || which aclocal19 || which aclocal-1.7 || which aclocal17 || which aclocal-1.5 || which aclocal15 || which aclocal || exit 1` $ACLOCAL || exit 1 echo "autoheader..." @@ -16,7 +16,7 @@ AUTOHEADER=${AUTOHEADER:-autoheader} $AUTOHEADER || exit 1 echo "automake..." -AUTOMAKE=${AUTOMAKE:-automake-1.7} +AUTOMAKE=`which automake-1.10 || which automake-1.9 || which automake-1.7 || exit 1` $AUTOMAKE --foreign --add-missing || automake --gnu --add-missing || exit 1 echo "autoconf..." From 6ddea11ae3b92fb7749701e03a563636533938e0 Mon Sep 17 00:00:00 2001 From: dormando Date: Thu, 24 Jul 2008 11:16:40 -0700 Subject: [PATCH 08/24] Update changelog, version bump. --- ChangeLog | 19 +++++++++++++++++++ configure.ac | 2 +- memcached.spec | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 10ad2a64b3..966e6b4a26 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2008-07-24 [Version 1.2.6-rc1 released] + + * Add support for newer automake (Facebook) + + * DTrace support for Solaris/etc (Trond Norbye) + + * LRU tests (Steve Yen) + + * Handle negative length items properly (Dormando) + + * Don't leave stale data after failed set attempts (Dormando) + + * Fix refcount leaks, which would result in OOM's on all sets + (Dormando) + + * Fix buffer overruns (Dustin Sallings, Tomash Brechko) + + * Fix memory corruption with CAS (Dustin Sallings) + 2008-06-11 * Fix -k to work with -d. (reported by Gary Zhu) diff --git a/configure.ac b/configure.ac index e7110db87e..01c51cf546 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.52) -AC_INIT(memcached, 1.2.5, brad@danga.com) +AC_INIT(memcached, 1.2.6, brad@danga.com) AC_CANONICAL_SYSTEM AC_CONFIG_SRCDIR(memcached.c) AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) diff --git a/memcached.spec b/memcached.spec index c59d6e1280..0274fd7b19 100644 --- a/memcached.spec +++ b/memcached.spec @@ -1,5 +1,5 @@ Name: memcached -Version: 1.2.5 +Version: 1.2.6 Release: 1%{?dist} Summary: High Performance, Distributed Memory Object Cache From fe912509075a38300a559a4fd0ec9a4d78dd7018 Mon Sep 17 00:00:00 2001 From: Trond Norbye Date: Tue, 29 Jul 2008 09:38:54 -0700 Subject: [PATCH 09/24] Fix 'make dist' target to include new files --- Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 3b2b990765..29085ecbbb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ bin_PROGRAMS = memcached memcached-debug -memcached_SOURCES = memcached.c slabs.c slabs.h items.c items.h assoc.c assoc.h memcached.h thread.c stats.c stats.h +memcached_SOURCES = memcached.c slabs.c slabs.h items.c items.h assoc.c assoc.h memcached.h thread.c stats.c stats.h memcached_dtrace.h memcached_debug_SOURCES = $(memcached_SOURCES) memcached_CPPFLAGS = -DNDEBUG memcached_LDADD = @DTRACE_OBJ@ @DAEMON_OBJ@ @@ -22,7 +22,7 @@ memcached_debug_dtrace.o: $(memcached_debug_OBJECTS) SUBDIRS = doc DIST_DIRS = scripts -EXTRA_DIST = doc scripts TODO t memcached.spec +EXTRA_DIST = doc scripts TODO t memcached.spec daemon.c memcached_dtrace.d test: memcached-debug prove $(srcdir)/t From 02ce111b0ec28d4c87fe6a3a6111ad79fbd9f599 Mon Sep 17 00:00:00 2001 From: dormando Date: Sat, 30 Aug 2008 19:12:09 -0700 Subject: [PATCH 10/24] Use a dedicated accept/dispatch thread. Patch originally from the Facebook crew, cleaned up and split by dormando. --- memcached.c | 8 ++++---- thread.c | 16 +++++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/memcached.c b/memcached.c index 3b7a187f72..694a1fb435 100644 --- a/memcached.c +++ b/memcached.c @@ -178,7 +178,7 @@ static void settings_init(void) { settings.factor = 1.25; settings.chunk_size = 48; /* space for a modest key and value */ #ifdef USE_THREADS - settings.num_threads = 4; + settings.num_threads = 4 + 1; /* N workers + 1 dispatcher */ #else settings.num_threads = 1; #endif @@ -2536,10 +2536,10 @@ static int server_socket(const int port, const bool is_udp) { { int c; - for (c = 0; c < settings.num_threads; c++) { + for (c = 1; c < settings.num_threads; c++) { /* this is guaranteed to hit all threads because we round-robin */ dispatch_conn_new(sfd, conn_read, EV_READ | EV_PERSIST, - UDP_READ_BUFFER_SIZE, 1); + UDP_READ_BUFFER_SIZE, is_udp); } } else { if (!(listen_conn_add = conn_new(sfd, conn_listening, @@ -2998,7 +2998,7 @@ int main (int argc, char **argv) { } break; case 't': - settings.num_threads = atoi(optarg); + settings.num_threads = atoi(optarg) + 1; /* Extra dispatch thread */ if (settings.num_threads == 0) { fprintf(stderr, "Number of threads must be greater than 0\n"); return 1; diff --git a/thread.c b/thread.c index 3bacb7ecdd..9f9e9c4215 100644 --- a/thread.c +++ b/thread.c @@ -5,6 +5,7 @@ * $Id$ */ #include "memcached.h" +#include #include #include #include @@ -360,7 +361,7 @@ static void thread_libevent_process(int fd, short which, void *arg) { } /* Which thread we assigned a connection to most recently. */ -static int last_thread = -1; +static int last_thread = 0; /* * Dispatches a new connection to another thread. This is only ever called @@ -370,9 +371,14 @@ static int last_thread = -1; void dispatch_conn_new(int sfd, int init_state, int event_flags, int read_buffer_size, int is_udp) { CQ_ITEM *item = cqi_new(); - int thread = (last_thread + 1) % settings.num_threads; - last_thread = thread; + int tid = last_thread % (settings.num_threads - 1); + + /* Skip the dispatch thread (0) */ + tid++; + LIBEVENT_THREAD *thread = threads + tid; + + last_thread = tid; item->sfd = sfd; item->init_state = init_state; @@ -380,10 +386,10 @@ void dispatch_conn_new(int sfd, int init_state, int event_flags, item->read_buffer_size = read_buffer_size; item->is_udp = is_udp; - cq_push(&threads[thread].new_conn_queue, item); + cq_push(&thread->new_conn_queue, item); MEMCACHED_CONN_DISPATCH(sfd, threads[thread].thread_id); - if (write(threads[thread].notify_send_fd, "", 1) != 1) { + if (write(thread->notify_send_fd, "", 1) != 1) { perror("Writing to thread notify pipe"); } } From ae9557f4a0a2b2979aede9211eb34f9765f43af9 Mon Sep 17 00:00:00 2001 From: dormando Date: Sat, 30 Aug 2008 19:15:24 -0700 Subject: [PATCH 11/24] Prevent starvation by busy threads. Patch originally from the Facebook crew, cleaned and separated by dormando. A bulk-loading thread could stick in the read loop for a long time without yielding to other connections. This gives a configurable limit to how many commands to process before the next connection is checked. --- memcached.c | 18 ++++++++++++++++-- memcached.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/memcached.c b/memcached.c index 694a1fb435..c0ead4e4d7 100644 --- a/memcached.c +++ b/memcached.c @@ -184,6 +184,7 @@ static void settings_init(void) { #endif settings.prefix_delimiter = ':'; settings.detail_enabled = 0; + settings.reqs_per_event = 20; } /* returns true if a deleted item's delete-locked-time is over, and it @@ -2164,6 +2165,7 @@ static void drive_machine(conn *c) { int sfd, flags = 1; socklen_t addrlen; struct sockaddr_storage addr; + int nreqs = settings.reqs_per_event; int res; assert(c != NULL); @@ -2202,7 +2204,9 @@ static void drive_machine(conn *c) { if (try_read_command(c) != 0) { continue; } - if ((c->udp ? try_read_udp(c) : try_read_network(c)) != 0) { + /* Only process nreqs at a time to avoid starving other + connections */ + if (--nreqs && (c->udp ? try_read_udp(c) : try_read_network(c)) != 0) { continue; } /* we have no command line and no data to read from network */ @@ -2750,6 +2754,9 @@ static void usage(void) { #ifdef USE_THREADS printf("-t number of threads to use, default 4\n"); + printf("-R Maximum number of requests per event\n" + " limits the number of requests process for a given con nection\n" + " to prevent starvation. default 20\n"); #endif return; } @@ -2928,7 +2935,7 @@ int main (int argc, char **argv) { setbuf(stderr, NULL); /* process arguments */ - while ((c = getopt(argc, argv, "a:bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:L")) != -1) { + while ((c = getopt(argc, argv, "a:bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:LR:")) != -1) { switch (c) { case 'a': /* access for unix domain socket, as octal mask (like chmod)*/ @@ -2977,6 +2984,13 @@ int main (int argc, char **argv) { case 'r': maxcore = 1; break; + case 'R': + settings.reqs_per_event = atoi(optarg); + if (settings.reqs_per_event == 0) { + fprintf(stderr, "Number of requests per event must be greater than 0\n"); + return 1; + } + break; case 'u': username = optarg; break; diff --git a/memcached.h b/memcached.h index fcef0b7be2..0cf0e0bf97 100644 --- a/memcached.h +++ b/memcached.h @@ -96,6 +96,8 @@ struct settings { int num_threads; /* number of libevent threads to run */ char prefix_delimiter; /* character that marks a key prefix (for stats) */ int detail_enabled; /* nonzero if we're collecting detailed stats */ + int reqs_per_event; /* Maximum number of io to process on each + io-event. */ }; extern struct stats stats; From 7417af47945e94407380c441eaa27715886cfe8e Mon Sep 17 00:00:00 2001 From: dormando Date: Sat, 30 Aug 2008 19:22:52 -0700 Subject: [PATCH 12/24] Duhr. Not a thread specific command. --- memcached.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/memcached.c b/memcached.c index c0ead4e4d7..9279d2897c 100644 --- a/memcached.c +++ b/memcached.c @@ -2754,10 +2754,10 @@ static void usage(void) { #ifdef USE_THREADS printf("-t number of threads to use, default 4\n"); +#endif printf("-R Maximum number of requests per event\n" " limits the number of requests process for a given con nection\n" " to prevent starvation. default 20\n"); -#endif return; } From a99f0935cda17c7998a5b46dd7fef9205b596587 Mon Sep 17 00:00:00 2001 From: dormando Date: Sat, 30 Aug 2008 23:05:08 -0700 Subject: [PATCH 13/24] Note the last accessed time of an object when evicted. If you're really clever and set all of your items to never expire, your evictions stat will skyrocket even when you have plenty of memory left. This added stat to the stats items command gives an extra window into how bad an eviction was without expressly looking at it. --- items.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/items.c b/items.c index d9e779312d..0b15a763e4 100644 --- a/items.c +++ b/items.c @@ -29,6 +29,7 @@ static uint64_t get_cas_id(); #define LARGEST_ID 255 typedef struct { unsigned int evicted; + rel_time_t evicted_time; unsigned int outofmemory; } itemstats_t; @@ -125,6 +126,7 @@ item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_tim if (search->refcount == 0) { if (search->exptime == 0 || search->exptime > current_time) { itemstats[id].evicted++; + itemstats[id].evicted_time = current_time - search->time; STATS_LOCK(); stats.evictions++; STATS_UNLOCK(); @@ -334,7 +336,7 @@ char *do_item_cachedump(const unsigned int slabs_clsid, const unsigned int limit } char *do_item_stats(int *bytes) { - size_t bufleft = (size_t) LARGEST_ID * 160; + size_t bufleft = (size_t) LARGEST_ID * 240; char *buffer = malloc(bufleft); char *bufcurr = buffer; rel_time_t now = current_time; @@ -351,9 +353,12 @@ char *do_item_stats(int *bytes) { "STAT items:%d:number %u\r\n" "STAT items:%d:age %u\r\n" "STAT items:%d:evicted %u\r\n" + "STAT items:%d:evicted_time %u\r\n" "STAT items:%d:outofmemory %u\r\n", - i, sizes[i], i, now - tails[i]->time, i, - itemstats[i].evicted, i, itemstats[i].outofmemory); + i, sizes[i], i, now - tails[i]->time, + i, itemstats[i].evicted, + i, itemstats[i].evicted_time, + i, itemstats[i].outofmemory); if (linelen + sizeof("END\r\n") < bufleft) { bufcurr += linelen; bufleft -= linelen; From afdf5a93c0103392b8ff00186f8dffcb00c77542 Mon Sep 17 00:00:00 2001 From: dormando Date: Sun, 31 Aug 2008 15:55:37 -0700 Subject: [PATCH 14/24] Fix thread DTrace probe. Trond pointed out that I missed fixing the thread dtrace probe *directly above* the other line I fixed. Har har oops. --- thread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thread.c b/thread.c index 9f9e9c4215..bd42dc46df 100644 --- a/thread.c +++ b/thread.c @@ -388,7 +388,7 @@ void dispatch_conn_new(int sfd, int init_state, int event_flags, cq_push(&thread->new_conn_queue, item); - MEMCACHED_CONN_DISPATCH(sfd, threads[thread].thread_id); + MEMCACHED_CONN_DISPATCH(sfd, thread->thread_id); if (write(thread->notify_send_fd, "", 1) != 1) { perror("Writing to thread notify pipe"); } From c2da04a70f773ef3c2756d882fbbee54675cc6c6 Mon Sep 17 00:00:00 2001 From: Jonathan Bastien-Filiatrault Date: Sun, 31 Aug 2008 22:38:45 -0700 Subject: [PATCH 15/24] Correct typo in configure script. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 01c51cf546..1770526eb3 100644 --- a/configure.ac +++ b/configure.ac @@ -29,7 +29,7 @@ AC_SUBST(DTRACE_DEBUG_OBJ) AC_SUBST(DTRACEFLAGS) AC_ARG_ENABLE(64bit, - [AS_HELP_STRING([--enable-64bit],[build 64bit verison])]) + [AS_HELP_STRING([--enable-64bit],[build 64bit version])]) if test "x$enable_64bit" == "xyes" then org_cflags=$CFLAGS From b25753691c6c70ad0e5569188bd8a0566fba3a95 Mon Sep 17 00:00:00 2001 From: Ricky Zhou Date: Sun, 31 Aug 2008 22:45:03 -0700 Subject: [PATCH 16/24] Use zeroed out thread structures via calloc. --- thread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thread.c b/thread.c index bd42dc46df..76ee128938 100644 --- a/thread.c +++ b/thread.c @@ -645,7 +645,7 @@ void thread_init(int nthreads, struct event_base *main_base) { pthread_mutex_init(&cqi_freelist_lock, NULL); cqi_freelist = NULL; - threads = malloc(sizeof(LIBEVENT_THREAD) * nthreads); + threads = calloc(nthreads, sizeof(LIBEVENT_THREAD)); if (! threads) { perror("Can't allocate thread descriptors"); exit(1); From 6caa28baacde4db97420d10b51878281e0a34210 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Sat, 6 Sep 2008 01:46:07 -0700 Subject: [PATCH 17/24] UDP/TCP can be disabled by setting their port to zero. modified by dormando a little. while tempting, don't do this: memcached -U 0 -p 0 ... and report it to the list ;) --- doc/memcached.1 | 2 +- memcached.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/memcached.1 b/doc/memcached.1 index 9063440a10..77a8ddcdcf 100644 --- a/doc/memcached.1 +++ b/doc/memcached.1 @@ -52,7 +52,7 @@ suggestions. Listen on TCP port , the default is port 11211. .TP .B \-U -Listen on UDP port , the default is 0, off. +Listen on UDP port , the default is port 11211, 0 is off. .TP .B \-M Disable automatic removal of items from the cache when out of memory. diff --git a/memcached.c b/memcached.c index 9279d2897c..0de4714728 100644 --- a/memcached.c +++ b/memcached.c @@ -165,7 +165,7 @@ static void stats_reset(void) { static void settings_init(void) { settings.access=0700; settings.port = 11211; - settings.udpport = 0; + settings.udpport = 11211; /* By default this string should be NULL for getaddrinfo() */ settings.inter = NULL; settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */ @@ -2718,7 +2718,7 @@ void do_run_deferred_deletes(void) static void usage(void) { printf(PACKAGE " " VERSION "\n"); printf("-p TCP port number to listen on (default: 11211)\n" - "-U UDP port number to listen on (default: 0, off)\n" + "-U UDP port number to listen on (default: 11211, 0 is off)\n" "-s unix socket path to listen on (disables network support)\n" "-a access mask for unix socket, in octal (default 0700)\n" "-l interface to listen on, default is INDRR_ANY\n" @@ -3187,7 +3187,7 @@ int main (int argc, char **argv) { if (settings.socketpath == NULL) { int udp_port; - if (server_socket(settings.port, 0)) { + if (settings.port && server_socket(settings.port, 0)) { fprintf(stderr, "failed to listen\n"); exit(EXIT_FAILURE); } @@ -3197,10 +3197,9 @@ int main (int argc, char **argv) { * then daemonise if needed, then init libevent (in some cases * descriptors created by libevent wouldn't survive forking). */ - udp_port = settings.udpport ? settings.udpport : settings.port; /* create the UDP listening socket and bind it */ - if (server_socket(udp_port, 1)) { + if (settings.udpport && server_socket(settings.udpport, 1)) { fprintf(stderr, "failed to listen on UDP port %d\n", settings.udpport); exit(EXIT_FAILURE); } From 4ab6df575e4d2a296c87dec5e6f93fb4dedf5556 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Sat, 6 Sep 2008 02:02:42 -0700 Subject: [PATCH 18/24] Fix a couple random indent issues. couple nitpicks from dormando. --- memcached.c | 66 ++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/memcached.c b/memcached.c index 0de4714728..de843d1fe3 100644 --- a/memcached.c +++ b/memcached.c @@ -2493,12 +2493,12 @@ static int server_socket(const int port, const bool is_udp) { snprintf(port_buf, NI_MAXSERV, "%d", port); error= getaddrinfo(settings.inter, port_buf, &hints, &ai); if (error != 0) { - if (error != EAI_SYSTEM) - fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); - else - perror("getaddrinfo()"); + if (error != EAI_SYSTEM) + fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); + else + perror("getaddrinfo()"); - return 1; + return 1; } for (next= ai; next; next= next->ai_next) { @@ -2527,34 +2527,34 @@ static int server_socket(const int port, const bool is_udp) { close(sfd); continue; } else { - success++; - if (!is_udp && listen(sfd, 1024) == -1) { - perror("listen()"); - close(sfd); - freeaddrinfo(ai); - return 1; - } - } + success++; + if (!is_udp && listen(sfd, 1024) == -1) { + perror("listen()"); + close(sfd); + freeaddrinfo(ai); + return 1; + } + } - if (is_udp) - { - int c; + if (is_udp) + { + int c; - for (c = 1; c < settings.num_threads; c++) { - /* this is guaranteed to hit all threads because we round-robin */ - dispatch_conn_new(sfd, conn_read, EV_READ | EV_PERSIST, - UDP_READ_BUFFER_SIZE, is_udp); - } - } else { - if (!(listen_conn_add = conn_new(sfd, conn_listening, - EV_READ | EV_PERSIST, 1, false, main_base))) { - fprintf(stderr, "failed to create listening connection\n"); - exit(EXIT_FAILURE); - } + for (c = 1; c < settings.num_threads; c++) { + /* this is guaranteed to hit all threads because we round-robin */ + dispatch_conn_new(sfd, conn_read, EV_READ | EV_PERSIST, + UDP_READ_BUFFER_SIZE, is_udp); + } + } else { + if (!(listen_conn_add = conn_new(sfd, conn_listening, + EV_READ | EV_PERSIST, 1, false, main_base))) { + fprintf(stderr, "failed to create listening connection\n"); + exit(EXIT_FAILURE); + } - listen_conn_add->next = listen_conn; - listen_conn = listen_conn_add; - } + listen_conn_add->next = listen_conn; + listen_conn = listen_conn_add; + } } freeaddrinfo(ai); @@ -3178,17 +3178,15 @@ int main (int argc, char **argv) { /* create unix mode sockets after dropping privileges */ if (settings.socketpath != NULL) { if (server_socket_unix(settings.socketpath,settings.access)) { - fprintf(stderr, "failed to listen\n"); + fprintf(stderr, "failed to listen on UNIX socket: %s\n", settings.socketpath); exit(EXIT_FAILURE); } } /* create the listening socket, bind it, and init */ if (settings.socketpath == NULL) { - int udp_port; - if (settings.port && server_socket(settings.port, 0)) { - fprintf(stderr, "failed to listen\n"); + fprintf(stderr, "failed to listen on TCP port %d\n", settings.port); exit(EXIT_FAILURE); } /* From 18474d58b4429d9744c3c516c35206edf10c1351 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Sat, 6 Sep 2008 02:08:34 -0700 Subject: [PATCH 19/24] Warning messages generated if setsockopt() does not pass. --- memcached.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/memcached.c b/memcached.c index de843d1fe3..364c07ce93 100644 --- a/memcached.c +++ b/memcached.c @@ -2512,9 +2512,17 @@ static int server_socket(const int port, const bool is_udp) { if (is_udp) { maximize_sndbuf(sfd); } else { - setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)); - setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); - setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); + error = setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)); + if (error != 0) + perror("setsockopt"); + + error = setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); + if (error != 0) + perror("setsockopt"); + + error = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); + if (error != 0) + perror("setsockopt"); } if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) { From 810c13bceed2d2e96449f285dc53913861cc4c0a Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Sat, 6 Sep 2008 02:12:23 -0700 Subject: [PATCH 20/24] Do the right thing if someone has a broken IPV6 stack which is only IPV6. --- memcached.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/memcached.c b/memcached.c index 364c07ce93..ead1c735a5 100644 --- a/memcached.c +++ b/memcached.c @@ -2481,12 +2481,10 @@ static int server_socket(const int port, const bool is_udp) { hints.ai_flags = AI_PASSIVE|AI_ADDRCONFIG; if (is_udp) { - hints.ai_protocol = IPPROTO_UDP; hints.ai_socktype = SOCK_DGRAM; hints.ai_family = AF_INET; /* This left here because of issues with OSX 10.5 */ } else { hints.ai_family = AF_UNSPEC; - hints.ai_protocol = IPPROTO_TCP; hints.ai_socktype = SOCK_STREAM; } @@ -2508,6 +2506,17 @@ static int server_socket(const int port, const bool is_udp) { return 1; } +#ifdef IPV6_V6ONLY + if (next->ai_family == AF_INET6) { + error = setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &flags, sizeof(flags)); + if (error != 0) { + perror("setsockopt"); + close(sfd); + continue; + } + } +#endif + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)); if (is_udp) { maximize_sndbuf(sfd); From 04319dddabaa06d15407ab6f793b160d3b1c5edb Mon Sep 17 00:00:00 2001 From: dormando Date: Sat, 6 Sep 2008 15:14:41 -0700 Subject: [PATCH 21/24] Remove managed instance code. Extra options confuse newbies. We should come back and do this right, probably built on top of the binary protocol and SE stuff. --- ChangeLog | 6 ++ doc/memcached.1 | 3 - memcached.c | 134 +------------------------------------------- memcached.h | 7 --- t/managed-buckets.t | 11 ---- 5 files changed, 7 insertions(+), 154 deletions(-) delete mode 100755 t/managed-buckets.t diff --git a/ChangeLog b/ChangeLog index 966e6b4a26..f5f2fc7487 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2008-09-06 + + * Remove managed instance code. Incomplete/etc. (Dormando) + +2008-07-29 [Version 1.2.6 released] + 2008-07-24 [Version 1.2.6-rc1 released] * Add support for newer automake (Facebook) diff --git a/doc/memcached.1 b/doc/memcached.1 index 77a8ddcdcf..979a96f31f 100644 --- a/doc/memcached.1 +++ b/doc/memcached.1 @@ -61,9 +61,6 @@ Additions will not be possible until adequate space is freed up. .B \-r Raise the core file size limit to the maximum allowable. .TP -.B \-b -Run a managed instanced (mnemonic: buckets)\n". -.TP .B \-f Use as the multiplier for computing the sizes of memory chunks that items are stored in. A lower value may result in less wasted memory depending diff --git a/memcached.c b/memcached.c index ead1c735a5..38c778a73c 100644 --- a/memcached.c +++ b/memcached.c @@ -112,8 +112,6 @@ static struct event_base *main_base; #define TRANSMIT_SOFT_ERROR 2 #define TRANSMIT_HARD_ERROR 3 -static int *buckets = 0; /* bucket->generation array for a managed instance */ - #define REALTIME_MAXDELTA 60*60*24*30 /* * given time value that's either unix time or delta from current unix time, return @@ -174,7 +172,6 @@ static void settings_init(void) { settings.oldest_live = 0; settings.evict_to_free = 1; /* push old items out of cache when memory runs out */ settings.socketpath = NULL; /* by default, not using a unix socket */ - settings.managed = false; settings.factor = 1.25; settings.chunk_size = 48; /* space for a modest key and value */ #ifdef USE_THREADS @@ -366,8 +363,6 @@ conn *conn_new(const int sfd, const int init_state, const int event_flags, c->write_and_go = conn_read; c->write_and_free = 0; c->item = 0; - c->bucket = -1; - c->gen = 0; c->noreply = false; @@ -1240,19 +1235,6 @@ static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, int stats_get_misses = 0; assert(c != NULL); - if (settings.managed) { - int bucket = c->bucket; - if (bucket == -1) { - out_string(c, "CLIENT_ERROR no BG data in managed mode"); - return; - } - c->bucket = -1; - if (buckets[bucket] != c->gen) { - out_string(c, "ERROR_NOT_OWNER"); - return; - } - } - do { while(key_token->length != 0) { @@ -1454,19 +1436,6 @@ static void process_update_command(conn *c, token_t *tokens, const size_t ntoken stats_prefix_record_set(key); } - if (settings.managed) { - int bucket = c->bucket; - if (bucket == -1) { - out_string(c, "CLIENT_ERROR no BG data in managed mode"); - return; - } - c->bucket = -1; - if (buckets[bucket] != c->gen) { - out_string(c, "ERROR_NOT_OWNER"); - return; - } - } - it = item_alloc(key, nkey, flags, realtime(exptime), vlen+2); if (it == 0) { @@ -1519,19 +1488,6 @@ static void process_arithmetic_command(conn *c, token_t *tokens, const size_t nt key = tokens[KEY_TOKEN].value; nkey = tokens[KEY_TOKEN].length; - if (settings.managed) { - int bucket = c->bucket; - if (bucket == -1) { - out_string(c, "CLIENT_ERROR no BG data in managed mode"); - return; - } - c->bucket = -1; - if (buckets[bucket] != c->gen) { - out_string(c, "ERROR_NOT_OWNER"); - return; - } - } - delta = strtoll(tokens[2].value, NULL, 10); if(errno == ERANGE) { @@ -1612,19 +1568,6 @@ static void process_delete_command(conn *c, token_t *tokens, const size_t ntoken set_noreply_maybe(c, tokens, ntokens); - if (settings.managed) { - int bucket = c->bucket; - if (bucket == -1) { - out_string(c, "CLIENT_ERROR no BG data in managed mode"); - return; - } - c->bucket = -1; - if (buckets[bucket] != c->gen) { - out_string(c, "ERROR_NOT_OWNER"); - return; - } - } - key = tokens[KEY_TOKEN].value; nkey = tokens[KEY_TOKEN].length; @@ -1767,67 +1710,6 @@ static void process_command(conn *c, char *command) { process_delete_command(c, tokens, ntokens); - } else if (ntokens == 3 && strcmp(tokens[COMMAND_TOKEN].value, "own") == 0) { - unsigned int bucket, gen; - if (!settings.managed) { - out_string(c, "CLIENT_ERROR not a managed instance"); - return; - } - - if (sscanf(tokens[1].value, "%u:%u", &bucket,&gen) == 2) { - if ((bucket < 0) || (bucket >= MAX_BUCKETS)) { - out_string(c, "CLIENT_ERROR bucket number out of range"); - return; - } - buckets[bucket] = gen; - out_string(c, "OWNED"); - return; - } else { - out_string(c, "CLIENT_ERROR bad format"); - return; - } - - } else if (ntokens == 3 && (strcmp(tokens[COMMAND_TOKEN].value, "disown")) == 0) { - - int bucket; - if (!settings.managed) { - out_string(c, "CLIENT_ERROR not a managed instance"); - return; - } - if (sscanf(tokens[1].value, "%u", &bucket) == 1) { - if ((bucket < 0) || (bucket >= MAX_BUCKETS)) { - out_string(c, "CLIENT_ERROR bucket number out of range"); - return; - } - buckets[bucket] = 0; - out_string(c, "DISOWNED"); - return; - } else { - out_string(c, "CLIENT_ERROR bad format"); - return; - } - - } else if (ntokens == 3 && (strcmp(tokens[COMMAND_TOKEN].value, "bg")) == 0) { - int bucket, gen; - if (!settings.managed) { - out_string(c, "CLIENT_ERROR not a managed instance"); - return; - } - if (sscanf(tokens[1].value, "%u:%u", &bucket, &gen) == 2) { - /* we never write anything back, even if input's wrong */ - if ((bucket < 0) || (bucket >= MAX_BUCKETS) || (gen <= 0)) { - /* do nothing, bad input */ - } else { - c->bucket = bucket; - c->gen = gen; - } - conn_set_state(c, conn_read); - return; - } else { - out_string(c, "CLIENT_ERROR bad format"); - return; - } - } else if (ntokens >= 2 && (strcmp(tokens[COMMAND_TOKEN].value, "stats") == 0)) { process_stat(c, tokens, ntokens); @@ -2755,7 +2637,6 @@ static void usage(void) { "-vv very verbose (also print client commands/reponses)\n" "-h print this help and exit\n" "-i print memcached and libevent license\n" - "-b run a managed instanced (mnemonic: buckets)\n" "-P save PID in , only used with -d option\n" "-f chunk size growth factor, default 1.25\n" "-n minimum space allocated for key+value+flags, default 48\n" @@ -2952,7 +2833,7 @@ int main (int argc, char **argv) { setbuf(stderr, NULL); /* process arguments */ - while ((c = getopt(argc, argv, "a:bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:LR:")) != -1) { + while ((c = getopt(argc, argv, "a:p:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:LR:")) != -1) { switch (c) { case 'a': /* access for unix domain socket, as octal mask (like chmod)*/ @@ -2962,9 +2843,6 @@ int main (int argc, char **argv) { case 'U': settings.udpport = atoi(optarg); break; - case 'b': - settings.managed = true; - break; case 'p': settings.port = atoi(optarg); break; @@ -3154,16 +3032,6 @@ int main (int argc, char **argv) { suffix_init(); slabs_init(settings.maxbytes, settings.factor, preallocate); - /* managed instance? alloc and zero a bucket array */ - if (settings.managed) { - buckets = malloc(sizeof(int) * MAX_BUCKETS); - if (buckets == 0) { - fprintf(stderr, "failed to allocate the bucket array"); - exit(EXIT_FAILURE); - } - memset(buckets, 0, sizeof(int) * MAX_BUCKETS); - } - /* * ignore SIGPIPE signals; we can use errno==EPIPE if we * need that information diff --git a/memcached.h b/memcached.h index 0cf0e0bf97..512df276c1 100644 --- a/memcached.h +++ b/memcached.h @@ -87,7 +87,6 @@ struct settings { char *inter; int verbose; rel_time_t oldest_live; /* ignore existing items older than this */ - bool managed; /* if 1, a tracker manages virtual buckets */ int evict_to_free; char *socketpath; /* path to unix socket if using local socket */ int access; /* access mask (a la chmod) for unix domain socket */ @@ -219,16 +218,10 @@ struct conn { int hdrsize; /* number of headers' worth of space is allocated */ int binary; /* are we in binary mode */ - int bucket; /* bucket number for the next command, if running as - a managed instance. -1 (_not_ 0) means invalid. */ - int gen; /* generation requested for the bucket */ bool noreply; /* True if the reply should not be sent. */ conn *next; /* Used for generating a list of conn structures */ }; -/* number of virtual buckets for a managed instance */ -#define MAX_BUCKETS 32768 - /* current time of day (updated periodically) */ extern volatile rel_time_t current_time; diff --git a/t/managed-buckets.t b/t/managed-buckets.t deleted file mode 100755 index 99e07d5c9d..0000000000 --- a/t/managed-buckets.t +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/perl - -use strict; -use Test::More skip_all => "Tests not written."; # tests => 1 -use FindBin qw($Bin); -use lib "$Bin/lib"; -use MemcachedTest; - -my $server = new_memcached(); -my $sock = $server->sock; - From 28d27b22c1c57906cb3cded87e44a4cf28866bca Mon Sep 17 00:00:00 2001 From: dormando Date: Sat, 6 Sep 2008 15:35:21 -0700 Subject: [PATCH 22/24] Display final errno status on the various listen failures. "failed to listen on TCP port 11211" is nice, but "Address already in use" is more nice. I have a feeling this isn't 100% correct given the ipv6 code, but either it's good enough or someone else will have to outpatch me. --- memcached.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/memcached.c b/memcached.c index 38c778a73c..001484d9b5 100644 --- a/memcached.c +++ b/memcached.c @@ -3062,16 +3062,22 @@ int main (int argc, char **argv) { /* create unix mode sockets after dropping privileges */ if (settings.socketpath != NULL) { + errno = 0; if (server_socket_unix(settings.socketpath,settings.access)) { fprintf(stderr, "failed to listen on UNIX socket: %s\n", settings.socketpath); + if (errno != 0) + perror("socket listen"); exit(EXIT_FAILURE); } } /* create the listening socket, bind it, and init */ if (settings.socketpath == NULL) { + errno = 0; if (settings.port && server_socket(settings.port, 0)) { fprintf(stderr, "failed to listen on TCP port %d\n", settings.port); + if (errno != 0) + perror("tcp listen"); exit(EXIT_FAILURE); } /* @@ -3082,8 +3088,11 @@ int main (int argc, char **argv) { */ /* create the UDP listening socket and bind it */ + errno = 0; if (settings.udpport && server_socket(settings.udpport, 1)) { fprintf(stderr, "failed to listen on UDP port %d\n", settings.udpport); + if (errno != 0) + perror("udp listen"); exit(EXIT_FAILURE); } } From 469915d9dc8615ec33126b3452f758a19825494c Mon Sep 17 00:00:00 2001 From: dormando Date: Sat, 6 Sep 2008 15:52:08 -0700 Subject: [PATCH 23/24] Update changelog, common contributors. I'm missing some contributors now. Have to look up your address probably. Please bitch if you've tossed more than a couple small patches and want to be included. --- ChangeLog | 20 ++++++++++++++++++++ doc/CONTRIBUTORS | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/ChangeLog b/ChangeLog index f5f2fc7487..374a5c188b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,27 @@ 2008-09-06 + * Display error status on listen failures (Dormando) + * Remove managed instance code. Incomplete/etc. (Dormando) + * Handle broken IPV6 stacks better (Brian Aker) + + * Generate warnings on setsockopt() failures (Brian Aker) + + * Fix some indentation issues (Brian Aker) + + * UDP/TCP can be disabled by setting their port to zero (Brian Aker) + + * Zero out libevent thread structures before use (Ricky Zhou) + + * New stat: Last accessed time for last evicted item per slab class. + (Dormando) + + * Use a dedicated socket accept thread (Facebook) + + * Add -R option. Limit the number of requests processed by a connection + at once. Prevents starving other threads if bulk loading. (Facebook) + 2008-07-29 [Version 1.2.6 released] 2008-07-24 [Version 1.2.6-rc1 released] diff --git a/doc/CONTRIBUTORS b/doc/CONTRIBUTORS index 47682e57cf..1d2a51d254 100644 --- a/doc/CONTRIBUTORS +++ b/doc/CONTRIBUTORS @@ -37,3 +37,7 @@ Eli Bingham Jean-Francois Bustarret Paul G Paul Lindner +Dustin Sallings +Tomash Brechko +Brian Aker +Dormando From 3f2c0fd1d8e95f371d7f9240ba62750c18457c8c Mon Sep 17 00:00:00 2001 From: dormando Date: Sat, 6 Sep 2008 16:02:07 -0700 Subject: [PATCH 24/24] Update doc/protocol.txt for the new evicted_time stat. --- doc/protocol.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/protocol.txt b/doc/protocol.txt index c7920df81e..d0849245d8 100644 --- a/doc/protocol.txt +++ b/doc/protocol.txt @@ -436,6 +436,9 @@ number Number of items presently stored in this class. Expired age Age of the oldest item in the LRU. evicted Number of times an item had to be evicted from the LRU before it expired. +evicted_time Seconds since the last access for the most recent item + evicted from this class. Use this to judge how + recently active your evicted data is. outofmemory Number of times the underlying slab class was unable to store a new item. This means you are running with -M or an eviction failed.