├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── Makefile.am ├── NEWS ├── README ├── README.md ├── assoc_chain.c ├── assoc_chain.h ├── assoc_cuckoo.c ├── assoc_cuckoo.h ├── bench ├── Makefile ├── bench_client.c ├── bench_common.h ├── bench_config.h ├── bench_trace_gen.cc ├── ycsb_workload_gen.sh └── ycsb_workload_settings │ ├── kv100M_op100M_latest.dat │ ├── kv100M_op100M_uniform.dat │ ├── kv100M_op100M_zipf.dat │ ├── kv10M_op10M_latest.dat │ ├── kv10M_op10M_uniform.dat │ ├── kv10M_op10M_zipf.dat │ ├── kv1M_op1M_latest.dat │ ├── kv1M_op1M_uniform.dat │ └── kv1M_op1M_zipf.dat ├── bit_util.h ├── cache.c ├── cache.h ├── configure.ac ├── daemon.c ├── doc ├── CONTRIBUTORS ├── Doxyfile ├── Makefile.am ├── memcached.1 ├── protocol-binary-range.xml ├── protocol-binary.xml ├── protocol.txt ├── readme.txt └── xml2rfc │ ├── reference.RFC.0768.xml │ ├── rfc2629-noinc.xsl │ ├── rfc2629-other.ent │ ├── rfc2629-refchk.xsl │ ├── rfc2629-xhtml.ent │ └── rfc2629.dtd ├── hash.c ├── hash.h ├── items.c ├── items.h ├── m4 └── c99-backport.m4 ├── memc3_config.h ├── memc3_util.c ├── memc3_util.h ├── memcached.c ├── memcached.h ├── memcached.spec ├── memcached_dtrace.d ├── perf_count.c ├── perf_count.h ├── protocol_binary.h ├── sasl_defs.c ├── sasl_defs.h ├── scripts ├── README.damemtop ├── damemtop ├── damemtop.yaml ├── mc_slab_mover ├── memcached-init ├── memcached-tool ├── memcached.service ├── memcached.sysv └── start-memcached ├── sizes.c ├── slabs.c ├── slabs.h ├── solaris_priv.c ├── stamp-h1 ├── stats.c ├── stats.h ├── t ├── 00-startup.t ├── 64bit.t ├── binary-get.t ├── binary-sasl.t ├── binary.t ├── bogus-commands.t ├── cas.t ├── daemonize.t ├── dash-M.t ├── evictions.t ├── expirations.t ├── flags.t ├── flush-all.t ├── getset.t ├── incrdecr.t ├── issue_104.t ├── issue_108.t ├── issue_14.t ├── issue_140.t ├── issue_152.t ├── issue_163.t ├── issue_183.t ├── issue_22.t ├── issue_29.t ├── issue_3.t ├── issue_41.t ├── issue_42.t ├── issue_50.t ├── issue_61.t ├── issue_67.t ├── issue_68.t ├── issue_70.t ├── item_size_max.t ├── lib │ └── MemcachedTest.pm ├── line-lengths.t ├── lru.t ├── maxconns.t ├── multiversioning.t ├── noreply.t ├── sasl │ └── memcached.conf ├── slabs_reassign.t ├── stats-detail.t ├── stats.t ├── stress-memcached.pl ├── touch.t ├── udp.t ├── unixsocket.t └── whitespace.t ├── testapp.c ├── thread.c ├── timedrun.c ├── trace.h ├── util.c ├── util.h └── version.m4 /.gitignore: -------------------------------------------------------------------------------- 1 | #* 2 | #*# 3 | *# 4 | *.*# 5 | *.class 6 | *.dSYM 7 | *.gcda 8 | *.gcno 9 | *.la 10 | *.lo 11 | *.o 12 | *.so 13 | *~ 14 | .#* 15 | .*.sw* 16 | .deps 17 | .deps/ 18 | .libs 19 | COPYING 20 | DS_Store 21 | INSTALL 22 | Makefile 23 | Makefile.in 24 | aclocal.m4 25 | autom4te.cache/ 26 | bench_cache 27 | bench_hash 28 | codebench/Makefile.in 29 | compile 30 | config.guconfig.h 31 | config.guess 32 | config.h 33 | config.h.in 34 | config.log 35 | config.status 36 | config.sub 37 | configure 38 | core 39 | depcomp 40 | ess 41 | gen-cpp 42 | gen-rb 43 | install-sh 44 | libtool 45 | memc3 46 | memcached 47 | memcached-debug 48 | missing 49 | nstall-sh 50 | sizes 51 | stamp-h1 52 | tags 53 | testapp 54 | timedrun 55 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Anatoly Vorobey 2 | Brad Fitzpatrick 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003, Danga Interactive, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | * Neither the name of the Danga Interactive nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = memc3 2 | pkginclude_HEADERS = protocol_binary.h 3 | noinst_PROGRAMS = sizes testapp timedrun 4 | 5 | BUILT_SOURCES= 6 | 7 | testapp_SOURCES = testapp.c util.c util.h 8 | 9 | timedrun_SOURCES = timedrun.c 10 | 11 | memc3_SOURCES = memcached.c memcached.h \ 12 | hash.c hash.h \ 13 | slabs.c slabs.h \ 14 | items.c items.h \ 15 | assoc_chain.c assoc_chain.h \ 16 | assoc_cuckoo.c assoc_cuckoo.h \ 17 | thread.c daemon.c \ 18 | stats.c stats.h \ 19 | util.c util.h \ 20 | trace.h cache.h sasl_defs.h \ 21 | memc3_util.c memc3_util.h 22 | 23 | if BUILD_CACHE 24 | memc3_SOURCES += cache.c 25 | testapp_SOURCES += cache.c 26 | endif 27 | 28 | if BUILD_SOLARIS_PRIVS 29 | memc3_SOURCES += solaris_priv.c 30 | endif 31 | 32 | if ENABLE_SASL 33 | memc3_SOURCES += sasl_defs.c 34 | endif 35 | 36 | memc3_debug_SOURCES = $(memc3_SOURCES) 37 | memc3_CPPFLAGS = 38 | memc3_debug_LDADD = @PROFILER_LDFLAGS@ 39 | memc3_debug_CFLAGS = @PROFILER_FLAGS@ 40 | 41 | memc3_LDADD = 42 | memc3_DEPENDENCIES = 43 | memc3_debug_DEPENDENCIES = 44 | CLEANFILES= 45 | 46 | SUBDIRS = doc 47 | DIST_DIRS = scripts 48 | EXTRA_DIST = doc scripts t memcached.spec memcached_dtrace.d version.m4 49 | 50 | MOSTLYCLEANFILES = *.gcov *.gcno *.gcda *.tcov 51 | 52 | test: memc3-debug sizes testapp 53 | $(srcdir)/sizes 54 | $(srcdir)/testapp 55 | prove $(srcdir)/t 56 | @if test `basename $(PROFILER)` = "gcov"; then \ 57 | for file in memc3_debug-*.gc??; do \ 58 | mv -f $$file `echo $$file | sed 's/memc3_debug-//'`; \ 59 | done && \ 60 | for file in *.gcda; do \ 61 | srcfile=`echo $$file | sed 's/.gcda/.c/'`; \ 62 | if test -n "`echo $(memc3_debug_SOURCES) | grep $$srcfile`"; then \ 63 | echo `$(PROFILER) $$srcfile` | sed 's/'$$srcfile':.*//'; \ 64 | fi \ 65 | done \ 66 | elif test `basename $(PROFILER)` = "tcov"; then \ 67 | files=`grep SRCFILE memc3-debug.profile/tcovd | sed 's/SRCFILE://' | sort | uniq` && \ 68 | $(PROFILER) -x memc3-debug.profile $$files 2>&1; \ 69 | for file in *.tcov; do \ 70 | srcfile=`echo $$file | sed 's/.tcov//'`; \ 71 | if test -n "`echo $(memc3_debug_SOURCES) | grep $$srcfile`"; then \ 72 | echo $$srcfile : `grep 'Percent of the file executed' $$file`; \ 73 | fi \ 74 | done \ 75 | else :; fi 76 | 77 | docs: 78 | (cat Doxyfile ; echo "PROJECT_NUMBER=`cat version.num`") | doxygen - 79 | 80 | dist-hook: 81 | rm -f $(distdir)/*/*~ $(distdir)/t/lib/*~ $(distdir)/*~ 82 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | http://www.danga.com/memcached/news.bml 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Dependencies: 2 | 3 | -- libevent, http://www.monkey.org/~provos/libevent/ (libevent-dev) 4 | 5 | If using Linux, you need a kernel with epoll. Sure, libevent will 6 | work with normal select, but it sucks. 7 | 8 | epoll isn't in Linux 2.4, but there's a backport at: 9 | 10 | http://www.xmailserver.org/linux-patches/nio-improve.html 11 | 12 | You want the epoll-lt patch (level-triggered). 13 | 14 | If you're using MacOS, you'll want libevent 1.1 or higher to deal with 15 | a kqueue bug. 16 | 17 | Also, be warned that the -k (mlockall) option to memcached might be 18 | dangerous when using a large cache. Just make sure the memcached machines 19 | don't swap. memcached does non-blocking network I/O, but not disk. (it 20 | should never go to disk, or you've lost the whole point of it) 21 | 22 | The memcached website is at: 23 | 24 | http://www.memcached.org 25 | 26 | Want to contribute? Up-to-date pointers should be at: 27 | 28 | http://contributing.appspot.com/memcached 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MemC3 2 | ===== 3 | 4 | MemC3 is an in-memory key-value cache, derived from Memcached but improved with memory-efficient and concurrent data structures. MemC3 applies multi-reader concurrent cuckoo hashing as its key-value index and CLOCK-replacement algorithm as cache eviction policy. As a result, MemC3 scales better, runs faster, and uses less memory. For details about the algorithms, performance evaluation and citations, please refer to [our paper in NSDI 2013][1]. For a re-engineered fast, concurrent, stand-alone implementation of concurrent cuckoo hash table, please check our [efficient/libcukoo repository](https://github.com/efficient/libcuckoo). 5 | 6 | [1]: http://www.cs.cmu.edu/~dga/papers/memc3-nsdi2013.pdf "MemC3: Compact and Concurrent Memcache with Dumber Caching and Smarter Hashing" 7 | 8 | Authors 9 | ======= 10 | 11 | MemC3 is developed by Bin Fan, David G. Andersen, and Michael Kaminsky. You can also email us at [libcuckoo-dev@googlegroups.com](mailto:libcuckoo-dev@googlegroups.com). 12 | 13 | Requirements 14 | ============ 15 | 16 | $ sudo apt-get install libevent-dev 17 | 18 | Building 19 | ========== 20 | 21 | $ autoreconf -fis 22 | $ ./configure 23 | $ make 24 | 25 | If you want to generate optimized build (e.g., with -O2), run ``./configure CFLAGS='-O2'`` instead of ``./configure`` 26 | before ``make``. Alternatively, if you want to create debug build (e.g., with -g -ggdb), use ``./configure CFLAGS='-g -ggdb'`` 27 | 28 | 29 | Benchmark 30 | ========= 31 | 32 | In our [NSDI paper][1], memc3 is benchmared using workloads gerenated by [Yahoo YCSB][2]. Note that, YCSB is designed to benchmark the performance of cloud file systems, but too slow when used to benchmark memc3 or memcached directly. Therefore, we pre-generate workloads (using script ``bench/ycsb_workloads_gen.sh`` which reads settings in ``bench/ycsb_workloads_settings``), and run clients to read these pre-generated traces before benchmarking. 33 | 34 | [2]: dl.acm.org/citation.cfm?id=1807152 "Benchmarking cloud serving systems with YCSB" 35 | -------------------------------------------------------------------------------- /assoc_chain.h: -------------------------------------------------------------------------------- 1 | /* associative array */ 2 | void assoc_init(const int hashpower_init); 3 | item *assoc_find(const char *key, const size_t nkey, const uint32_t hv); 4 | int assoc_insert(item *item, const uint32_t hv); 5 | void assoc_delete(const char *key, const size_t nkey, const uint32_t hv); 6 | void do_assoc_move_next_bucket(void); 7 | int start_assoc_maintenance_thread(void); 8 | void stop_assoc_maintenance_thread(void); 9 | 10 | 11 | // added to keep same API as other hashtable impl 12 | void assoc_destroy(void); 13 | void assoc_pre_bench(void); 14 | void assoc_post_bench(void); 15 | -------------------------------------------------------------------------------- /assoc_cuckoo.h: -------------------------------------------------------------------------------- 1 | /* associative array */ 2 | void assoc2_init(const int hashpower_init); 3 | item *assoc2_find(const char *key, const size_t nkey, const uint32_t hv); 4 | int assoc2_insert(item *item, const uint32_t hv); 5 | void assoc2_delete(const char *key, const size_t nkey, const uint32_t hv); 6 | /* void do_assoc_move_next_bucket(void); */ 7 | /* int start_assoc_maintenance_thread(void); */ 8 | /* void stop_assoc_maintenance_thread(void); */ 9 | 10 | void assoc2_destroy(void); 11 | void assoc2_pre_bench(void); 12 | void assoc2_post_bench(void); 13 | -------------------------------------------------------------------------------- /bench/Makefile: -------------------------------------------------------------------------------- 1 | all: bench_trace_gen bench_client 2 | 3 | bench_trace_gen: bench_trace_gen.cc 4 | g++ -std=c++0x -o bench_trace_gen bench_trace_gen.cc -lssl -lcrypto -lz 5 | 6 | bench_client: bench_client.o 7 | gcc -std=gnu99 -O2 -o bench_client bench_client.o -lmemcached -L/usr/lib -L/usr/local/lib 8 | 9 | bench_client.o: bench_client.c 10 | gcc -std=gnu99 -O2 -c bench_client.c -I/usr/include -I/usr/include 11 | 12 | clean: 13 | -rm *.o bench_client bench_trace_gen 14 | -------------------------------------------------------------------------------- /bench/bench_common.h: -------------------------------------------------------------------------------- 1 | #ifndef _BENCH_COMMON_H_ 2 | #define _BENCH_COMMON_H_ 3 | 4 | #include "bench_config.h" 5 | 6 | /* type of each query */ 7 | enum query_types{ 8 | query_put=0, 9 | query_get, 10 | query_del, 11 | }; 12 | 13 | /* 14 | * format of each query, it has a key and a type and we don't care 15 | * the value 16 | */ 17 | typedef struct __attribute__((__packed__)) { 18 | char hashed_key[NKEY]; 19 | char type; 20 | } query; 21 | 22 | /* bench result */ 23 | typedef struct __attribute__((__packed__)) { 24 | double total_tput; 25 | double total_time; 26 | size_t total_hits; 27 | size_t total_miss; 28 | size_t total_gets; 29 | size_t total_puts; 30 | size_t num_threads; 31 | } result_t; 32 | #endif 33 | -------------------------------------------------------------------------------- /bench/bench_config.h: -------------------------------------------------------------------------------- 1 | #ifndef __BENCH_CONFIG__ 2 | #define __BENCH_CONFIG__ 3 | 4 | /* 5 | * size of the key in bytes 6 | */ 7 | #define NKEY 16 8 | #define NVAL 32 9 | 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /bench/bench_trace_gen.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bench_common.h" 9 | 10 | using namespace std; 11 | 12 | static void sha1(char hash[20], const char* buf, size_t count) 13 | { 14 | SHA1(reinterpret_cast(buf), count, reinterpret_cast(hash)); 15 | } 16 | 17 | 18 | int main(int argc, char **argv) { 19 | 20 | if (argc <= 1) { 21 | cout << "usage: ./bench_trace_gen output_filename < input_filename" 22 | << endl; 23 | exit (1); 24 | } 25 | 26 | //size_t val_len = static_cast(-1); 27 | //size_t val_len = atoi(argv[1]); 28 | size_t key_len = NKEY; 29 | size_t val_len = NVAL; 30 | size_t num_queries = 0; 31 | 32 | FILE *fp = fopen(argv[1], "w"); 33 | 34 | fwrite(&key_len, sizeof(size_t), 1, fp); 35 | fwrite(&val_len, sizeof(size_t), 1, fp); 36 | 37 | const size_t tmp_size = 1048576; 38 | char* tmp = new char[tmp_size]; 39 | 40 | while (fgets(tmp, tmp_size, stdin)) { 41 | char buf[20]; 42 | char rawkey[1024]; 43 | query q; 44 | if (sscanf(tmp, "\"operationcount\"=\"%zu\"", &num_queries)) { 45 | fwrite(&num_queries, sizeof(num_queries), 1, fp); 46 | continue; 47 | } else if (sscanf(tmp, "INSERT usertable %s [ field", rawkey)) { 48 | q.type = query_put; 49 | sha1(buf, rawkey, strlen(rawkey)); 50 | memcpy(q.hashed_key, buf, key_len); 51 | } else if (sscanf(tmp, "UPDATE usertable %s [ field", rawkey)) { 52 | q.type = query_put; 53 | sha1(buf, rawkey, strlen(rawkey)); 54 | memcpy(q.hashed_key, buf, key_len); 55 | } 56 | else if (sscanf(tmp, "READ usertable %s [", rawkey)) { 57 | q.type = query_get; 58 | sha1(buf, rawkey, strlen(rawkey)); 59 | memcpy(q.hashed_key, buf, key_len); 60 | } else 61 | continue; 62 | 63 | fwrite(&q, sizeof(q), 1, fp); 64 | } 65 | delete tmp; 66 | fclose(fp); 67 | } 68 | -------------------------------------------------------------------------------- /bench/ycsb_workload_gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Please change this according to your ycsb installation 3 | # so that, ${YCSB_HOME}/bin/ycsb is the binary 4 | YCSB_HOME=/path/to/ycsb/ 5 | 6 | for setting in `ls ycsb_workload_settings` 7 | do 8 | echo using predefined workloadb to create transaction records for $setting with 5% updates 9 | echo generateing $setting.load, the insertions used before benchmark 10 | ${YCSB_HOME}/bin/ycsb load basic -P ${YCSB_HOME}/workloads/workloadb -P ./workloads/$setting.dat > $setting.load 11 | echo generateing $setting.run, the lookup queries used before benchmark 12 | ${YCSB_HOME}/bin/ycsb run basic -P ${YCSB_HOME}/workloads/workloadb -P ./workloads/$setting.dat > $setting.run 13 | 14 | echo using predefined workloadc to create transaction records for $setting with reads only 15 | echo generateing $setting.load, the insertions used before benchmark 16 | ${YCSB_HOME}/bin/ycsb load basic -P ${YCSB_HOME}/workloads/workloadc -P ./workloads/$setting.dat > $setting.load 17 | echo generateing $setting.run, the lookup queries used before benchmark 18 | ${YCSB_HOME}/bin/ycsb run basic -P ${YCSB_HOME}/workloads/workloadc -P ./workloads/$setting.dat > $setting.run 19 | done 20 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv100M_op100M_latest.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 100M here 6 | recordcount=100000000 7 | # number of queries, 100M 8 | operationcount=100000000 9 | # distribution 10 | requestdistribution=latest 11 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv100M_op100M_uniform.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 100M here 6 | recordcount=100000000 7 | # number of queries, 100M 8 | operationcount=100000000 9 | # distribution 10 | requestdistribution=uniform 11 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv100M_op100M_zipf.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 100M here 6 | recordcount=100000000 7 | # number of queries, 100M 8 | operationcount=100000000 9 | # distribution 10 | requestdistribution=zipfian 11 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv10M_op10M_latest.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 10M here 6 | recordcount=10000000 7 | # number of queries, 10M 8 | operationcount=10000000 9 | # distribution 10 | requestdistribution=latest 11 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv10M_op10M_uniform.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 10M here 6 | recordcount=10000000 7 | # number of queries, 10M 8 | operationcount=10000000 9 | # distribution 10 | requestdistribution=uniform 11 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv10M_op10M_zipf.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 10M here 6 | recordcount=10000000 7 | # number of queries, 10M 8 | operationcount=10000000 9 | # distribution 10 | requestdistribution=zipfian 11 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv1M_op1M_latest.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 1M here 6 | recordcount=1000000 7 | # number of queries, 1M 8 | operationcount=1000000 9 | # distribution 10 | requestdistribution=latest 11 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv1M_op1M_uniform.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 1M here 6 | recordcount=1000000 7 | # number of queries, 1M 8 | operationcount=1000000 9 | # distribution 10 | requestdistribution=uniform 11 | -------------------------------------------------------------------------------- /bench/ycsb_workload_settings/kv1M_op1M_zipf.dat: -------------------------------------------------------------------------------- 1 | # this is for kv, so 1 field only 2 | fieldcount=1 3 | # value length, let's do with 32byte values 4 | fieldlength=32 5 | # number of unique keys, 1M here 6 | recordcount=1000000 7 | # number of queries, 1M 8 | operationcount=1000000 9 | # distribution 10 | requestdistribution=zipfian 11 | -------------------------------------------------------------------------------- /bit_util.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #ifndef _BIT_UTIL_H_ 3 | #define _BIT_UTIL_H_ 4 | 5 | // inspired from http://www-graphics.stanford.edu/~seander/bithacks.html#ZeroInWord 6 | #define haszero4(x) (((x) - 0x1111ULL) & (~(x)) & 0x8888ULL) 7 | #define hasvalue4(x,n) (haszero4((x) ^ (0x1111ULL * (n)))) 8 | 9 | #define haszero8(x) (((x) - 0x01010101ULL) & (~(x)) & 0x80808080ULL) 10 | #define hasvalue8(x,n) (haszero8((x) ^ (0x01010101ULL * (n)))) 11 | 12 | #define haszero12(x) (((x) - 0x001001001001ULL) & (~(x)) & 0x800800800800ULL) 13 | #define hasvalue12(x,n) (haszero12((x) ^ (0x001001001001ULL * (n)))) 14 | 15 | #define haszero16(x) (((x) - 0x0001000100010001ULL) & (~(x)) & 0x8000800080008000ULL) 16 | #define hasvalue16(x,n) (haszero16((x) ^ (0x0001000100010001ULL * (n)))) 17 | 18 | /* inline bool hasvalue12(const uint64_t x, const uint32_t n) { */ 19 | /* //return haszero12((x) ^ (0x001001001001ULL * (n))); */ 20 | /* uint64_t t1 = (uint64_t) (n) << 36; */ 21 | /* uint64_t t2 = (uint64_t) (n) << 24; */ 22 | /* uint64_t t3 = (uint64_t) (n) << 12; */ 23 | /* return haszero12((x) ^ (t1 + t2 + t3 + n)); */ 24 | 25 | /* } */ 26 | 27 | static unsigned char numones[256] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,\ 28 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,\ 29 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,\ 30 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\ 31 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,\ 32 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\ 33 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\ 34 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,\ 35 | 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,\ 36 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\ 37 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\ 38 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,\ 39 | 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\ 40 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,\ 41 | 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,\ 42 | 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; 43 | 44 | static inline size_t rank(char* bv, size_t len) { 45 | 46 | 47 | size_t ret = 0; 48 | for (size_t i = 0; i < len/8; i++) { 49 | unsigned char c = bv[i]; 50 | ret += numones[c]; 51 | } 52 | return ret; 53 | } 54 | 55 | 56 | #endif //_BIT_UTIL_H 57 | -------------------------------------------------------------------------------- /cache.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef NDEBUG 7 | #include 8 | #endif 9 | 10 | #include "cache.h" 11 | 12 | #ifndef NDEBUG 13 | const uint64_t redzone_pattern = 0xdeadbeefcafebabe; 14 | int cache_error = 0; 15 | #endif 16 | 17 | const int initial_pool_size = 64; 18 | 19 | cache_t* cache_create(const char *name, size_t bufsize, size_t align, 20 | cache_constructor_t* constructor, 21 | cache_destructor_t* destructor) { 22 | cache_t* ret = calloc(1, sizeof(cache_t)); 23 | char* nm = strdup(name); 24 | void** ptr = calloc(initial_pool_size, sizeof(void*)); 25 | if (ret == NULL || nm == NULL || ptr == NULL || 26 | pthread_mutex_init(&ret->mutex, NULL) == -1) { 27 | free(ret); 28 | free(nm); 29 | free(ptr); 30 | return NULL; 31 | } 32 | 33 | ret->name = nm; 34 | ret->ptr = ptr; 35 | ret->freetotal = initial_pool_size; 36 | ret->constructor = constructor; 37 | ret->destructor = destructor; 38 | 39 | #ifndef NDEBUG 40 | ret->bufsize = bufsize + 2 * sizeof(redzone_pattern); 41 | #else 42 | ret->bufsize = bufsize; 43 | #endif 44 | 45 | return ret; 46 | } 47 | 48 | static inline void* get_object(void *ptr) { 49 | #ifndef NDEBUG 50 | uint64_t *pre = ptr; 51 | return pre + 1; 52 | #else 53 | return ptr; 54 | #endif 55 | } 56 | 57 | void cache_destroy(cache_t *cache) { 58 | while (cache->freecurr > 0) { 59 | void *ptr = cache->ptr[--cache->freecurr]; 60 | if (cache->destructor) { 61 | cache->destructor(get_object(ptr), NULL); 62 | } 63 | free(ptr); 64 | } 65 | free(cache->name); 66 | free(cache->ptr); 67 | pthread_mutex_destroy(&cache->mutex); 68 | } 69 | 70 | void* cache_alloc(cache_t *cache) { 71 | void *ret; 72 | void *object; 73 | pthread_mutex_lock(&cache->mutex); 74 | if (cache->freecurr > 0) { 75 | ret = cache->ptr[--cache->freecurr]; 76 | object = get_object(ret); 77 | } else { 78 | object = ret = malloc(cache->bufsize); 79 | if (ret != NULL) { 80 | object = get_object(ret); 81 | 82 | if (cache->constructor != NULL && 83 | cache->constructor(object, NULL, 0) != 0) { 84 | free(ret); 85 | object = NULL; 86 | } 87 | } 88 | } 89 | pthread_mutex_unlock(&cache->mutex); 90 | 91 | #ifndef NDEBUG 92 | if (object != NULL) { 93 | /* add a simple form of buffer-check */ 94 | uint64_t *pre = ret; 95 | *pre = redzone_pattern; 96 | ret = pre+1; 97 | memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)), 98 | &redzone_pattern, sizeof(redzone_pattern)); 99 | } 100 | #endif 101 | 102 | return object; 103 | } 104 | 105 | void cache_free(cache_t *cache, void *ptr) { 106 | pthread_mutex_lock(&cache->mutex); 107 | 108 | #ifndef NDEBUG 109 | /* validate redzone... */ 110 | if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)), 111 | &redzone_pattern, sizeof(redzone_pattern)) != 0) { 112 | raise(SIGABRT); 113 | cache_error = 1; 114 | pthread_mutex_unlock(&cache->mutex); 115 | return; 116 | } 117 | uint64_t *pre = ptr; 118 | --pre; 119 | if (*pre != redzone_pattern) { 120 | raise(SIGABRT); 121 | cache_error = -1; 122 | pthread_mutex_unlock(&cache->mutex); 123 | return; 124 | } 125 | ptr = pre; 126 | #endif 127 | if (cache->freecurr < cache->freetotal) { 128 | cache->ptr[cache->freecurr++] = ptr; 129 | } else { 130 | /* try to enlarge free connections array */ 131 | size_t newtotal = cache->freetotal * 2; 132 | void **new_free = realloc(cache->ptr, sizeof(char *) * newtotal); 133 | if (new_free) { 134 | cache->freetotal = newtotal; 135 | cache->ptr = new_free; 136 | cache->ptr[cache->freecurr++] = ptr; 137 | } else { 138 | if (cache->destructor) { 139 | cache->destructor(ptr, NULL); 140 | } 141 | free(ptr); 142 | 143 | } 144 | } 145 | pthread_mutex_unlock(&cache->mutex); 146 | } 147 | 148 | -------------------------------------------------------------------------------- /cache.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #ifndef CACHE_H 3 | #define CACHE_H 4 | #include 5 | 6 | #ifdef HAVE_UMEM_H 7 | #include 8 | #define cache_t umem_cache_t 9 | #define cache_alloc(a) umem_cache_alloc(a, UMEM_DEFAULT) 10 | #define cache_free(a, b) umem_cache_free(a, b) 11 | #define cache_create(a,b,c,d,e) umem_cache_create((char*)a, b, c, d, e, NULL, NULL, NULL, 0) 12 | #define cache_destroy(a) umem_cache_destroy(a); 13 | 14 | #else 15 | 16 | #ifndef NDEBUG 17 | /* may be used for debug purposes */ 18 | extern int cache_error; 19 | #endif 20 | 21 | /** 22 | * Constructor used to initialize allocated objects 23 | * 24 | * @param obj pointer to the object to initialized. 25 | * @param notused1 This parameter is currently not used. 26 | * @param notused2 This parameter is currently not used. 27 | * @return you should return 0, but currently this is not checked 28 | */ 29 | typedef int cache_constructor_t(void* obj, void* notused1, int notused2); 30 | /** 31 | * Destructor used to clean up allocated objects before they are 32 | * returned to the operating system. 33 | * 34 | * @param obj pointer to the object to initialized. 35 | * @param notused1 This parameter is currently not used. 36 | * @param notused2 This parameter is currently not used. 37 | * @return you should return 0, but currently this is not checked 38 | */ 39 | typedef void cache_destructor_t(void* obj, void* notused); 40 | 41 | /** 42 | * Definition of the structure to keep track of the internal details of 43 | * the cache allocator. Touching any of these variables results in 44 | * undefined behavior. 45 | */ 46 | typedef struct { 47 | /** Mutex to protect access to the structure */ 48 | pthread_mutex_t mutex; 49 | /** Name of the cache objects in this cache (provided by the caller) */ 50 | char *name; 51 | /** List of pointers to available buffers in this cache */ 52 | void **ptr; 53 | /** The size of each element in this cache */ 54 | size_t bufsize; 55 | /** The capacity of the list of elements */ 56 | int freetotal; 57 | /** The current number of free elements */ 58 | int freecurr; 59 | /** The constructor to be called each time we allocate more memory */ 60 | cache_constructor_t* constructor; 61 | /** The destructor to be called each time before we release memory */ 62 | cache_destructor_t* destructor; 63 | } cache_t; 64 | 65 | /** 66 | * Create an object cache. 67 | * 68 | * The object cache will let you allocate objects of the same size. It is fully 69 | * MT safe, so you may allocate objects from multiple threads without having to 70 | * do any syncrhonization in the application code. 71 | * 72 | * @param name the name of the object cache. This name may be used for debug purposes 73 | * and may help you track down what kind of object you have problems with 74 | * (buffer overruns, leakage etc) 75 | * @param bufsize the size of each object in the cache 76 | * @param align the alignment requirements of the objects in the cache. 77 | * @param constructor the function to be called to initialize memory when we need 78 | * to allocate more memory from the os. 79 | * @param destructor the function to be called before we release the memory back 80 | * to the os. 81 | * @return a handle to an object cache if successful, NULL otherwise. 82 | */ 83 | cache_t* cache_create(const char* name, size_t bufsize, size_t align, 84 | cache_constructor_t* constructor, 85 | cache_destructor_t* destructor); 86 | /** 87 | * Destroy an object cache. 88 | * 89 | * Destroy and invalidate an object cache. You should return all buffers allocated 90 | * with cache_alloc by using cache_free before calling this function. Not doing 91 | * so results in undefined behavior (the buffers may or may not be invalidated) 92 | * 93 | * @param handle the handle to the object cache to destroy. 94 | */ 95 | void cache_destroy(cache_t* handle); 96 | /** 97 | * Allocate an object from the cache. 98 | * 99 | * @param handle the handle to the object cache to allocate from 100 | * @return a pointer to an initialized object from the cache, or NULL if 101 | * the allocation cannot be satisfied. 102 | */ 103 | void* cache_alloc(cache_t* handle); 104 | /** 105 | * Return an object back to the cache. 106 | * 107 | * The caller should return the object in an initialized state so that 108 | * the object may be returned in an expected state from cache_alloc. 109 | * 110 | * @param handle handle to the object cache to return the object to 111 | * @param ptr pointer to the object to return. 112 | */ 113 | void cache_free(cache_t* handle, void* ptr); 114 | #endif 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /daemon.c: -------------------------------------------------------------------------------- 1 | /* $Header: /cvsroot/wikipedia/willow/src/bin/willow/daemon.c,v 1.1 2005/05/02 19:15:21 kateturner Exp $ */ 2 | /* $NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $ */ 3 | /*- 4 | * Copyright (c) 1990, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | */ 31 | 32 | #if defined __SUNPRO_C || defined __DECC || defined __HP_cc 33 | # pragma ident "@(#)$Header: /cvsroot/wikipedia/willow/src/bin/willow/daemon.c,v 1.1 2005/05/02 19:15:21 kateturner Exp $" 34 | # pragma ident "$NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $" 35 | #endif 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "memcached.h" 43 | 44 | int daemonize(int nochdir, int noclose) 45 | { 46 | int fd; 47 | 48 | switch (fork()) { 49 | case -1: 50 | return (-1); 51 | case 0: 52 | break; 53 | default: 54 | _exit(EXIT_SUCCESS); 55 | } 56 | 57 | if (setsid() == -1) 58 | return (-1); 59 | 60 | if (nochdir == 0) { 61 | if(chdir("/") != 0) { 62 | perror("chdir"); 63 | return (-1); 64 | } 65 | } 66 | 67 | if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) { 68 | if(dup2(fd, STDIN_FILENO) < 0) { 69 | perror("dup2 stdin"); 70 | return (-1); 71 | } 72 | if(dup2(fd, STDOUT_FILENO) < 0) { 73 | perror("dup2 stdout"); 74 | return (-1); 75 | } 76 | if(dup2(fd, STDERR_FILENO) < 0) { 77 | perror("dup2 stderr"); 78 | return (-1); 79 | } 80 | 81 | if (fd > STDERR_FILENO) { 82 | if(close(fd) < 0) { 83 | perror("close"); 84 | return (-1); 85 | } 86 | } 87 | } 88 | return (0); 89 | } 90 | -------------------------------------------------------------------------------- /doc/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | MEMCACHED CONTRIBUTORS 2 | 3 | This file contains a list of people who have contributed code and 4 | effort to the memcached project. If you don't see your name mentioned 5 | send email to the memcached mailing list so you can be immortalized. 6 | 7 | Also see the ChangeLog for even more people who have helped over the 8 | years by submitting fixes, patches and reporting bugs. 9 | 10 | 11 | Major authors: 12 | -------------- 13 | 14 | Brad Fitzpatrick -- maintainer, original implementations 15 | 16 | Anatoly Vorobey -- lots of the modern server code 17 | 18 | Steven Grimm -- iov writing (less CPU), UDP mode, 19 | non-2.0 slab mantissas, multithread, ... 20 | 21 | Other Contributors 22 | ------------------ 23 | 24 | Evan Martin 25 | Nathan Neulinger 26 | Eric Hodel 27 | Michael Johnson 28 | Paul Querna 29 | Jamie McCarthy 30 | Philip Neustrom 31 | Andrew O'Brien 32 | Josh Rotenberg 33 | Robin H. Johnson 34 | Tim Yardley 35 | Paolo Borelli 36 | Eli Bingham 37 | Jean-Francois Bustarret 38 | Paul G 39 | Paul Lindner 40 | Dormando 41 | Dustin Sallings 42 | Chris Goffinet 43 | Tomash Brechko 44 | Brian Aker 45 | Trond Norbye 46 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = memcached.1 2 | 3 | EXTRA_DIST = *.txt 4 | 5 | BUILT_SOURCES= 6 | 7 | if BUILD_SPECIFICATIONS 8 | BUILT_SOURCES += protocol-binary.txt protocol-binary-range.txt 9 | MOSTLYCLEANFILES = protocol-binary.txt protocol-binary-range.txt 10 | endif 11 | 12 | %.txt: %.full 13 | @XML2RFC@ $< $@ 14 | 15 | %.chk: %.xml xml2rfc/rfc2629-refchk.xsl 16 | @XSLTPROC@ xml2rfc/rfc2629-refchk.xsl $< >$@ 17 | 18 | %.full: %.xml xml2rfc/rfc2629-noinc.xsl 19 | @XSLTPROC@ xml2rfc/rfc2629-noinc.xsl $< >$@ 20 | 21 | -------------------------------------------------------------------------------- /doc/memcached.1: -------------------------------------------------------------------------------- 1 | .TH MEMCACHED 1 "April 11, 2005" 2 | .SH NAME 3 | memcached \- high-performance memory object caching system 4 | .SH SYNOPSIS 5 | .B memcached 6 | .RI [ options ] 7 | .br 8 | .SH DESCRIPTION 9 | This manual page documents briefly the 10 | .B memcached 11 | memory object caching daemon. 12 | .PP 13 | .B memcached 14 | is a flexible memory object caching daemon designed to alleviate database load 15 | in dynamic web applications by storing objects in memory. It's based on 16 | libevent to scale to any size needed, and is specifically optimized to avoid 17 | swapping and always use non-blocking I/O. 18 | .br 19 | .SH OPTIONS 20 | These programs follow the usual GNU command line syntax. A summary of options 21 | is included below. 22 | .TP 23 | .B \-s 24 | Unix socket path to listen on (disables network support). 25 | .TP 26 | .B \-a 27 | Permissions (in octal format) for Unix socket created with \-s option. 28 | .TP 29 | .B \-l 30 | Listen on ; default to INADDR_ANY. This is an important option to 31 | consider as there is no other way to secure the installation. Binding to an 32 | internal or firewalled network interface is suggested. 33 | .TP 34 | .B \-d 35 | Run memcached as a daemon. 36 | .TP 37 | .B \-u 38 | Assume the identity of (only when run as root). 39 | .TP 40 | .B \-m 41 | Use MB memory max to use for object storage; the default is 64 megabytes. 42 | .TP 43 | .B \-c 44 | Use max simultaneous connections; the default is 1024. 45 | .TP 46 | .B \-R 47 | This option seeks to prevent client starvation by setting a limit to the 48 | number of sequential requests the server will process from an individual 49 | client connection. Once a connection has exceeded this value, the server will 50 | attempt to process I/O on other connections before handling any further 51 | request from this connection. The default value for this option is 20. 52 | .TP 53 | .B \-k 54 | Lock down all paged memory. This is a somewhat dangerous option with large 55 | caches, so consult the README and memcached homepage for configuration 56 | suggestions. 57 | .TP 58 | .B \-p 59 | Listen on TCP port , the default is port 11211. 60 | .TP 61 | .B \-U 62 | Listen on UDP port , the default is port 11211, 0 is off. 63 | .TP 64 | .B \-M 65 | Disable automatic removal of items from the cache when out of memory. 66 | Additions will not be possible until adequate space is freed up. 67 | .TP 68 | .B \-r 69 | Raise the core file size limit to the maximum allowable. 70 | .TP 71 | .B \-f 72 | Use as the multiplier for computing the sizes of memory chunks that 73 | items are stored in. A lower value may result in less wasted memory depending 74 | on the total amount of memory available and the distribution of item sizes. 75 | The default is 1.25. 76 | .TP 77 | .B \-n 78 | Allocate a minimum of bytes for the item key, value, and flags. The 79 | default is 48. If you have a lot of small keys and values, you can get a 80 | significant memory efficiency gain with a lower value. If you use a high 81 | chunk growth factor (\-f option), on the other hand, you may want to increase 82 | the size to allow a bigger percentage of your items to fit in the most densely 83 | packed (smallest) chunks. 84 | .TP 85 | .B \-C 86 | Disable the use of CAS (and reduce the per-item size by 8 bytes). 87 | .TP 88 | .B \-h 89 | Show the version of memcached and a summary of options. 90 | .TP 91 | .B \-v 92 | Be verbose during the event loop; print out errors and warnings. 93 | .TP 94 | .B \-vv 95 | Be even more verbose; same as \-v but also print client commands and 96 | responses. 97 | .TP 98 | .B \-i 99 | Print memcached and libevent licenses. 100 | .TP 101 | .B \-P 102 | Print pidfile to , only used under \-d option. 103 | .TP 104 | .B \-t 105 | Number of threads to use to process incoming requests. This option is only 106 | meaningful if memcached was compiled with thread support enabled. It is 107 | typically not useful to set this higher than the number of CPU cores on the 108 | memcached server. The default is 4. 109 | .TP 110 | .B \-D 111 | Use as the delimiter between key prefixes and IDs. This is used for 112 | per-prefix stats reporting. The default is ":" (colon). If this option is 113 | specified, stats collection is turned on automatically; if not, then it may 114 | be turned on by sending the "stats detail on" command to the server. 115 | .TP 116 | .B \-L 117 | Try to use large memory pages (if available). Increasing the memory page size 118 | could reduce the number of TLB misses and improve the performance. In order to 119 | get large pages from the OS, memcached will allocate the total item-cache in 120 | one large chunk. Only available if supported on your OS. 121 | .TP 122 | .B \-B 123 | Specify the binding protocol to use. By default, the server will 124 | autonegotiate client connections. By using this option, you can 125 | specify the protocol clients must speak. Possible options are "auto" 126 | (the default, autonegotiation behavior), "ascii" and "binary". 127 | .TP 128 | .B \-I 129 | Override the default size of each slab page. Default is 1mb. Default is 1m, 130 | minimum is 1k, max is 128m. Adjusting this value changes the item size limit. 131 | Beware that this also increases the number of slabs (use -v to view), and the 132 | overal memory usage of memcached. 133 | .TP 134 | .B \-o 135 | Comma separated list of extended or experimental options. See -h or wiki for 136 | up to date list. 137 | .br 138 | .SH LICENSE 139 | The memcached daemon is copyright Danga Interactive and is distributed under 140 | the BSD license. Note that daemon clients are licensed separately. 141 | .br 142 | .SH SEE ALSO 143 | The README file that comes with memcached 144 | .br 145 | .B http://www.danga.com/memcached 146 | .SH AUTHOR 147 | The memcached daemon was written by Anatoly Vorobey 148 | .B 149 | and Brad Fitzpatrick 150 | .B 151 | and the rest of the crew of Danga Interactive 152 | .B http://www.danga.com 153 | .br 154 | -------------------------------------------------------------------------------- /doc/readme.txt: -------------------------------------------------------------------------------- 1 | To build the documentation you need xml2rfc ( http://xml.resource.org/ ). 2 | -------------------------------------------------------------------------------- /doc/xml2rfc/reference.RFC.0768.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | User Datagram Protocol 7 | 8 | University of Southern California (USC)/Information Sciences Institute 9 |
10 | 11 | 4676 Admiralty Way 12 | Marina del Rey 13 | CA 14 | 90291 15 | US 16 | +1 213 822 1511
17 |
18 | 19 | 20 | 21 | 22 |
23 | -------------------------------------------------------------------------------- /doc/xml2rfc/rfc2629-noinc.xsl: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | .xml 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | http://xml.resource.org/public/rfc/ 51 | 52 | .xml 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /doc/xml2rfc/rfc2629-other.ent: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /doc/xml2rfc/rfc2629-refchk.xsl: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /doc/xml2rfc/rfc2629.dtd: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 17 | 18 | 19 | 34 | 35 | 36 | 39 | 40 | 41 | 44 | %rfc2629-xhtml; 45 | 46 | 49 | %rfc2629-other; 50 | 51 | 52 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 92 | 93 | 94 | 100 | 101 | 117 | 118 | 119 | 122 | 123 | 124 | 126 | 127 | 128 | 129 | 131 | 132 | 133 | 138 | 139 | 141 | 143 | 144 | 145 | 146 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 175 | 176 | 177 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 191 | 192 | 200 | 201 | 202 | 203 | 206 | 207 | 209 | 210 | 214 | 215 | 216 | 221 | 222 | 223 | 225 | 226 | 227 | 231 | 232 | 233 | 236 | 237 | 238 | 240 | 241 | 242 | 244 | 245 | 246 | 254 | 255 | 256 | 257 | 266 | 267 | 268 | 269 | 270 | 273 | 274 | 277 | 278 | 279 | 280 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 291 | 292 | 295 | 296 | 299 | 300 | 304 | 305 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_H 2 | #define HASH_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | uint32_t hash(const void *key, size_t length, const uint32_t initval); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #endif /* HASH_H */ 15 | 16 | -------------------------------------------------------------------------------- /items.h: -------------------------------------------------------------------------------- 1 | /* See items.c */ 2 | uint64_t get_cas_id(void); 3 | 4 | /*@null@*/ 5 | item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes); 6 | void item_free(item *it); 7 | bool item_size_ok(const size_t nkey, const int flags, const int nbytes); 8 | 9 | int do_item_link(item *it, const uint32_t hv); /** may fail if transgresses limits */ 10 | int do_item_link_nolock(item *it, const uint32_t hv); 11 | void do_item_unlink(item *it, const uint32_t hv); 12 | void do_item_unlink_nolock(item *it, const uint32_t hv); 13 | void do_item_remove(item *it); 14 | void do_item_update(item *it); /** update LRU time to current and reposition */ 15 | int do_item_replace(item *it, item *new_it, const uint32_t hv); 16 | 17 | /*@null@*/ 18 | char *do_item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes); 19 | void do_item_stats(ADD_STAT add_stats, void *c); 20 | /*@null@*/ 21 | void do_item_stats_sizes(ADD_STAT add_stats, void *c); 22 | void do_item_flush_expired(void); 23 | 24 | item *do_item_get(const char *key, const size_t nkey, const uint32_t hv); 25 | item *do_item_touch(const char *key, const size_t nkey, uint32_t exptime, const uint32_t hv); 26 | void item_stats_reset(void); 27 | extern pthread_mutex_t cache_lock; 28 | void item_stats_evictions(uint64_t *evicted); 29 | -------------------------------------------------------------------------------- /m4/c99-backport.m4: -------------------------------------------------------------------------------- 1 | # AC_PROG_CC_C99 ([ACTION-IF-AVAILABLE], [ACTION-IF-UNAVAILABLE]) 2 | # ---------------------------------------------------------------- 3 | # If the C compiler is not in ISO C99 mode by default, try to add an 4 | # option to output variable CC to make it so. This macro tries 5 | # various options that select ISO C99 on some system or another. It 6 | # considers the compiler to be in ISO C99 mode if it handles _Bool, 7 | # // comments, flexible array members, inline, long long int, mixed 8 | # code and declarations, named initialization of structs, restrict, 9 | # va_copy, varargs macros, variable declarations in for loops and 10 | # variable length arrays. 11 | AC_DEFUN([AC_PROG_CC_C99], 12 | [AC_C_STD_TRY([c99], 13 | [[#include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // Check varargs macros. These examples are taken from C99 6.10.3.5. 20 | #define debug(...) fprintf (stderr, __VA_ARGS__) 21 | #define showlist(...) puts (#__VA_ARGS__) 22 | #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) 23 | static void 24 | test_varargs_macros (void) 25 | { 26 | int x = 1234; 27 | int y = 5678; 28 | debug ("Flag"); 29 | debug ("X = %d\n", x); 30 | showlist (The first, second, and third items.); 31 | report (x>y, "x is %d but y is %d", x, y); 32 | } 33 | 34 | // Check long long types. 35 | #define BIG64 18446744073709551615ull 36 | #define BIG32 4294967295ul 37 | #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) 38 | #if !BIG_OK 39 | your preprocessor is broken; 40 | #endif 41 | #if BIG_OK 42 | #else 43 | your preprocessor is broken; 44 | #endif 45 | static long long int bignum = -9223372036854775807LL; 46 | static unsigned long long int ubignum = BIG64; 47 | 48 | struct incomplete_array 49 | { 50 | int datasize; 51 | double data[]; 52 | }; 53 | 54 | struct named_init { 55 | int number; 56 | const wchar_t *name; 57 | double average; 58 | }; 59 | 60 | typedef const char *ccp; 61 | 62 | static inline int 63 | test_restrict (ccp restrict text) 64 | { 65 | // See if C++-style comments work. 66 | // Iterate through items via the restricted pointer. 67 | // Also check for declarations in for loops. 68 | for (unsigned int i = 0; *(text+i) != '\0'; ++i) 69 | continue; 70 | return 0; 71 | } 72 | 73 | // Check varargs and va_copy. 74 | static void 75 | test_varargs (const char *format, ...) 76 | { 77 | va_list args; 78 | va_start (args, format); 79 | va_list args_copy; 80 | va_copy (args_copy, args); 81 | 82 | const char *str; 83 | int number; 84 | float fnumber; 85 | 86 | while (*format) 87 | { 88 | switch (*format++) 89 | { 90 | case 's': // string 91 | str = va_arg (args_copy, const char *); 92 | break; 93 | case 'd': // int 94 | number = va_arg (args_copy, int); 95 | break; 96 | case 'f': // float 97 | fnumber = va_arg (args_copy, double); 98 | break; 99 | default: 100 | break; 101 | } 102 | } 103 | va_end (args_copy); 104 | va_end (args); 105 | } 106 | ]], 107 | [[ 108 | // Check bool. 109 | _Bool success = false; 110 | 111 | // Check restrict. 112 | if (test_restrict ("String literal") == 0) 113 | success = true; 114 | char *restrict newvar = "Another string"; 115 | 116 | // Check varargs. 117 | test_varargs ("s, d' f .", "string", 65, 34.234); 118 | test_varargs_macros (); 119 | 120 | // Check flexible array members. 121 | struct incomplete_array *ia = 122 | malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); 123 | ia->datasize = 10; 124 | for (int i = 0; i < ia->datasize; ++i) 125 | ia->data[i] = i * 1.234; 126 | 127 | // Check named initializers. 128 | struct named_init ni = { 129 | .number = 34, 130 | .name = L"Test wide string", 131 | .average = 543.34343, 132 | }; 133 | 134 | ni.number = 58; 135 | 136 | int dynamic_array[ni.number]; 137 | dynamic_array[ni.number - 1] = 543; 138 | 139 | // work around unused variable warnings 140 | return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' 141 | || dynamic_array[ni.number - 1] != 543); 142 | ]], 143 | dnl Try 144 | dnl GCC -std=gnu99 (unused restrictive modes: -std=c99 -std=iso9899:1999) 145 | dnl AIX -qlanglvl=extc99 (unused restrictive mode: -qlanglvl=stdc99) 146 | dnl Intel ICC -c99 147 | dnl IRIX -c99 148 | dnl Solaris (unused because it causes the compiler to assume C99 semantics for 149 | dnl library functions, and this is invalid before Solaris 10: -xc99) 150 | dnl Tru64 -c99 151 | dnl with extended modes being tried first. 152 | [[-std=gnu99 -c99 -qlanglvl=extc99]], [$1], [$2])[]dnl 153 | ])# AC_PROG_CC_C99 154 | 155 | # AC_C_STD_TRY(STANDARD, TEST-PROLOGUE, TEST-BODY, OPTION-LIST, 156 | # ACTION-IF-AVAILABLE, ACTION-IF-UNAVAILABLE) 157 | # -------------------------------------------------------------- 158 | # Check whether the C compiler accepts features of STANDARD (e.g `c89', `c99') 159 | # by trying to compile a program of TEST-PROLOGUE and TEST-BODY. If this fails, 160 | # try again with each compiler option in the space-separated OPTION-LIST; if one 161 | # helps, append it to CC. If eventually successful, run ACTION-IF-AVAILABLE, 162 | # else ACTION-IF-UNAVAILABLE. 163 | AC_DEFUN([AC_C_STD_TRY], 164 | [AC_MSG_CHECKING([for $CC option to accept ISO ]m4_translit($1, [c], [C])) 165 | AC_CACHE_VAL(ac_cv_prog_cc_$1, 166 | [ac_cv_prog_cc_$1=no 167 | ac_save_CC=$CC 168 | AC_LANG_CONFTEST([AC_LANG_PROGRAM([$2], [$3])]) 169 | for ac_arg in '' $4 170 | do 171 | CC="$ac_save_CC $ac_arg" 172 | _AC_COMPILE_IFELSE([], [ac_cv_prog_cc_$1=$ac_arg]) 173 | test "x$ac_cv_prog_cc_$1" != "xno" && break 174 | done 175 | rm -f conftest.$ac_ext 176 | CC=$ac_save_CC 177 | ])# AC_CACHE_VAL 178 | case "x$ac_cv_prog_cc_$1" in 179 | x) 180 | AC_MSG_RESULT([none needed]) ;; 181 | xno) 182 | AC_MSG_RESULT([unsupported]) ;; 183 | *) 184 | CC="$CC $ac_cv_prog_cc_$1" 185 | AC_MSG_RESULT([$ac_cv_prog_cc_$1]) ;; 186 | esac 187 | AS_IF([test "x$ac_cv_prog_cc_$1" != xno], [$5], [$6]) 188 | ])# AC_C_STD_TRY 189 | -------------------------------------------------------------------------------- /memc3_config.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEMC3_CONFIG__ 2 | #define __MEMC3_CONFIG__ 3 | 4 | //#define MEMC3_ASSOC_CHAIN 1 5 | #define MEMC3_ASSOC_CUCKOO 1 6 | 7 | /* 8 | * make sure one and only one option above is enabled 9 | */ 10 | #if (MEMC3_ASSOC_CUCKOO + MEMC3_ASSOC_CHAIN != 1) 11 | #error "you must specify one and only one hashtable" 12 | #endif 13 | 14 | 15 | //#define MEMC3_CACHE_LRU 1 16 | #define MEMC3_CACHE_CLOCK 1 17 | #if (MEMC3_CACHE_LRU + MEMC3_CACHE_CLOCK != 1) 18 | #error "you must specify one and only one eviction policy" 19 | #endif 20 | 21 | 22 | /* 23 | * enable huge table to reduce TLB misses 24 | */ 25 | //#define MEMC3_ENABLE_HUGEPAGE 26 | 27 | /* 28 | * enable key comparison by casting bits into ints 29 | */ 30 | #define MEMC3_ENABLE_INT_KEYCMP 31 | 32 | /* 33 | * disable locking 34 | */ 35 | //#define MEMC3_LOCK_NONE 1 36 | 37 | /* 38 | * enable global locking 39 | */ 40 | //#define MEMC3_LOCK_GLOBAL 1 41 | 42 | /* 43 | * enable bucket locking 44 | */ 45 | //#define MEMC3_LOCK_FINEGRAIN 1 46 | 47 | /* 48 | * enable optimistic locking 49 | */ 50 | #define MEMC3_LOCK_OPT 1 51 | 52 | 53 | #if (MEMC3_LOCK_OPT + MEMC3_LOCK_FINEGRAIN + MEMC3_LOCK_GLOBAL + MEMC3_LOCK_NONE != 1) 54 | #error "you must specify one and only locking policy" 55 | #endif 56 | 57 | 58 | /* 59 | * enable tag 60 | */ 61 | #define MEMC3_ENABLE_TAG 62 | 63 | /* 64 | * enable parallel cuckoo 65 | */ 66 | #define MEMC3_ASSOC_CUCKOO_WIDTH 1 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /memc3_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #define __USE_GNU 10 | #include 11 | 12 | #include "memcached.h" 13 | 14 | #include "memc3_util.h" 15 | 16 | void print_memc3_settings() 17 | { 18 | printf("memc3 settings:\n"); 19 | #ifdef MEMC3_ASSOC_CHAIN 20 | printf("\thashtable = built-in hashtable\n"); 21 | #endif 22 | 23 | #ifdef MEMC3_CACHE_LRU 24 | printf("\teviction = LRU\n"); 25 | #endif 26 | 27 | #ifdef MEMC3_CACHE_CLOCK 28 | printf("\teviction = CLOCK\n"); 29 | #endif 30 | 31 | #ifdef DO_PERF_COUNTING 32 | printf("\t+perf_counting\n"); 33 | #endif 34 | 35 | #ifdef MEMC3_ENABLE_HUGEPAGE 36 | printf("\t+hugepage\n"); 37 | #endif 38 | 39 | #ifdef MEMC3_ENABLE_INT_KEYCMP 40 | printf("\t+int_keycmp\n"); 41 | #endif 42 | 43 | #ifdef MEMC3_ASSOC_CUCKOO 44 | printf("\t+cuckoo \n"); 45 | #ifdef MEMC3_ENABLE_TAG 46 | printf("\t+tag\n"); 47 | #endif 48 | #if (MEMC3_ASSOC_CUCKOO_WIDTH > 1) 49 | printf("\t+%d-way cuckoo_path\n", MEMC3_ASSOC_CUCKOO_WIDTH); 50 | #endif 51 | 52 | #endif 53 | 54 | 55 | #ifdef MEMC3_LOCK_GLOBAL 56 | printf("\t+global lock\n"); 57 | #endif 58 | 59 | #ifdef MEMC3_LOCK_NONE 60 | printf("\t+no locking\n"); 61 | #endif 62 | 63 | #ifdef MEMC3_LOCK_OPT 64 | printf("\t+opt lock\n"); 65 | #endif 66 | 67 | #ifdef MEMC3_LOCK_FINEGRAIN 68 | printf("\t+bucket lock\n"); 69 | #endif 70 | 71 | 72 | printf("\n"); 73 | } 74 | 75 | #define HUGEPAGE_SIZE 2097152 76 | 77 | void *alloc(size_t size) 78 | { 79 | #if defined(MEMC3_ENABLE_HUGEPAGE) && defined(__linux__) 80 | if (size % HUGEPAGE_SIZE != 0) 81 | size = (size / HUGEPAGE_SIZE + 1) * HUGEPAGE_SIZE; 82 | int shmid = shmget(IPC_PRIVATE, size, /*IPC_CREAT |*/ SHM_HUGETLB | SHM_R | SHM_W); 83 | if (shmid == -1) { 84 | perror("shmget failed"); 85 | exit(EXIT_FAILURE); 86 | } 87 | void *p = shmat(shmid, NULL, 0); 88 | if (p == (void *)-1) { 89 | perror("Shared memory attach failed"); 90 | exit(EXIT_FAILURE); 91 | } 92 | if (shmctl(shmid, IPC_RMID, NULL) == -1) { 93 | perror("shmctl failed"); 94 | } 95 | return p; 96 | #else 97 | void *p = malloc(size); 98 | if (NULL == p) { 99 | perror("malloc failed"); 100 | assert(0); 101 | } 102 | return p; 103 | #endif 104 | } 105 | 106 | void dealloc(void *p) 107 | { 108 | #ifdef MEMC3_ENABLE_HUGEPAGE 109 | if (shmdt(p)) { 110 | perror(""); 111 | assert(0); 112 | } 113 | #else 114 | free(p); 115 | #endif 116 | } 117 | 118 | double timeval_diff(struct timeval *start, 119 | struct timeval *end) 120 | { 121 | /* Calculate the second difference*/ 122 | double r = end->tv_sec - start->tv_sec; 123 | 124 | /* Calculate the microsecond difference */ 125 | if (end->tv_usec > start->tv_usec) 126 | r += (end->tv_usec - start->tv_usec)/1000000.0; 127 | else if (end->tv_usec < start->tv_usec) 128 | r -= (start->tv_usec - end->tv_usec)/1000000.0; 129 | 130 | return r; 131 | } 132 | 133 | int get_cpunum() 134 | { 135 | int num = 0; 136 | 137 | #ifdef __linux__ 138 | cpu_set_t cpuset; 139 | CPU_ZERO(&cpuset); 140 | sched_getaffinity(0, sizeof(cpuset), &cpuset); 141 | for (int i = 0; i < 32; i++) 142 | { 143 | if (CPU_ISSET(i, &cpuset)) 144 | num++; 145 | } 146 | #endif 147 | return num; 148 | } 149 | -------------------------------------------------------------------------------- /memc3_util.h: -------------------------------------------------------------------------------- 1 | #ifndef __BENCH_UTIL__ 2 | #define __BENCH_UTIL__ 3 | #include "memc3_config.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* forward declaration */ 10 | void print_memc3_settings(void); 11 | 12 | void *alloc(size_t size); 13 | void dealloc(void *p); 14 | 15 | double timeval_diff(struct timeval *start, struct timeval *end); 16 | int get_cpunum(void); 17 | 18 | /* Obtain a backtrace and print it to stdout. */ 19 | static inline void 20 | print_trace (void) 21 | { 22 | void *array[10]; 23 | size_t size; 24 | char **strings; 25 | size_t i; 26 | 27 | size = backtrace (array, 10); 28 | strings = backtrace_symbols (array, size); 29 | 30 | printf ("Obtained %zd stack frames.\n", size); 31 | 32 | for (i = 0; i < size; i++) 33 | printf ("%s\n", strings[i]); 34 | 35 | free (strings); 36 | } 37 | 38 | static inline void 39 | print_key(char* key, int nkey) { 40 | for (int i = 0; i < nkey; i ++) { 41 | printf("%x", *((char*) &key[i])); 42 | } 43 | } 44 | 45 | // added by Bin, needed by CLOCK 46 | static inline uint8_t bv_getbit(void *bv, uint32_t index) { 47 | char* bitmap = (char*) bv; 48 | uint32_t byte_index = index >> 3; 49 | char byte_mask = 1 << (index & 7); 50 | return bitmap[byte_index] & byte_mask; 51 | } 52 | 53 | // added by Bin, needed by CLOCK 54 | static inline void bv_setbit(void *bv, uint32_t index, uint8_t v) { 55 | char* bitmap = (char*) bv; 56 | uint32_t byte_index = index >> 3; 57 | char byte_mask = (v & 1) << (index & 7); 58 | bitmap[byte_index] &= ~byte_mask; 59 | bitmap[byte_index] |= byte_mask; 60 | } 61 | 62 | #ifdef MEMC3_ENABLE_INT_KEYCMP 63 | 64 | #define INT_KEYCMP_UNIT uint64_t 65 | 66 | static uint64_t keycmp_mask[] = {0x0000000000000000ULL, 67 | 0x00000000000000ffULL, 68 | 0x000000000000ffffULL, 69 | 0x0000000000ffffffULL, 70 | 0x00000000ffffffffULL, 71 | 0x000000ffffffffffULL, 72 | 0x0000ffffffffffffULL, 73 | 0x00ffffffffffffffULL}; 74 | static inline 75 | bool keycmp(const char* key1, const char* key2, size_t len) { 76 | 77 | INT_KEYCMP_UNIT v_key1; 78 | INT_KEYCMP_UNIT v_key2; 79 | size_t k = 0; 80 | while ((len ) >= k + sizeof(INT_KEYCMP_UNIT)) { 81 | v_key1 = *(INT_KEYCMP_UNIT *) (key1 + k); 82 | v_key2 = *(INT_KEYCMP_UNIT *) (key2 + k); 83 | if (v_key1 != v_key2) 84 | return false; 85 | k += sizeof(INT_KEYCMP_UNIT); 86 | } 87 | /* 88 | * this code only works for little endian 89 | */ 90 | if (len - k) { 91 | v_key1 = *(INT_KEYCMP_UNIT *) (key1 + k); 92 | v_key2 = *(INT_KEYCMP_UNIT *) (key2 + k); 93 | return ((v_key1 ^ v_key2) & keycmp_mask[len - k]) == 0; 94 | } 95 | return true; 96 | } 97 | 98 | #else 99 | static inline 100 | bool keycmp(const char* key1, const char* key2, size_t len) { 101 | return memcmp(key1, key2, len) == 0; 102 | } 103 | #endif 104 | 105 | 106 | #ifdef DO_PERF_COUNTING 107 | #include "perf_count.h" 108 | 109 | static const char *perf_count_names[] = 110 | { 111 | //"Cycle", 112 | //"Instr", 113 | //"CacheRef", 114 | //"CacheMiss", 115 | //"BranchInstr", 116 | //"BranchMiss", 117 | //"L1DReadAccess", 118 | //"L1DReadMiss", 119 | "LLReadAccess", 120 | "LLReadMiss", 121 | "DTLBReadAccess", 122 | "DTLBReadMiss", 123 | //"PageFault", 124 | //"CtxSwitch", 125 | }; 126 | 127 | static const enum PERF_COUNT_TYPE perf_count_types[] = 128 | { 129 | //PERF_COUNT_TYPE_HW_CPU_CYCLES, 130 | //PERF_COUNT_TYPE_HW_INSTRUCTIONS, 131 | //PERF_COUNT_TYPE_HW_CACHE_REFERENCES, 132 | //PERF_COUNT_TYPE_HW_CACHE_MISSES, 133 | //PERF_COUNT_TYPE_HW_BRANCH_INSTRUCTIONS, 134 | //PERF_COUNT_TYPE_HW_BRANCH_MISSES, 135 | //PERF_COUNT_TYPE_HW_CACHE_L1D_READ_ACCESS, 136 | //PERF_COUNT_TYPE_HW_CACHE_L1D_READ_MISS, 137 | PERF_COUNT_TYPE_HW_CACHE_LL_READ_ACCESS, 138 | PERF_COUNT_TYPE_HW_CACHE_LL_READ_MISS, 139 | PERF_COUNT_TYPE_HW_CACHE_DTLB_READ_ACCESS, 140 | PERF_COUNT_TYPE_HW_CACHE_DTLB_READ_MISS, 141 | //PERF_COUNT_TYPE_SW_PAGE_FAULTS, 142 | //PERF_COUNT_TYPE_SW_CONTEXT_SWITCHES, 143 | }; 144 | 145 | static inline void print_perf_counts(perf_count_t perf_count, double tdiff) 146 | { 147 | for (size_t i = 0; i < sizeof(perf_count_types) / sizeof(perf_count_types[0]); i++) 148 | { 149 | printf("%-14s: %.5f %.5f M/sec\n", perf_count_names[i], 150 | (float) perf_count_get_by_index(perf_count, (int)i), 151 | (float) perf_count_get_by_index(perf_count, (int)i) / tdiff / 1000000); 152 | } 153 | printf("\n"); 154 | } 155 | 156 | #define TIME(label, statement, totalnum) \ 157 | do { \ 158 | printf("<%s>\n", label); \ 159 | struct timeval tvs, tve; \ 160 | perf_count_t perf_count = perf_count_init(perf_count_types, sizeof(perf_count_types) / sizeof(perf_count_types[0]), 0); \ 161 | perf_count_reset(perf_count); \ 162 | perf_count_start(perf_count); \ 163 | gettimeofday(&tvs, NULL); \ 164 | do { statement; } while(0); \ 165 | gettimeofday(&tve, NULL); \ 166 | perf_count_stop(perf_count); \ 167 | double tvsd = (double)tvs.tv_sec + (double)tvs.tv_usec/1000000; \ 168 | double tved = (double)tve.tv_sec + (double)tve.tv_usec/1000000; \ 169 | double tdiff = tved - tvsd; \ 170 | printf("\ttime: %.5f sec\n", tdiff); \ 171 | if (totalnum) printf("\ttotal: %zu\n\ttput: %.5f\n", totalnum, totalnum/tdiff); \ 172 | print_perf_counts(perf_count, tdiff); \ 173 | printf("\n"); \ 174 | perf_count_free(perf_count); \ 175 | } while (0) 176 | 177 | #else 178 | 179 | #define TIME(label, statement, totalnum) \ 180 | do { \ 181 | printf("<%s>\n", label); \ 182 | struct timeval tvs, tve; \ 183 | gettimeofday(&tvs, NULL); \ 184 | do { statement; } while(0); \ 185 | gettimeofday(&tve, NULL); \ 186 | double tvsd = (double)tvs.tv_sec + (double)tvs.tv_usec/1000000; \ 187 | double tved = (double)tve.tv_sec + (double)tve.tv_usec/1000000; \ 188 | printf("%s\n\ttime: %.5f sec\n", label, tved-tvsd); \ 189 | if (totalnum) printf("\ttotal: %zu\n\ttput: %.5f\n", totalnum, totalnum/(tved-tvsd)); \ 190 | printf("\n"); \ 191 | } while (0) 192 | #endif 193 | 194 | #endif 195 | -------------------------------------------------------------------------------- /memcached.spec: -------------------------------------------------------------------------------- 1 | Name: memcached 2 | Version: 1.4.13 3 | Release: 1%{?dist} 4 | Summary: High Performance, Distributed Memory Object Cache 5 | 6 | Group: System Environment/Daemons 7 | License: BSD 8 | URL: http://www.danga.com/memcached/ 9 | Source0: http://memcached.googlecode.com/files/%{name}-1.4.13.tar.gz 10 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 11 | 12 | BuildRequires: libevent-devel 13 | BuildRequires: perl(Test::More) 14 | BuildRequires: /usr/bin/prove 15 | Requires: initscripts 16 | Requires(post): /sbin/chkconfig 17 | Requires(preun): /sbin/chkconfig, /sbin/service 18 | Requires(postun): /sbin/service 19 | 20 | %description 21 | memcached is a high-performance, distributed memory object caching 22 | system, generic in nature, but intended for use in speeding up dynamic 23 | web applications by alleviating database load. 24 | 25 | %prep 26 | %setup -q -n %{name}-1.4.13 27 | 28 | 29 | %build 30 | %configure 31 | 32 | make %{?_smp_mflags} 33 | 34 | %check 35 | make test 36 | 37 | %install 38 | rm -rf %{buildroot} 39 | make install DESTDIR=%{buildroot} 40 | 41 | # remove memcached-debug 42 | rm -f %{buildroot}/%{_bindir}/memcached-debug 43 | 44 | # Perl script for monitoring memcached 45 | install -Dp -m0755 scripts/memcached-tool %{buildroot}%{_bindir}/memcached-tool 46 | 47 | # Init script 48 | install -Dp -m0755 scripts/memcached.sysv %{buildroot}%{_initrddir}/memcached 49 | 50 | # Default configs 51 | mkdir -p %{buildroot}/%{_sysconfdir}/sysconfig 52 | cat <%{buildroot}/%{_sysconfdir}/sysconfig/%{name} 53 | PORT="11211" 54 | USER="nobody" 55 | MAXCONN="1024" 56 | CACHESIZE="64" 57 | OPTIONS="" 58 | EOF 59 | 60 | # pid directory 61 | mkdir -p %{buildroot}/%{_localstatedir}/run/memcached 62 | 63 | %clean 64 | rm -rf %{buildroot} 65 | 66 | 67 | %post 68 | /sbin/chkconfig --add %{name} 69 | 70 | %preun 71 | if [ "$1" = 0 ] ; then 72 | /sbin/service %{name} stop > /dev/null 2>&1 73 | /sbin/chkconfig --del %{name} 74 | fi 75 | exit 0 76 | 77 | %postun 78 | if [ "$1" -ge 1 ]; then 79 | /sbin/service %{name} condrestart > /dev/null 2>&1 80 | fi 81 | exit 0 82 | 83 | 84 | %files 85 | %defattr(-,root,root,-) 86 | %doc AUTHORS ChangeLog COPYING NEWS README doc/CONTRIBUTORS doc/*.txt 87 | %config(noreplace) %{_sysconfdir}/sysconfig/%{name} 88 | 89 | %dir %attr(750,nobody,nobody) %{_localstatedir}/run/memcached 90 | %{_bindir}/memcached-tool 91 | %{_bindir}/memcached 92 | %{_mandir}/man1/memcached.1* 93 | %{_initrddir}/memcached 94 | %{_includedir}/memcached 95 | 96 | %changelog 97 | * Mon Nov 2 2009 Dormando - 1.4.3-1 98 | - Fix autogen more. 99 | 100 | * Sat Aug 29 2009 Dustin Sallings - 1.4.1-1 101 | - Autogenerate the version number from tags. 102 | 103 | * Wed Jul 4 2007 Paul Lindner - 1.2.2-5 104 | - Use /var/run/memcached/ directory to hold PID file 105 | 106 | * Sat May 12 2007 Paul Lindner - 1.2.2-4 107 | - Remove tabs from spec file, rpmlint reports no more errors 108 | 109 | * Thu May 10 2007 Paul Lindner - 1.2.2-3 110 | - Enable build-time regression tests 111 | - add dependency on initscripts 112 | - remove memcached-debug (not needed in dist) 113 | - above suggestions from Bernard Johnson 114 | 115 | * Mon May 7 2007 Paul Lindner - 1.2.2-2 116 | - Tidyness improvements suggested by Ruben Kerkhof in bugzilla #238994 117 | 118 | * Fri May 4 2007 Paul Lindner - 1.2.2-1 119 | - Initial spec file created via rpmdev-newspec 120 | -------------------------------------------------------------------------------- /perf_count.c: -------------------------------------------------------------------------------- 1 | #include "perf_count.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | struct perf_count_ctx 12 | { 13 | int num_groups; 14 | int num_events; 15 | struct perf_event_attr *events; 16 | int *fds; 17 | uint64_t *counters; 18 | }; 19 | 20 | static const struct perf_event_attr perf_count_mapping[] = 21 | { 22 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, 23 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, 24 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES }, 25 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES }, 26 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, 27 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, 28 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BUS_CYCLES }, 29 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, 30 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, 31 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, 32 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, 33 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, 34 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, 35 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, 36 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, 37 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_DTLB | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, 38 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_DTLB | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, 39 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_DTLB | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, 40 | { .type = PERF_TYPE_HW_CACHE, .config = PERF_COUNT_HW_CACHE_DTLB | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, 41 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_CLOCK }, 42 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, 43 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, 44 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, 45 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS }, 46 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS_MIN }, 47 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ }, 48 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_ALIGNMENT_FAULTS }, 49 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_EMULATION_FAULTS }, 50 | }; 51 | 52 | static int 53 | sys_perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) 54 | { 55 | attr->size = sizeof(*attr); 56 | return (int)syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); 57 | } 58 | 59 | perf_count_t 60 | perf_count_init(const enum PERF_COUNT_TYPE *perf_count_types, int num_events, int system_wide) 61 | { 62 | if (perf_count_types == NULL) 63 | return NULL; 64 | 65 | struct perf_count_ctx *ctx = (struct perf_count_ctx *)malloc(sizeof(struct perf_count_ctx)); 66 | assert(ctx); 67 | 68 | if (system_wide) 69 | ctx->num_groups = (int)sysconf(_SC_NPROCESSORS_ONLN); 70 | else 71 | ctx->num_groups = 1; 72 | ctx->num_events = num_events; 73 | 74 | ctx->events = (struct perf_event_attr *)calloc(sizeof(struct perf_event_attr), (size_t)ctx->num_events); 75 | assert(ctx->events); 76 | ctx->fds = (int *)calloc(sizeof(int), (size_t)ctx->num_groups * (size_t)ctx->num_events); 77 | assert(ctx->fds); 78 | ctx->counters = (uint64_t *)calloc(sizeof(uint64_t), (size_t)ctx->num_events); 79 | assert(ctx->counters); 80 | 81 | for (int event = 0; event < ctx->num_events; event++) 82 | { 83 | assert(perf_count_types[event] < sizeof(perf_count_mapping) / sizeof(perf_count_mapping[0])); 84 | 85 | ctx->events[event] = perf_count_mapping[perf_count_types[event]]; 86 | ctx->events[event].read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; 87 | } 88 | 89 | for (int group = 0; group < ctx->num_groups; group++) 90 | for (int event = 0; event < ctx->num_events; event++) 91 | { 92 | pid_t pid; 93 | int cpu; 94 | 95 | if (system_wide) 96 | { 97 | pid = -1; 98 | // XXX: assuming the IDs of online cpus range from 0 to (num_cpus - 1) 99 | cpu = group; 100 | } 101 | else 102 | { 103 | // this process 104 | pid = 0; 105 | cpu = -1; 106 | } 107 | 108 | ctx->fds[group * ctx->num_events + event] = sys_perf_event_open(&ctx->events[event], pid, cpu, -1, 0); 109 | assert(ctx->fds[group * ctx->num_events + event] >= 0); 110 | } 111 | 112 | return ctx; 113 | } 114 | 115 | void 116 | perf_count_free(perf_count_t perf_count) 117 | { 118 | struct perf_count_ctx *ctx = (struct perf_count_ctx *)perf_count; 119 | 120 | for (int group = 0; group < ctx->num_groups; group++) 121 | for (int event = 0; event < ctx->num_events; event++) 122 | close(ctx->fds[group * ctx->num_events + event]); 123 | 124 | free(ctx->counters); 125 | free(ctx->fds); 126 | free(ctx->events); 127 | free(ctx); 128 | } 129 | 130 | static void 131 | perf_count_accumulate(perf_count_t perf_count, int additive) 132 | { 133 | struct perf_count_ctx *ctx = (struct perf_count_ctx *)perf_count; 134 | 135 | for (int event = 0; event < ctx->num_events; event++) 136 | { 137 | uint64_t count[3]; 138 | uint64_t accum_count[3] = {0, 0, 0}; 139 | 140 | for (int group = 0; group < ctx->num_groups; group++) 141 | { 142 | count[0] = count[1] = count[2] = 0; 143 | ssize_t len = read(ctx->fds[group * ctx->num_events + event], count, sizeof(count)); 144 | //printf("%d %ld %ld %ld\n", len, count[0], count[1], count[2]); 145 | assert((size_t)len == sizeof(count)); 146 | 147 | accum_count[0] += count[0]; 148 | accum_count[1] += count[1]; 149 | accum_count[2] += count[2]; 150 | } 151 | 152 | if (accum_count[2] == 0) 153 | { 154 | // no event occurred at all 155 | } 156 | else 157 | { 158 | if (accum_count[2] < accum_count[1]) 159 | { 160 | // need to scale 161 | accum_count[0] = (uint64_t)((double)accum_count[0] * (double)accum_count[1] / (double)accum_count[2] + 0.5); 162 | } 163 | } 164 | 165 | if (additive) 166 | { 167 | ctx->counters[event] += accum_count[0]; 168 | // due to the scaling, we may observe a negative increment 169 | if ((int64_t)ctx->counters[event] < 0) 170 | ctx->counters[event] = 0; 171 | } 172 | else 173 | ctx->counters[event] -= accum_count[0]; 174 | } 175 | } 176 | 177 | void 178 | perf_count_start(perf_count_t perf_count) 179 | { 180 | perf_count_accumulate(perf_count, 0); 181 | } 182 | 183 | void 184 | perf_count_stop(perf_count_t perf_count) 185 | { 186 | perf_count_accumulate(perf_count, 1); 187 | } 188 | 189 | void 190 | perf_count_reset(perf_count_t perf_count) 191 | { 192 | struct perf_count_ctx *ctx = (struct perf_count_ctx *)perf_count; 193 | 194 | for (int event = 0; event < ctx->num_events; event++) 195 | ctx->counters[event] = 0; 196 | } 197 | 198 | uint64_t 199 | perf_count_get_by_type(perf_count_t perf_count, enum PERF_COUNT_TYPE type) 200 | { 201 | if (type >= sizeof(perf_count_mapping) / sizeof(perf_count_mapping[0])) 202 | return PERF_COUNT_INVALID; 203 | 204 | struct perf_count_ctx *ctx = (struct perf_count_ctx *)perf_count; 205 | 206 | for (int event = 0; event < ctx->num_events; event++) 207 | { 208 | if (ctx->events[event].type == perf_count_mapping[type].type && 209 | ctx->events[event].config == perf_count_mapping[type].config) 210 | return ctx->counters[event]; 211 | } 212 | 213 | return PERF_COUNT_INVALID; 214 | } 215 | 216 | uint64_t 217 | perf_count_get_by_index(perf_count_t perf_count, int index) 218 | { 219 | struct perf_count_ctx *ctx = (struct perf_count_ctx *)perf_count; 220 | 221 | if (index < 0 || index >= ctx->num_events) 222 | return PERF_COUNT_INVALID; 223 | 224 | return ctx->counters[index]; 225 | } 226 | 227 | -------------------------------------------------------------------------------- /perf_count.h: -------------------------------------------------------------------------------- 1 | #ifndef __PERF_COUNT__ 2 | #define __PERF_COUNT__ 3 | 4 | #include 5 | 6 | enum PERF_COUNT_TYPE 7 | { 8 | PERF_COUNT_TYPE_HW_CPU_CYCLES, 9 | PERF_COUNT_TYPE_HW_INSTRUCTIONS, 10 | PERF_COUNT_TYPE_HW_CACHE_REFERENCES, 11 | PERF_COUNT_TYPE_HW_CACHE_MISSES, 12 | PERF_COUNT_TYPE_HW_BRANCH_INSTRUCTIONS, 13 | PERF_COUNT_TYPE_HW_BRANCH_MISSES, 14 | PERF_COUNT_TYPE_HW_BUS_CYCLES, 15 | PERF_COUNT_TYPE_HW_CACHE_L1D_READ_ACCESS, 16 | PERF_COUNT_TYPE_HW_CACHE_L1D_READ_MISS, 17 | PERF_COUNT_TYPE_HW_CACHE_L1D_PREFETCH_ACCESS, // not working? 18 | PERF_COUNT_TYPE_HW_CACHE_L1D_PREFETCH_MISS, // not working? 19 | PERF_COUNT_TYPE_HW_CACHE_LL_READ_ACCESS, 20 | PERF_COUNT_TYPE_HW_CACHE_LL_READ_MISS, 21 | PERF_COUNT_TYPE_HW_CACHE_LL_PREFETCH_ACCESS, // not working? 22 | PERF_COUNT_TYPE_HW_CACHE_LL_PREFETCH_MISS, // not working? 23 | PERF_COUNT_TYPE_HW_CACHE_DTLB_READ_ACCESS, 24 | PERF_COUNT_TYPE_HW_CACHE_DTLB_READ_MISS, 25 | PERF_COUNT_TYPE_HW_CACHE_DTLB_PREFETCH_ACCESS, // not working? 26 | PERF_COUNT_TYPE_HW_CACHE_DTLB_PREFETCH_MISS, // not working? 27 | PERF_COUNT_TYPE_SW_CPU_CLOCK, 28 | PERF_COUNT_TYPE_SW_TASK_CLOCK, 29 | PERF_COUNT_TYPE_SW_PAGE_FAULTS, 30 | PERF_COUNT_TYPE_SW_CONTEXT_SWITCHES, 31 | PERF_COUNT_TYPE_SW_CPU_MIGRATIONS, 32 | PERF_COUNT_TYPE_SW_PAGE_FAULTS_MIN, 33 | PERF_COUNT_TYPE_SW_PAGE_FAULTS_MAJ, 34 | PERF_COUNT_TYPE_SW_ALIGNMENT_FAULTS, 35 | PERF_COUNT_TYPE_SW_EMULATION_FAULTS, 36 | }; 37 | 38 | typedef void *perf_count_t; 39 | 40 | #define PERF_COUNT_INVALID ((uint64_t)-1) 41 | 42 | // system_wide would require CAP_SYS_ADMIN 43 | perf_count_t perf_count_init(const enum PERF_COUNT_TYPE *perf_count_types, int num_events, int system_wide); 44 | void perf_count_free(perf_count_t perf_count); 45 | 46 | void perf_count_start(perf_count_t perf_count); 47 | void perf_count_stop(perf_count_t perf_count); 48 | void perf_count_reset(perf_count_t perf_count); 49 | 50 | uint64_t perf_count_get_by_type(perf_count_t perf_count, enum PERF_COUNT_TYPE type); 51 | uint64_t perf_count_get_by_index(perf_count_t perf_count, int index); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /sasl_defs.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #include "memcached.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | char my_sasl_hostname[1025]; 9 | 10 | #ifdef HAVE_SASL_CB_GETCONF 11 | /* The locations we may search for a SASL config file if the user didn't 12 | * specify one in the environment variable SASL_CONF_PATH 13 | */ 14 | const char * const locations[] = { 15 | "/etc/sasl/memcached.conf", 16 | "/etc/sasl2/memcached.conf", 17 | NULL 18 | }; 19 | #endif 20 | 21 | #ifndef HAVE_SASL_CALLBACK_FT 22 | typedef int (*sasl_callback_ft)(void); 23 | #endif 24 | 25 | #ifdef ENABLE_SASL_PWDB 26 | #define MAX_ENTRY_LEN 256 27 | 28 | static const char *memcached_sasl_pwdb; 29 | 30 | static int sasl_server_userdb_checkpass(sasl_conn_t *conn, 31 | void *context, 32 | const char *user, 33 | const char *pass, 34 | unsigned passlen, 35 | struct propctx *propctx) 36 | { 37 | size_t unmlen = strlen(user); 38 | if ((passlen + unmlen) > (MAX_ENTRY_LEN - 4)) { 39 | fprintf(stderr, 40 | "WARNING: Failed to authenticate <%s> due to too long password (%d)\n", 41 | user, passlen); 42 | return SASL_NOAUTHZ; 43 | } 44 | 45 | FILE *pwfile = fopen(memcached_sasl_pwdb, "r"); 46 | if (pwfile == NULL) { 47 | if (settings.verbose) { 48 | vperror("WARNING: Failed to open sasl database <%s>", 49 | memcached_sasl_pwdb); 50 | } 51 | return SASL_NOAUTHZ; 52 | } 53 | 54 | char buffer[MAX_ENTRY_LEN]; 55 | bool ok = false; 56 | 57 | while ((fgets(buffer, sizeof(buffer), pwfile)) != NULL) { 58 | if (memcmp(user, buffer, unmlen) == 0 && buffer[unmlen] == ':') { 59 | /* This is the correct user */ 60 | ++unmlen; 61 | if (memcmp(pass, buffer + unmlen, passlen) == 0 && 62 | (buffer[unmlen + passlen] == ':' || /* Additional tokens */ 63 | buffer[unmlen + passlen] == '\n' || /* end of line */ 64 | buffer[unmlen + passlen] == '\r'|| /* dos format? */ 65 | buffer[unmlen + passlen] == '\0')) { /* line truncated */ 66 | ok = true; 67 | } 68 | 69 | break; 70 | } 71 | } 72 | (void)fclose(pwfile); 73 | if (ok) { 74 | return SASL_OK; 75 | } 76 | 77 | if (settings.verbose) { 78 | fprintf(stderr, "INFO: User <%s> failed to authenticate\n", user); 79 | } 80 | 81 | return SASL_NOAUTHZ; 82 | } 83 | #endif 84 | 85 | #ifdef HAVE_SASL_CB_GETCONF 86 | static int sasl_getconf(void *context, const char **path) 87 | { 88 | *path = getenv("SASL_CONF_PATH"); 89 | 90 | if (*path == NULL) { 91 | for (int i = 0; locations[i] != NULL; ++i) { 92 | if (access(locations[i], F_OK) == 0) { 93 | *path = locations[i]; 94 | break; 95 | } 96 | } 97 | } 98 | 99 | if (settings.verbose) { 100 | if (*path != NULL) { 101 | fprintf(stderr, "Reading configuration from: <%s>\n", *path); 102 | } else { 103 | fprintf(stderr, "Failed to locate a config path\n"); 104 | } 105 | 106 | } 107 | 108 | return (*path != NULL) ? SASL_OK : SASL_FAIL; 109 | } 110 | #endif 111 | 112 | static int sasl_log(void *context, int level, const char *message) 113 | { 114 | bool log = true; 115 | 116 | switch (level) { 117 | case SASL_LOG_NONE: 118 | log = false; 119 | break; 120 | case SASL_LOG_PASS: 121 | case SASL_LOG_TRACE: 122 | case SASL_LOG_DEBUG: 123 | case SASL_LOG_NOTE: 124 | if (settings.verbose < 2) { 125 | log = false; 126 | } 127 | break; 128 | case SASL_LOG_WARN: 129 | case SASL_LOG_FAIL: 130 | if (settings.verbose < 1) { 131 | log = false; 132 | } 133 | break; 134 | default: 135 | /* This is an error */ 136 | ; 137 | } 138 | 139 | if (log) { 140 | fprintf(stderr, "SASL (severity %d): %s\n", level, message); 141 | } 142 | 143 | return SASL_OK; 144 | } 145 | 146 | static sasl_callback_t sasl_callbacks[] = { 147 | #ifdef ENABLE_SASL_PWDB 148 | { SASL_CB_SERVER_USERDB_CHECKPASS, sasl_server_userdb_checkpass, NULL }, 149 | #endif 150 | 151 | { SASL_CB_LOG, (sasl_callback_ft)sasl_log, NULL }, 152 | 153 | #ifdef HAVE_SASL_CB_GETCONF 154 | { SASL_CB_GETCONF, sasl_getconf, NULL }, 155 | #endif 156 | 157 | { SASL_CB_LIST_END, NULL, NULL } 158 | }; 159 | 160 | void init_sasl(void) { 161 | #ifdef ENABLE_SASL_PWDB 162 | memcached_sasl_pwdb = getenv("MEMCACHED_SASL_PWDB"); 163 | if (memcached_sasl_pwdb == NULL) { 164 | if (settings.verbose) { 165 | fprintf(stderr, 166 | "INFO: MEMCACHED_SASL_PWDB not specified. " 167 | "Internal passwd database disabled\n"); 168 | } 169 | sasl_callbacks[0].id = SASL_CB_LIST_END; 170 | sasl_callbacks[0].proc = NULL; 171 | } 172 | #endif 173 | 174 | memset(my_sasl_hostname, 0, sizeof(my_sasl_hostname)); 175 | if (gethostname(my_sasl_hostname, sizeof(my_sasl_hostname)-1) == -1) { 176 | if (settings.verbose) { 177 | fprintf(stderr, "Error discovering hostname for SASL\n"); 178 | } 179 | my_sasl_hostname[0] = '\0'; 180 | } 181 | 182 | if (sasl_server_init(sasl_callbacks, "memcached") != SASL_OK) { 183 | fprintf(stderr, "Error initializing sasl.\n"); 184 | exit(EXIT_FAILURE); 185 | } else { 186 | if (settings.verbose) { 187 | fprintf(stderr, "Initialized SASL.\n"); 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /sasl_defs.h: -------------------------------------------------------------------------------- 1 | #ifndef SASL_DEFS_H 2 | #define SASL_DEFS_H 1 3 | 4 | // Longest one I could find was ``9798-U-RSA-SHA1-ENC'' 5 | #define MAX_SASL_MECH_LEN 32 6 | 7 | #if defined(HAVE_SASL_SASL_H) && defined(ENABLE_SASL) 8 | 9 | #include 10 | void init_sasl(void); 11 | 12 | extern char my_sasl_hostname[1025]; 13 | 14 | #else /* End of SASL support */ 15 | 16 | typedef void* sasl_conn_t; 17 | 18 | #define init_sasl() {} 19 | #define sasl_dispose(x) {} 20 | #define sasl_server_new(a, b, c, d, e, f, g, h) 1 21 | #define sasl_listmech(a, b, c, d, e, f, g, h) 1 22 | #define sasl_server_start(a, b, c, d, e, f) 1 23 | #define sasl_server_step(a, b, c, d, e) 1 24 | #define sasl_getprop(a, b, c) {} 25 | 26 | #define SASL_OK 0 27 | #define SASL_CONTINUE -1 28 | 29 | #endif /* sasl compat */ 30 | 31 | #endif /* SASL_DEFS_H */ 32 | -------------------------------------------------------------------------------- /scripts/README.damemtop: -------------------------------------------------------------------------------- 1 | dormando's awesome memcached top 2 | 3 | A flexible 'top' like utility for viewing memcached clusters. 4 | 5 | Under development. Latest code is available at: 6 | http://github.com/dormando/damemtop 7 | 8 | See --help for full information. 9 | 10 | Requires 'AnyEvent', and 'YAML' libraries from CPAN: 11 | http://search.cpan.org/ 12 | 13 | 'AnyEvent' depends on 'common::sense' (also at CPAN). 14 | 15 | If you have a large cluster and want higher performance, find 16 | and install 'EV' from CPAN. AnyEvent will automagically use it 17 | and use epoll, kqeueue, etc, for socket handling. 18 | 19 | Pester me for questions/bugs/ideas. As of writing the util is 20 | in early release and missing many future features. 21 | -------------------------------------------------------------------------------- /scripts/damemtop.yaml: -------------------------------------------------------------------------------- 1 | delay: 3 2 | mode: t 3 | top_mode: 4 | sort_column: "hostname" 5 | sort_order: "asc" 6 | columns: 7 | - hostname 8 | - all_version 9 | - all_fill_rate 10 | - hit_rate 11 | - evictions 12 | - bytes_written 13 | - "2:get_hits" 14 | servers: 15 | - 127.0.0.1:11211 16 | - 127.0.0.2:11211 17 | -------------------------------------------------------------------------------- /scripts/mc_slab_mover: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | # See memcached for LICENSE 3 | # Copyright 2011 Dormando (dormando@rydia.net) 4 | 5 | =head1 NAME 6 | 7 | mc_slab_mover -- example utility for slab page reassignment for memcached 8 | 9 | =head1 SYNOPSIS 10 | 11 | $ mc_slab_mover --host="127.0.0.1:11211" --verbose 12 | $ mc_slab_mover --host="127.0.0.1:11211" --automove 13 | $ mc_slab_mover --host="127.0.0.1:11211" --sleep=60 --loops=4 --automove 14 | 15 | =head1 DESCRIPTION 16 | 17 | This utility is an example implementation of an algorithm for reassigning 18 | slab memory in a running memcached instance. If memcached's built-in 19 | automover isn't working for you, you may use this script as an example 20 | base and expand on it. We welcome modifications or alternatives on the 21 | mailing list. 22 | 23 | =head1 ALGORITHM 24 | 25 | The default algorithm is simple, and may serve for a common case: over 26 | time one slab may grow in use compare to others, and as evictions stop 27 | in one slab and start in another it will reassign memory. 28 | 29 | If a slab has the most evictions three times in a row, it will pull a page 30 | from a slab which has had zero evictions three times in a row. 31 | 32 | There are many traffic patterns where this does not work well. IE: If you 33 | never use expirations and rely on the LRU (so all slabs always evict), 34 | it will not be as likely to find source pages to move. 35 | 36 | =head1 OPTIONS 37 | 38 | =over 39 | 40 | =item --host="IP:PORT" 41 | 42 | The hostname to connect to. NOTE: If connection to the host breaks, script 43 | will stop. 44 | 45 | =item --sleep=10 46 | 47 | How long to wait between loops for gathering stats. 48 | 49 | =item --loops=3 50 | 51 | How many loops to run before making a decision for a move. 52 | 53 | =item --verbose 54 | 55 | Prints a formatted dump of some common statistics per loop. 56 | 57 | =item --automove 58 | 59 | Enables the automover, and will attempt to move memory around if it finds 60 | viable candidates. 61 | 62 | =back 63 | 64 | =head1 AUTHOR 65 | 66 | Dormando ELE 67 | 68 | =head1 LICENSE 69 | 70 | Licensed for use and redistribution under the same terms as Memcached itself. 71 | 72 | =cut 73 | 74 | use warnings; 75 | use strict; 76 | 77 | use IO::Socket::INET; 78 | 79 | use FindBin; 80 | use Data::Dumper qw/Dumper/; 81 | use Getopt::Long; 82 | 83 | my %opts = ('sleep' => 10, automove => 0, verbose => 0, loops => 3); 84 | GetOptions( 85 | "host=s" => \$opts{host}, 86 | "sleep=i" => \$opts{'sleep'}, 87 | "loops=i" => \$opts{loops}, 88 | "automove" => \$opts{automove}, 89 | "verbose" => \$opts{verbose}, 90 | ) or usage(); 91 | 92 | die "Must specify at least --host='127.0.0.1:11211'" unless $opts{host}; 93 | my $sock = IO::Socket::INET->new(PeerAddr => $opts{host}, 94 | Timeout => 3); 95 | die "$!\n" unless $sock; 96 | 97 | my %stats = (); 98 | my %move = (winner => 0, wins => 0); 99 | 100 | $SIG{INT} = sub { 101 | print "STATS: ", Dumper(\%stats), "\n"; 102 | exit; 103 | }; 104 | $SIG{USR1} = sub { 105 | print "STATS: ", Dumper(\%stats), "\n"; 106 | }; 107 | run(); 108 | 109 | sub usage { 110 | print qq{Usage: 111 | mc_slab_ratios --host="127.0.0.1:11211" --verbose --automove 112 | run `perldoc mc_slab_ratios` for full information 113 | 114 | }; 115 | exit 1; 116 | } 117 | 118 | sub run { 119 | my $slabs_before = grab_stats(); 120 | 121 | while (1) { 122 | sleep $opts{'sleep'}; 123 | my $slabs_after = grab_stats(); 124 | 125 | my ($totals, $sorted) = calc_results_evicted($slabs_before, $slabs_after); 126 | # my ($totals, $sorted) = calc_results_numratio($slabs_before, $slabs_after); 127 | 128 | my $pct = sub { 129 | my ($num, $divisor) = @_; 130 | return 0 unless $divisor; 131 | return ($num / $divisor); 132 | }; 133 | if ($opts{verbose}) { 134 | printf " %02s: %-8s (pct ) %-10s (pct ) %-6s (pct ) get_hits (pct ) cmd_set (pct )\n", 135 | 'sb', 'evicted', 'items', 'pages'; 136 | for my $slab (@$sorted) { 137 | printf " %02d: %-8d (%.2f%%) %-10s (%.4f%%) %-6d (%.2f%%) %-8d (%.3f%%) %-7d (%.2f%%)\n", 138 | $slab->{slab}, $slab->{evicted_d}, 139 | $pct->($slab->{evicted_d}, $totals->{evicted_d}), 140 | $slab->{number}, 141 | $pct->($slab->{number}, $totals->{number}), 142 | $slab->{total_pages}, 143 | $pct->($slab->{total_pages}, $totals->{total_pages}), 144 | $slab->{get_hits_d}, 145 | $pct->($slab->{get_hits_d}, $totals->{get_hits_d}), 146 | $slab->{cmd_set_d}, 147 | $pct->($slab->{cmd_set_d}, $totals->{cmd_set_d}); 148 | } 149 | } 150 | 151 | next unless @$sorted; 152 | my $highest = $sorted->[-1]; 153 | $stats{$highest->{slab}}++; 154 | print " (winner: ", $highest->{slab}, " wins: ", $stats{$highest->{slab}}, ")\n"; 155 | automove_basic($totals, $sorted) if ($opts{automove}); 156 | 157 | $slabs_before = $slabs_after; 158 | } 159 | } 160 | 161 | sub grab_stats { 162 | my %slabs = (); 163 | for my $stat (qw/items slabs/) { 164 | print $sock "stats $stat\r\n"; 165 | while (my $line = <$sock>) { 166 | chomp $line; 167 | last if ($line =~ m/^END/); 168 | if ($line =~ m/^STAT (?:items:)?(\d+):(\S+) (\S+)/) { 169 | my ($slab, $var, $val) = ($1, $2, $3); 170 | $slabs{$slab}->{$var} = $val; 171 | } 172 | } 173 | } 174 | 175 | return \%slabs; 176 | } 177 | 178 | # Really stupid algo, same as the initial algo built into memcached. 179 | # If a slab "wins" most evictions 3 times in a row, pick from a slab which 180 | # has had 0 evictions 3 times in a row and move it over. 181 | sub automove_basic { 182 | my ($totals, $sorted) = @_; 183 | 184 | my $source = 0; 185 | my $dest = 0; 186 | my $high = $sorted->[-1]; 187 | return unless $high->{evicted_d} > 0; 188 | if ($move{winner} == $high->{slab}) { 189 | $move{wins}++; 190 | $dest = $move{winner} if $move{wins} >= $opts{loops}; 191 | } else { 192 | $move{wins} = 1; 193 | $move{winner} = $high->{slab}; 194 | } 195 | for my $slab (@$sorted) { 196 | my $id = $slab->{slab}; 197 | if ($slab->{evicted_d} == 0 && $slab->{total_pages} > 2) { 198 | $move{zeroes}->{$id}++; 199 | $source = $id if (!$source && $move{zeroes}->{$id} >= $opts{loops}); 200 | } else { 201 | delete $move{zeroes}->{$slab->{slab}} 202 | if exists $move{zeroes}->{$slab->{slab}}; 203 | } 204 | } 205 | 206 | if ($source && $dest) { 207 | print " slabs reassign $source $dest\n"; 208 | print $sock "slabs reassign $source $dest\r\n"; 209 | my $res = <$sock>; 210 | print " RES: ", $res; 211 | } elsif ($dest && !$source) { 212 | print "FAIL: want to move memory to $dest but no valid source slab available\n"; 213 | } 214 | } 215 | 216 | # Using just the evicted stats. 217 | sub calc_results_evicted { 218 | my ($slabs, $totals) = calc_slabs(@_); 219 | my @sorted = sort { $a->{evicted_d} <=> $b->{evicted_d} } values %$slabs; 220 | return ($totals, \@sorted); 221 | } 222 | 223 | # Weighted ratios of evictions vs total stored items 224 | # Seems to fail as an experiment, but it tries to weight stats. 225 | # In this case evictions in underused classes tend to get vastly inflated 226 | sub calc_results_numratio { 227 | my ($slabs, $totals) = calc_slabs(@_, sub { 228 | my ($sb, $sa, $s) = @_; 229 | if ($s->{evicted_d}) { 230 | $s->{numratio} = $s->{evicted_d} / $s->{number}; 231 | } else { $s->{numratio} = 0; } 232 | }); 233 | my @sorted = sort { $a->{numratio} <=> $b->{numratio} } values %$slabs; 234 | return ($totals, \@sorted); 235 | } 236 | 237 | sub calc_slabs { 238 | my ($slabs_before, $slabs_after, $code) = @_; 239 | my %slabs = (); 240 | my %totals = (); 241 | for my $id (keys %$slabs_after) { 242 | my $sb = $slabs_before->{$id}; 243 | my $sa = $slabs_after->{$id}; 244 | next unless ($sb && $sa); 245 | my %slab = %$sa; 246 | for my $key (keys %slab) { 247 | # Add totals, diffs 248 | if ($slab{$key} =~ m/^\d+$/) { 249 | $totals{$key} += $slab{$key}; 250 | $slab{$key . '_d'} = $sa->{$key} - $sb->{$key}; 251 | $totals{$key . '_d'} += $sa->{$key} - $sb->{$key}; 252 | } 253 | } 254 | # External code 255 | $code->($sb, $sa, \%slab) if $code; 256 | $slab{slab} = $id; 257 | $slabs{$id} = \%slab; 258 | } 259 | return (\%slabs, \%totals); 260 | } 261 | -------------------------------------------------------------------------------- /scripts/memcached-init: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | ### BEGIN INIT INFO 3 | # Provides: memcached 4 | # Required-Start: $syslog 5 | # Required-Stop: $syslog 6 | # Should-Start: $local_fs 7 | # Should-Stop: $local_fs 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: memcached - Memory caching daemon 11 | # Description: memcached - Memory caching daemon 12 | ### END INIT INFO 13 | 14 | # Usage: 15 | # cp /etc/memcached.conf /etc/memcached_server1.conf 16 | # cp /etc/memcached.conf /etc/memcached_server2.conf 17 | # start all instances: 18 | # /etc/init.d/memcached start 19 | # start one instance: 20 | # /etc/init.d/memcached start server1 21 | # stop all instances: 22 | # /etc/init.d/memcached stop 23 | # stop one instance: 24 | # /etc/init.d/memcached stop server1 25 | # There is no "status" command. 26 | 27 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 28 | DAEMON=/usr/bin/memcached 29 | DAEMONNAME=memcached 30 | DAEMONBOOTSTRAP=/usr/share/memcached/scripts/start-memcached 31 | DESC=memcached 32 | 33 | test -x $DAEMON || exit 0 34 | test -x $DAEMONBOOTSTRAP || exit 0 35 | 36 | set -e 37 | 38 | FILES=(/etc/memcached_*.conf) 39 | # check for alternative config schema 40 | if [ -r "${FILES[0]}" ]; then 41 | CONFIGS=() 42 | for FILE in "${FILES[@]}"; 43 | do 44 | # remove prefix 45 | NAME=${FILE#/etc/} 46 | # remove suffix 47 | NAME=${NAME%.conf} 48 | 49 | # check optional second param 50 | if [ $# -ne 2 ]; 51 | then 52 | # add to config array 53 | CONFIGS+=($NAME) 54 | elif [ "memcached_$2" == "$NAME" ]; 55 | then 56 | # use only one memcached 57 | CONFIGS=($NAME) 58 | break; 59 | fi; 60 | done; 61 | 62 | if [ ${#CONFIGS[@]} == 0 ]; 63 | then 64 | echo "Config not exist for: $2" >&2 65 | exit 1 66 | fi; 67 | else 68 | CONFIGS=(memcached) 69 | fi; 70 | 71 | CONFIG_NUM=${#CONFIGS[@]} 72 | for ((i=0; i < $CONFIG_NUM; i++)); do 73 | NAME=${CONFIGS[${i}]} 74 | PIDFILE="/var/run/${NAME}.pid" 75 | 76 | case "$1" in 77 | start) 78 | echo -n "Starting $DESC: " 79 | start-stop-daemon --start --quiet --exec "$DAEMONBOOTSTRAP" -- /etc/${NAME}.conf $PIDFILE 80 | echo "$NAME." 81 | ;; 82 | stop) 83 | echo -n "Stopping $DESC: " 84 | start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON 85 | echo "$NAME." 86 | rm -f $PIDFILE 87 | ;; 88 | 89 | restart|force-reload) 90 | # 91 | # If the "reload" option is implemented, move the "force-reload" 92 | # option to the "reload" entry above. If not, "force-reload" is 93 | # just the same as "restart". 94 | # 95 | echo -n "Restarting $DESC: " 96 | start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE 97 | rm -f $PIDFILE 98 | sleep 1 99 | start-stop-daemon --start --quiet --exec "$DAEMONBOOTSTRAP" -- /etc/${NAME}.conf $PIDFILE 100 | echo "$NAME." 101 | ;; 102 | *) 103 | N=/etc/init.d/$NAME 104 | # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 105 | echo "Usage: $N {start|stop|restart|force-reload}" >&2 106 | exit 1 107 | ;; 108 | esac 109 | done; 110 | 111 | exit 0 112 | -------------------------------------------------------------------------------- /scripts/memcached-tool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | # memcached-tool: 4 | # stats/management tool for memcached. 5 | # 6 | # Author: 7 | # Brad Fitzpatrick 8 | # 9 | # License: 10 | # public domain. I give up all rights to this 11 | # tool. modify and copy at will. 12 | # 13 | 14 | use strict; 15 | use IO::Socket::INET; 16 | 17 | my $addr = shift; 18 | my $mode = shift || "display"; 19 | my ($from, $to); 20 | 21 | if ($mode eq "display") { 22 | undef $mode if @ARGV; 23 | } elsif ($mode eq "move") { 24 | $from = shift; 25 | $to = shift; 26 | undef $mode if $from < 6 || $from > 17; 27 | undef $mode if $to < 6 || $to > 17; 28 | print STDERR "ERROR: parameters out of range\n\n" unless $mode; 29 | } elsif ($mode eq 'dump') { 30 | ; 31 | } elsif ($mode eq 'stats') { 32 | ; 33 | } else { 34 | undef $mode; 35 | } 36 | 37 | undef $mode if @ARGV; 38 | 39 | die 40 | "Usage: memcached-tool [mode]\n 41 | memcached-tool 10.0.0.5:11211 display # shows slabs 42 | memcached-tool 10.0.0.5:11211 # same. (default is display) 43 | memcached-tool 10.0.0.5:11211 stats # shows general stats 44 | memcached-tool 10.0.0.5:11211 dump # dumps keys and values 45 | " unless $addr && $mode; 46 | 47 | 48 | my $sock; 49 | if ($addr =~ m:/:) { 50 | $sock = IO::Socket::UNIX->new( 51 | Peer => $addr, 52 | ); 53 | } 54 | else { 55 | $addr .= ':11211' unless $addr =~ /:\d+$/; 56 | 57 | $sock = IO::Socket::INET->new( 58 | PeerAddr => $addr, 59 | Proto => 'tcp', 60 | ); 61 | } 62 | die "Couldn't connect to $addr\n" unless $sock; 63 | 64 | if ($mode eq 'dump') { 65 | my %items; 66 | my $totalitems; 67 | 68 | print $sock "stats items\r\n"; 69 | 70 | while (<$sock>) { 71 | last if /^END/; 72 | if (/^STAT items:(\d*):number (\d*)/) { 73 | $items{$1} = $2; 74 | $totalitems += $2; 75 | } 76 | } 77 | print STDERR "Dumping memcache contents\n"; 78 | print STDERR " Number of buckets: " . scalar(keys(%items)) . "\n"; 79 | print STDERR " Number of items : $totalitems\n"; 80 | 81 | foreach my $bucket (sort(keys(%items))) { 82 | print STDERR "Dumping bucket $bucket - " . $items{$bucket} . " total items\n"; 83 | print $sock "stats cachedump $bucket $items{$bucket}\r\n"; 84 | my %keyexp; 85 | while (<$sock>) { 86 | last if /^END/; 87 | # return format looks like this 88 | # ITEM foo [6 b; 1176415152 s] 89 | if (/^ITEM (\S+) \[.* (\d+) s\]/) { 90 | $keyexp{$1} = $2; 91 | } 92 | } 93 | 94 | foreach my $k (keys(%keyexp)) { 95 | print $sock "get $k\r\n"; 96 | my $response = <$sock>; 97 | if ($response =~ /VALUE (\S+) (\d+) (\d+)/) { 98 | my $flags = $2; 99 | my $len = $3; 100 | my $val; 101 | read $sock, $val, $len; 102 | print "add $k $flags $keyexp{$k} $len\r\n$val\r\n"; 103 | # get the END 104 | $_ = <$sock>; 105 | $_ = <$sock>; 106 | } 107 | } 108 | } 109 | exit; 110 | } 111 | 112 | if ($mode eq 'stats') { 113 | my %items; 114 | 115 | print $sock "stats\r\n"; 116 | 117 | while (<$sock>) { 118 | last if /^END/; 119 | chomp; 120 | if (/^STAT\s+(\S*)\s+(.*)/) { 121 | $items{$1} = $2; 122 | } 123 | } 124 | printf ("#%-17s %5s %11s\n", $addr, "Field", "Value"); 125 | foreach my $name (sort(keys(%items))) { 126 | printf ("%24s %12s\n", $name, $items{$name}); 127 | 128 | } 129 | exit; 130 | } 131 | 132 | # display mode: 133 | 134 | my %items; # class -> { number, age, chunk_size, chunks_per_page, 135 | # total_pages, total_chunks, used_chunks, 136 | # free_chunks, free_chunks_end } 137 | 138 | print $sock "stats items\r\n"; 139 | my $max = 0; 140 | while (<$sock>) { 141 | last if /^END/; 142 | if (/^STAT items:(\d+):(\w+) (\d+)/) { 143 | $items{$1}{$2} = $3; 144 | $max = $1; 145 | } 146 | } 147 | 148 | print $sock "stats slabs\r\n"; 149 | while (<$sock>) { 150 | last if /^END/; 151 | if (/^STAT (\d+):(\w+) (\d+)/) { 152 | $items{$1}{$2} = $3; 153 | } 154 | } 155 | 156 | print " # Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM\n"; 157 | foreach my $n (1..$max) { 158 | my $it = $items{$n}; 159 | next if (0 == $it->{total_pages}); 160 | my $size = $it->{chunk_size} < 1024 ? 161 | "$it->{chunk_size}B" : 162 | sprintf("%.1fK", $it->{chunk_size} / 1024.0); 163 | my $full = $it->{free_chunks_end} == 0 ? "yes" : " no"; 164 | printf("%3d %8s %9ds %7d %7d %7s %8d %8d %4d\n", 165 | $n, $size, $it->{age}, $it->{total_pages}, 166 | $it->{number}, $full, $it->{evicted}, 167 | $it->{evicted_time}, $it->{outofmemory}); 168 | } 169 | 170 | -------------------------------------------------------------------------------- /scripts/memcached.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=memcached daemon 3 | After=network.target 4 | 5 | [Service] 6 | EnvironmentFile=/etc/sysconfig/memcached 7 | ExecStart=/usr/bin/memcached -p ${PORT} -u ${USER} -m ${CACHESIZE} -c ${MAXCONN} $OPTIONS 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /scripts/memcached.sysv: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # chkconfig: - 55 45 4 | # description: The memcached daemon is a network memory cache service. 5 | # processname: memcached 6 | # config: /etc/sysconfig/memcached 7 | 8 | # Source function library. 9 | . /etc/rc.d/init.d/functions 10 | 11 | PORT=11211 12 | USER=nobody 13 | MAXCONN=1024 14 | CACHESIZE=64 15 | OPTIONS="" 16 | 17 | if [ -f /etc/sysconfig/memcached ];then 18 | . /etc/sysconfig/memcached 19 | fi 20 | 21 | # Check that networking is up. 22 | if [ "$NETWORKING" = "no" ] 23 | then 24 | exit 0 25 | fi 26 | 27 | RETVAL=0 28 | prog="memcached" 29 | 30 | start () { 31 | echo -n $"Starting $prog: " 32 | # insure that /var/run/memcached has proper permissions 33 | chown $USER /var/run/memcached 34 | daemon memcached -d -p $PORT -u $USER -m $CACHESIZE -c $MAXCONN -P /var/run/memcached/memcached.pid $OPTIONS 35 | RETVAL=$? 36 | echo 37 | [ $RETVAL -eq 0 ] && touch /var/lock/subsys/memcached 38 | } 39 | stop () { 40 | echo -n $"Stopping $prog: " 41 | killproc memcached 42 | RETVAL=$? 43 | echo 44 | if [ $RETVAL -eq 0 ] ; then 45 | rm -f /var/lock/subsys/memcached 46 | rm -f /var/run/memcached.pid 47 | fi 48 | } 49 | 50 | restart () { 51 | stop 52 | start 53 | } 54 | 55 | 56 | # See how we were called. 57 | case "$1" in 58 | start) 59 | start 60 | ;; 61 | stop) 62 | stop 63 | ;; 64 | status) 65 | status memcached 66 | ;; 67 | restart|reload) 68 | restart 69 | ;; 70 | condrestart) 71 | [ -f /var/lock/subsys/memcached ] && restart || : 72 | ;; 73 | *) 74 | echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}" 75 | exit 1 76 | esac 77 | 78 | exit $? 79 | -------------------------------------------------------------------------------- /scripts/start-memcached: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | # start-memcached 4 | # 2003/2004 - Jay Bonci 5 | # This script handles the parsing of the /etc/memcached.conf file 6 | # and was originally created for the Debian distribution. 7 | # Anyone may use this little script under the same terms as 8 | # memcached itself. 9 | 10 | use POSIX qw(setsid); 11 | use strict; 12 | 13 | if($> != 0 and $< != 0) 14 | { 15 | print STDERR "Only root wants to run start-memcached.\n"; 16 | exit; 17 | } 18 | 19 | my $params; my $etchandle; my $etcfile = "/etc/memcached.conf"; 20 | 21 | # This script assumes that memcached is located at /usr/bin/memcached, and 22 | # that the pidfile is writable at /var/run/memcached.pid 23 | 24 | my $memcached = "/usr/bin/memcached"; 25 | my $pidfile = "/var/run/memcached.pid"; 26 | 27 | if (scalar(@ARGV) == 2) { 28 | $etcfile = shift(@ARGV); 29 | $pidfile = shift(@ARGV); 30 | } 31 | 32 | # If we don't get a valid logfile parameter in the /etc/memcached.conf file, 33 | # we'll just throw away all of our in-daemon output. We need to re-tie it so 34 | # that non-bash shells will not hang on logout. Thanks to Michael Renner for 35 | # the tip 36 | my $fd_reopened = "/dev/null"; 37 | 38 | sub handle_logfile 39 | { 40 | my ($logfile) = @_; 41 | $fd_reopened = $logfile; 42 | } 43 | 44 | sub reopen_logfile 45 | { 46 | my ($logfile) = @_; 47 | 48 | open *STDERR, ">>$logfile"; 49 | open *STDOUT, ">>$logfile"; 50 | open *STDIN, ">>/dev/null"; 51 | $fd_reopened = $logfile; 52 | } 53 | 54 | # This is set up in place here to support other non -[a-z] directives 55 | 56 | my $conf_directives = { 57 | "logfile" => \&handle_logfile, 58 | }; 59 | 60 | if(open $etchandle, $etcfile) 61 | { 62 | foreach my $line (<$etchandle>) 63 | { 64 | $line ||= ""; 65 | $line =~ s/\#.*//g; 66 | $line =~ s/\s+$//g; 67 | $line =~ s/^\s+//g; 68 | next unless $line; 69 | next if $line =~ /^\-[dh]/; 70 | 71 | if($line =~ /^[^\-]/) 72 | { 73 | my ($directive, $arg) = $line =~ /^(.*?)\s+(.*)/; 74 | $conf_directives->{$directive}->($arg); 75 | next; 76 | } 77 | 78 | push @$params, $line; 79 | } 80 | 81 | }else{ 82 | $params = []; 83 | } 84 | 85 | push @$params, "-u root" unless(grep "-u", @$params); 86 | $params = join " ", @$params; 87 | 88 | if(-e $pidfile) 89 | { 90 | open PIDHANDLE, "$pidfile"; 91 | my $localpid = ; 92 | close PIDHANDLE; 93 | 94 | chomp $localpid; 95 | if(-d "/proc/$localpid") 96 | { 97 | print STDERR "memcached is already running.\n"; 98 | exit; 99 | }else{ 100 | `rm -f $localpid`; 101 | } 102 | 103 | } 104 | 105 | my $pid = fork(); 106 | 107 | if($pid == 0) 108 | { 109 | # setsid makes us the session leader 110 | setsid(); 111 | reopen_logfile($fd_reopened); 112 | # must fork again now that tty is closed 113 | $pid = fork(); 114 | if ($pid) { 115 | if(open PIDHANDLE,">$pidfile") 116 | { 117 | print PIDHANDLE $pid; 118 | close PIDHANDLE; 119 | }else{ 120 | 121 | print STDERR "Can't write pidfile to $pidfile.\n"; 122 | } 123 | exit(0); 124 | } 125 | exec "$memcached $params"; 126 | exit(0); 127 | 128 | } 129 | -------------------------------------------------------------------------------- /sizes.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "memcached.h" 4 | 5 | static void display(const char *name, size_t size) { 6 | printf("%s\t%d\n", name, (int)size); 7 | } 8 | 9 | int main(int argc, char **argv) { 10 | 11 | display("Slab Stats", sizeof(struct slab_stats)); 12 | display("Thread stats", 13 | sizeof(struct thread_stats) 14 | - (200 * sizeof(struct slab_stats))); 15 | display("Global stats", sizeof(struct stats)); 16 | display("Settings", sizeof(struct settings)); 17 | display("Item (no cas)", sizeof(item)); 18 | display("Item (cas)", sizeof(item) + sizeof(uint64_t)); 19 | display("Libevent thread", 20 | sizeof(LIBEVENT_THREAD) - sizeof(struct thread_stats)); 21 | display("Connection", sizeof(conn)); 22 | 23 | printf("----------------------------------------\n"); 24 | 25 | display("libevent thread cumulative", sizeof(LIBEVENT_THREAD)); 26 | display("Thread stats cumulative\t", sizeof(struct thread_stats)); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /slabs.h: -------------------------------------------------------------------------------- 1 | /* slabs memory allocation */ 2 | #ifndef SLABS_H 3 | #define SLABS_H 4 | 5 | /** Init the subsystem. 1st argument is the limit on no. of bytes to allocate, 6 | 0 if no limit. 2nd argument is the growth factor; each slab will use a chunk 7 | size equal to the previous slab's chunk size times this factor. 8 | 3rd argument specifies if the slab allocator should allocate all memory 9 | up front (if true), or allocate memory in chunks as it is needed (if false) 10 | */ 11 | void slabs_init(const size_t limit, const double factor, const bool prealloc); 12 | 13 | 14 | /** 15 | * Given object size, return id to use when allocating/freeing memory for object 16 | * 0 means error: can't store such a large object 17 | */ 18 | 19 | unsigned int slabs_clsid(const size_t size); 20 | 21 | /** Allocate object of given length. 0 on error */ /*@null@*/ 22 | void *slabs_alloc(const size_t size, unsigned int id); 23 | 24 | /** Free previously allocated object */ 25 | void slabs_free(void *ptr, size_t size, unsigned int id); 26 | 27 | /** Adjust the stats for memory requested */ 28 | void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal); 29 | 30 | /** Return a datum for stats in binary protocol */ 31 | bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c); 32 | 33 | /** Fill buffer with stats */ /*@null@*/ 34 | void slabs_stats(ADD_STAT add_stats, void *c); 35 | 36 | int start_slab_maintenance_thread(void); 37 | void stop_slab_maintenance_thread(void); 38 | 39 | enum reassign_result_type { 40 | REASSIGN_OK=0, REASSIGN_RUNNING, REASSIGN_BADCLASS, REASSIGN_NOSPARE, 41 | REASSIGN_DEST_NOT_FULL, REASSIGN_SRC_NOT_SAFE, REASSIGN_SRC_DST_SAME 42 | }; 43 | 44 | enum reassign_result_type slabs_reassign(int src, int dst); 45 | 46 | #ifdef MEMC3_CACHE_CLOCK 47 | item* slabs_cache_evict(unsigned int id); 48 | void slabs_cache_update(item* it); 49 | #endif 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /solaris_priv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "memcached.h" 5 | 6 | /* 7 | * this section of code will drop all (Solaris) privileges including 8 | * those normally granted to all userland process (basic privileges). The 9 | * effect of this is that after running this code, the process will not able 10 | * to fork(), exec(), etc. See privileges(5) for more information. 11 | */ 12 | void drop_privileges(void) { 13 | priv_set_t *privs = priv_str_to_set("basic", ",", NULL); 14 | 15 | if (privs == NULL) { 16 | perror("priv_str_to_set"); 17 | exit(EXIT_FAILURE); 18 | } 19 | 20 | (void)priv_delset(privs, PRIV_FILE_LINK_ANY); 21 | (void)priv_delset(privs, PRIV_PROC_EXEC); 22 | (void)priv_delset(privs, PRIV_PROC_FORK); 23 | (void)priv_delset(privs, PRIV_PROC_INFO); 24 | (void)priv_delset(privs, PRIV_PROC_SESSION); 25 | 26 | if (setppriv(PRIV_SET, PRIV_PERMITTED, privs) != 0) { 27 | perror("setppriv(PRIV_SET, PRIV_PERMITTED)"); 28 | exit(EXIT_FAILURE); 29 | } 30 | 31 | priv_emptyset(privs); 32 | 33 | if (setppriv(PRIV_SET, PRIV_INHERITABLE, privs) != 0) { 34 | perror("setppriv(PRIV_SET, PRIV_INHERITABLE)"); 35 | exit(EXIT_FAILURE); 36 | } 37 | 38 | if (setppriv(PRIV_SET, PRIV_LIMIT, privs) != 0) { 39 | perror("setppriv(PRIV_SET, PRIV_LIMIT)"); 40 | exit(EXIT_FAILURE); 41 | } 42 | 43 | priv_freeset(privs); 44 | } 45 | -------------------------------------------------------------------------------- /stamp-h1: -------------------------------------------------------------------------------- 1 | timestamp for config.h 2 | -------------------------------------------------------------------------------- /stats.h: -------------------------------------------------------------------------------- 1 | /* stats */ 2 | void stats_prefix_init(void); 3 | void stats_prefix_clear(void); 4 | void stats_prefix_record_get(const char *key, const size_t nkey, const bool is_hit); 5 | void stats_prefix_record_delete(const char *key, const size_t nkey); 6 | void stats_prefix_record_set(const char *key, const size_t nkey); 7 | /*@null@*/ 8 | char *stats_prefix_dump(int *length); 9 | -------------------------------------------------------------------------------- /t/00-startup.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 18; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | eval { 10 | my $server = new_memcached(); 11 | ok($server, "started the server"); 12 | }; 13 | is($@, '', 'Basic startup works'); 14 | 15 | eval { 16 | my $server = new_memcached("-l fooble"); 17 | }; 18 | ok($@, "Died with illegal -l args"); 19 | 20 | eval { 21 | my $server = new_memcached("-l 127.0.0.1"); 22 | }; 23 | is($@,'', "-l 127.0.0.1 works"); 24 | 25 | eval { 26 | my $server = new_memcached('-C'); 27 | my $stats = mem_stats($server->sock, 'settings'); 28 | is('no', $stats->{'cas_enabled'}); 29 | }; 30 | is($@, '', "-C works"); 31 | 32 | eval { 33 | my $server = new_memcached('-b 8675'); 34 | my $stats = mem_stats($server->sock, 'settings'); 35 | is('8675', $stats->{'tcp_backlog'}); 36 | }; 37 | is($@, '', "-b works"); 38 | 39 | foreach my $val ('auto', 'ascii') { 40 | eval { 41 | my $server = new_memcached("-B $val"); 42 | my $stats = mem_stats($server->sock, 'settings'); 43 | ok($stats->{'binding_protocol'} =~ /$val/, "$val works"); 44 | }; 45 | is($@, '', "$val works"); 46 | } 47 | 48 | # For the binary test, we just verify it starts since we don't have an easy bin client. 49 | eval { 50 | my $server = new_memcached("-B binary"); 51 | }; 52 | is($@, '', "binary works"); 53 | 54 | eval { 55 | my $server = new_memcached("-vv -B auto"); 56 | }; 57 | is($@, '', "auto works"); 58 | 59 | eval { 60 | my $server = new_memcached("-vv -B ascii"); 61 | }; 62 | is($@, '', "ascii works"); 63 | 64 | 65 | # For the binary test, we just verify it starts since we don't have an easy bin client. 66 | eval { 67 | my $server = new_memcached("-vv -B binary"); 68 | }; 69 | is($@, '', "binary works"); 70 | 71 | 72 | # Should blow up with something invalid. 73 | eval { 74 | my $server = new_memcached("-B http"); 75 | }; 76 | ok($@, "Died with illegal -B arg."); 77 | 78 | # Should not allow -t 0 79 | eval { 80 | my $server = new_memcached("-t 0"); 81 | }; 82 | ok($@, "Died with illegal 0 thread count"); 83 | -------------------------------------------------------------------------------- /t/64bit.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | $ENV{T_MEMD_INITIAL_MALLOC} = "4294967328"; # 2**32 + 32 , just over 4GB 10 | $ENV{T_MEMD_SLABS_ALLOC} = 0; # don't preallocate slabs 11 | 12 | my $server = new_memcached("-m 4098 -M"); 13 | my $sock = $server->sock; 14 | 15 | my ($stats, $slabs) = @_; 16 | 17 | $stats = mem_stats($sock); 18 | 19 | if ($stats->{'pointer_size'} eq "32") { 20 | plan skip_all => 'Skipping 64-bit tests on 32-bit build'; 21 | exit 0; 22 | } else { 23 | plan tests => 6; 24 | } 25 | 26 | is($stats->{'pointer_size'}, 64, "is 64 bit"); 27 | is($stats->{'limit_maxbytes'}, "4297064448", "max bytes is 4098 MB"); 28 | 29 | $slabs = mem_stats($sock, 'slabs'); 30 | is($slabs->{'total_malloced'}, "4294967328", "expected (faked) value of total_malloced"); 31 | is($slabs->{'active_slabs'}, 0, "no active slabs"); 32 | 33 | my $hit_limit = 0; 34 | for (1..5) { 35 | my $size = 400 * 1024; 36 | my $data = "a" x $size; 37 | print $sock "set big$_ 0 0 $size\r\n$data\r\n"; 38 | my $res = <$sock>; 39 | $hit_limit = 1 if $res ne "STORED\r\n"; 40 | } 41 | ok($hit_limit, "hit size limit"); 42 | 43 | $slabs = mem_stats($sock, 'slabs'); 44 | is($slabs->{'active_slabs'}, 1, "1 active slab"); 45 | -------------------------------------------------------------------------------- /t/binary-get.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 8; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | my $count = 1; 13 | 14 | foreach my $blob ("mooo\0", "mumble\0\0\0\0\r\rblarg", "\0", "\r") { 15 | my $key = "foo$count"; 16 | my $len = length($blob); 17 | print "len is $len\n"; 18 | print $sock "set $key 0 0 $len\r\n$blob\r\n"; 19 | is(scalar <$sock>, "STORED\r\n", "stored $key"); 20 | mem_get_is($sock, $key, $blob); 21 | $count++; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /t/bogus-commands.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 1; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | print $sock "boguscommand slkdsldkfjsd\r\n"; 13 | is(scalar <$sock>, "ERROR\r\n", "got error back"); 14 | -------------------------------------------------------------------------------- /t/cas.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 43; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | 10 | my $server = new_memcached(); 11 | my $sock = $server->sock; 12 | my $sock2 = $server->new_sock; 13 | 14 | my @result; 15 | my @result2; 16 | 17 | ok($sock != $sock2, "have two different connections open"); 18 | 19 | sub check_args { 20 | my ($line, $name) = @_; 21 | 22 | my $svr = new_memcached(); 23 | my $s = $svr->sock; 24 | 25 | print $s $line; 26 | is(scalar <$s>, "CLIENT_ERROR bad command line format\r\n", $name); 27 | undef $svr; 28 | } 29 | 30 | check_args "cas bad blah 0 0 0\r\n\r\n", "bad flags"; 31 | check_args "cas bad 0 blah 0 0\r\n\r\n", "bad exp"; 32 | check_args "cas bad 0 0 blah 0\r\n\r\n", "bad cas"; 33 | check_args "cas bad 0 0 0 blah\r\n\r\n", "bad size"; 34 | 35 | # gets foo (should not exist) 36 | print $sock "gets foo\r\n"; 37 | is(scalar <$sock>, "END\r\n", "gets failed"); 38 | 39 | # set foo 40 | print $sock "set foo 0 0 6\r\nbarval\r\n"; 41 | is(scalar <$sock>, "STORED\r\n", "stored barval"); 42 | 43 | # gets foo and verify identifier exists 44 | @result = mem_gets($sock, "foo"); 45 | mem_gets_is($sock,$result[0],"foo","barval"); 46 | 47 | # cas fail 48 | print $sock "cas foo 0 0 6 123\r\nbarva2\r\n"; 49 | is(scalar <$sock>, "EXISTS\r\n", "cas failed for foo"); 50 | 51 | # gets foo - success 52 | @result = mem_gets($sock, "foo"); 53 | mem_gets_is($sock,$result[0],"foo","barval"); 54 | 55 | # cas success 56 | print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n"; 57 | is(scalar <$sock>, "STORED\r\n", "cas success, set foo"); 58 | 59 | # cas failure (reusing the same key) 60 | print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n"; 61 | is(scalar <$sock>, "EXISTS\r\n", "reusing a CAS ID"); 62 | 63 | # delete foo 64 | print $sock "delete foo\r\n"; 65 | is(scalar <$sock>, "DELETED\r\n", "deleted foo"); 66 | 67 | # cas missing 68 | print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n"; 69 | is(scalar <$sock>, "NOT_FOUND\r\n", "cas failed, foo does not exist"); 70 | 71 | # cas empty 72 | print $sock "cas foo 0 0 6 \r\nbarva2\r\n"; 73 | is(scalar <$sock>, "ERROR\r\n", "cas empty, throw error"); 74 | # cant parse barval2\r\n 75 | is(scalar <$sock>, "ERROR\r\n", "error out on barval2 parsing"); 76 | 77 | # set foo1 78 | print $sock "set foo1 0 0 1\r\n1\r\n"; 79 | is(scalar <$sock>, "STORED\r\n", "set foo1"); 80 | # set foo2 81 | print $sock "set foo2 0 0 1\r\n2\r\n"; 82 | is(scalar <$sock>, "STORED\r\n", "set foo2"); 83 | 84 | # gets foo1 check 85 | print $sock "gets foo1\r\n"; 86 | ok(scalar <$sock> =~ /VALUE foo1 0 1 (\d+)\r\n/, "gets foo1 regexp success"); 87 | my $foo1_cas = $1; 88 | is(scalar <$sock>, "1\r\n","gets foo1 data is 1"); 89 | is(scalar <$sock>, "END\r\n","gets foo1 END"); 90 | 91 | # gets foo2 check 92 | print $sock "gets foo2\r\n"; 93 | ok(scalar <$sock> =~ /VALUE foo2 0 1 (\d+)\r\n/,"gets foo2 regexp success"); 94 | my $foo2_cas = $1; 95 | is(scalar <$sock>, "2\r\n","gets foo2 data is 2"); 96 | is(scalar <$sock>, "END\r\n","gets foo2 END"); 97 | 98 | # validate foo1 != foo2 99 | ok($foo1_cas != $foo2_cas,"foo1 != foo2 single-gets success"); 100 | 101 | # multi-gets 102 | print $sock "gets foo1 foo2\r\n"; 103 | ok(scalar <$sock> =~ /VALUE foo1 0 1 (\d+)\r\n/, "validating first set of data is foo1"); 104 | $foo1_cas = $1; 105 | is(scalar <$sock>, "1\r\n", "validating foo1 set of data is 1"); 106 | ok(scalar <$sock> =~ /VALUE foo2 0 1 (\d+)\r\n/, "validating second set of data is foo2"); 107 | $foo2_cas = $1; 108 | is(scalar <$sock>, "2\r\n", "validating foo2 set of data is 2"); 109 | is(scalar <$sock>, "END\r\n","validating foo1,foo2 gets is over - END"); 110 | 111 | # validate foo1 != foo2 112 | ok($foo1_cas != $foo2_cas, "foo1 != foo2 multi-gets success"); 113 | 114 | ### simulate race condition with cas 115 | 116 | # gets foo1 - success 117 | @result = mem_gets($sock, "foo1"); 118 | ok($result[0] != "", "sock - gets foo1 is not empty"); 119 | 120 | # gets foo2 - success 121 | @result2 = mem_gets($sock2, "foo1"); 122 | ok($result2[0] != "","sock2 - gets foo1 is not empty"); 123 | 124 | print $sock "cas foo1 0 0 6 $result[0]\r\nbarva2\r\n"; 125 | print $sock2 "cas foo1 0 0 5 $result2[0]\r\napple\r\n"; 126 | 127 | my $res1 = <$sock>; 128 | my $res2 = <$sock2>; 129 | 130 | ok( ( $res1 eq "STORED\r\n" && $res2 eq "EXISTS\r\n") || 131 | ( $res1 eq "EXISTS\r\n" && $res2 eq "STORED\r\n"), 132 | "cas on same item from two sockets"); 133 | 134 | ### bug 15: http://code.google.com/p/memcached/issues/detail?id=15 135 | 136 | # set foo 137 | print $sock "set bug15 0 0 1\r\n0\r\n"; 138 | is(scalar <$sock>, "STORED\r\n", "stored 0"); 139 | 140 | # Check out the first gets. 141 | print $sock "gets bug15\r\n"; 142 | ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success"); 143 | my $bug15_cas = $1; 144 | is(scalar <$sock>, "0\r\n", "gets bug15 data is 0"); 145 | is(scalar <$sock>, "END\r\n","gets bug15 END"); 146 | 147 | # Increment 148 | print $sock "incr bug15 1\r\n"; 149 | is(scalar <$sock>, "1\r\n", "incr worked"); 150 | 151 | # Validate a changed CAS 152 | print $sock "gets bug15\r\n"; 153 | ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success"); 154 | my $next_bug15_cas = $1; 155 | is(scalar <$sock>, "1\r\n", "gets bug15 data is 0"); 156 | is(scalar <$sock>, "END\r\n","gets bug15 END"); 157 | 158 | ok($bug15_cas != $next_bug15_cas, "CAS changed"); 159 | -------------------------------------------------------------------------------- /t/daemonize.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 7; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | use File::Temp qw(tempfile); 10 | 11 | my (undef, $tmpfn) = tempfile(); 12 | 13 | my $server = new_memcached("-d -P $tmpfn"); 14 | my $sock = $server->sock; 15 | sleep 0.5; 16 | 17 | ok(-e $tmpfn, "pid file exists"); 18 | ok(-s $tmpfn, "pid file has length"); 19 | 20 | open (my $fh, $tmpfn) or die; 21 | my $readpid = do { local $/; <$fh>; }; 22 | chomp $readpid; 23 | close ($fh); 24 | 25 | ok(kill(0, $readpid), "process is still running"); 26 | 27 | my $stats = mem_stats($sock); 28 | is($stats->{pid}, $readpid, "memcached reports same pid as file"); 29 | 30 | ok($server->new_sock, "opened new socket"); 31 | ok(kill(9, $readpid), "sent KILL signal"); 32 | sleep 0.5; 33 | ok(! $server->new_sock, "failed to open new socket"); 34 | -------------------------------------------------------------------------------- /t/dash-M.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached('-M -m 1'); 10 | my $sock = $server->sock; 11 | 12 | my $value = "B" x 8192; 13 | my $vallen = length($value); 14 | 15 | my $resp = "STORED\r\n"; 16 | my $key = 0; 17 | 18 | while($resp eq "STORED\r\n") { 19 | print $sock "set dash$key 0 0 $vallen\r\n$value\r\n"; 20 | $key++; 21 | $resp = scalar <$sock>; 22 | } 23 | 24 | my $max_stored = $key - 1; 25 | 26 | plan tests => $max_stored + 1; 27 | 28 | print $sock "set dash$key 0 0 $vallen\r\n$value\r\n"; 29 | is(scalar <$sock>, "SERVER_ERROR out of memory storing object\r\n", 30 | "failed to add another one."); 31 | 32 | for($key = 0; $key < $max_stored; $key++) { 33 | mem_get_is $sock, "dash$key", $value, "Failed at dash$key"; 34 | } 35 | -------------------------------------------------------------------------------- /t/evictions.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Test the 'stats items' evictions counters. 3 | 4 | use strict; 5 | use Test::More tests => 92; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | 10 | my $server = new_memcached("-m 3"); 11 | my $sock = $server->sock; 12 | my $value = "B"x66560; 13 | my $key = 0; 14 | 15 | # These aren't set to expire. 16 | for ($key = 0; $key < 40; $key++) { 17 | print $sock "set key$key 0 0 66560\r\n$value\r\n"; 18 | is(scalar <$sock>, "STORED\r\n", "stored key$key"); 19 | } 20 | 21 | # These ones would expire in 600 seconds. 22 | for ($key = 0; $key < 50; $key++) { 23 | print $sock "set key$key 0 600 66560\r\n$value\r\n"; 24 | is(scalar <$sock>, "STORED\r\n", "stored key$key"); 25 | } 26 | 27 | my $stats = mem_stats($sock, "items"); 28 | my $evicted = $stats->{"items:31:evicted"}; 29 | isnt($evicted, "0", "check evicted"); 30 | my $evicted_nonzero = $stats->{"items:31:evicted_nonzero"}; 31 | isnt($evicted_nonzero, "0", "check evicted_nonzero"); 32 | -------------------------------------------------------------------------------- /t/expirations.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 15; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $expire; 12 | 13 | sub wait_for_early_second { 14 | my $have_hires = eval "use Time::HiRes (); 1"; 15 | if ($have_hires) { 16 | my $tsh = Time::HiRes::time(); 17 | my $ts = int($tsh); 18 | return if ($tsh - $ts) < 0.5; 19 | } 20 | 21 | my $ts = int(time()); 22 | while (1) { 23 | my $t = int(time()); 24 | return if $t != $ts; 25 | select undef, undef, undef, 0.10; # 1/10th of a second sleeps until time changes. 26 | } 27 | } 28 | 29 | wait_for_early_second(); 30 | 31 | print $sock "set foo 0 1 6\r\nfooval\r\n"; 32 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 33 | 34 | mem_get_is($sock, "foo", "fooval"); 35 | sleep(1.5); 36 | mem_get_is($sock, "foo", undef); 37 | 38 | $expire = time() - 1; 39 | print $sock "set foo 0 $expire 6\r\nfooval\r\n"; 40 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 41 | mem_get_is($sock, "foo", undef, "already expired"); 42 | 43 | $expire = time() + 1; 44 | print $sock "set foo 0 $expire 6\r\nfoov+1\r\n"; 45 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 46 | mem_get_is($sock, "foo", "foov+1"); 47 | sleep(2.2); 48 | mem_get_is($sock, "foo", undef, "now expired"); 49 | 50 | $expire = time() - 20; 51 | print $sock "set boo 0 $expire 6\r\nbooval\r\n"; 52 | is(scalar <$sock>, "STORED\r\n", "stored boo"); 53 | mem_get_is($sock, "boo", undef, "now expired"); 54 | 55 | print $sock "add add 0 2 6\r\naddval\r\n"; 56 | is(scalar <$sock>, "STORED\r\n", "stored add"); 57 | mem_get_is($sock, "add", "addval"); 58 | # second add fails 59 | print $sock "add add 0 2 7\r\naddval2\r\n"; 60 | is(scalar <$sock>, "NOT_STORED\r\n", "add failure"); 61 | sleep(2.3); 62 | print $sock "add add 0 2 7\r\naddval3\r\n"; 63 | is(scalar <$sock>, "STORED\r\n", "stored add again"); 64 | mem_get_is($sock, "add", "addval3"); 65 | -------------------------------------------------------------------------------- /t/flags.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 6; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | # set foo (and should get it) 13 | for my $flags (0, 123, 2**16-1) { 14 | print $sock "set foo $flags 0 6\r\nfooval\r\n"; 15 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 16 | mem_get_is({ sock => $sock, 17 | flags => $flags }, "foo", "fooval", "got flags $flags back"); 18 | } 19 | -------------------------------------------------------------------------------- /t/flush-all.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 21; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $expire; 12 | 13 | print $sock "set foo 0 0 6\r\nfooval\r\n"; 14 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 15 | 16 | mem_get_is($sock, "foo", "fooval"); 17 | print $sock "flush_all\r\n"; 18 | is(scalar <$sock>, "OK\r\n", "did flush_all"); 19 | mem_get_is($sock, "foo", undef); 20 | 21 | # Test flush_all with zero delay. 22 | print $sock "set foo 0 0 6\r\nfooval\r\n"; 23 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 24 | 25 | mem_get_is($sock, "foo", "fooval"); 26 | print $sock "flush_all 0\r\n"; 27 | is(scalar <$sock>, "OK\r\n", "did flush_all"); 28 | mem_get_is($sock, "foo", undef); 29 | 30 | # check that flush_all doesn't blow away items that immediately get set 31 | print $sock "set foo 0 0 3\r\nnew\r\n"; 32 | is(scalar <$sock>, "STORED\r\n", "stored foo = 'new'"); 33 | mem_get_is($sock, "foo", 'new'); 34 | 35 | # and the other form, specifying a flush_all time... 36 | my $expire = time() + 2; 37 | print $sock "flush_all $expire\r\n"; 38 | is(scalar <$sock>, "OK\r\n", "did flush_all in future"); 39 | 40 | print $sock "set foo 0 0 4\r\n1234\r\n"; 41 | is(scalar <$sock>, "STORED\r\n", "stored foo = '1234'"); 42 | mem_get_is($sock, "foo", '1234'); 43 | sleep(3); 44 | mem_get_is($sock, "foo", undef); 45 | 46 | print $sock "set foo 0 0 5\r\n12345\r\n"; 47 | is(scalar <$sock>, "STORED\r\n", "stored foo = '12345'"); 48 | mem_get_is($sock, "foo", '12345'); 49 | print $sock "flush_all 86400\r\n"; 50 | is(scalar <$sock>, "OK\r\n", "did flush_all for far future"); 51 | # Check foo still exists. 52 | mem_get_is($sock, "foo", '12345'); 53 | print $sock "set foo2 0 0 5\r\n54321\r\n"; 54 | is(scalar <$sock>, "STORED\r\n", "stored foo2 = '54321'"); 55 | mem_get_is($sock, "foo", '12345'); 56 | mem_get_is($sock, "foo2", '54321'); 57 | -------------------------------------------------------------------------------- /t/getset.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 539; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | 10 | my $server = new_memcached(); 11 | my $sock = $server->sock; 12 | 13 | 14 | # set foo (and should get it) 15 | print $sock "set foo 0 0 6\r\nfooval\r\n"; 16 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 17 | mem_get_is($sock, "foo", "fooval"); 18 | 19 | # add bar (and should get it) 20 | print $sock "add bar 0 0 6\r\nbarval\r\n"; 21 | is(scalar <$sock>, "STORED\r\n", "stored barval"); 22 | mem_get_is($sock, "bar", "barval"); 23 | 24 | # add foo (but shouldn't get new value) 25 | print $sock "add foo 0 0 5\r\nfoov2\r\n"; 26 | is(scalar <$sock>, "NOT_STORED\r\n", "not stored"); 27 | mem_get_is($sock, "foo", "fooval"); 28 | 29 | # replace bar (should work) 30 | print $sock "replace bar 0 0 6\r\nbarva2\r\n"; 31 | is(scalar <$sock>, "STORED\r\n", "replaced barval 2"); 32 | 33 | # replace notexist (shouldn't work) 34 | print $sock "replace notexist 0 0 6\r\nbarva2\r\n"; 35 | is(scalar <$sock>, "NOT_STORED\r\n", "didn't replace notexist"); 36 | 37 | # delete foo. 38 | print $sock "delete foo\r\n"; 39 | is(scalar <$sock>, "DELETED\r\n", "deleted foo"); 40 | 41 | # delete foo again. not found this time. 42 | print $sock "delete foo\r\n"; 43 | is(scalar <$sock>, "NOT_FOUND\r\n", "deleted foo, but not found"); 44 | 45 | # add moo 46 | # 47 | print $sock "add moo 0 0 6\r\nmooval\r\n"; 48 | is(scalar <$sock>, "STORED\r\n", "stored barval"); 49 | mem_get_is($sock, "moo", "mooval"); 50 | 51 | # check-and-set (cas) failure case, try to set value with incorrect cas unique val 52 | print $sock "cas moo 0 0 6 0\r\nMOOVAL\r\n"; 53 | is(scalar <$sock>, "EXISTS\r\n", "check and set with invalid id"); 54 | 55 | # test "gets", grab unique ID 56 | print $sock "gets moo\r\n"; 57 | # VALUE moo 0 6 3084947704 58 | # 59 | my @retvals = split(/ /, scalar <$sock>); 60 | my $data = scalar <$sock>; # grab data 61 | my $dot = scalar <$sock>; # grab dot on line by itself 62 | is($retvals[0], "VALUE", "get value using 'gets'"); 63 | my $unique_id = $retvals[4]; 64 | # clean off \r\n 65 | $unique_id =~ s/\r\n$//; 66 | ok($unique_id =~ /^\d+$/, "unique ID '$unique_id' is an integer"); 67 | # now test that we can store moo with the correct unique id 68 | print $sock "cas moo 0 0 6 $unique_id\r\nMOOVAL\r\n"; 69 | is(scalar <$sock>, "STORED\r\n"); 70 | mem_get_is($sock, "moo", "MOOVAL"); 71 | 72 | # pipeling is okay 73 | print $sock "set foo 0 0 6\r\nfooval\r\ndelete foo\r\nset foo 0 0 6\r\nfooval\r\ndelete foo\r\n"; 74 | is(scalar <$sock>, "STORED\r\n", "pipeline set"); 75 | is(scalar <$sock>, "DELETED\r\n", "pipeline delete"); 76 | is(scalar <$sock>, "STORED\r\n", "pipeline set"); 77 | is(scalar <$sock>, "DELETED\r\n", "pipeline delete"); 78 | 79 | 80 | # Test sets up to a large size around 1MB. 81 | # Everything up to 1MB - 1k should succeed, everything 1MB +1k should fail. 82 | 83 | my $len = 1024; 84 | while ($len < 1024*1028) { 85 | my $val = "B"x$len; 86 | if ($len > (1024*1024)) { 87 | # Ensure causing a memory overflow doesn't leave stale data. 88 | print $sock "set foo_$len 0 0 3\r\nMOO\r\n"; 89 | is(scalar <$sock>, "STORED\r\n"); 90 | print $sock "set foo_$len 0 0 $len\r\n$val\r\n"; 91 | is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "failed to store size $len"); 92 | mem_get_is($sock, "foo_$len"); 93 | } else { 94 | print $sock "set foo_$len 0 0 $len\r\n$val\r\n"; 95 | is(scalar <$sock>, "STORED\r\n", "stored size $len"); 96 | } 97 | $len += 2048; 98 | } 99 | 100 | -------------------------------------------------------------------------------- /t/incrdecr.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 23; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | # Bug 21 13 | print $sock "set bug21 0 0 19\r\n9223372036854775807\r\n"; 14 | is(scalar <$sock>, "STORED\r\n", "stored text"); 15 | print $sock "incr bug21 1\r\n"; 16 | is(scalar <$sock>, "9223372036854775808\r\n", "bug21 incr 1"); 17 | print $sock "incr bug21 1\r\n"; 18 | is(scalar <$sock>, "9223372036854775809\r\n", "bug21 incr 2"); 19 | print $sock "decr bug21 1\r\n"; 20 | is(scalar <$sock>, "9223372036854775808\r\n", "bug21 decr"); 21 | 22 | print $sock "set num 0 0 1\r\n1\r\n"; 23 | is(scalar <$sock>, "STORED\r\n", "stored num"); 24 | mem_get_is($sock, "num", 1, "stored 1"); 25 | 26 | print $sock "incr num 1\r\n"; 27 | is(scalar <$sock>, "2\r\n", "+ 1 = 2"); 28 | mem_get_is($sock, "num", 2); 29 | 30 | print $sock "incr num 8\r\n"; 31 | is(scalar <$sock>, "10\r\n", "+ 8 = 10"); 32 | mem_get_is($sock, "num", 10); 33 | 34 | print $sock "decr num 1\r\n"; 35 | is(scalar <$sock>, "9\r\n", "- 1 = 9"); 36 | 37 | print $sock "decr num 9\r\n"; 38 | is(scalar <$sock>, "0\r\n", "- 9 = 0"); 39 | 40 | print $sock "decr num 5\r\n"; 41 | is(scalar <$sock>, "0\r\n", "- 5 = 0"); 42 | 43 | printf $sock "set num 0 0 10\r\n4294967296\r\n"; 44 | is(scalar <$sock>, "STORED\r\n", "stored 2**32"); 45 | 46 | print $sock "incr num 1\r\n"; 47 | is(scalar <$sock>, "4294967297\r\n", "4294967296 + 1 = 4294967297"); 48 | 49 | printf $sock "set num 0 0 %d\r\n18446744073709551615\r\n", length("18446744073709551615"); 50 | is(scalar <$sock>, "STORED\r\n", "stored 2**64-1"); 51 | 52 | print $sock "incr num 1\r\n"; 53 | is(scalar <$sock>, "0\r\n", "(2**64 - 1) + 1 = 0"); 54 | 55 | print $sock "decr bogus 5\r\n"; 56 | is(scalar <$sock>, "NOT_FOUND\r\n", "can't decr bogus key"); 57 | 58 | print $sock "decr incr 5\r\n"; 59 | is(scalar <$sock>, "NOT_FOUND\r\n", "can't incr bogus key"); 60 | 61 | print $sock "set bigincr 0 0 1\r\n0\r\n"; 62 | is(scalar <$sock>, "STORED\r\n", "stored bigincr"); 63 | print $sock "incr bigincr 18446744073709551610\r\n"; 64 | is(scalar <$sock>, "18446744073709551610\r\n"); 65 | 66 | print $sock "set text 0 0 2\r\nhi\r\n"; 67 | is(scalar <$sock>, "STORED\r\n", "stored hi"); 68 | print $sock "incr text 1\r\n"; 69 | is(scalar <$sock>, 70 | "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 71 | "hi - 1 = 0"); 72 | -------------------------------------------------------------------------------- /t/issue_104.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 6; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | # first get should miss 13 | print $sock "get foo\r\n"; 14 | is(scalar <$sock>, "END\r\n", "get foo"); 15 | 16 | # Now set and get (should hit) 17 | print $sock "set foo 0 0 6\r\nfooval\r\n"; 18 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 19 | mem_get_is($sock, "foo", "fooval"); 20 | 21 | my $stats = mem_stats($sock); 22 | is($stats->{cmd_get}, 2, "Should have 2 get requests"); 23 | is($stats->{get_hits}, 1, "Should have 1 hit"); 24 | is($stats->{get_misses}, 1, "Should have 1 miss"); 25 | -------------------------------------------------------------------------------- /t/issue_108.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 4; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $key = "del_key"; 12 | 13 | print $sock "add $key 0 0 1\r\nx\r\n"; 14 | is (scalar <$sock>, "STORED\r\n", "Added a key"); 15 | 16 | print $sock "delete $key 0\r\n"; 17 | is (scalar <$sock>, "DELETED\r\n", "Properly deleted with 0"); 18 | 19 | print $sock "add $key 0 0 1\r\nx\r\n"; 20 | is (scalar <$sock>, "STORED\r\n", "Added again a key"); 21 | 22 | print $sock "delete $key 0 noreply\r\n"; 23 | # will not reply, but a subsequent add will succeed 24 | 25 | print $sock "add $key 0 0 1\r\nx\r\n"; 26 | is (scalar <$sock>, "STORED\r\n", "Add succeeded after quiet deletion."); 27 | 28 | -------------------------------------------------------------------------------- /t/issue_14.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 21; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $value = "B"x66560; 12 | my $key = 0; 13 | 14 | for ($key = 0; $key < 10; $key++) { 15 | print $sock "set key$key 0 2 66560\r\n$value\r\n"; 16 | is (scalar <$sock>, "STORED\r\n", "stored key$key"); 17 | } 18 | 19 | #print $sock "stats slabs" 20 | my $first_stats = mem_stats($sock, "slabs"); 21 | my $first_malloc = $first_stats->{total_malloced}; 22 | 23 | sleep(4); 24 | 25 | for ($key = 10; $key < 20; $key++) { 26 | print $sock "set key$key 0 2 66560\r\n$value\r\n"; 27 | is (scalar <$sock>, "STORED\r\n", "stored key$key"); 28 | } 29 | 30 | my $second_stats = mem_stats($sock, "slabs"); 31 | my $second_malloc = $second_stats->{total_malloced}; 32 | 33 | 34 | is ($second_malloc, $first_malloc, "Memory grows..") 35 | -------------------------------------------------------------------------------- /t/issue_140.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | plan skip_all => 'Fix for Issue 140 was only an illusion'; 10 | 11 | plan tests => 7; 12 | 13 | my $server = new_memcached(); 14 | my $sock = $server->sock; 15 | 16 | print $sock "set a 0 0 1\r\na\r\n"; 17 | is (scalar <$sock>, "STORED\r\n", "stored key"); 18 | 19 | my $stats = mem_stats($sock, "items"); 20 | my $age = $stats->{"items:1:age"}; 21 | isnt ($age, "0", "Age should not be zero"); 22 | 23 | print $sock "flush_all\r\n"; 24 | is (scalar <$sock>, "OK\r\n", "items flushed"); 25 | 26 | my $stats = mem_stats($sock, "items"); 27 | my $age = $stats->{"items:1:age"}; 28 | is ($age, undef, "all should be gone"); 29 | 30 | print $sock "set a 0 1 1\r\na\r\n"; 31 | is (scalar <$sock>, "STORED\r\n", "stored key"); 32 | 33 | my $stats = mem_stats($sock, "items"); 34 | my $age = $stats->{"items:1:age"}; 35 | isnt ($age, "0", "Age should not be zero"); 36 | 37 | sleep(3); 38 | 39 | my $stats = mem_stats($sock, "items"); 40 | my $age = $stats->{"items:1:age"}; 41 | is ($age, undef, "all should be gone"); 42 | -------------------------------------------------------------------------------- /t/issue_152.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 2; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $key = "a"x251; 12 | 13 | print $sock "set a 1 0 1\r\na\r\n"; 14 | is (scalar <$sock>, "STORED\r\n", "Stored key"); 15 | 16 | print $sock "get a $key\r\n"; 17 | is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n", "illegal key"); 18 | -------------------------------------------------------------------------------- /t/issue_163.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 7; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $value1 = "A"x66560; 12 | my $value2 = "B"x66570; 13 | 14 | print $sock "set key 0 1 66560\r\n$value1\r\n"; 15 | is (scalar <$sock>, "STORED\r\n", "stored key"); 16 | 17 | my $stats = mem_stats($sock, "slabs"); 18 | my $requested = $stats->{"31:mem_requested"}; 19 | isnt ($requested, "0", "We should have requested some memory"); 20 | 21 | sleep(3); 22 | print $sock "set key 0 0 66570\r\n$value2\r\n"; 23 | is (scalar <$sock>, "STORED\r\n", "stored key"); 24 | 25 | my $stats = mem_stats($sock, "items"); 26 | my $reclaimed = $stats->{"items:31:reclaimed"}; 27 | is ($reclaimed, "1", "Objects should be reclaimed"); 28 | 29 | print $sock "delete key\r\n"; 30 | is (scalar <$sock>, "DELETED\r\n", "deleted key"); 31 | 32 | print $sock "set key 0 0 66560\r\n$value1\r\n"; 33 | is (scalar <$sock>, "STORED\r\n", "stored key"); 34 | 35 | my $stats = mem_stats($sock, "slabs"); 36 | my $requested2 = $stats->{"31:mem_requested"}; 37 | is ($requested2, $requested, "we've not allocated and freed the same amont"); 38 | -------------------------------------------------------------------------------- /t/issue_183.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 5; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | print $sock "set key 0 0 1\r\n1\r\n"; 12 | is (scalar <$sock>, "STORED\r\n", "stored key"); 13 | my $s1 = mem_stats($sock); 14 | my $r1 = $s1->{"reclaimed"}; 15 | is ($r1, "0", "Objects should not be reclaimed"); 16 | sleep(2); 17 | print $sock "flush_all\r\n"; 18 | is (scalar <$sock>, "OK\r\n", "Cache flushed"); 19 | print $sock "set key 0 0 1\r\n1\r\n"; 20 | is (scalar <$sock>, "STORED\r\n", "stored key"); 21 | my $s2 = mem_stats($sock); 22 | my $r2 = $s2->{"reclaimed"}; 23 | is ($r2, "1", "Objects should be reclaimed"); 24 | -------------------------------------------------------------------------------- /t/issue_22.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 84; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached("-m 3"); 10 | my $sock = $server->sock; 11 | my $value = "B"x66560; 12 | my $key = 0; 13 | 14 | for ($key = 0; $key < 40; $key++) { 15 | print $sock "set key$key 0 0 66560\r\n$value\r\n"; 16 | is (scalar <$sock>, "STORED\r\n", "stored key$key"); 17 | } 18 | 19 | my $first_stats = mem_stats($sock, "items"); 20 | my $first_evicted = $first_stats->{"items:31:evicted"}; 21 | # I get 1 eviction on a 32 bit binary, but 4 on a 64 binary.. 22 | # Just check that I have evictions... 23 | isnt ($first_evicted, "0", "check evicted"); 24 | 25 | print $sock "stats reset\r\n"; 26 | is (scalar <$sock>, "RESET\r\n", "Stats reset"); 27 | 28 | my $second_stats = mem_stats($sock, "items"); 29 | my $second_evicted = $second_stats->{"items:31:evicted"}; 30 | is ($second_evicted, "0", "check evicted"); 31 | 32 | for ($key = 40; $key < 80; $key++) { 33 | print $sock "set key$key 0 0 66560\r\n$value\r\n"; 34 | is (scalar <$sock>, "STORED\r\n", "stored key$key"); 35 | } 36 | 37 | my $last_stats = mem_stats($sock, "items"); 38 | my $last_evicted = $last_stats->{"items:31:evicted"}; 39 | is ($last_evicted, "40", "check evicted"); 40 | -------------------------------------------------------------------------------- /t/issue_29.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 4; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | print $sock "set issue29 0 0 0\r\n\r\n"; 13 | is (scalar <$sock>, "STORED\r\n", "stored issue29"); 14 | 15 | my $first_stats = mem_stats($sock, "slabs"); 16 | my $first_used = $first_stats->{"1:used_chunks"}; 17 | 18 | is(1, $first_used, "Used one"); 19 | 20 | print $sock "set issue29_b 0 0 0\r\n\r\n"; 21 | is (scalar <$sock>, "STORED\r\n", "stored issue29_b"); 22 | 23 | my $second_stats = mem_stats($sock, "slabs"); 24 | my $second_used = $second_stats->{"1:used_chunks"}; 25 | 26 | is(2, $second_used, "Used two") 27 | -------------------------------------------------------------------------------- /t/issue_3.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 8; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $key = "del_key"; 12 | 13 | print $sock "delete $key\r\n"; 14 | is (scalar <$sock>, "NOT_FOUND\r\n", "not found on delete"); 15 | 16 | print $sock "delete $key 10\r\n"; 17 | is (scalar <$sock>, "CLIENT_ERROR bad command line format." 18 | . " Usage: delete [noreply]\r\n", "invalid delete"); 19 | 20 | print $sock "add $key 0 0 1\r\nx\r\n"; 21 | is (scalar <$sock>, "STORED\r\n", "Add before a broken delete."); 22 | 23 | print $sock "delete $key 10 noreply\r\n"; 24 | # Does not reply 25 | # is (scalar <$sock>, "ERROR\r\n", "Even more invalid delete"); 26 | 27 | print $sock "add $key 0 0 1\r\nx\r\n"; 28 | is (scalar <$sock>, "NOT_STORED\r\n", "Failed to add after failed silent delete."); 29 | 30 | print $sock "delete $key noreply\r\n"; 31 | # Will not reply, so let's do a set and check that. 32 | 33 | print $sock "set $key 0 0 1\r\nx\r\n"; 34 | is (scalar <$sock>, "STORED\r\n", "Stored a key"); 35 | 36 | print $sock "delete $key\r\n"; 37 | is (scalar <$sock>, "DELETED\r\n", "Properly deleted"); 38 | 39 | print $sock "set $key 0 0 1\r\nx\r\n"; 40 | is (scalar <$sock>, "STORED\r\n", "Stored a key"); 41 | 42 | print $sock "delete $key noreply\r\n"; 43 | # will not reply, but a subsequent add will succeed 44 | 45 | print $sock "add $key 0 0 1\r\nx\r\n"; 46 | is (scalar <$sock>, "STORED\r\n", "Add succeeded after deletion."); 47 | 48 | -------------------------------------------------------------------------------- /t/issue_41.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use POSIX qw(ceil); 6 | use Test::More tests => 691; 7 | use FindBin qw($Bin); 8 | use lib "$Bin/lib"; 9 | use MemcachedTest; 10 | 11 | my $server = new_memcached(); 12 | my $sock = $server->sock; 13 | 14 | my $factor = 2; 15 | my $val = "x" x $factor; 16 | my $key = ''; 17 | 18 | # SET items of diverse size to the daemon so it can attempt 19 | # to return a large stats output for slabs 20 | for (my $i=0; $i<69; $i++) { 21 | for (my $j=0; $j<10; $j++) { 22 | $key = "$i:$j"; 23 | print $sock "set key$key 0 0 $factor\r\n$val\r\n"; 24 | is (scalar <$sock>, "STORED\r\n", "stored key$key"); 25 | } 26 | $factor *= 1.2; 27 | $factor = ceil($factor); 28 | $val = "x" x $factor; 29 | } 30 | 31 | # This request will kill the daemon if it has not allocated 32 | # enough memory internally. 33 | my $stats = mem_stats($sock, "slabs"); 34 | 35 | # Verify whether the daemon is still running or not by asking 36 | # it for statistics. 37 | print $sock "version\r\n"; 38 | my $v = scalar <$sock>; 39 | ok(defined $v && length($v), "memcached didn't respond"); 40 | -------------------------------------------------------------------------------- /t/issue_42.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 11; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $value = "B"x10; 12 | my $key = 0; 13 | 14 | for ($key = 0; $key < 10; $key++) { 15 | print $sock "set key$key 0 0 10\r\n$value\r\n"; 16 | is (scalar <$sock>, "STORED\r\n", "stored key$key"); 17 | } 18 | 19 | my $first_stats = mem_stats($sock, "slabs"); 20 | my $req = $first_stats->{"1:mem_requested"}; 21 | ok ($req == "640" || $req == "800", "Check allocated size"); 22 | -------------------------------------------------------------------------------- /t/issue_50.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 1; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached('-B binary'); 10 | my $sock = $server->sock; 11 | 12 | $SIG{ALRM} = sub { die "alarm\n" }; 13 | alarm(2); 14 | print $sock "Here's a bunch of garbage that doesn't look like the bin prot."; 15 | my $rv = <$sock>; 16 | ok(1, "Either the above worked and quit, or hung forever."); 17 | -------------------------------------------------------------------------------- /t/issue_61.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 7; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached("-R 1"); 10 | my $sock = $server->sock; 11 | 12 | print $sock "set foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\n"; 13 | is (scalar <$sock>, "STORED\r\n", "stored foobar"); 14 | is (scalar <$sock>, "STORED\r\n", "stored foobar"); 15 | is (scalar <$sock>, "STORED\r\n", "stored foobar"); 16 | is (scalar <$sock>, "STORED\r\n", "stored foobar"); 17 | is (scalar <$sock>, "STORED\r\n", "stored foobar"); 18 | is (scalar <$sock>, "STORED\r\n", "stored foobar"); 19 | my $stats = mem_stats($sock); 20 | is ($stats->{"conn_yields"}, "5", "Got a decent number of yields"); 21 | -------------------------------------------------------------------------------- /t/issue_67.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 22; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | use Carp qw(croak); 9 | 10 | use Cwd; 11 | my $builddir = getcwd; 12 | 13 | $ENV{'MEMCACHED_PORT_FILENAME'} = "/tmp/ports.$$"; 14 | 15 | sub read_ports { 16 | my %rv = (); 17 | open(my $f, "/tmp/ports.$$") || die("Can't open ports file."); 18 | while(<$f>) { 19 | my ($type, $port) = split(/:\s+/); 20 | $rv{$type} = $port + 0; 21 | } 22 | unlink "/tmp/ports.$$"; 23 | return %rv; 24 | } 25 | 26 | sub validate_port { 27 | my ($name, $got, $expected) = @_; 28 | # diag "Wanted $expected, got $got"; 29 | if ($expected == -1) { 30 | ok(!defined($got), "$name expected no port, got $got"); 31 | } elsif ($expected == 0) { 32 | ok($got != 11211, "$name expected random port (got $got)"); 33 | } else { 34 | is($got, $expected, "$name"); 35 | } 36 | } 37 | 38 | sub run_server { 39 | my ($args) = @_; 40 | 41 | my $exe = "$builddir/memcached-debug"; 42 | croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe; 43 | 44 | my $childpid = fork(); 45 | 46 | my $root = ''; 47 | $root = "-u root" if ($< == 0); 48 | my $cmd = "$builddir/timedrun 10 $exe $root $args"; 49 | 50 | unless($childpid) { 51 | exec $cmd; 52 | exit; # NOTREACHED 53 | } 54 | 55 | for (1..20) { 56 | if (-f "/tmp/ports.$$") { 57 | return Memcached::Handle->new(pid => $childpid); 58 | } 59 | select undef, undef, undef, 0.10; 60 | } 61 | croak "Failed to start server."; 62 | } 63 | 64 | sub when { 65 | my ($name, $params, $expected_tcp, $expected_udp) = @_; 66 | 67 | my $server = run_server($params); 68 | my %ports = read_ports(); 69 | 70 | validate_port($name, $ports{'TCP INET'}, $expected_tcp); 71 | validate_port($name, $ports{'UDP INET'}, $expected_udp); 72 | } 73 | 74 | # Disabling the defaults since it conflicts with a running instance. 75 | # when('no arguments', '', 11211, 11211); 76 | when('specifying tcp port', '-p 11212', 11212, 11212); 77 | when('specifying udp port', '-U 11222', 11222, 11222); 78 | when('specifying tcp ephemeral port', '-p -1', 0, 0); 79 | when('specifying udp ephemeral port', '-U -1', 0, 0); 80 | when('tcp port disabled', '-p 0', -1, -1); 81 | when('udp port disabled', '-U 0', -1, -1); 82 | when('specifying tcp and udp ports', '-p 11232 -U 11233', 11232, 11233); 83 | when('specifying tcp and disabling udp', '-p 11242 -U 0', 11242, -1); 84 | when('specifying udp and disabling tcp', '-p -1 -U 11252', 0, 11252); 85 | when('specifying tcp and ephemeral udp', '-p 11262 -U -1', 11262, 0); 86 | when('specifying udp and ephemeral tcp', '-p -1 -U 11272', 0, 11272); 87 | -------------------------------------------------------------------------------- /t/issue_68.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 996; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | for (my $keyi = 1; $keyi < 250; $keyi++) { 13 | my $key = "x" x $keyi; 14 | print $sock "set $key 0 0 1\r\n9\r\n"; 15 | is (scalar <$sock>, "STORED\r\n", "stored $key"); 16 | mem_get_is($sock, $key, "9"); 17 | print $sock "incr $key 1\r\n"; 18 | is (scalar <$sock>, "10\r\n", "incr $key to 10"); 19 | mem_get_is($sock, $key, "10"); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /t/issue_70.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 4; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | print $sock "set issue70 0 0 0\r\n\r\n"; 13 | is (scalar <$sock>, "STORED\r\n", "stored issue70"); 14 | 15 | print $sock "set issue70 0 0 -1\r\n"; 16 | is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); 17 | 18 | print $sock "set issue70 0 0 4294967295\r\n"; 19 | is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); 20 | 21 | print $sock "set issue70 0 0 2147483647\r\nscoobyscoobydoo"; 22 | is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); 23 | -------------------------------------------------------------------------------- /t/item_size_max.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 7; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | my $stats = mem_stats($sock, ' settings'); 13 | 14 | # Ensure default still works. 15 | is($stats->{item_size_max}, 1024 * 1024); 16 | $server->stop(); 17 | 18 | # Should die. 19 | eval { 20 | $server = new_memcached('-I 1000'); 21 | }; 22 | ok($@ && $@ =~ m/^Failed/, "Shouldn't start with < 1k item max"); 23 | 24 | eval { 25 | $server = new_memcached('-I 256m'); 26 | }; 27 | ok($@ && $@ =~ m/^Failed/, "Shouldn't start with > 128m item max"); 28 | 29 | # Minimum. 30 | $server = new_memcached('-I 1024'); 31 | my $stats = mem_stats($server->sock, ' settings'); 32 | is($stats->{item_size_max}, 1024); 33 | $server->stop(); 34 | 35 | # Reasonable but unreasonable. 36 | $server = new_memcached('-I 1049600'); 37 | my $stats = mem_stats($server->sock, ' settings'); 38 | is($stats->{item_size_max}, 1049600); 39 | $server->stop(); 40 | 41 | # Suffix kilobytes. 42 | $server = new_memcached('-I 512k'); 43 | my $stats = mem_stats($server->sock, ' settings'); 44 | is($stats->{item_size_max}, 524288); 45 | $server->stop(); 46 | 47 | # Suffix megabytes. 48 | $server = new_memcached('-I 32m'); 49 | my $stats = mem_stats($server->sock, ' settings'); 50 | is($stats->{item_size_max}, 33554432); 51 | $server->stop(); 52 | 53 | -------------------------------------------------------------------------------- /t/lib/MemcachedTest.pm: -------------------------------------------------------------------------------- 1 | package MemcachedTest; 2 | use strict; 3 | use IO::Socket::INET; 4 | use IO::Socket::UNIX; 5 | use Exporter 'import'; 6 | use Carp qw(croak); 7 | use vars qw(@EXPORT); 8 | 9 | # Instead of doing the substitution with Autoconf, we assume that 10 | # cwd == builddir. 11 | use Cwd; 12 | my $builddir = getcwd; 13 | 14 | 15 | @EXPORT = qw(new_memcached sleep mem_get_is mem_gets mem_gets_is mem_stats 16 | supports_sasl free_port); 17 | 18 | sub sleep { 19 | my $n = shift; 20 | select undef, undef, undef, $n; 21 | } 22 | 23 | sub mem_stats { 24 | my ($sock, $type) = @_; 25 | $type = $type ? " $type" : ""; 26 | print $sock "stats$type\r\n"; 27 | my $stats = {}; 28 | while (<$sock>) { 29 | last if /^(\.|END)/; 30 | /^(STAT|ITEM) (\S+)\s+([^\r\n]+)/; 31 | #print " slabs: $_"; 32 | $stats->{$2} = $3; 33 | } 34 | return $stats; 35 | } 36 | 37 | sub mem_get_is { 38 | # works on single-line values only. no newlines in value. 39 | my ($sock_opts, $key, $val, $msg) = @_; 40 | my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {}; 41 | my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts; 42 | 43 | my $expect_flags = $opts->{flags} || 0; 44 | my $dval = defined $val ? "'$val'" : ""; 45 | $msg ||= "$key == $dval"; 46 | 47 | print $sock "get $key\r\n"; 48 | if (! defined $val) { 49 | my $line = scalar <$sock>; 50 | if ($line =~ /^VALUE/) { 51 | $line .= scalar(<$sock>) . scalar(<$sock>); 52 | } 53 | Test::More::is($line, "END\r\n", $msg); 54 | } else { 55 | my $len = length($val); 56 | my $body = scalar(<$sock>); 57 | my $expected = "VALUE $key $expect_flags $len\r\n$val\r\nEND\r\n"; 58 | if (!$body || $body =~ /^END/) { 59 | Test::More::is($body, $expected, $msg); 60 | return; 61 | } 62 | $body .= scalar(<$sock>) . scalar(<$sock>); 63 | Test::More::is($body, $expected, $msg); 64 | } 65 | } 66 | 67 | sub mem_gets { 68 | # works on single-line values only. no newlines in value. 69 | my ($sock_opts, $key) = @_; 70 | my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {}; 71 | my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts; 72 | my $val; 73 | my $expect_flags = $opts->{flags} || 0; 74 | 75 | print $sock "gets $key\r\n"; 76 | my $response = <$sock>; 77 | if ($response =~ /^END/) { 78 | return "NOT_FOUND"; 79 | } 80 | else 81 | { 82 | $response =~ /VALUE (.*) (\d+) (\d+) (\d+)/; 83 | my $flags = $2; 84 | my $len = $3; 85 | my $identifier = $4; 86 | read $sock, $val , $len; 87 | # get the END 88 | $_ = <$sock>; 89 | $_ = <$sock>; 90 | 91 | return ($identifier,$val); 92 | } 93 | 94 | } 95 | sub mem_gets_is { 96 | # works on single-line values only. no newlines in value. 97 | my ($sock_opts, $identifier, $key, $val, $msg) = @_; 98 | my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {}; 99 | my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts; 100 | 101 | my $expect_flags = $opts->{flags} || 0; 102 | my $dval = defined $val ? "'$val'" : ""; 103 | $msg ||= "$key == $dval"; 104 | 105 | print $sock "gets $key\r\n"; 106 | if (! defined $val) { 107 | my $line = scalar <$sock>; 108 | if ($line =~ /^VALUE/) { 109 | $line .= scalar(<$sock>) . scalar(<$sock>); 110 | } 111 | Test::More::is($line, "END\r\n", $msg); 112 | } else { 113 | my $len = length($val); 114 | my $body = scalar(<$sock>); 115 | my $expected = "VALUE $key $expect_flags $len $identifier\r\n$val\r\nEND\r\n"; 116 | if (!$body || $body =~ /^END/) { 117 | Test::More::is($body, $expected, $msg); 118 | return; 119 | } 120 | $body .= scalar(<$sock>) . scalar(<$sock>); 121 | Test::More::is($body, $expected, $msg); 122 | } 123 | } 124 | 125 | sub free_port { 126 | my $type = shift || "tcp"; 127 | my $sock; 128 | my $port; 129 | while (!$sock) { 130 | $port = int(rand(20000)) + 30000; 131 | $sock = IO::Socket::INET->new(LocalAddr => '127.0.0.1', 132 | LocalPort => $port, 133 | Proto => $type, 134 | ReuseAddr => 1); 135 | } 136 | return $port; 137 | } 138 | 139 | sub supports_udp { 140 | my $output = `$builddir/memcached-debug -h`; 141 | return 0 if $output =~ /^memcached 1\.1\./; 142 | return 1; 143 | } 144 | 145 | sub supports_sasl { 146 | my $output = `$builddir/memcached-debug -h`; 147 | return 1 if $output =~ /sasl/i; 148 | return 0; 149 | } 150 | 151 | sub new_memcached { 152 | my ($args, $passed_port) = @_; 153 | my $port = $passed_port || free_port(); 154 | my $host = '127.0.0.1'; 155 | 156 | if ($ENV{T_MEMD_USE_DAEMON}) { 157 | my ($host, $port) = ($ENV{T_MEMD_USE_DAEMON} =~ m/^([^:]+):(\d+)$/); 158 | my $conn = IO::Socket::INET->new(PeerAddr => "$host:$port"); 159 | if ($conn) { 160 | return Memcached::Handle->new(conn => $conn, 161 | host => $host, 162 | port => $port); 163 | } 164 | croak("Failed to connect to specified memcached server.") unless $conn; 165 | } 166 | 167 | my $udpport = free_port("udp"); 168 | $args .= " -p $port"; 169 | if (supports_udp()) { 170 | $args .= " -U $udpport"; 171 | } 172 | if ($< == 0) { 173 | $args .= " -u root"; 174 | } 175 | 176 | my $childpid = fork(); 177 | 178 | my $exe = "$builddir/memcached-debug"; 179 | croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe; 180 | croak("memcached binary not executable\n") unless -x _; 181 | 182 | unless ($childpid) { 183 | exec "$builddir/timedrun 600 $exe $args"; 184 | exit; # never gets here. 185 | } 186 | 187 | # unix domain sockets 188 | if ($args =~ /-s (\S+)/) { 189 | sleep 1; 190 | my $filename = $1; 191 | my $conn = IO::Socket::UNIX->new(Peer => $filename) || 192 | croak("Failed to connect to unix domain socket: $! '$filename'"); 193 | 194 | return Memcached::Handle->new(pid => $childpid, 195 | conn => $conn, 196 | domainsocket => $filename, 197 | host => $host, 198 | port => $port); 199 | } 200 | 201 | # try to connect / find open port, only if we're not using unix domain 202 | # sockets 203 | 204 | for (1..20) { 205 | my $conn = IO::Socket::INET->new(PeerAddr => "127.0.0.1:$port"); 206 | if ($conn) { 207 | return Memcached::Handle->new(pid => $childpid, 208 | conn => $conn, 209 | udpport => $udpport, 210 | host => $host, 211 | port => $port); 212 | } 213 | select undef, undef, undef, 0.10; 214 | } 215 | croak("Failed to startup/connect to memcached server."); 216 | } 217 | 218 | ############################################################################ 219 | package Memcached::Handle; 220 | sub new { 221 | my ($class, %params) = @_; 222 | return bless \%params, $class; 223 | } 224 | 225 | sub DESTROY { 226 | my $self = shift; 227 | kill 2, $self->{pid}; 228 | } 229 | 230 | sub stop { 231 | my $self = shift; 232 | kill 15, $self->{pid}; 233 | } 234 | 235 | sub host { $_[0]{host} } 236 | sub port { $_[0]{port} } 237 | sub udpport { $_[0]{udpport} } 238 | 239 | sub sock { 240 | my $self = shift; 241 | 242 | if ($self->{conn} && ($self->{domainsocket} || getpeername($self->{conn}))) { 243 | return $self->{conn}; 244 | } 245 | return $self->new_sock; 246 | } 247 | 248 | sub new_sock { 249 | my $self = shift; 250 | if ($self->{domainsocket}) { 251 | return IO::Socket::UNIX->new(Peer => $self->{domainsocket}); 252 | } else { 253 | return IO::Socket::INET->new(PeerAddr => "$self->{host}:$self->{port}"); 254 | } 255 | } 256 | 257 | sub new_udp_sock { 258 | my $self = shift; 259 | return IO::Socket::INET->new(PeerAddr => '127.0.0.1', 260 | PeerPort => $self->{udpport}, 261 | Proto => 'udp', 262 | LocalAddr => '127.0.0.1', 263 | LocalPort => MemcachedTest::free_port('udp'), 264 | ); 265 | 266 | } 267 | 268 | 1; 269 | -------------------------------------------------------------------------------- /t/line-lengths.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use FindBin qw($Bin); 4 | our @files; 5 | 6 | BEGIN { 7 | chdir "$Bin/.." or die; 8 | @files = ( "doc/protocol.txt" ); 9 | } 10 | 11 | use Test::More tests => scalar(@files); 12 | 13 | foreach my $f (@files) { 14 | open(my $fh, $f) or die("Can't open $f"); 15 | my @long_lines = (); 16 | my $line_number = 0; 17 | while(<$fh>) { 18 | $line_number++; 19 | if(length($_) > 80) { 20 | push(@long_lines, $line_number); 21 | } 22 | } 23 | close($fh); 24 | ok(@long_lines == 0, "$f has a long lines: @long_lines"); 25 | } 26 | -------------------------------------------------------------------------------- /t/lru.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 149; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | # assuming max slab is 1M and default mem is 64M 10 | my $server = new_memcached(); 11 | my $sock = $server->sock; 12 | 13 | # create a big value for the largest slab 14 | my $max = 1024 * 1024; 15 | my $big = 'x' x (1024 * 1024 - 250); 16 | 17 | ok(length($big) > 512 * 1024); 18 | ok(length($big) < 1024 * 1024); 19 | 20 | # test that an even bigger value is rejected while we're here 21 | my $too_big = $big . $big . $big; 22 | my $len = length($too_big); 23 | print $sock "set too_big 0 0 $len\r\n$too_big\r\n"; 24 | is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "too_big not stored"); 25 | 26 | # set the big value 27 | my $len = length($big); 28 | print $sock "set big 0 0 $len\r\n$big\r\n"; 29 | is(scalar <$sock>, "STORED\r\n", "stored big"); 30 | mem_get_is($sock, "big", $big); 31 | 32 | # no evictions yet 33 | my $stats = mem_stats($sock); 34 | is($stats->{"evictions"}, "0", "no evictions to start"); 35 | 36 | # set many big items, enough to get evictions 37 | for (my $i = 0; $i < 100; $i++) { 38 | print $sock "set item_$i 0 0 $len\r\n$big\r\n"; 39 | is(scalar <$sock>, "STORED\r\n", "stored item_$i"); 40 | } 41 | 42 | # some evictions should have happened 43 | my $stats = mem_stats($sock); 44 | my $evictions = int($stats->{"evictions"}); 45 | ok($evictions == 37, "some evictions happened"); 46 | 47 | # the first big value should be gone 48 | mem_get_is($sock, "big", undef); 49 | 50 | # the earliest items should be gone too 51 | for (my $i = 0; $i < $evictions - 1; $i++) { 52 | mem_get_is($sock, "item_$i", undef); 53 | } 54 | 55 | # check that the non-evicted are the right ones 56 | for (my $i = $evictions - 1; $i < $evictions + 4; $i++) { 57 | mem_get_is($sock, "item_$i", $big); 58 | } 59 | -------------------------------------------------------------------------------- /t/maxconns.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # NOTE: This test never worked. Memcached would ignore maxconns requests lower 3 | # than the current ulimit. Test needs to be updated. 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use Test::More tests => 11; 9 | 10 | use FindBin qw($Bin); 11 | use lib "$Bin/lib"; 12 | use MemcachedTest; 13 | 14 | 15 | # start up a server with 10 maximum connections 16 | my $server = new_memcached('-c 100'); 17 | my $sock = $server->sock; 18 | my @sockets; 19 | 20 | ok(defined($sock), 'Connection 0'); 21 | push (@sockets, $sock); 22 | 23 | 24 | foreach my $conn (1..10) { 25 | $sock = $server->new_sock; 26 | ok(defined($sock), "Made connection $conn"); 27 | push(@sockets, $sock); 28 | } 29 | -------------------------------------------------------------------------------- /t/multiversioning.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 13; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $sock2 = $server->new_sock; 12 | 13 | ok($sock != $sock2, "have two different connections open"); 14 | 15 | # set large value 16 | my $size = 256 * 1024; # 256 kB 17 | my $bigval = "0123456789abcdef" x ($size / 16); 18 | $bigval =~ s/^0/\[/; $bigval =~ s/f$/\]/; 19 | my $bigval2 = uc($bigval); 20 | 21 | print $sock "set big 0 0 $size\r\n$bigval\r\n"; 22 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 23 | mem_get_is($sock, "big", $bigval, "big value got correctly"); 24 | 25 | print $sock "get big\r\n"; 26 | my $buf; 27 | is(read($sock, $buf, $size / 2), $size / 2, "read half the answer back"); 28 | like($buf, qr/VALUE big/, "buf has big value header in it"); 29 | like($buf, qr/abcdef/, "buf has some data in it"); 30 | unlike($buf, qr/abcde\]/, "buf doesn't yet close"); 31 | 32 | # sock2 interrupts (maybe sock1 is slow) and deletes stuff: 33 | print $sock2 "delete big\r\n"; 34 | is(scalar <$sock2>, "DELETED\r\n", "deleted big from sock2 while sock1's still reading it"); 35 | mem_get_is($sock2, "big", undef, "nothing from sock2 now. gone from namespace."); 36 | print $sock2 "set big 0 0 $size\r\n$bigval2\r\n"; 37 | is(scalar <$sock2>, "STORED\r\n", "stored big w/ val2"); 38 | mem_get_is($sock2, "big", $bigval2, "big value2 got correctly"); 39 | 40 | # sock1 resumes reading... 41 | $buf .= <$sock>; 42 | $buf .= <$sock>; 43 | like($buf, qr/abcde\]/, "buf now closes"); 44 | 45 | # and if sock1 reads again, it's the uppercase version: 46 | mem_get_is($sock, "big", $bigval2, "big value2 got correctly from sock1"); 47 | -------------------------------------------------------------------------------- /t/noreply.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 9; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | 10 | my $server = new_memcached(); 11 | my $sock = $server->sock; 12 | 13 | 14 | # Test that commands can take 'noreply' parameter. 15 | print $sock "flush_all noreply\r\n"; 16 | print $sock "flush_all 0 noreply\r\n"; 17 | 18 | print $sock "verbosity 0 noreply\r\n"; 19 | 20 | print $sock "add noreply:foo 0 0 1 noreply\r\n1\r\n"; 21 | mem_get_is($sock, "noreply:foo", "1"); 22 | 23 | print $sock "set noreply:foo 0 0 1 noreply\r\n2\r\n"; 24 | mem_get_is($sock, "noreply:foo", "2"); 25 | 26 | print $sock "replace noreply:foo 0 0 1 noreply\r\n3\r\n"; 27 | mem_get_is($sock, "noreply:foo", "3"); 28 | 29 | print $sock "append noreply:foo 0 0 1 noreply\r\n4\r\n"; 30 | mem_get_is($sock, "noreply:foo", "34"); 31 | 32 | print $sock "prepend noreply:foo 0 0 1 noreply\r\n5\r\n"; 33 | my @result = mem_gets($sock, "noreply:foo"); 34 | ok($result[1] eq "534"); 35 | 36 | print $sock "cas noreply:foo 0 0 1 $result[0] noreply\r\n6\r\n"; 37 | mem_get_is($sock, "noreply:foo", "6"); 38 | 39 | print $sock "incr noreply:foo 3 noreply\r\n"; 40 | mem_get_is($sock, "noreply:foo", "9"); 41 | 42 | print $sock "decr noreply:foo 2 noreply\r\n"; 43 | mem_get_is($sock, "noreply:foo", "7"); 44 | 45 | print $sock "delete noreply:foo noreply\r\n"; 46 | mem_get_is($sock, "noreply:foo"); 47 | 48 | -------------------------------------------------------------------------------- /t/sasl/memcached.conf: -------------------------------------------------------------------------------- 1 | mech_list: plain cram-md5 2 | log_level: 5 3 | sasldb_path: /tmp/test-memcached.sasldb 4 | -------------------------------------------------------------------------------- /t/slabs_reassign.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More tests => 131; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | 10 | # Enable manual slab reassign, cap at 6 slabs 11 | my $server = new_memcached('-o slab_reassign -m 4'); 12 | my $stats = mem_stats($server->sock, ' settings'); 13 | is($stats->{slab_reassign}, "yes"); 14 | 15 | my $sock = $server->sock; 16 | 17 | # Fill a largeish slab until it evicts (honors the -m 6) 18 | my $bigdata = 'x' x 70000; # slab 31 19 | for (1 .. 60) { 20 | print $sock "set bfoo$_ 0 0 70000\r\n", $bigdata, "\r\n"; 21 | is(scalar <$sock>, "STORED\r\n", "stored key"); 22 | } 23 | 24 | # Fill a smaller slab until it evicts 25 | my $smalldata = 'y' x 20000; # slab 25 26 | for (1 .. 60) { 27 | print $sock "set sfoo$_ 0 0 20000\r\n", $smalldata, "\r\n"; 28 | is(scalar <$sock>, "STORED\r\n", "stored key"); 29 | } 30 | 31 | my $items_before = mem_stats($sock, "items"); 32 | isnt($items_before->{"items:31:evicted"}, 0, "slab 31 evicted is nonzero"); 33 | isnt($items_before->{"items:25:evicted"}, 0, "slab 25 evicted is nonzero"); 34 | 35 | my $slabs_before = mem_stats($sock, "slabs"); 36 | # Move a large slab to the smaller slab 37 | print $sock "slabs reassign 31 25\r\n"; 38 | is(scalar <$sock>, "OK\r\n", "slab rebalancer started"); 39 | 40 | # Still working out how/if to signal the thread. For now, just sleep. 41 | sleep 2; 42 | 43 | # Check that stats counters increased 44 | my $slabs_after = mem_stats($sock, "slabs"); 45 | $stats = mem_stats($sock); 46 | 47 | isnt($stats->{slabs_moved}, 0, "slabs moved is nonzero"); 48 | 49 | # Check that slab stats reflect the change 50 | ok($slabs_before->{"31:total_pages"} != $slabs_after->{"31:total_pages"}, 51 | "slab 31 pagecount changed"); 52 | ok($slabs_before->{"25:total_pages"} != $slabs_after->{"25:total_pages"}, 53 | "slab 25 pagecount changed"); 54 | 55 | # Try to move another slab, see that it complains 56 | print $sock "slabs reassign 31 25\r\n"; 57 | like(scalar <$sock>, qr/^NOTFULL/, "Cannot re-run against class with empty space"); 58 | 59 | # Try to move a page backwards. Should complain that source class isn't "safe" 60 | # to move from. 61 | print $sock "slabs reassign 25 31\r\n"; 62 | like(scalar <$sock>, qr/^UNSAFE/, "Cannot move an unsafe slab back"); 63 | 64 | # Try to insert items into both slabs 65 | print $sock "set bfoo51 0 0 70000\r\n", $bigdata, "\r\n"; 66 | is(scalar <$sock>, "STORED\r\n", "stored key"); 67 | 68 | print $sock "set sfoo51 0 0 20000\r\n", $smalldata, "\r\n"; 69 | is(scalar <$sock>, "STORED\r\n", "stored key"); 70 | 71 | # Do need to come up with better automated tests for this. 72 | -------------------------------------------------------------------------------- /t/stats-detail.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 24; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | my $expire; 12 | 13 | print $sock "stats detail dump\r\n"; 14 | is(scalar <$sock>, "END\r\n", "verified empty stats at start"); 15 | 16 | print $sock "stats detail on\r\n"; 17 | is(scalar <$sock>, "OK\r\n", "detail collection turned on"); 18 | 19 | print $sock "set foo:123 0 0 6\r\nfooval\r\n"; 20 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 21 | 22 | print $sock "stats detail dump\r\n"; 23 | is(scalar <$sock>, "PREFIX foo get 0 hit 0 set 1 del 0\r\n", "details after set"); 24 | is(scalar <$sock>, "END\r\n", "end of details"); 25 | 26 | mem_get_is($sock, "foo:123", "fooval"); 27 | print $sock "stats detail dump\r\n"; 28 | is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 1 del 0\r\n", "details after get with hit"); 29 | is(scalar <$sock>, "END\r\n", "end of details"); 30 | 31 | mem_get_is($sock, "foo:124", undef); 32 | 33 | print $sock "stats detail dump\r\n"; 34 | is(scalar <$sock>, "PREFIX foo get 2 hit 1 set 1 del 0\r\n", "details after get without hit"); 35 | is(scalar <$sock>, "END\r\n", "end of details"); 36 | 37 | print $sock "delete foo:125\r\n"; 38 | is(scalar <$sock>, "NOT_FOUND\r\n", "sent delete command"); 39 | 40 | print $sock "stats detail dump\r\n"; 41 | is(scalar <$sock>, "PREFIX foo get 2 hit 1 set 1 del 1\r\n", "details after delete"); 42 | is(scalar <$sock>, "END\r\n", "end of details"); 43 | 44 | print $sock "stats reset\r\n"; 45 | is(scalar <$sock>, "RESET\r\n", "stats cleared"); 46 | 47 | print $sock "stats detail dump\r\n"; 48 | is(scalar <$sock>, "END\r\n", "empty stats after clear"); 49 | 50 | mem_get_is($sock, "foo:123", "fooval"); 51 | print $sock "stats detail dump\r\n"; 52 | is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 0 del 0\r\n", "details after clear and get"); 53 | is(scalar <$sock>, "END\r\n", "end of details"); 54 | 55 | print $sock "stats detail off\r\n"; 56 | is(scalar <$sock>, "OK\r\n", "detail collection turned off"); 57 | 58 | mem_get_is($sock, "foo:124", undef); 59 | 60 | mem_get_is($sock, "foo:123", "fooval"); 61 | print $sock "stats detail dump\r\n"; 62 | is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 0 del 0\r\n", "details after stats turned off"); 63 | is(scalar <$sock>, "END\r\n", "end of details"); 64 | -------------------------------------------------------------------------------- /t/stats.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 95; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $server = new_memcached(); 10 | my $sock = $server->sock; 11 | 12 | 13 | ## Output looks like this: 14 | ## 15 | ## STAT pid 22969 16 | ## STAT uptime 13 17 | ## STAT time 1259170891 18 | ## STAT version 1.4.3 19 | ## STAT libevent 1.4.13-stable. 20 | ## STAT pointer_size 32 21 | ## STAT rusage_user 0.001198 22 | ## STAT rusage_system 0.003523 23 | ## STAT curr_connections 10 24 | ## STAT total_connections 11 25 | ## STAT connection_structures 11 26 | ## STAT cmd_get 0 27 | ## STAT cmd_set 0 28 | ## STAT cmd_flush 0 29 | ## STAT get_hits 0 30 | ## STAT get_misses 0 31 | ## STAT delete_misses 0 32 | ## STAT delete_hits 0 33 | ## STAT incr_misses 0 34 | ## STAT incr_hits 0 35 | ## STAT decr_misses 0 36 | ## STAT decr_hits 0 37 | ## STAT cas_misses 0 38 | ## STAT cas_hits 0 39 | ## STAT cas_badval 0 40 | ## STAT auth_cmds 0 41 | ## STAT auth_unknowns 0 42 | ## STAT bytes_read 7 43 | ## STAT bytes_written 0 44 | ## STAT limit_maxbytes 67108864 45 | ## STAT accepting_conns 1 46 | ## STAT listen_disabled_num 0 47 | ## STAT threads 4 48 | ## STAT conn_yields 0 49 | ## STAT bytes 0 50 | ## STAT curr_items 0 51 | ## STAT total_items 0 52 | ## STAT evictions 0 53 | ## STAT reclaimed 0 54 | 55 | # note that auth stats are tested in auth specfic tests 56 | 57 | 58 | my $stats = mem_stats($sock); 59 | 60 | # Test number of keys 61 | is(scalar(keys(%$stats)), 48, "48 stats values"); 62 | 63 | # Test initial state 64 | foreach my $key (qw(curr_items total_items bytes cmd_get cmd_set get_hits evictions get_misses 65 | bytes_written delete_hits delete_misses incr_hits incr_misses decr_hits 66 | decr_misses listen_disabled_num)) { 67 | is($stats->{$key}, 0, "initial $key is zero"); 68 | } 69 | is($stats->{accepting_conns}, 1, "initial accepting_conns is one"); 70 | 71 | # Do some operations 72 | 73 | print $sock "set foo 0 0 6\r\nfooval\r\n"; 74 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 75 | mem_get_is($sock, "foo", "fooval"); 76 | 77 | my $stats = mem_stats($sock); 78 | 79 | foreach my $key (qw(total_items curr_items cmd_get cmd_set get_hits)) { 80 | is($stats->{$key}, 1, "after one set/one get $key is 1"); 81 | } 82 | 83 | my $cache_dump = mem_stats($sock, " cachedump 1 100"); 84 | ok(defined $cache_dump->{'foo'}, "got foo from cachedump"); 85 | 86 | print $sock "delete foo\r\n"; 87 | is(scalar <$sock>, "DELETED\r\n", "deleted foo"); 88 | 89 | my $stats = mem_stats($sock); 90 | is($stats->{delete_hits}, 1); 91 | is($stats->{delete_misses}, 0); 92 | 93 | print $sock "delete foo\r\n"; 94 | is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't delete foo again"); 95 | 96 | my $stats = mem_stats($sock); 97 | is($stats->{delete_hits}, 1); 98 | is($stats->{delete_misses}, 1); 99 | 100 | # incr stats 101 | 102 | sub check_incr_stats { 103 | my ($ih, $im, $dh, $dm) = @_; 104 | my $stats = mem_stats($sock); 105 | 106 | is($stats->{incr_hits}, $ih); 107 | is($stats->{incr_misses}, $im); 108 | is($stats->{decr_hits}, $dh); 109 | is($stats->{decr_misses}, $dm); 110 | } 111 | 112 | print $sock "incr i 1\r\n"; 113 | is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't incr a missing thing"); 114 | check_incr_stats(0, 1, 0, 0); 115 | 116 | print $sock "decr d 1\r\n"; 117 | is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't decr a missing thing"); 118 | check_incr_stats(0, 1, 0, 1); 119 | 120 | print $sock "set n 0 0 1\r\n0\r\n"; 121 | is(scalar <$sock>, "STORED\r\n", "stored n"); 122 | 123 | print $sock "incr n 3\r\n"; 124 | is(scalar <$sock>, "3\r\n", "incr works"); 125 | check_incr_stats(1, 1, 0, 1); 126 | 127 | print $sock "decr n 1\r\n"; 128 | is(scalar <$sock>, "2\r\n", "decr works"); 129 | check_incr_stats(1, 1, 1, 1); 130 | 131 | # cas stats 132 | 133 | sub check_cas_stats { 134 | my ($ch, $cm, $cb) = @_; 135 | my $stats = mem_stats($sock); 136 | 137 | is($stats->{cas_hits}, $ch); 138 | is($stats->{cas_misses}, $cm); 139 | is($stats->{cas_badval}, $cb); 140 | } 141 | 142 | check_cas_stats(0, 0, 0); 143 | 144 | print $sock "cas c 0 0 1 99999999\r\nz\r\n"; 145 | is(scalar <$sock>, "NOT_FOUND\r\n", "missed cas"); 146 | check_cas_stats(0, 1, 0); 147 | 148 | print $sock "set c 0 0 1\r\nx\r\n"; 149 | is(scalar <$sock>, "STORED\r\n", "stored c"); 150 | my ($id, $v) = mem_gets($sock, 'c'); 151 | is('x', $v, 'got the expected value'); 152 | 153 | print $sock "cas c 0 0 1 99999999\r\nz\r\n"; 154 | is(scalar <$sock>, "EXISTS\r\n", "missed cas"); 155 | check_cas_stats(0, 1, 1); 156 | my ($newid, $v) = mem_gets($sock, 'c'); 157 | is('x', $v, 'got the expected value'); 158 | 159 | print $sock "cas c 0 0 1 $id\r\nz\r\n"; 160 | is(scalar <$sock>, "STORED\r\n", "good cas"); 161 | check_cas_stats(1, 1, 1); 162 | my ($newid, $v) = mem_gets($sock, 'c'); 163 | is('z', $v, 'got the expected value'); 164 | 165 | my $settings = mem_stats($sock, ' settings'); 166 | is(1024, $settings->{'maxconns'}); 167 | is('NULL', $settings->{'domain_socket'}); 168 | is('on', $settings->{'evictions'}); 169 | is('yes', $settings->{'cas_enabled'}); 170 | is('no', $settings->{'auth_enabled_sasl'}); 171 | 172 | print $sock "stats reset\r\n"; 173 | is(scalar <$sock>, "RESET\r\n", "good stats reset"); 174 | 175 | my $stats = mem_stats($sock); 176 | is(0, $stats->{'cmd_get'}); 177 | is(0, $stats->{'cmd_set'}); 178 | is(0, $stats->{'get_hits'}); 179 | is(0, $stats->{'get_misses'}); 180 | is(0, $stats->{'delete_misses'}); 181 | is(0, $stats->{'delete_hits'}); 182 | is(0, $stats->{'incr_misses'}); 183 | is(0, $stats->{'incr_hits'}); 184 | is(0, $stats->{'decr_misses'}); 185 | is(0, $stats->{'decr_hits'}); 186 | is(0, $stats->{'cas_misses'}); 187 | is(0, $stats->{'cas_hits'}); 188 | is(0, $stats->{'cas_badval'}); 189 | is(0, $stats->{'evictions'}); 190 | is(0, $stats->{'reclaimed'}); 191 | 192 | print $sock "flush_all\r\n"; 193 | is(scalar <$sock>, "OK\r\n", "flushed"); 194 | 195 | my $stats = mem_stats($sock); 196 | is($stats->{cmd_flush}, 1, "after one flush cmd_flush is 1"); 197 | -------------------------------------------------------------------------------- /t/stress-memcached.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # 3 | 4 | use strict; 5 | use lib '../../api/perl/lib'; 6 | use Cache::Memcached; 7 | use Time::HiRes qw(time); 8 | 9 | unless (@ARGV == 2) { 10 | die "Usage: stress-memcached.pl ip:port threads\n"; 11 | } 12 | 13 | my $host = shift; 14 | my $threads = shift; 15 | 16 | my $memc = new Cache::Memcached; 17 | $memc->set_servers([$host]); 18 | 19 | unless ($memc->set("foo", "bar") && 20 | $memc->get("foo") eq "bar") { 21 | die "memcached not running at $host ?\n"; 22 | } 23 | $memc->disconnect_all(); 24 | 25 | 26 | my $running = 0; 27 | while (1) { 28 | if ($running < $threads) { 29 | my $cpid = fork(); 30 | if ($cpid) { 31 | $running++; 32 | #print "Launched $cpid. Running $running threads.\n"; 33 | } else { 34 | stress(); 35 | exit 0; 36 | } 37 | } else { 38 | wait(); 39 | $running--; 40 | } 41 | } 42 | 43 | sub stress { 44 | undef $memc; 45 | $memc = new Cache::Memcached; 46 | $memc->set_servers([$host]); 47 | 48 | my ($t1, $t2); 49 | my $start = sub { $t1 = time(); }; 50 | my $stop = sub { 51 | my $op = shift; 52 | $t2 = time(); 53 | my $td = sprintf("%0.3f", $t2 - $t1); 54 | if ($td > 0.25) { print "Took $td seconds for: $op\n"; } 55 | }; 56 | 57 | my $max = rand(50); 58 | my $sets = 0; 59 | 60 | for (my $i = 0; $i < $max; $i++) { 61 | my $key = key($i); 62 | my $set = $memc->set($key, $key); 63 | $sets++ if $set; 64 | } 65 | 66 | for (1..int(rand(500))) { 67 | my $rand = int(rand($max)); 68 | my $key = key($rand); 69 | my $meth = int(rand(3)); 70 | my $exp = int(rand(3)); 71 | undef $exp unless $exp; 72 | $start->(); 73 | if ($meth == 0) { 74 | $memc->add($key, $key, $exp); 75 | $stop->("add"); 76 | } elsif ($meth == 1) { 77 | $memc->delete($key); 78 | $stop->("delete"); 79 | } else { 80 | $memc->set($key, $key, $exp); 81 | $stop->("set"); 82 | } 83 | $rand = int(rand($max)); 84 | $key = key($rand); 85 | $start->(); 86 | my $v = $memc->get($key); 87 | $stop->("get"); 88 | if ($v && $v ne $key) { die "Bogus: $v for key $rand\n"; } 89 | } 90 | 91 | $start->(); 92 | my $multi = $memc->get_multi(map { key(int(rand($max))) } (1..$max)); 93 | $stop->("get_multi"); 94 | } 95 | 96 | sub key { 97 | my $n = shift; 98 | $_ = sprintf("%04d", $n); 99 | if ($n % 2) { $_ .= "a"x20; } 100 | $_; 101 | } 102 | -------------------------------------------------------------------------------- /t/touch.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 4; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | 10 | my $server = new_memcached(); 11 | my $sock = $server->sock; 12 | 13 | # set foo (and should get it) 14 | print $sock "set foo 0 2 6\r\nfooval\r\n"; 15 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 16 | mem_get_is($sock, "foo", "fooval"); 17 | 18 | # touch it 19 | print $sock "touch foo 10\r\n"; 20 | is(scalar <$sock>, "TOUCHED\r\n", "touched foo"); 21 | 22 | sleep 2; 23 | mem_get_is($sock, "foo", "fooval"); 24 | -------------------------------------------------------------------------------- /t/unixsocket.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use Test::More tests => 3; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | my $filename = "/tmp/memcachetest$$"; 10 | 11 | my $server = new_memcached("-s $filename"); 12 | my $sock = $server->sock; 13 | 14 | ok(-S $filename, "creating unix domain socket $filename"); 15 | 16 | # set foo (and should get it) 17 | print $sock "set foo 0 0 6\r\nfooval\r\n"; 18 | 19 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 20 | mem_get_is($sock, "foo", "fooval"); 21 | 22 | unlink($filename); 23 | 24 | ## Just some basic stuff for now... 25 | -------------------------------------------------------------------------------- /t/whitespace.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use FindBin qw($Bin); 4 | our @files; 5 | 6 | BEGIN { 7 | chdir "$Bin/.." or die; 8 | 9 | my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am); 10 | push(@exempted, glob("doc/*.xml")); 11 | push(@exempted, glob("doc/xml2rfc/*.xsl")); 12 | push(@exempted, glob("m4/*backport*m4")); 13 | my %exempted_hash = map { $_ => 1 } @exempted; 14 | 15 | my @stuff = split /\0/, `git ls-files -z -c -m -o --exclude-standard`; 16 | @files = grep { ! $exempted_hash{$_} } @stuff; 17 | 18 | # We won't find any files if git isn't installed. If git isn't 19 | # installed, they're probably not doing any useful development, or 20 | # at the very least am will clean up whitespace when we receive 21 | # their patch. 22 | unless (@files) { 23 | use Test::More; 24 | plan skip_all => "Skipping tests probably because you don't have git."; 25 | exit 0; 26 | } 27 | } 28 | 29 | use Test::More tests => scalar(@files); 30 | 31 | foreach my $f (@files) { 32 | open(my $fh, $f) or die; 33 | my $before = do { local $/; <$fh>; }; 34 | close ($fh); 35 | my $after = $before; 36 | $after =~ s/\t/ /g; 37 | $after =~ s/ +$//mg; 38 | $after .= "\n" unless $after =~ /\n$/; 39 | ok ($after eq $before, "$f (see devtools/clean-whitespace.pl)"); 40 | } 41 | -------------------------------------------------------------------------------- /timedrun.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | static int caught = 0; 11 | 12 | static void caught_signal(int which) 13 | { 14 | caught = which; 15 | } 16 | 17 | static int wait_for_process(pid_t pid) 18 | { 19 | int rv = EX_SOFTWARE; 20 | int stats = 0; 21 | int i = 0; 22 | struct sigaction sig_handler; 23 | 24 | sig_handler.sa_handler = caught_signal; 25 | sig_handler.sa_flags = 0; 26 | 27 | sigaction(SIGALRM, &sig_handler, NULL); 28 | sigaction(SIGHUP, &sig_handler, NULL); 29 | sigaction(SIGINT, &sig_handler, NULL); 30 | sigaction(SIGTERM, &sig_handler, NULL); 31 | sigaction(SIGPIPE, &sig_handler, NULL); 32 | 33 | /* Loop forever waiting for the process to quit */ 34 | for (i = 0; ;i++) { 35 | pid_t p = waitpid(pid, &stats, 0); 36 | if (p == pid) { 37 | /* child exited. Let's get out of here */ 38 | rv = WIFEXITED(stats) ? 39 | WEXITSTATUS(stats) : 40 | (0x80 | WTERMSIG(stats)); 41 | break; 42 | } else { 43 | int sig = 0; 44 | switch (i) { 45 | case 0: 46 | /* On the first iteration, pass the signal through */ 47 | sig = caught > 0 ? caught : SIGTERM; 48 | if (caught == SIGALRM) { 49 | fprintf(stderr, "Timeout.. killing the process\n"); 50 | } 51 | break; 52 | case 1: 53 | sig = SIGTERM; 54 | break; 55 | default: 56 | sig = SIGKILL; 57 | break; 58 | } 59 | if (kill(pid, sig) < 0) { 60 | /* Kill failed. Must have lost the process. :/ */ 61 | perror("lost child when trying to kill"); 62 | } 63 | /* Wait up to 5 seconds for the pid */ 64 | alarm(5); 65 | } 66 | } 67 | return rv; 68 | } 69 | 70 | static int spawn_and_wait(char **argv) 71 | { 72 | int rv = EX_SOFTWARE; 73 | pid_t pid = fork(); 74 | 75 | switch (pid) { 76 | case -1: 77 | perror("fork"); 78 | rv = EX_OSERR; 79 | break; /* NOTREACHED */ 80 | case 0: 81 | execvp(argv[0], argv); 82 | perror("exec"); 83 | rv = EX_SOFTWARE; 84 | break; /* NOTREACHED */ 85 | default: 86 | rv = wait_for_process(pid); 87 | } 88 | return rv; 89 | } 90 | 91 | int main(int argc, char **argv) 92 | { 93 | int naptime = 0; 94 | assert(argc > 2); 95 | 96 | naptime = atoi(argv[1]); 97 | assert(naptime > 0 && naptime < 1800); 98 | 99 | alarm(naptime); 100 | 101 | return spawn_and_wait(argv+2); 102 | } 103 | -------------------------------------------------------------------------------- /trace.h: -------------------------------------------------------------------------------- 1 | #ifndef TRACE_H 2 | #define TRACE_H 3 | 4 | #ifdef ENABLE_DTRACE 5 | #include "memcached_dtrace.h" 6 | #else 7 | #define MEMCACHED_ASSOC_DELETE(arg0, arg1, arg2) 8 | #define MEMCACHED_ASSOC_DELETE_ENABLED() (0) 9 | #define MEMCACHED_ASSOC_FIND(arg0, arg1, arg2) 10 | #define MEMCACHED_ASSOC_FIND_ENABLED() (0) 11 | #define MEMCACHED_ASSOC_INSERT(arg0, arg1, arg2) 12 | #define MEMCACHED_ASSOC_INSERT_ENABLED() (0) 13 | #define MEMCACHED_COMMAND_ADD(arg0, arg1, arg2, arg3, arg4) 14 | #define MEMCACHED_COMMAND_ADD_ENABLED() (0) 15 | #define MEMCACHED_COMMAND_APPEND(arg0, arg1, arg2, arg3, arg4) 16 | #define MEMCACHED_COMMAND_APPEND_ENABLED() (0) 17 | #define MEMCACHED_COMMAND_CAS(arg0, arg1, arg2, arg3, arg4) 18 | #define MEMCACHED_COMMAND_CAS_ENABLED() (0) 19 | #define MEMCACHED_COMMAND_DECR(arg0, arg1, arg2, arg3) 20 | #define MEMCACHED_COMMAND_DECR_ENABLED() (0) 21 | #define MEMCACHED_COMMAND_DELETE(arg0, arg1, arg2) 22 | #define MEMCACHED_COMMAND_DELETE_ENABLED() (0) 23 | #define MEMCACHED_COMMAND_GET(arg0, arg1, arg2, arg3, arg4) 24 | #define MEMCACHED_COMMAND_GET_ENABLED() (0) 25 | #define MEMCACHED_COMMAND_TOUCH(arg0, arg1, arg2, arg3, arg4) 26 | #define MEMCACHED_COMMAND_TOUCH_ENABLED() (0) 27 | #define MEMCACHED_COMMAND_INCR(arg0, arg1, arg2, arg3) 28 | #define MEMCACHED_COMMAND_INCR_ENABLED() (0) 29 | #define MEMCACHED_COMMAND_PREPEND(arg0, arg1, arg2, arg3, arg4) 30 | #define MEMCACHED_COMMAND_PREPEND_ENABLED() (0) 31 | #define MEMCACHED_COMMAND_REPLACE(arg0, arg1, arg2, arg3, arg4) 32 | #define MEMCACHED_COMMAND_REPLACE_ENABLED() (0) 33 | #define MEMCACHED_COMMAND_SET(arg0, arg1, arg2, arg3, arg4) 34 | #define MEMCACHED_COMMAND_SET_ENABLED() (0) 35 | #define MEMCACHED_CONN_ALLOCATE(arg0) 36 | #define MEMCACHED_CONN_ALLOCATE_ENABLED() (0) 37 | #define MEMCACHED_CONN_CREATE(arg0) 38 | #define MEMCACHED_CONN_CREATE_ENABLED() (0) 39 | #define MEMCACHED_CONN_DESTROY(arg0) 40 | #define MEMCACHED_CONN_DESTROY_ENABLED() (0) 41 | #define MEMCACHED_CONN_DISPATCH(arg0, arg1) 42 | #define MEMCACHED_CONN_DISPATCH_ENABLED() (0) 43 | #define MEMCACHED_CONN_RELEASE(arg0) 44 | #define MEMCACHED_CONN_RELEASE_ENABLED() (0) 45 | #define MEMCACHED_ITEM_LINK(arg0, arg1, arg2) 46 | #define MEMCACHED_ITEM_LINK_ENABLED() (0) 47 | #define MEMCACHED_ITEM_REMOVE(arg0, arg1, arg2) 48 | #define MEMCACHED_ITEM_REMOVE_ENABLED() (0) 49 | #define MEMCACHED_ITEM_REPLACE(arg0, arg1, arg2, arg3, arg4, arg5) 50 | #define MEMCACHED_ITEM_REPLACE_ENABLED() (0) 51 | #define MEMCACHED_ITEM_UNLINK(arg0, arg1, arg2) 52 | #define MEMCACHED_ITEM_UNLINK_ENABLED() (0) 53 | #define MEMCACHED_ITEM_UPDATE(arg0, arg1, arg2) 54 | #define MEMCACHED_ITEM_UPDATE_ENABLED() (0) 55 | #define MEMCACHED_PROCESS_COMMAND_END(arg0, arg1, arg2) 56 | #define MEMCACHED_PROCESS_COMMAND_END_ENABLED() (0) 57 | #define MEMCACHED_PROCESS_COMMAND_START(arg0, arg1, arg2) 58 | #define MEMCACHED_PROCESS_COMMAND_START_ENABLED() (0) 59 | #define MEMCACHED_SLABS_ALLOCATE(arg0, arg1, arg2, arg3) 60 | #define MEMCACHED_SLABS_ALLOCATE_ENABLED() (0) 61 | #define MEMCACHED_SLABS_ALLOCATE_FAILED(arg0, arg1) 62 | #define MEMCACHED_SLABS_ALLOCATE_FAILED_ENABLED() (0) 63 | #define MEMCACHED_SLABS_FREE(arg0, arg1, arg2) 64 | #define MEMCACHED_SLABS_FREE_ENABLED() (0) 65 | #define MEMCACHED_SLABS_SLABCLASS_ALLOCATE(arg0) 66 | #define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_ENABLED() (0) 67 | #define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(arg0) 68 | #define MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED_ENABLED() (0) 69 | #endif 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "memcached.h" 10 | 11 | /* Avoid warnings on solaris, where isspace() is an index into an array, and gcc uses signed chars */ 12 | #define xisspace(c) isspace((unsigned char)c) 13 | 14 | bool safe_strtoull(const char *str, uint64_t *out) { 15 | assert(out != NULL); 16 | errno = 0; 17 | *out = 0; 18 | char *endptr; 19 | unsigned long long ull = strtoull(str, &endptr, 10); 20 | if ((errno == ERANGE) || (str == endptr)) { 21 | return false; 22 | } 23 | 24 | if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) { 25 | if ((long long) ull < 0) { 26 | /* only check for negative signs in the uncommon case when 27 | * the unsigned number is so big that it's negative as a 28 | * signed number. */ 29 | if (strchr(str, '-') != NULL) { 30 | return false; 31 | } 32 | } 33 | *out = ull; 34 | return true; 35 | } 36 | return false; 37 | } 38 | 39 | bool safe_strtoll(const char *str, int64_t *out) { 40 | assert(out != NULL); 41 | errno = 0; 42 | *out = 0; 43 | char *endptr; 44 | long long ll = strtoll(str, &endptr, 10); 45 | if ((errno == ERANGE) || (str == endptr)) { 46 | return false; 47 | } 48 | 49 | if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) { 50 | *out = ll; 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | bool safe_strtoul(const char *str, uint32_t *out) { 57 | char *endptr = NULL; 58 | unsigned long l = 0; 59 | assert(out); 60 | assert(str); 61 | *out = 0; 62 | errno = 0; 63 | 64 | l = strtoul(str, &endptr, 10); 65 | if ((errno == ERANGE) || (str == endptr)) { 66 | return false; 67 | } 68 | 69 | if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) { 70 | if ((long) l < 0) { 71 | /* only check for negative signs in the uncommon case when 72 | * the unsigned number is so big that it's negative as a 73 | * signed number. */ 74 | if (strchr(str, '-') != NULL) { 75 | return false; 76 | } 77 | } 78 | *out = l; 79 | return true; 80 | } 81 | 82 | return false; 83 | } 84 | 85 | bool safe_strtol(const char *str, int32_t *out) { 86 | assert(out != NULL); 87 | errno = 0; 88 | *out = 0; 89 | char *endptr; 90 | long l = strtol(str, &endptr, 10); 91 | if ((errno == ERANGE) || (str == endptr)) { 92 | return false; 93 | } 94 | 95 | if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) { 96 | *out = l; 97 | return true; 98 | } 99 | return false; 100 | } 101 | 102 | void vperror(const char *fmt, ...) { 103 | int old_errno = errno; 104 | char buf[1024]; 105 | va_list ap; 106 | 107 | va_start(ap, fmt); 108 | if (vsnprintf(buf, sizeof(buf), fmt, ap) == -1) { 109 | buf[sizeof(buf) - 1] = '\0'; 110 | } 111 | va_end(ap); 112 | 113 | errno = old_errno; 114 | 115 | perror(buf); 116 | } 117 | 118 | #ifndef HAVE_HTONLL 119 | static uint64_t mc_swap64(uint64_t in) { 120 | #ifdef ENDIAN_LITTLE 121 | /* Little endian, flip the bytes around until someone makes a faster/better 122 | * way to do this. */ 123 | int64_t rv = 0; 124 | int i = 0; 125 | for(i = 0; i<8; i++) { 126 | rv = (rv << 8) | (in & 0xff); 127 | in >>= 8; 128 | } 129 | return rv; 130 | #else 131 | /* big-endian machines don't need byte swapping */ 132 | return in; 133 | #endif 134 | } 135 | 136 | uint64_t ntohll(uint64_t val) { 137 | return mc_swap64(val); 138 | } 139 | 140 | uint64_t htonll(uint64_t val) { 141 | return mc_swap64(val); 142 | } 143 | #endif 144 | 145 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Wrappers around strtoull/strtoll that are safer and easier to 3 | * use. For tests and assumptions, see internal_tests.c. 4 | * 5 | * str a NULL-terminated base decimal 10 unsigned integer 6 | * out out parameter, if conversion succeeded 7 | * 8 | * returns true if conversion succeeded. 9 | */ 10 | bool safe_strtoull(const char *str, uint64_t *out); 11 | bool safe_strtoll(const char *str, int64_t *out); 12 | bool safe_strtoul(const char *str, uint32_t *out); 13 | bool safe_strtol(const char *str, int32_t *out); 14 | 15 | #ifndef HAVE_HTONLL 16 | extern uint64_t htonll(uint64_t); 17 | extern uint64_t ntohll(uint64_t); 18 | #endif 19 | 20 | #ifdef __GCC 21 | # define __gcc_attribute__ __attribute__ 22 | #else 23 | # define __gcc_attribute__(x) 24 | #endif 25 | 26 | /** 27 | * Vararg variant of perror that makes for more useful error messages 28 | * when reporting with parameters. 29 | * 30 | * @param fmt a printf format 31 | */ 32 | void vperror(const char *fmt, ...) 33 | __gcc_attribute__ ((format (printf, 1, 2))); 34 | -------------------------------------------------------------------------------- /version.m4: -------------------------------------------------------------------------------- 1 | m4_define([VERSION_NUMBER], [1.4.13]) 2 | --------------------------------------------------------------------------------