├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── ci.yml ├── .gitignore ├── .shipit ├── AUTHORS ├── BUILD ├── CONTRIBUTING.md ├── COPYING ├── ChangeLog ├── Doxyfile ├── HACKING ├── LICENSE ├── LICENSE.bipbuffer ├── LICENSE.itoa_ljust ├── Makefile.am ├── NEWS ├── README.md ├── assoc.c ├── assoc.h ├── authfile.c ├── authfile.h ├── autogen.sh ├── base64.c ├── base64.h ├── bipbuffer.c ├── bipbuffer.h ├── cache.c ├── cache.h ├── configure.ac ├── crawler.c ├── crawler.h ├── crc32c.c ├── crc32c.h ├── daemon.c ├── darwin_priv.c ├── devtools ├── Dockerfile.alpine ├── Dockerfile.arch ├── Dockerfile.fedora ├── Dockerfile.ubuntu ├── bench_noreply.pl ├── clean-whitespace.pl └── slab_loadgen ├── doc ├── CONTRIBUTORS ├── Doxyfile ├── Makefile.am ├── memcached.1 ├── napi_ids.txt ├── new_lru.txt ├── protocol-binary-range.txt ├── protocol-binary-range.xml ├── protocol-binary.full ├── protocol-binary.txt ├── protocol-binary.xml ├── protocol.txt ├── readme.txt ├── storage.txt ├── threads.txt ├── tls.txt └── xml2rfc │ ├── reference.RFC.0768.xml │ ├── reference.RFC.2119.xml │ ├── rfc2629-noinc.xsl │ ├── rfc2629-other.ent │ ├── rfc2629-refchk.xsl │ ├── rfc2629-xhtml.ent │ └── rfc2629.dtd ├── docker-compose.yml ├── extstore.c ├── extstore.h ├── freebsd_priv.c ├── globals.c ├── hash.c ├── hash.h ├── items.c ├── items.h ├── itoa_ljust.c ├── itoa_ljust.h ├── jenkins_hash.c ├── jenkins_hash.h ├── linux_priv.c ├── logger.c ├── logger.h ├── m4 └── c99-backport.m4 ├── md5.c ├── md5.h ├── memcached.c ├── memcached.h ├── memcached.spec.in ├── memcached_dtrace.d ├── murmur3_hash.c ├── murmur3_hash.h ├── openbsd_priv.c ├── proto_bin.c ├── proto_bin.h ├── proto_proxy.c ├── proto_proxy.h ├── proto_text.c ├── proto_text.h ├── protocol_binary.h ├── proxy.h ├── proxy_await.c ├── proxy_config.c ├── proxy_jump_hash.c ├── proxy_lua.c ├── proxy_network.c ├── proxy_request.c ├── proxy_ring_hash.c ├── proxy_ustats.c ├── proxy_xxhash.c ├── queue.h ├── restart.c ├── restart.h ├── sasl_defs.c ├── sasl_defs.h ├── scripts ├── README.damemtop ├── damemtop ├── damemtop.yaml ├── memcached-automove ├── memcached-automove-extstore ├── memcached-init ├── memcached-server.upstart ├── memcached-tool ├── memcached-tool.1 ├── memcached.service ├── memcached.sysconfig ├── memcached.sysv ├── memcached.upstart ├── memcached@.service └── start-memcached ├── sizes.c ├── slab_automove.c ├── slab_automove.h ├── slab_automove_extstore.c ├── slab_automove_extstore.h ├── slabs.c ├── slabs.h ├── solaris_priv.c ├── stats_prefix.c ├── stats_prefix.h ├── storage.c ├── storage.h ├── t ├── 00-startup.t ├── 64bit.t ├── README-TLS.md ├── ascii-auth.t ├── authfile ├── binary-extstore.t ├── binary-get.t ├── binary-sasl.t ├── binary.t ├── bogus-commands.t ├── cacert.pem ├── cakey.pem ├── cas.t ├── chunked-extstore.t ├── chunked-items.t ├── client_crt.pem ├── client_key.pem ├── conn-limits.t ├── daemonize.t ├── dash-M.t ├── dyn-maxbytes.t ├── error-extstore.t ├── evictions.t ├── expirations.t ├── extstore-buckets.t ├── extstore-jbod.t ├── extstore.t ├── flags.t ├── flush-all.t ├── getandtouch.t ├── getset.t ├── idle-timeout.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_192.t ├── issue_22.t ├── issue_260.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-crawler.t ├── lru-maintainer.t ├── lru.t ├── malicious-commands.t ├── maxconns.t ├── metaget.t ├── misbehave.t ├── multiversioning.t ├── noreply.t ├── proxy.t ├── quit.t ├── refhang.t ├── restart.t ├── sasl │ └── memcached.conf ├── server.pem ├── server_crt.pem ├── server_key.pem ├── shutdown.t ├── slabhang.t ├── slabs-reassign-chunked.t ├── slabs-reassign2.t ├── slabs_reassign.t ├── ssl_cert_refresh.t ├── ssl_ports.t ├── ssl_proto_version.t ├── ssl_session_resumption.t ├── ssl_settings.t ├── ssl_verify_modes.t ├── startfile.lua ├── stats-conns.t ├── stats-detail.t ├── stats.t ├── stress-memcached.pl ├── strtol-testing.t ├── touch.t ├── udp.t ├── unixsocket.t ├── watcher.t ├── watcher_connid.t └── whitespace.t ├── testapp.c ├── thread.c ├── timedrun.c ├── tls.c ├── tls.h ├── trace.h ├── util.c ├── util.h ├── vendor ├── .gitignore ├── Makefile ├── fetch.sh ├── lua │ └── .gitignore └── mcmc │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── example.c │ ├── mcmc.c │ └── mcmc.h ├── version.pl ├── version.sh └── xxhash.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Include some extra information to help with bug reports 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **DO NOT SUBMIT SECURITY REPORTS HERE** 11 | 12 | If you have a security vulnerability, _please report it to the maintainers 13 | privately_. You will be able to file your bug and claim credit once we have a 14 | fix implemented. 15 | 16 | **Describe the bug** 17 | A clear and concise description of what the bug is. 18 | 19 | **To Reproduce** 20 | Any information useful for reproducing the bug or crash. Workload, changes in workload, versions affected. If seeing a new issue after an upgrade, please include both before and after versions. 21 | 22 | **System Information** 23 | - OS/Distro: 24 | - Version of OS/distro: 25 | - Version of memcached: 26 | - Hardware detail: 27 | 28 | **Detail (please include!)** 29 | Always include the output of `stats`, `stats settings`, and optionally `stats items` and `stats slabs`. These can be provided to a maintainer privately if necessary. Please sanitize anything secret from the data. 30 | 31 | If you have a segfault or crash, please try to resolve the crash with `addr2line`. If possible, get a core dump and include a full back trace. 32 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ubuntu-build-test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Install deps 11 | run: | 12 | sudo apt-get update -y 13 | sudo apt-get install -y libevent-dev libseccomp-dev git libsasl2-dev libio-socket-ssl-perl 14 | - name: Build 15 | run: | 16 | gcc --version 17 | ./autogen.sh 18 | ./configure --enable-seccomp --enable-tls --enable-sasl --enable-sasl-pwdb 19 | make -j 20 | - name: Test 21 | run: PARALLEL=5 make test 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.[aois] 2 | *.gcov 3 | *.gcda 4 | *.gcno 5 | *.tcov 6 | .cache 7 | .deps 8 | .buildbot 9 | INSTALL 10 | Makefile 11 | Makefile.in 12 | config.guess 13 | config.h 14 | config.h.in 15 | config.log 16 | config.status 17 | config.sub 18 | aclocal.m4 19 | compile 20 | autom4te.cache 21 | configure 22 | depcomp 23 | install-sh 24 | memcached 25 | memcached-debug 26 | memcached-debug.profile 27 | missing 28 | stamp-h1 29 | tags 30 | TAGS 31 | cscope.out 32 | memcached_dtrace.h 33 | *~ 34 | memcached-*.tar.gz 35 | memcached-*.tar.gz.sha1 36 | /sizes 37 | /version.m4 38 | /version.num 39 | /testapp 40 | /timedrun 41 | /doc/doxy 42 | /memcached.spec 43 | .*.swp 44 | /compile_commands.json 45 | -------------------------------------------------------------------------------- /.shipit: -------------------------------------------------------------------------------- 1 | steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist, AddToSVNDir 2 | 3 | AddToSVNDir.dir = ../website/dist 4 | svn.tagpattern = %v 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Anatoly Vorobey 2 | Brad Fitzpatrick 3 | Alan "Dormando" Kasindorf 4 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | See below if building the proxy 2 | 3 | To build memcached in your machine from local repo you will have to install 4 | autotools, automake and libevent. In a debian based system that will look 5 | like this 6 | 7 | sudo apt-get install autotools-dev 8 | sudo apt-get install automake 9 | sudo apt-get install libevent-dev 10 | 11 | After that you can build memcached binary using automake 12 | 13 | cd memcached 14 | ./autogen.sh 15 | ./configure 16 | make 17 | make test 18 | 19 | It should create the binary in the same folder, which you can run 20 | 21 | ./memcached 22 | 23 | You can telnet into that memcached to ensure it is up and running 24 | 25 | telnet 127.0.0.1 11211 26 | stats 27 | 28 | IF BUILDING PROXY, AN EXTRA STEP IS NECESSARY: 29 | 30 | cd memcached 31 | cd vendor 32 | ./fetch.sh 33 | cd .. 34 | ./autogen.sh 35 | ./configure --enable-proxy 36 | make 37 | make test 38 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | See: https://github.com/memcached/memcached/wiki/DevelopmentRepos 2 | 3 | It's worth repeating here, that the biggest contribution anyone can make is to 4 | help run new releases! Any feedback we get is greatly appreciated. Hard to 5 | know what to work on and what to prioritize if we don't hear from you :) 6 | 7 | The easiest thing to do is to run the latest version on one machine in your 8 | cluster sometimes. Then when you do need to upgrade, you should also have 9 | confidence in a well tested version. 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | * Hacking Memcached 2 | 3 | * Prerequisites 4 | 5 | - autoconf 6 | - automake 7 | - autotools 8 | - libevent 9 | 10 | * Getting Started 11 | 12 | After checking out a git repository, you must first run autogen.sh 13 | once in order to create the configure script. 14 | 15 | Next, run the configure script and start doing builds. 16 | 17 | IE: 18 | ./autogen.sh && ./configure && make && make test 19 | 20 | * Setting up Git 21 | 22 | Though not required, there are a couple of things you can add to git 23 | to help development. 24 | 25 | ** Pre Commit Hook 26 | 27 | The pre-commit hook can be used to ensure that your tree passes tests 28 | before allowing a commit. To do so, add the following to 29 | .git/hooks/pre-commit (which must be executable): 30 | 31 | #!/bin/sh 32 | make test 33 | 34 | ** Post Commit Hook 35 | 36 | Because the version number changes on each commit, it's good to use a 37 | post commit hook to update the version number after each commit so as 38 | to keep the reporting accurate. To do so, add the following to 39 | .git/hooks/post-commit (which must be executable) 40 | 41 | #!/bin/sh 42 | ./version.sh 43 | 44 | ** Running memcached in gdb for tests. 45 | 46 | By default `make test` will spawn a memcached daemon for each test. 47 | This doesn't let you easily drop into gdb or run verbosely. 48 | 49 | If you export the environment variable 50 | T_MEMD_USE_DAEMON="127.0.0.1:11211" the tests will use an existing 51 | daemon at that address. 52 | 53 | * Debugging seccomp issues 54 | 55 | If new functionality fails when built with seccomp / drop privileges 56 | support, it can be debugged in one of two ways: 57 | 58 | Run the memcached via strace. For example: 59 | 60 | strace -o /tmp/memcache.strace -f -- ./memcached 61 | less /tmp/memcache.strace 62 | 63 | And look for calls which failed due to access restriction. They will 64 | show up with result: "-1 (errno 13)". Then add them to linux_priv.c. 65 | 66 | Alternatively, change the definition in linux_priv.c to: 67 | 68 | #define DENY_ACTION SCMP_ACT_TRAP 69 | 70 | and the process will crash with a coredump on all policy violations. 71 | In strace output those can be seen as: 72 | 73 | SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, 74 | si_call_addr=0x358a443454d, si_syscall=__NR_write, 75 | si_arch=AUDIT_ARCH_X86_64} --- 76 | 77 | In that output, the si_syscall shows which operation has been 78 | blocked. In this case that's `write()`. 79 | 80 | * Sending patches 81 | 82 | See current instructions at https://github.com/memcached/memcached/wiki/DevelopmentRepos 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /LICENSE.bipbuffer: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Willem-Hendrik Thiart 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 met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * The names of its contributors may not be used to endorse or promote 12 | products derived from this software without specific prior written 13 | permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL WILLEM-HENDRIK THIART BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /LICENSE.itoa_ljust: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Arturo Martin-de-Nicolas 2 | arturomdn@gmail.com 3 | https://github.com/amdn/itoa_ljust/ 4 | All rights reserved. 5 | 6 | This implementation is loosely based on the structure of FastInt32ToBufferLeft 7 | in: 8 | 9 | Protocol Buffers - Google's data interchange format 10 | Copyright 2008 Google Inc. All rights reserved. 11 | https://developers.google.com/protocol-buffers/ 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are 15 | met: 16 | 17 | * Redistributions of source code must retain the above copyright 18 | notice, this list of conditions and the following disclaimer. 19 | 20 | * Redistributions in binary form must reproduce the above 21 | copyright notice, this list of conditions and the following disclaimer 22 | in the documentation and/or other materials provided with the 23 | distribution. 24 | 25 | * Neither the name of Google Inc. nor the names of its 26 | contributors may be used to endorse or promote products derived from 27 | this software without specific prior written permission. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | https://memcached.org/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Memcached 2 | 3 | Memcached is a high performance multithreaded event-based key/value cache 4 | store intended to be used in a distributed system. 5 | 6 | See: https://memcached.org/about 7 | 8 | A fun story explaining usage: https://memcached.org/tutorial 9 | 10 | If you're having trouble, try the wiki: https://memcached.org/wiki 11 | 12 | If you're trying to troubleshoot odd behavior or timeouts, see: 13 | https://memcached.org/timeouts 14 | 15 | https://memcached.org/ is a good resource in general. Please use the mailing 16 | list to ask questions, github issues aren't seen by everyone! 17 | 18 | ## Dependencies 19 | 20 | * libevent - https://www.monkey.org/~provos/libevent/ (libevent-dev) 21 | * libseccomp (optional, experimental, linux) - enables process restrictions for 22 | better security. Tested only on x86-64 architectures. 23 | * openssl (optional) - enables TLS support. need relatively up to date 24 | version. pkg-config is needed to find openssl dependencies (such as -lz). 25 | 26 | ## Environment 27 | 28 | Be warned that the -k (mlockall) option to memcached might be 29 | dangerous when using a large cache. Just make sure the memcached machines 30 | don't swap. memcached does non-blocking network I/O, but not disk. (it 31 | should never go to disk, or you've lost the whole point of it) 32 | 33 | ## Build status 34 | 35 | See https://build.memcached.org/ for multi-platform regression testing status. 36 | 37 | ## Bug reports 38 | 39 | Feel free to use the issue tracker on github. 40 | 41 | **If you are reporting a security bug** please contact a maintainer privately. 42 | We follow responsible disclosure: we handle reports privately, prepare a 43 | patch, allow notifications to vendor lists. Then we push a fix release and your 44 | bug can be posted publicly with credit in our release notes and commit 45 | history. 46 | 47 | ## Website 48 | 49 | * https://www.memcached.org 50 | 51 | ## Contributing 52 | 53 | See https://github.com/memcached/memcached/wiki/DevelopmentRepos 54 | -------------------------------------------------------------------------------- /assoc.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 | void assoc_start_expand(uint64_t curr_items); 10 | /* walk functions */ 11 | void *assoc_get_iterator(void); 12 | bool assoc_iterate(void *iterp, item **it); 13 | void assoc_iterate_final(void *iterp); 14 | 15 | extern unsigned int hashpower; 16 | extern unsigned int item_lock_hashpower; 17 | -------------------------------------------------------------------------------- /authfile.h: -------------------------------------------------------------------------------- 1 | #ifndef AUTHFILE_H 2 | #define AUTHFILE_H 3 | 4 | enum authfile_ret { 5 | AUTHFILE_OK = 0, 6 | AUTHFILE_OOM, 7 | AUTHFILE_STATFAIL, // not likely, but just to be sure 8 | AUTHFILE_OPENFAIL, 9 | AUTHFILE_MALFORMED, 10 | }; 11 | 12 | // FIXME: mc_authfile or something? 13 | enum authfile_ret authfile_load(const char *file); 14 | int authfile_check(const char *user, const char *pass); 15 | 16 | #endif /* AUTHFILE_H */ 17 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Get the initial version. 4 | perl version.pl 5 | 6 | die() { 7 | echo "$@" 8 | exit 1 9 | } 10 | 11 | # Try to locate a program by using which, and verify that the file is an 12 | # executable 13 | locate_binary() { 14 | for f in $@ 15 | do 16 | file=`which $f 2>/dev/null | grep -v '^no '` 17 | if test -n "$file" -a -x "$file"; then 18 | echo $file 19 | return 0 20 | fi 21 | done 22 | 23 | echo "" 24 | return 1 25 | } 26 | 27 | echo "aclocal..." 28 | if test x$ACLOCAL = x; then 29 | ACLOCAL=`locate_binary aclocal-1.14 aclocal-1.13 aclocal-1.12 aclocal-1.11 aclocal-1.10 aclocal-1.9 aclocal19 aclocal-1.7 aclocal17 aclocal-1.5 aclocal15 aclocal` 30 | if test x$ACLOCAL = x; then 31 | die "Did not find a supported aclocal" 32 | fi 33 | fi 34 | $ACLOCAL || exit 1 35 | 36 | echo "autoheader..." 37 | AUTOHEADER=${AUTOHEADER:-autoheader} 38 | $AUTOHEADER || exit 1 39 | 40 | echo "automake..." 41 | if test x$AUTOMAKE = x; then 42 | AUTOMAKE=`locate_binary automake-1.14 automake-1.13 automake-1.12 automake-1.11 automake-1.10 automake-1.9 automake-1.7 automake` 43 | if test x$AUTOMAKE = x; then 44 | die "Did not find a supported automake" 45 | fi 46 | fi 47 | $AUTOMAKE --foreign --add-missing || $AUTOMAKE --gnu --add-missing || exit 1 48 | 49 | echo "autoconf..." 50 | AUTOCONF=${AUTOCONF:-autoconf} 51 | $AUTOCONF || exit 1 52 | 53 | -------------------------------------------------------------------------------- /base64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Base64 encoding/decoding (RFC1341) 3 | * Copyright (c) 2005, Jouni Malinen 4 | * 5 | * This software may be distributed under the terms of the BSD license. 6 | * See base64.c for more details 7 | */ 8 | 9 | #ifndef BASE64_H 10 | #define BASE64_H 11 | 12 | size_t base64_encode(const unsigned char *src, size_t len, 13 | unsigned char *out, size_t out_len); 14 | size_t base64_decode(const unsigned char *src, size_t len, 15 | unsigned char *out, size_t out_len); 16 | 17 | #endif /* BASE64_H */ 18 | -------------------------------------------------------------------------------- /bipbuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef BIPBUFFER_H 2 | #define BIPBUFFER_H 3 | 4 | typedef struct 5 | { 6 | unsigned long int size; 7 | 8 | /* region A */ 9 | unsigned int a_start, a_end; 10 | 11 | /* region B */ 12 | unsigned int b_end; 13 | 14 | /* is B inuse? */ 15 | int b_inuse; 16 | 17 | unsigned char data[]; 18 | } bipbuf_t; 19 | 20 | /** 21 | * Create a new bip buffer. 22 | * 23 | * malloc()s space 24 | * 25 | * @param[in] size The size of the buffer */ 26 | bipbuf_t *bipbuf_new(const unsigned int size); 27 | 28 | /** 29 | * Initialise a bip buffer. Use memory provided by user. 30 | * 31 | * No malloc()s are performed. 32 | * 33 | * @param[in] size The size of the array */ 34 | void bipbuf_init(bipbuf_t* me, const unsigned int size); 35 | 36 | /** 37 | * Free the bip buffer */ 38 | void bipbuf_free(bipbuf_t *me); 39 | 40 | /* TODO: DOCUMENTATION */ 41 | unsigned char *bipbuf_request(bipbuf_t* me, const int size); 42 | int bipbuf_push(bipbuf_t* me, const int size); 43 | 44 | /** 45 | * @param[in] data The data to be offered to the buffer 46 | * @param[in] size The size of the data to be offered 47 | * @return number of bytes offered */ 48 | int bipbuf_offer(bipbuf_t *me, const unsigned char *data, const int size); 49 | 50 | /** 51 | * Look at data. Don't move cursor 52 | * 53 | * @param[in] len The length of the data to be peeked 54 | * @return data on success, NULL if we can't peek at this much data */ 55 | unsigned char *bipbuf_peek(const bipbuf_t* me, const unsigned int len); 56 | 57 | /** 58 | * Look at data. Don't move cursor 59 | * 60 | * @param[in] len The length of the data returned 61 | * @return data on success, NULL if nothing available */ 62 | unsigned char *bipbuf_peek_all(const bipbuf_t* me, unsigned int *len); 63 | 64 | /** 65 | * Get pointer to data to read. Move the cursor on. 66 | * 67 | * @param[in] len The length of the data to be polled 68 | * @return pointer to data, NULL if we can't poll this much data */ 69 | unsigned char *bipbuf_poll(bipbuf_t* me, const unsigned int size); 70 | 71 | /** 72 | * @return the size of the bipbuffer */ 73 | int bipbuf_size(const bipbuf_t* me); 74 | 75 | /** 76 | * @return 1 if buffer is empty; 0 otherwise */ 77 | int bipbuf_is_empty(const bipbuf_t* me); 78 | 79 | /** 80 | * @return how much space we have assigned */ 81 | int bipbuf_used(const bipbuf_t* cb); 82 | 83 | /** 84 | * @return bytes of unused space */ 85 | int bipbuf_unused(const bipbuf_t* me); 86 | 87 | #endif /* BIPBUFFER_H */ 88 | -------------------------------------------------------------------------------- /crawler.h: -------------------------------------------------------------------------------- 1 | #ifndef CRAWLER_H 2 | #define CRAWLER_H 3 | 4 | #define LRU_CRAWLER_CAP_REMAINING -1 5 | 6 | typedef struct { 7 | uint64_t histo[61]; 8 | uint64_t ttl_hourplus; 9 | uint64_t noexp; 10 | uint64_t reclaimed; 11 | uint64_t seen; 12 | rel_time_t start_time; 13 | rel_time_t end_time; 14 | bool run_complete; 15 | } crawlerstats_t; 16 | 17 | struct crawler_expired_data { 18 | pthread_mutex_t lock; 19 | crawlerstats_t crawlerstats[POWER_LARGEST]; 20 | /* redundant with crawlerstats_t so we can get overall start/stop/done */ 21 | rel_time_t start_time; 22 | rel_time_t end_time; 23 | bool crawl_complete; 24 | bool is_external; /* whether this was an alloc local or remote to the module. */ 25 | }; 26 | 27 | enum crawler_result_type { 28 | CRAWLER_OK=0, CRAWLER_RUNNING, CRAWLER_BADCLASS, CRAWLER_NOTSTARTED, CRAWLER_ERROR 29 | }; 30 | int start_item_crawler_thread(void); 31 | #define CRAWLER_WAIT true 32 | #define CRAWLER_NOWAIT false 33 | int stop_item_crawler_thread(bool wait); 34 | int init_lru_crawler(void *arg); 35 | enum crawler_result_type lru_crawler_crawl(char *slabs, enum crawler_run_type, 36 | void *c, const int sfd, unsigned int remaining); 37 | int lru_crawler_start(uint8_t *ids, uint32_t remaining, 38 | const enum crawler_run_type type, void *data, 39 | void *c, const int sfd); 40 | void lru_crawler_pause(void); 41 | void lru_crawler_resume(void); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /crc32c.h: -------------------------------------------------------------------------------- 1 | #ifndef CRC32C_H 2 | #define CRC32C_H 3 | 4 | 5 | // crc32c.h -- header for crc32c.c 6 | // Copyright (C) 2015 Mark Adler 7 | // See crc32c.c for the license. 8 | 9 | #include 10 | 11 | // Return the CRC-32C of buf[0..len-1] given the starting CRC crc. This can be 12 | // used to calculate the CRC of a sequence of bytes a chunk at a time, using 13 | // the previously returned crc in the next call. The first call must be with 14 | // crc == 0. crc32c() uses the Intel crc32 hardware instruction if available. 15 | typedef uint32_t (*crc_func)(uint32_t crc, const void *buf, size_t len); 16 | extern crc_func crc32c; 17 | 18 | void crc32c_init(void); 19 | 20 | // Expose a prototype for the crc32c software variant simply for testing purposes 21 | uint32_t crc32c_sw(uint32_t crc, void const *buf, size_t len); 22 | 23 | #endif /* CRC32C_H */ 24 | -------------------------------------------------------------------------------- /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 | goto err_cleanup; 71 | } 72 | if(dup2(fd, STDOUT_FILENO) < 0) { 73 | perror("dup2 stdout"); 74 | goto err_cleanup; 75 | } 76 | if(dup2(fd, STDERR_FILENO) < 0) { 77 | perror("dup2 stderr"); 78 | goto err_cleanup; 79 | } 80 | 81 | if(close(fd) < 0) { 82 | perror("close"); 83 | return (-1); 84 | } 85 | } 86 | return (0); 87 | 88 | err_cleanup: 89 | close(fd); 90 | return (-1); 91 | } 92 | -------------------------------------------------------------------------------- /darwin_priv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "memcached.h" 6 | 7 | #pragma clang diagnostic push 8 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 9 | /* 10 | * the sandbox api is marked deprecated, however still used 11 | * by couple of major softwares/libraries like openssh 12 | */ 13 | void drop_privileges(void) { 14 | extern char *__progname; 15 | char *error = NULL; 16 | 17 | if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, &error) < 0) { 18 | fprintf(stderr, "%s: sandbox_init: %s\n", __progname, error); 19 | sandbox_free_error(error); 20 | exit(EXIT_FAILURE); 21 | } 22 | } 23 | 24 | #pragma clang diagnostic pop 25 | 26 | void setup_privilege_violations_handler(void) { 27 | // not needed 28 | } 29 | -------------------------------------------------------------------------------- /devtools/Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | ARG CONFIGURE_OPTS="--enable-seccomp" 4 | 5 | RUN apk update && apk add --no-cache musl-dev libevent-dev libseccomp-dev linux-headers gcc make automake autoconf perl-test-harness-utils git 6 | 7 | RUN adduser -S memcached 8 | ADD . /src 9 | WORKDIR /src 10 | 11 | RUN ./autogen.sh 12 | RUN ./configure ${CONFIGURE_OPTS} 13 | RUN make -j 14 | 15 | USER memcached 16 | 17 | CMD make test 18 | -------------------------------------------------------------------------------- /devtools/Dockerfile.arch: -------------------------------------------------------------------------------- 1 | FROM archlinux/base:latest 2 | 3 | ARG CONFIGURE_OPTS="--enable-seccomp" 4 | 5 | RUN pacman -Sy && pacman --noconfirm -S gcc automake autoconf libevent libseccomp git make perl 6 | RUN ln -s /usr/bin/core_perl/prove /usr/bin/prove 7 | 8 | RUN useradd -ms /bin/bash memcached 9 | ADD . /src 10 | WORKDIR /src 11 | 12 | RUN aclocal 13 | RUN autoheader 14 | RUN automake --gnu --add-missing 15 | RUN autoconf 16 | 17 | RUN ./configure ${CONFIGURE_OPTS} 18 | RUN make -j 19 | 20 | USER memcached 21 | 22 | CMD make test 23 | -------------------------------------------------------------------------------- /devtools/Dockerfile.fedora: -------------------------------------------------------------------------------- 1 | FROM fedora:latest 2 | 3 | ARG CONFIGURE_OPTS="--enable-seccomp" 4 | 5 | RUN dnf install -y perl automake autoconf libseccomp-devel libevent-devel gcc make git 6 | 7 | RUN useradd -ms /bin/bash memcached 8 | ADD . /src 9 | WORKDIR /src 10 | 11 | RUN aclocal && autoheader && automake --foreign --add-missing && autoconf 12 | RUN ./configure ${CONFIGURE_OPTS} 13 | RUN make -j 14 | 15 | USER memcached 16 | 17 | CMD make test 18 | -------------------------------------------------------------------------------- /devtools/Dockerfile.ubuntu: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | ARG CONFIGURE_OPTS="--enable-seccomp" 4 | 5 | RUN apt-get update && apt-get install -y build-essential automake1.11 autoconf libevent-dev libseccomp-dev git 6 | 7 | RUN useradd -ms /bin/bash memcached 8 | ADD . /src 9 | WORKDIR /src 10 | 11 | RUN ./autogen.sh 12 | RUN ./configure ${CONFIGURE_OPTS} 13 | RUN make -j 14 | 15 | USER memcached 16 | 17 | CMD make test 18 | -------------------------------------------------------------------------------- /devtools/bench_noreply.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | # 3 | use warnings; 4 | use strict; 5 | 6 | use IO::Socket::INET; 7 | 8 | use FindBin; 9 | 10 | @ARGV == 1 or @ARGV == 2 11 | or die "Usage: $FindBin::Script HOST:PORT [COUNT]\n"; 12 | 13 | # Note that it's better to run the test over the wire, because for 14 | # localhost the task may become CPU bound. 15 | my $addr = $ARGV[0]; 16 | my $count = $ARGV[1] || 10_000; 17 | 18 | my $sock = IO::Socket::INET->new(PeerAddr => $addr, 19 | Timeout => 3); 20 | die "$!\n" unless $sock; 21 | 22 | 23 | # By running 'noreply' test first we also ensure there are no reply 24 | # packets left in the network. 25 | foreach my $noreply (1, 0) { 26 | use Time::HiRes qw(gettimeofday tv_interval); 27 | 28 | print "'noreply' is ", $noreply ? "enabled" : "disabled", ":\n"; 29 | my $param = $noreply ? 'noreply' : ''; 30 | my $start = [gettimeofday]; 31 | foreach (1 .. $count) { 32 | print $sock "add foo 0 0 1 $param\r\n1\r\n"; 33 | scalar<$sock> unless $noreply; 34 | print $sock "set foo 0 0 1 $param\r\n1\r\n"; 35 | scalar<$sock> unless $noreply; 36 | print $sock "replace foo 0 0 1 $param\r\n1\r\n"; 37 | scalar<$sock> unless $noreply; 38 | print $sock "append foo 0 0 1 $param\r\n1\r\n"; 39 | scalar<$sock> unless $noreply; 40 | print $sock "prepend foo 0 0 1 $param\r\n1\r\n"; 41 | scalar<$sock> unless $noreply; 42 | print $sock "incr foo 1 $param\r\n"; 43 | scalar<$sock> unless $noreply; 44 | print $sock "decr foo 1 $param\r\n"; 45 | scalar<$sock> unless $noreply; 46 | print $sock "delete foo $param\r\n"; 47 | scalar<$sock> unless $noreply; 48 | } 49 | my $end = [gettimeofday]; 50 | printf("update commands: %.2f secs\n\n", tv_interval($start, $end)); 51 | } 52 | -------------------------------------------------------------------------------- /devtools/clean-whitespace.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use FindBin qw($Bin); 4 | chdir "$Bin/.." or die; 5 | 6 | my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md md5.c md5.h); 7 | push(@exempted, glob("doc/*.xml")); 8 | push(@exempted, glob("doc/*.full")); 9 | push(@exempted, glob("doc/xml2rfc/*.xsl")); 10 | push(@exempted, glob("m4/*backport*m4")); 11 | my %exempted_hash = map { $_ => 1 } @exempted; 12 | 13 | my @stuff = split /\0/, `git ls-files -z -c -m -o --exclude-standard`; 14 | my @files = grep { ! $exempted_hash{$_} && $_ !~ m/^vendor\// } @stuff; 15 | 16 | unless (@files) { 17 | warn "ERROR: You don't seem to be running this from a git checkout\n"; 18 | exit; 19 | } 20 | 21 | foreach my $f (@files) { 22 | open(my $fh, $f) or die; 23 | my $before = do { local $/; <$fh>; }; 24 | close ($fh); 25 | my $after = $before; 26 | $after =~ s/\t/ /g; 27 | $after =~ s/ +$//mg; 28 | $after .= "\n" unless $after =~ /\n$/; 29 | next if $after eq $before; 30 | open(my $fh, ">$f") or die; 31 | print $fh $after; 32 | close($fh); 33 | } 34 | -------------------------------------------------------------------------------- /devtools/slab_loadgen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # Copyright 2017 Facebook 3 | # Licensed under the same terms as memcached itself. 4 | 5 | import argparse 6 | import socket 7 | import csv 8 | from time import sleep 9 | 10 | parser = argparse.ArgumentParser(description="daemon for emulating set/get pressure") 11 | parser.add_argument("--host", help="host to connect to", 12 | type=str, default="localhost:11211") 13 | parser.add_argument("-s", "--sleep", help="seconds between rounds", 14 | type=int, default="1") 15 | parser.add_argument("-c", "--config", help="load specification file", 16 | type=str, default="./config") 17 | 18 | args = parser.parse_args() 19 | 20 | # prefix, size, count, do_gets 21 | with open(args.config, newline='') as f: 22 | reader = csv.reader(f) 23 | tests = [row for row in reader] 24 | 25 | host, port = args.host.split(':') 26 | 27 | c = socket.create_connection((host, port), 5) 28 | s = c.makefile(mode="rw", buffering=1) 29 | 30 | global_counter = 0 31 | 32 | while True: 33 | # LATER: stat argument file 34 | # LATER: reload arg file if necessary 35 | for test in tests: 36 | prefix = test[0] 37 | size = int(test[1]) 38 | count = int(test[2]) 39 | do_gets = int(test[3]) 40 | value = 'x'*size 41 | # issue N 'noreply' sets per specified size 42 | for i in range(count): 43 | s.write('set {}{} 0 0 {} noreply\r\n{}\r\n'.format(prefix,global_counter,size,value)) 44 | global_counter += 1 45 | # TODO: issue N gets per specified size? 46 | sleep(args.sleep) 47 | -------------------------------------------------------------------------------- /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 | A list is generated from git here: https://memcached.org/about 11 | 12 | Major authors: 13 | -------------- 14 | 15 | Brad Fitzpatrick -- maintainer, original implementations 16 | 17 | Anatoly Vorobey -- lots of the modern server code 18 | 19 | Steven Grimm -- iov writing (less CPU), UDP mode, 20 | non-2.0 slab mantissas, multithread, ... 21 | 22 | Other Contributors 23 | ------------------ 24 | 25 | Evan Martin 26 | Nathan Neulinger 27 | Eric Hodel 28 | Michael Johnson 29 | Paul Querna 30 | Jamie McCarthy 31 | Philip Neustrom 32 | Andrew O'Brien 33 | Josh Rotenberg 34 | Robin H. Johnson 35 | Tim Yardley 36 | Paolo Borelli 37 | Eli Bingham 38 | Jean-Francois Bustarret 39 | Paul G 40 | Paul Lindner 41 | Dormando 42 | Dustin Sallings 43 | Chris Goffinet 44 | Tomash Brechko 45 | Brian Aker 46 | Trond Norbye 47 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = memcached.1 2 | 3 | SPECIFICATIONS = protocol-binary.txt protocol-binary-range.txt 4 | 5 | EXTRA_DIST = $(srcdir)/*.txt 6 | 7 | # When we make dist always include the generated specifications so people don't 8 | # have to have xml2rfc and xsltproc installed 9 | EXTRA_DIST += $(SPECIFICATIONS) 10 | 11 | BUILT_SOURCES = 12 | MOSTLYCLEANFILES = 13 | #if BUILD_SPECIFICATIONS 14 | #BUILT_SOURCES += $(SPECIFICATIONS) 15 | #MOSTLYCLEANFILES += $(SPECIFICATIONS) 16 | #endif 17 | 18 | #RFC2629_XSL = $(srcdir)/xml2rfc/rfc2629-noinc.xsl 19 | #RFC2629_DTD = $(srcdir)/xml2rfc/rfc2629.dtd 20 | 21 | #%.txt: %.full $(RFC2629_DTD) 22 | # @XML2RFC@ -d $(RFC2629_DTD) -c $(builddir) $< $@ 23 | # 24 | #%.full: %.xml $(RFC2629_XSL) 25 | # @XSLTPROC@ --nonet $(RFC2629_XSL) $< > $@ 26 | 27 | # Prevent make from deleting intermediate files 28 | #all-full: $(SPECIFICATIONS:.txt=.full) 29 | 30 | #clean-local: 31 | # rm -f $(SPECIFICATIONS:.txt=.full) 32 | 33 | #.PHONY: all-full 34 | -------------------------------------------------------------------------------- /doc/napi_ids.txt: -------------------------------------------------------------------------------- 1 | NAPI ID based worker thread selection 2 | -N | --napi_ids= 3 | 4 | By default memcached assigns connections to worker threads in a round-robin 5 | manner. NAPI ID based worker thread selection enables worker threads to be 6 | selected based on the NIC HW RX queue on which the incoming request is 7 | received. 8 | 9 | This is enabled using SO_INCOMING_NAPI_ID socket option that is supported 10 | in linux kernels 4.12 or higher. This socket option returns a system level 11 | unique ID called NAPI ID that is associated with a RX queue on which the 12 | last packet associated with that socket is received. 13 | 14 | This allows memcached to split the incoming flows among threads based on 15 | the RX queue on which they are received. Each worker thread is associated 16 | with a NIC HW receive queue and services all the connection requests 17 | received on a specific RX queue. This mapping between a memcached thread 18 | and a HW NIC queue streamlines the flow of data from the NIC to the 19 | application. In addition, an optimal path with reduced context switches is 20 | possible, if epoll based busy polling 21 | (sysctl -w net.core.busy_poll = ) is also enabled. 22 | 23 | This feature is enabled via a new command line parameter -N or 24 | "--napi_ids=", where is the number of available/assigned NIC 25 | hardware RX queues through which requests associated with a connection are 26 | received. The number of napi_ids specified cannot be greater than the number 27 | of worker threads specified using -t/--threads option. If the option is 28 | not specified, or the conditions not met, the code defaults to round robin 29 | thread selection. 30 | 31 | During a normal run, each worker thread gets associated with a napi_id and 32 | this will establish a 1:1 mapping between the thread and queues. If a new 33 | napi_id is received after each thread is associated with its own napi_id 34 | (this can happen if num_napi_ids argument doesn't exactly match with the 35 | number of RX queues OR if the NIC driver goes through a reload), a stats 36 | error counter called 'unexpected_napi_ids' is incremented and all the 37 | napi_id's associated with the threads are reset. 38 | 39 | If -N option is used, but the connection requests are received from a 40 | virtual interface like loopback, napi_id returned can be 0. This condition 41 | is tracked via a stats counter called 'round_robin_fallback'. 42 | -------------------------------------------------------------------------------- /doc/new_lru.txt: -------------------------------------------------------------------------------- 1 | In versions new enough to have the `-o lru_maintainer` option, a new LRU 2 | mechanic is available. 3 | 4 | Previously: Each slab class has an independent doubly-linked list comprising 5 | its LRU. Items are pulled from the bottom and either reclaimed or evicted as 6 | needed. 7 | 8 | Now, enabling `-o lru_maintainer` changes all of the behavior below: 9 | 10 | * LRU's are now split between HOT, WARM, and COLD LRU's. New items enter the 11 | HOT LRU. 12 | * Items hit at least twice are considered active. 13 | * LRU updates only happen as items reach the bottom of an LRU. If active in 14 | HOT, move to WARM, if active in WARM, stay in WARM. If active in COLD, move 15 | to WARM. 16 | The exception is that items active in COLD are immediately moved to WARM. 17 | * HOT/WARM each capped at N% of memory available for that slab class. COLD 18 | is uncapped (by default, as of this writing). 19 | * HOT/WARM are also age capped by ratios of COLD's age. IE: HOT is capped at 20 | N% of memory, or 10% of the age of COLD, whichever comes first. 21 | * Items flow from HOT/WARM into COLD. 22 | * A background thread exists which shuffles items between/within the LRU's as 23 | limits are reached. This includes moves from COLD to WARM. 24 | * The background thread can also control the lru_crawler, if enabled. 25 | 26 | The primary goal is to better protect active items from "scanning". Items 27 | which are never hit again will flow from HOT, through COLD, and out the 28 | bottom. Items occasionally active (reaching COLD, but being hit before 29 | eviction), move to WARM. There they can stay relatively protected. 30 | 31 | A secondary goal is to improve latency. The LRU locks are no longer used on 32 | most item reads, largely during sets and from the background thread. Also the 33 | background thread is likely to find expired items and release them back to the 34 | slab class asynchronously, which speeds up new allocations. 35 | 36 | It is recommended to use this feature with the lru crawler as well: 37 | `memcached -o lru_maintainer,lru_crawler` - Then it will automatically scan 38 | slab classes for items with expired TTL's. If your items are always set to 39 | never expire, you can omit this option safely. 40 | 41 | An extra option: `-o temporary_ttl=N` (when used with lru_maintainer) will make 42 | items with a TTL less than or equal to this value use a fourth TEMP LRU. Items 43 | stored in TEMP are never bumped within its LRU or moved to other LRU's. They 44 | also cannot be evicted. This can help reduce holes and load on the LRU crawler. 45 | 46 | Do not set temporary_ttl too high or memory could become exhausted. 47 | -------------------------------------------------------------------------------- /doc/readme.txt: -------------------------------------------------------------------------------- 1 | To build the documentation you need xml2rfc ( http://xml.resource.org/ ). 2 | -------------------------------------------------------------------------------- /doc/threads.txt: -------------------------------------------------------------------------------- 1 | Multithreading in memcached *was* originally simple: 2 | 3 | - One listener thread 4 | - N "event worker" threads 5 | - Some misc background threads 6 | 7 | Each worker thread is assigned connections, and runs its own epoll loop. The 8 | central hash table, LRU lists, and some statistics counters are covered by 9 | global locks. Protocol parsing, data transfer happens in threads. Data lookups 10 | and modifications happen under central locks. 11 | 12 | THIS HAS CHANGED! 13 | 14 | - A secondary small hash table of locks is used to lock an item by its hash 15 | value. This prevents multiple threads from acting on the same item at the 16 | same time. 17 | - This secondary hash table is mapped to the central hash tables buckets. This 18 | allows multiple threads to access the hash table in parallel. Only one 19 | thread may read or write against a particular hash table bucket. 20 | - atomic refcounts per item are used to manage garbage collection and 21 | mutability. 22 | 23 | - When pulling an item off of the LRU tail for eviction or re-allocation, the 24 | system must attempt to lock the item's bucket, which is done with a trylock 25 | to avoid deadlocks. If a bucket is in use (and not by that thread) it will 26 | walk up the LRU a little in an attempt to fetch a non-busy item. 27 | 28 | - Each LRU (and sub-LRU's in newer modes) has an independent lock. 29 | 30 | - Raw accesses to the slab class are protected by a global slabs_lock. This 31 | is a short lock which covers pushing and popping free memory. 32 | 33 | - item_lock must be held while modifying an item. 34 | - slabs_lock must be held while modifying the ITEM_SLABBED flag bit within an item. 35 | - ITEM_LINKED must not be set before an item has a key copied into it. 36 | - items without ITEM_SLABBED set cannot have their memory zeroed out. 37 | 38 | LOCK ORDERS: 39 | 40 | (incomplete as of writing, sorry): 41 | 42 | item_lock -> lru_lock -> slabs_lock 43 | 44 | lru_lock -> item_trylock 45 | 46 | Various stats_locks should never have other locks as dependencies. 47 | 48 | Various locks exist for background threads. They can be used to pause the 49 | thread execution or update settings while the threads are idle. They may call 50 | item or lru locks. 51 | 52 | A low priority issue: 53 | 54 | - If you remove the per-thread stats lock, CPU usage goes down by less than a 55 | point of a percent, and it does not improve scalability. 56 | - In my testing, the remaining global STATS_LOCK calls never seem to collide. 57 | 58 | Yes, more stats can be moved to threads, and those locks can actually be 59 | removed entirely on x86-64 systems. However my tests haven't shown that as 60 | beneficial so far, so I've prioritized other work. 61 | -------------------------------------------------------------------------------- /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/reference.RFC.2119.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Key words for use in RFCs to Indicate Requirement Levels 7 | 8 | Harvard University 9 |
10 | 11 | 1350 Mass. Ave. 12 | Cambridge 13 | MA 02138 14 | - +1 617 495 3864 15 | sob@harvard.edu
16 | 17 | General 18 | keyword 19 | 20 | 21 | In many standards track documents several words are used to signify 22 | the requirements in the specification. These words are often 23 | capitalized. This document defines these words as they should be 24 | interpreted in IETF documents. Authors who follow these guidelines 25 | should incorporate this phrase near the beginning of their document: 26 | 27 | 28 | 29 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL 30 | NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and 31 | "OPTIONAL" in this document are to be interpreted as described in 32 | RFC 2119. 33 | 34 | 35 | Note that the force of these words is modified by the requirement 36 | level of the document in which they are used. 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | alpine: 4 | build: 5 | context: . 6 | dockerfile: devtools/Dockerfile.alpine 7 | ubuntu: 8 | build: 9 | context: . 10 | dockerfile: devtools/Dockerfile.ubuntu 11 | arch: 12 | build: 13 | context: . 14 | dockerfile: devtools/Dockerfile.arch 15 | fedora: 16 | build: 17 | context: . 18 | dockerfile: devtools/Dockerfile.fedora 19 | -------------------------------------------------------------------------------- /freebsd_priv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "memcached.h" 8 | 9 | /* 10 | * dropping privileges is entering in capability mode 11 | * in FreeBSD vocabulary. 12 | */ 13 | void drop_privileges() { 14 | cap_rights_t wd, rd; 15 | 16 | if (cap_rights_init(&wd, CAP_WRITE, CAP_READ) == NULL) { 17 | fprintf(stderr, "cap_rights_init write protection failed: %s\n", strerror(errno)); 18 | exit(EXIT_FAILURE); 19 | } 20 | 21 | if (cap_rights_init(&rd, CAP_FCNTL, CAP_READ, CAP_EVENT) == NULL) { 22 | fprintf(stderr, "cap_rights_init read protection failed: %s\n", strerror(errno)); 23 | exit(EXIT_FAILURE); 24 | } 25 | 26 | if (cap_rights_limit(STDIN_FILENO, &rd) != 0) { 27 | fprintf(stderr, "cap_rights_limit stdin failed: %s\n", strerror(errno)); 28 | exit(EXIT_FAILURE); 29 | } 30 | 31 | if (cap_rights_limit(STDOUT_FILENO, &wd) != 0) { 32 | fprintf(stderr, "cap_rights_limit stdout failed: %s\n", strerror(errno)); 33 | exit(EXIT_FAILURE); 34 | } 35 | 36 | if (cap_rights_limit(STDERR_FILENO, &wd) != 0) { 37 | fprintf(stderr, "cap_rights_limit stderr failed: %s\n", strerror(errno)); 38 | exit(EXIT_FAILURE); 39 | } 40 | 41 | if (cap_enter() != 0) { 42 | fprintf(stderr, "cap_enter failed: %s\n", strerror(errno)); 43 | exit(EXIT_FAILURE); 44 | } 45 | } 46 | 47 | void setup_privilege_violations_handler(void) { 48 | // not needed 49 | } 50 | -------------------------------------------------------------------------------- /globals.c: -------------------------------------------------------------------------------- 1 | #include "memcached.h" 2 | 3 | /* 4 | * This file contains global variables shared across the rest of the 5 | * memcached codebase. These were originally in memcached.c but had 6 | * to be removed to make the rest of the object files linkable into 7 | * the test infrastructure. 8 | * 9 | */ 10 | 11 | /* 12 | * We keep the current time of day in a global variable that's updated by a 13 | * timer event. This saves us a bunch of time() system calls (we really only 14 | * need to get the time once a second, whereas there can be tens of thousands 15 | * of requests a second) and allows us to use server-start-relative timestamps 16 | * rather than absolute UNIX timestamps, a space savings on systems where 17 | * sizeof(time_t) > sizeof(unsigned int). 18 | */ 19 | volatile rel_time_t current_time; 20 | 21 | /** exported globals **/ 22 | struct stats stats; 23 | struct stats_state stats_state; 24 | struct settings settings; 25 | struct slab_rebalance slab_rebal; 26 | volatile int slab_rebalance_signal; 27 | -------------------------------------------------------------------------------- /hash.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | 3 | #include "memcached.h" 4 | #include "jenkins_hash.h" 5 | #include "murmur3_hash.h" 6 | #define XXH_INLINE_ALL // modifier for xxh3's include below 7 | #include "xxhash.h" 8 | 9 | hash_func hash; 10 | 11 | static uint32_t XXH3_hash(const void *key, size_t length) { 12 | return (uint32_t)XXH3_64bits(key, length); 13 | } 14 | 15 | int hash_init(enum hashfunc_type type) { 16 | switch(type) { 17 | case JENKINS_HASH: 18 | hash = jenkins_hash; 19 | settings.hash_algorithm = "jenkins"; 20 | break; 21 | case MURMUR3_HASH: 22 | hash = MurmurHash3_x86_32; 23 | settings.hash_algorithm = "murmur3"; 24 | break; 25 | case XXH3_HASH: 26 | hash = XXH3_hash; 27 | settings.hash_algorithm = "xxh3"; 28 | break; 29 | default: 30 | return -1; 31 | } 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_H 2 | #define HASH_H 3 | 4 | typedef uint32_t (*hash_func)(const void *key, size_t length); 5 | extern hash_func hash; 6 | 7 | enum hashfunc_type { 8 | JENKINS_HASH=0, MURMUR3_HASH, XXH3_HASH 9 | }; 10 | 11 | int hash_init(enum hashfunc_type type); 12 | 13 | #endif /* HASH_H */ 14 | 15 | -------------------------------------------------------------------------------- /items.h: -------------------------------------------------------------------------------- 1 | #define HOT_LRU 0 2 | #define WARM_LRU 64 3 | #define COLD_LRU 128 4 | #define TEMP_LRU 192 5 | 6 | #define CLEAR_LRU(id) (id & ~(3<<6)) 7 | #define GET_LRU(id) (id & (3<<6)) 8 | 9 | /* See items.c */ 10 | uint64_t get_cas_id(void); 11 | void set_cas_id(uint64_t new_cas); 12 | 13 | /*@null@*/ 14 | item *do_item_alloc(char *key, const size_t nkey, const unsigned int flags, const rel_time_t exptime, const int nbytes); 15 | item_chunk *do_item_alloc_chunk(item_chunk *ch, const size_t bytes_remain); 16 | item *do_item_alloc_pull(const size_t ntotal, const unsigned int id); 17 | void item_free(item *it); 18 | bool item_size_ok(const size_t nkey, const int flags, const int nbytes); 19 | 20 | int do_item_link(item *it, const uint32_t hv); /** may fail if transgresses limits */ 21 | void do_item_unlink(item *it, const uint32_t hv); 22 | void do_item_unlink_nolock(item *it, const uint32_t hv); 23 | void do_item_remove(item *it); 24 | void do_item_update(item *it); /** update LRU time to current and reposition */ 25 | void do_item_update_nolock(item *it); 26 | int do_item_replace(item *it, item *new_it, const uint32_t hv); 27 | void do_item_link_fixup(item *it); 28 | 29 | int item_is_flushed(item *it); 30 | unsigned int do_get_lru_size(uint32_t id); 31 | 32 | void do_item_linktail_q(item *it); 33 | void do_item_unlinktail_q(item *it); 34 | item *do_item_crawl_q(item *it); 35 | 36 | void *item_lru_bump_buf_create(void); 37 | 38 | #define LRU_PULL_EVICT 1 39 | #define LRU_PULL_CRAWL_BLOCKS 2 40 | #define LRU_PULL_RETURN_ITEM 4 /* fill info struct if available */ 41 | 42 | struct lru_pull_tail_return { 43 | item *it; 44 | uint32_t hv; 45 | }; 46 | 47 | int lru_pull_tail(const int orig_id, const int cur_lru, 48 | const uint64_t total_bytes, const uint8_t flags, const rel_time_t max_age, 49 | struct lru_pull_tail_return *ret_it); 50 | 51 | /*@null@*/ 52 | char *item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes); 53 | void item_stats(ADD_STAT add_stats, void *c); 54 | void do_item_stats_add_crawl(const int i, const uint64_t reclaimed, 55 | const uint64_t unfetched, const uint64_t checked); 56 | void item_stats_totals(ADD_STAT add_stats, void *c); 57 | /*@null@*/ 58 | void item_stats_sizes(ADD_STAT add_stats, void *c); 59 | void item_stats_sizes_init(void); 60 | void item_stats_sizes_enable(ADD_STAT add_stats, void *c); 61 | void item_stats_sizes_disable(ADD_STAT add_stats, void *c); 62 | void item_stats_sizes_add(item *it); 63 | void item_stats_sizes_remove(item *it); 64 | bool item_stats_sizes_status(void); 65 | 66 | /* stats getter for slab automover */ 67 | typedef struct { 68 | int64_t evicted; 69 | int64_t outofmemory; 70 | uint32_t age; 71 | } item_stats_automove; 72 | void fill_item_stats_automove(item_stats_automove *am); 73 | 74 | item *do_item_get(const char *key, const size_t nkey, const uint32_t hv, conn *c, const bool do_update); 75 | item *do_item_touch(const char *key, const size_t nkey, uint32_t exptime, const uint32_t hv, conn *c); 76 | void do_item_bump(conn *c, item *it, const uint32_t hv); 77 | void item_stats_reset(void); 78 | extern pthread_mutex_t lru_locks[POWER_LARGEST]; 79 | 80 | int start_lru_maintainer_thread(void *arg); 81 | int stop_lru_maintainer_thread(void); 82 | int init_lru_maintainer(void); 83 | void lru_maintainer_pause(void); 84 | void lru_maintainer_resume(void); 85 | 86 | void *lru_bump_buf_create(void); 87 | -------------------------------------------------------------------------------- /itoa_ljust.h: -------------------------------------------------------------------------------- 1 | #ifndef ITOA_LJUST_H 2 | #define ITOA_LJUST_H 3 | 4 | //=== itoa_ljust.h - Fast integer to ascii conversion 5 | // 6 | // Fast and simple integer to ASCII conversion: 7 | // 8 | // - 32 and 64-bit integers 9 | // - signed and unsigned 10 | // - user supplied buffer must be large enough for all decimal digits 11 | // in value plus minus sign if negative 12 | // - left-justified 13 | // - NUL terminated 14 | // - return value is pointer to NUL terminator 15 | // 16 | // Copyright (c) 2016 Arturo Martin-de-Nicolas 17 | // arturomdn@gmail.com 18 | // https://github.com/amdn/itoa_ljust/ 19 | //===----------------------------------------------------------------------===// 20 | 21 | #include 22 | 23 | char* itoa_u32(uint32_t u, char* buffer); 24 | char* itoa_32( int32_t i, char* buffer); 25 | char* itoa_u64(uint64_t u, char* buffer); 26 | char* itoa_64( int64_t i, char* buffer); 27 | 28 | #endif // ITOA_LJUST_H 29 | -------------------------------------------------------------------------------- /jenkins_hash.h: -------------------------------------------------------------------------------- 1 | #ifndef JENKINS_HASH_H 2 | #define JENKINS_HASH_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | uint32_t jenkins_hash(const void *key, size_t length); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | #endif /* JENKINS_HASH_H */ 15 | 16 | -------------------------------------------------------------------------------- /murmur3_hash.c: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // MurmurHash3 was written by Austin Appleby, and is placed in the public 3 | // domain. The author hereby disclaims copyright to this source code. 4 | 5 | // Note - The x86 and x64 versions do _not_ produce the same results, as the 6 | // algorithms are optimized for their respective platforms. You can still 7 | // compile and run any of them on any platform, but your performance with the 8 | // non-native version will be less than optimal. 9 | 10 | #include "murmur3_hash.h" 11 | 12 | //----------------------------------------------------------------------------- 13 | // Platform-specific functions and macros 14 | 15 | // Microsoft Visual Studio 16 | 17 | #if defined(_MSC_VER) 18 | 19 | #define FORCE_INLINE __forceinline 20 | 21 | #include 22 | 23 | #define ROTL32(x,y) _rotl(x,y) 24 | 25 | #define BIG_CONSTANT(x) (x) 26 | 27 | // Other compilers 28 | 29 | #else // defined(_MSC_VER) 30 | 31 | #define FORCE_INLINE inline __attribute__((always_inline)) 32 | 33 | static inline uint32_t rotl32 ( uint32_t x, int8_t r ) 34 | { 35 | return (x << r) | (x >> (32 - r)); 36 | } 37 | 38 | #define ROTL32(x,y) rotl32(x,y) 39 | 40 | #define BIG_CONSTANT(x) (x##LLU) 41 | 42 | #endif // !defined(_MSC_VER) 43 | 44 | //----------------------------------------------------------------------------- 45 | // Block read - if your platform needs to do endian-swapping or can only 46 | // handle aligned reads, do the conversion here 47 | 48 | static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) 49 | { 50 | return p[i]; 51 | } 52 | 53 | //----------------------------------------------------------------------------- 54 | // Finalization mix - force all bits of a hash block to avalanche 55 | 56 | static FORCE_INLINE uint32_t fmix32 ( uint32_t h ) 57 | { 58 | h ^= h >> 16; 59 | h *= 0x85ebca6b; 60 | h ^= h >> 13; 61 | h *= 0xc2b2ae35; 62 | h ^= h >> 16; 63 | 64 | return h; 65 | } 66 | 67 | //----------------------------------------------------------------------------- 68 | 69 | /* Definition modified slightly from the public domain interface (no seed + 70 | * return value */ 71 | uint32_t MurmurHash3_x86_32 ( const void * key, size_t length) 72 | { 73 | const uint8_t * data = (const uint8_t*)key; 74 | const int nblocks = length / 4; 75 | 76 | uint32_t h1 = 0; 77 | 78 | uint32_t c1 = 0xcc9e2d51; 79 | uint32_t c2 = 0x1b873593; 80 | 81 | //---------- 82 | // body 83 | 84 | const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); 85 | 86 | for(int i = -nblocks; i; i++) 87 | { 88 | uint32_t k1 = getblock32(blocks,i); 89 | 90 | k1 *= c1; 91 | k1 = ROTL32(k1,15); 92 | k1 *= c2; 93 | 94 | h1 ^= k1; 95 | h1 = ROTL32(h1,13); 96 | h1 = h1*5+0xe6546b64; 97 | } 98 | 99 | //---------- 100 | // tail 101 | 102 | const uint8_t * tail = (const uint8_t*)(data + nblocks*4); 103 | 104 | uint32_t k1 = 0; 105 | 106 | switch(length & 3) 107 | { 108 | case 3: k1 ^= tail[2] << 16; 109 | case 2: k1 ^= tail[1] << 8; 110 | case 1: k1 ^= tail[0]; 111 | k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; 112 | }; 113 | 114 | //---------- 115 | // finalization 116 | 117 | h1 ^= length; 118 | 119 | h1 = fmix32(h1); 120 | 121 | //*(uint32_t*)out = h1; 122 | return h1; 123 | } 124 | 125 | -------------------------------------------------------------------------------- /murmur3_hash.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // MurmurHash3 was written by Austin Appleby, and is placed in the public 3 | // domain. The author hereby disclaims copyright to this source code. 4 | 5 | #ifndef MURMURHASH3_H 6 | #define MURMURHASH3_H 7 | 8 | //----------------------------------------------------------------------------- 9 | // Platform-specific functions and macros 10 | #include 11 | #include 12 | 13 | //----------------------------------------------------------------------------- 14 | 15 | uint32_t MurmurHash3_x86_32(const void *key, size_t length); 16 | 17 | //----------------------------------------------------------------------------- 18 | 19 | #endif // MURMURHASH3_H 20 | -------------------------------------------------------------------------------- /openbsd_priv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "memcached.h" 7 | 8 | /* 9 | * this section of code will drop all (OpenBSD) privileges including 10 | * those normally granted to all userland process (basic privileges). The 11 | * effect of this is that after running this code, the process will not able 12 | * to fork(), exec(), etc. See pledge(2) for more information. 13 | */ 14 | void drop_privileges() { 15 | extern char *__progname; 16 | 17 | if (settings.socketpath != NULL) { 18 | if (pledge("stdio unix", NULL) == -1) { 19 | fprintf(stderr, "%s: pledge: %s\n", __progname, strerror(errno)); 20 | exit(EXIT_FAILURE); 21 | } 22 | } else { 23 | if (pledge("stdio inet", NULL) == -1) { 24 | fprintf(stderr, "%s: pledge: %s\n", __progname, strerror(errno)); 25 | exit(EXIT_FAILURE); 26 | } 27 | } 28 | } 29 | 30 | void setup_privilege_violations_handler(void) { 31 | // not needed 32 | } 33 | -------------------------------------------------------------------------------- /proto_bin.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTO_BIN_H 2 | #define PROTO_BIN_H 3 | 4 | /* binary protocol handlers */ 5 | int try_read_command_binary(conn *c); 6 | void complete_nread_binary(conn *c); 7 | void write_bin_error(conn *c, protocol_binary_response_status err, 8 | const char *errstr, int swallow); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /proto_proxy.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTO_PROXY_H 2 | #define PROTO_PROXY_H 3 | 4 | void proxy_stats(void *arg, ADD_STAT add_stats, conn *c); 5 | void process_proxy_stats(void *arg, ADD_STAT add_stats, conn *c); 6 | 7 | /* proxy mode handlers */ 8 | int try_read_command_proxy(conn *c); 9 | void complete_nread_proxy(conn *c); 10 | void proxy_cleanup_conn(conn *c); 11 | void proxy_thread_init(void *ctx, LIBEVENT_THREAD *thr); 12 | void *proxy_init(bool proxy_uring); 13 | // TODO: need better names or a better interface for these. can be confusing 14 | // to reason about the order. 15 | void proxy_start_reload(void *arg); 16 | int proxy_load_config(void *arg); 17 | void proxy_worker_reload(void *arg, LIBEVENT_THREAD *thr); 18 | 19 | void proxy_submit_cb(io_queue_t *q); 20 | void proxy_complete_cb(io_queue_t *q); 21 | void proxy_return_cb(io_pending_t *pending); 22 | void proxy_finalize_cb(io_pending_t *pending); 23 | 24 | /* lua */ 25 | int proxy_register_libs(void *ctx, LIBEVENT_THREAD *t, void *state); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /proto_text.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTO_TEXT_H 2 | #define PROTO_TEXT_H 3 | 4 | /* text protocol handlers */ 5 | void complete_nread_ascii(conn *c); 6 | int try_read_command_asciiauth(conn *c); 7 | int try_read_command_ascii(conn *c); 8 | void process_command_ascii(conn *c, char *command); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /proxy_jump_hash.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | 3 | #include "proxy.h" 4 | 5 | typedef struct { 6 | struct proxy_hash_caller phc; // passed back to proxy API 7 | unsigned int buckets; 8 | } mcplib_jump_hash_t; 9 | 10 | static uint32_t mcplib_dist_jump_hash_get_server(uint64_t hash, void *ctx) { 11 | mcplib_jump_hash_t *jh = ctx; 12 | 13 | int64_t b = -1, j = 0; 14 | while (j < jh->buckets) { 15 | b = j; 16 | hash = hash * 2862933555777941757ULL + 1; 17 | j = (b + 1) * ((double)(1LL << 31) / (double)((hash >> 33) + 1)); 18 | } 19 | return b; 20 | } 21 | 22 | // stack = [pool, option] 23 | static int mcplib_dist_jump_hash_new(lua_State *L) { 24 | luaL_checktype(L, 1, LUA_TTABLE); 25 | lua_Unsigned buckets = lua_rawlen(L, 1); 26 | 27 | mcplib_jump_hash_t *jh = lua_newuserdatauv(L, sizeof(mcplib_jump_hash_t), 0); 28 | 29 | // don't need to loop through the table at all, just need its length. 30 | // could optimize startup time by adding hints to the module for how to 31 | // format pool (ie; just a total count or the full table) 32 | jh->buckets = buckets; 33 | jh->phc.ctx = jh; 34 | jh->phc.selector_func = mcplib_dist_jump_hash_get_server; 35 | 36 | lua_pushlightuserdata(L, &jh->phc); 37 | 38 | // - return [UD, lightuserdata] 39 | return 2; 40 | } 41 | 42 | int mcplib_open_dist_jump_hash(lua_State *L) { 43 | const struct luaL_Reg jump_f[] = { 44 | {"new", mcplib_dist_jump_hash_new}, 45 | {NULL, NULL}, 46 | }; 47 | 48 | luaL_newlib(L, jump_f); 49 | 50 | return 1; 51 | } 52 | -------------------------------------------------------------------------------- /proxy_ustats.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | 3 | #include "proxy.h" 4 | 5 | // mcp.add_stat(index, name) 6 | // creates a custom lua stats counter 7 | int mcplib_add_stat(lua_State *L) { 8 | LIBEVENT_THREAD *t = lua_touserdata(L, lua_upvalueindex(MCP_THREAD_UPVALUE)); 9 | if (t != NULL) { 10 | proxy_lua_error(L, "add_stat must be called from config_pools"); 11 | return 0; 12 | } 13 | int idx = luaL_checkinteger(L, -2); 14 | const char *name = luaL_checkstring(L, -1); 15 | 16 | if (idx < 1) { 17 | proxy_lua_error(L, "stat index must be 1 or higher"); 18 | return 0; 19 | } 20 | // max user counters? 1024? some weird number. 21 | if (idx > 1024) { 22 | proxy_lua_error(L, "stat index must be 1024 or less"); 23 | return 0; 24 | } 25 | // max name length? avoids errors if something huge gets thrown in. 26 | if (strlen(name) > STAT_KEY_LEN - 6) { 27 | // we prepend "user_" to the output. + null byte. 28 | proxy_lua_ferror(L, "stat name too long: %s\n", name); 29 | return 0; 30 | } 31 | // restrict characters, at least no spaces/newlines. 32 | for (int x = 0; x < strlen(name); x++) { 33 | if (isspace(name[x])) { 34 | proxy_lua_error(L, "stat cannot contain spaces or newlines"); 35 | return 0; 36 | } 37 | } 38 | 39 | proxy_ctx_t *ctx = lua_touserdata(L, lua_upvalueindex(MCP_CONTEXT_UPVALUE)); 40 | 41 | STAT_L(ctx); 42 | struct proxy_user_stats *us = &ctx->user_stats; 43 | 44 | // if num_stats is 0 we need to init sizes. 45 | // TODO (v2): malloc fail checking. (should be rare/impossible) 46 | if (us->num_stats < idx) { 47 | // don't allocate counters memory for the global ctx. 48 | char **nnames = calloc(idx, sizeof(char *)); 49 | if (us->names != NULL) { 50 | for (int x = 0; x < us->num_stats; x++) { 51 | nnames[x] = us->names[x]; 52 | } 53 | free(us->names); 54 | } 55 | us->names = nnames; 56 | us->num_stats = idx; 57 | } 58 | 59 | idx--; // real slot start as 0. 60 | // if slot has string in it, free first 61 | if (us->names[idx] != NULL) { 62 | free(us->names[idx]); 63 | } 64 | // strdup name into string slot 65 | // TODO (v2): malloc failure. 66 | us->names[idx] = strdup(name); 67 | STAT_UL(ctx); 68 | 69 | return 0; 70 | } 71 | 72 | int mcplib_stat(lua_State *L) { 73 | LIBEVENT_THREAD *t = lua_touserdata(L, lua_upvalueindex(MCP_THREAD_UPVALUE)); 74 | if (t == NULL) { 75 | proxy_lua_error(L, "stat must be called from router handlers"); 76 | return 0; 77 | } 78 | 79 | struct proxy_user_stats *tus = t->proxy_user_stats; 80 | if (tus == NULL) { 81 | proxy_lua_error(L, "no stats counters initialized"); 82 | return 0; 83 | } 84 | 85 | int idx = luaL_checkinteger(L, -2); 86 | int change = luaL_checkinteger(L, -1); 87 | 88 | if (idx < 1 || idx > tus->num_stats) { 89 | proxy_lua_error(L, "stat index out of range"); 90 | return 0; 91 | } 92 | 93 | idx--; // actual array is 0 indexed. 94 | WSTAT_L(t); 95 | tus->counters[idx] += change; 96 | WSTAT_UL(t); 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /proxy_xxhash.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | 3 | #include "proxy.h" 4 | 5 | static struct proxy_hash_func mcplib_hash_xxhash = { 6 | XXH3_64bits_withSeed, 7 | }; 8 | 9 | int mcplib_open_hash_xxhash(lua_State *L) { 10 | lua_pushlightuserdata(L, &mcplib_hash_xxhash); 11 | return 1; 12 | } 13 | -------------------------------------------------------------------------------- /restart.h: -------------------------------------------------------------------------------- 1 | #ifndef RESTART_H 2 | #define RESTART_H 3 | 4 | #define RESTART_TAG_MAXLEN 255 5 | 6 | // Track the pointer size for restart fiddling. 7 | #if SIZEOF_VOID_P == 8 8 | typedef uint64_t mc_ptr_t; 9 | #else 10 | typedef uint32_t mc_ptr_t; 11 | #endif 12 | 13 | enum restart_get_kv_ret { 14 | RESTART_OK=0, RESTART_NOTAG, RESTART_BADLINE, RESTART_DONE 15 | }; 16 | 17 | typedef int (*restart_check_cb)(const char *tag, void *ctx, void *data); 18 | typedef int (*restart_save_cb)(const char *tag, void *ctx, void *data); 19 | void restart_register(const char *tag, restart_check_cb ccb, restart_save_cb scb, void *data); 20 | 21 | void restart_set_kv(void *ctx, const char *key, const char *fmt, ...); 22 | enum restart_get_kv_ret restart_get_kv(void *ctx, char **key, char **val); 23 | 24 | bool restart_mmap_open(const size_t limit, const char *file, void **mem_base); 25 | void restart_mmap_close(void); 26 | unsigned int restart_fixup(void *old_base); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /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/memcached-server.upstart: -------------------------------------------------------------------------------- 1 | description "memcached-server - high-performance memory caching daemon (instance)" 2 | author "Cameron Norman " 3 | 4 | stop on stop-memcached-servers or runlevel [016] 5 | 6 | expect daemon 7 | respawn 8 | 9 | # Instance w/ default value so main server starts without SERVER param 10 | env SERVER="" 11 | instance $SERVER 12 | usage "SERVER - instance of memcached with corresponding /etc/memcached_$SERVER config" 13 | 14 | script 15 | if test "x$SERVER" = "x"; then 16 | name="memcached" 17 | else 18 | name="memcached_$SERVER" 19 | fi 20 | 21 | test -f /etc/${name}.conf || { 22 | echo "No config found, not starting."; exit 1 23 | } 24 | 25 | exec /usr/share/memcached/scripts/start-memcached /etc/${name}.conf /var/run/${name}.pid 26 | end script 27 | -------------------------------------------------------------------------------- /scripts/memcached-tool.1: -------------------------------------------------------------------------------- 1 | .TH MEMCACHED-TOOL 1 "Jul 2, 2013" 2 | .SH NAME 3 | memcached-tool \- stats and management tool for memcached 4 | 5 | .SH SYNOPSIS 6 | .B memcached-tool 7 | .RI < host [: port "] | " /path/to/socket "> [" mode ] 8 | 9 | .SH DESCRIPTION 10 | .B memcached-tool 11 | is a Perl script used to print statistics from a running memcached instance. 12 | The first parameter specifies the address of the daemon either by a hostname, 13 | optionally followed by the port number (the default is 11211), or a path to 14 | UNIX domain socket. The second parameter specifies the mode in which the tool 15 | should run. 16 | 17 | .SH MODES 18 | .TP 19 | .B display 20 | Print slab class statistics. This is the default mode if no mode is specified. 21 | The printed columns are: 22 | .RS 23 | .TP 24 | .B # 25 | Number of the slab class. 26 | .TP 27 | .B Item_Size 28 | The amount of space each chunk uses. One item uses one chunk of the 29 | appropriate size. 30 | .TP 31 | .B Max_age 32 | Age of the oldest item in the LRU. 33 | .TP 34 | .B Pages 35 | Total number of pages allocated to the slab class. 36 | .TP 37 | .B Count 38 | Number of items presently stored in this class. Expired items are not 39 | automatically excluded. 40 | .TP 41 | .B Full? 42 | Yes if there are no free chunks at the end of the last allocated page. 43 | .TP 44 | .B Evicted 45 | Number of times an item had to be evicted from the LRU before it expired. 46 | .TP 47 | .B Evict_Time 48 | Seconds since the last access for the most recent item evicted from this 49 | class. 50 | .TP 51 | .B OOM 52 | Number of times the underlying slab class was unable to store a new item. 53 | .RE 54 | 55 | .TP 56 | .B stats 57 | Print general-purpose statistics of the daemon. Each line contains the name of 58 | the statistic and its value. 59 | .TP 60 | .B dump [limit] 61 | Make a partial dump of the cache written in the add statements of the 62 | memcached protocol. If 63 | .B limit 64 | is given and is a strictly positive 65 | integer, then the dump is limited to that number of items. 66 | 67 | .SH SEE ALSO 68 | .BR memcached (1), 69 | .br 70 | .B https://www.memcached.org 71 | 72 | .SH AUTHOR 73 | The memcached-tool script was written by Brad Fitzpatrick 74 | .B 75 | -------------------------------------------------------------------------------- /scripts/memcached.sysconfig: -------------------------------------------------------------------------------- 1 | # These defaults will be used by every memcached instance, unless overridden 2 | # by values in /etc/sysconfig/memcached. 3 | USER="nobody" 4 | MAXCONN="1024" 5 | CACHESIZE="64" 6 | OPTIONS="" 7 | 8 | # The PORT variable will only be used by memcached.service, not by 9 | # memcached@xxxxx services, which will use the xxxxx 10 | PORT="11211" 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/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/memcached.upstart: -------------------------------------------------------------------------------- 1 | description "memcached - high performance memory caching daemon" 2 | author "Cameron Norman " 3 | 4 | start on filesystem 5 | stop on runlevel [016] 6 | 7 | pre-start script 8 | ret=0 9 | 10 | test -x /usr/bin/memcached || { stop; exit 0; } 11 | 12 | # Main 13 | start memcached-server || ret=$? 14 | # Instances 15 | for i in /etc/memcached_*.conf; do 16 | i=${i#/etc/memcached_} 17 | i=${i%.conf} 18 | test "$i" = "*" && continue 19 | start memcached-server SERVER=$i || ret=$? 20 | done 21 | 22 | exit $ret 23 | end script 24 | 25 | post-stop exec initctl emit stop-memcached-servers 26 | -------------------------------------------------------------------------------- /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/(?{$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 | unlink $pidfile; 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 | #ifdef EXTSTORE 20 | display("extstore header", sizeof(item_hdr)); 21 | #endif 22 | display("Libevent thread", 23 | sizeof(LIBEVENT_THREAD) - sizeof(struct thread_stats)); 24 | display("Connection", sizeof(conn)); 25 | display("Response object", sizeof(mc_resp)); 26 | display("Response bundle", sizeof(mc_resp_bundle)); 27 | display("Response objects per bundle", MAX_RESP_PER_BUNDLE); 28 | 29 | printf("----------------------------------------\n"); 30 | 31 | display("libevent thread cumulative", sizeof(LIBEVENT_THREAD)); 32 | display("Thread stats cumulative\t", sizeof(struct thread_stats)); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /slab_automove.h: -------------------------------------------------------------------------------- 1 | #ifndef SLAB_AUTOMOVE_H 2 | #define SLAB_AUTOMOVE_H 3 | 4 | /* default automove functions */ 5 | void *slab_automove_init(struct settings *settings); 6 | void slab_automove_free(void *arg); 7 | void slab_automove_run(void *arg, int *src, int *dst); 8 | 9 | typedef void *(*slab_automove_init_func)(struct settings *settings); 10 | typedef void (*slab_automove_free_func)(void *arg); 11 | typedef void (*slab_automove_run_func)(void *arg, int *src, int *dst); 12 | 13 | typedef struct { 14 | slab_automove_init_func init; 15 | slab_automove_free_func free; 16 | slab_automove_run_func run; 17 | } slab_automove_reg_t; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /slab_automove_extstore.h: -------------------------------------------------------------------------------- 1 | #ifndef SLAB_AUTOMOVE_EXTSTORE_H 2 | #define SLAB_AUTOMOVE_EXTSTORE_H 3 | 4 | void *slab_automove_extstore_init(struct settings *settings); 5 | void slab_automove_extstore_free(void *arg); 6 | void slab_automove_extstore_run(void *arg, int *src, int *dst); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /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, const uint32_t *slab_sizes, void *mem_base_external, bool reuse_mem); 12 | 13 | /** Call only during init. Pre-allocates all available memory */ 14 | void slabs_prefill_global(void); 15 | 16 | /** 17 | * Given object size, return id to use when allocating/freeing memory for object 18 | * 0 means error: can't store such a large object 19 | */ 20 | 21 | unsigned int slabs_clsid(const size_t size); 22 | unsigned int slabs_size(const int clsid); 23 | 24 | /** Allocate object of given length. 0 on error */ /*@null@*/ 25 | #define SLABS_ALLOC_NO_NEWPAGE 1 26 | void *slabs_alloc(const size_t size, unsigned int id, unsigned int flags); 27 | 28 | /** Free previously allocated object */ 29 | void slabs_free(void *ptr, size_t size, unsigned int id); 30 | 31 | /** Adjust global memory limit up or down */ 32 | bool slabs_adjust_mem_limit(size_t new_mem_limit); 33 | 34 | typedef struct { 35 | unsigned int chunks_per_page; 36 | unsigned int chunk_size; 37 | long int free_chunks; 38 | long int total_pages; 39 | } slab_stats_automove; 40 | void fill_slab_stats_automove(slab_stats_automove *am); 41 | unsigned int global_page_pool_size(bool *mem_flag); 42 | 43 | /** Fill buffer with stats */ /*@null@*/ 44 | void slabs_stats(ADD_STAT add_stats, void *c); 45 | 46 | /* Hints as to freespace in slab class */ 47 | unsigned int slabs_available_chunks(unsigned int id, bool *mem_flag, unsigned int *chunks_perslab); 48 | 49 | void slabs_mlock(void); 50 | void slabs_munlock(void); 51 | 52 | int start_slab_maintenance_thread(void); 53 | void stop_slab_maintenance_thread(void); 54 | 55 | enum reassign_result_type { 56 | REASSIGN_OK=0, REASSIGN_RUNNING, REASSIGN_BADCLASS, REASSIGN_NOSPARE, 57 | REASSIGN_SRC_DST_SAME 58 | }; 59 | 60 | enum reassign_result_type slabs_reassign(int src, int dst); 61 | 62 | void slabs_rebalancer_pause(void); 63 | void slabs_rebalancer_resume(void); 64 | 65 | #ifdef EXTSTORE 66 | void slabs_set_storage(void *arg); 67 | #endif 68 | 69 | /* Fixup for restartable code. */ 70 | unsigned int slabs_fixup(char *chunk, const int border); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /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 | 46 | void setup_privilege_violations_handler(void) { 47 | // not needed 48 | } 49 | -------------------------------------------------------------------------------- /stats_prefix.h: -------------------------------------------------------------------------------- 1 | #ifndef STATS_PREFIX_H 2 | #define STATS_PREFIX_H 3 | 4 | /* The stats prefix subsystem stores detailed statistics for each key prefix. 5 | * Simple statistics like total number of GETS are stored by the Stats 6 | * subsystem defined elsewhere. 7 | * 8 | * Suppose the prefix delimiter is ":", then "user:123" and "user:456" both 9 | * have the same prefix "user". 10 | */ 11 | 12 | 13 | /* Initialize the stats prefix subsystem. Should be called once before other 14 | * functions are called. The global hash initialization should be done before 15 | * using this subsystem. 16 | */ 17 | void stats_prefix_init(char prefix_delimiter); 18 | 19 | /* Clear previously collected stats. Requires you to have the acquired 20 | * the STATS_LOCK() first. 21 | */ 22 | void stats_prefix_clear(void); 23 | 24 | /* Record a GET for a key */ 25 | void stats_prefix_record_get(const char *key, const size_t nkey, const bool is_hit); 26 | 27 | /* Record a DELETE for a key */ 28 | void stats_prefix_record_delete(const char *key, const size_t nkey); 29 | 30 | /* Record a SET for a key */ 31 | void stats_prefix_record_set(const char *key, const size_t nkey); 32 | 33 | /* Return the collected stats in a textual for suitable for writing to a client. 34 | * The size of the output text is stored in the length parameter. 35 | * Returns NULL on error 36 | */ 37 | char *stats_prefix_dump(int *length); 38 | 39 | /* Visible for testing */ 40 | #define PREFIX_HASH_SIZE 256 41 | typedef struct _prefix_stats PREFIX_STATS; 42 | struct _prefix_stats { 43 | char *prefix; 44 | size_t prefix_len; 45 | uint64_t num_gets; 46 | uint64_t num_sets; 47 | uint64_t num_deletes; 48 | uint64_t num_hits; 49 | PREFIX_STATS *next; 50 | }; 51 | 52 | /* Return the PREFIX_STATS structure for the specified key, creating it if 53 | * it does not already exist. Returns NULL if the key does not contain 54 | * prefix delimiter, or if there was an error. Requires you to have acquired 55 | * STATS_LOCK() first. 56 | */ 57 | PREFIX_STATS *stats_prefix_find(const char *key, const size_t nkey); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /storage.h: -------------------------------------------------------------------------------- 1 | #ifndef STORAGE_H 2 | #define STORAGE_H 3 | 4 | void storage_delete(void *e, item *it); 5 | #ifdef EXTSTORE 6 | #define STORAGE_delete(e, it) \ 7 | do { \ 8 | storage_delete(e, it); \ 9 | } while (0) 10 | #else 11 | #define STORAGE_delete(...) 12 | #endif 13 | 14 | // API. 15 | void storage_stats(ADD_STAT add_stats, conn *c); 16 | void process_extstore_stats(ADD_STAT add_stats, conn *c); 17 | bool storage_validate_item(void *e, item *it); 18 | int storage_get_item(conn *c, item *it, mc_resp *resp); 19 | 20 | // callbacks for the IO queue subsystem. 21 | void storage_submit_cb(io_queue_t *q); 22 | void storage_complete_cb(io_queue_t *q); 23 | void storage_finalize_cb(io_pending_t *pending); 24 | 25 | // Thread functions. 26 | int start_storage_write_thread(void *arg); 27 | void storage_write_pause(void); 28 | void storage_write_resume(void); 29 | int start_storage_compact_thread(void *arg); 30 | void storage_compact_pause(void); 31 | void storage_compact_resume(void); 32 | 33 | // Init functions. 34 | struct extstore_conf_file *storage_conf_parse(char *arg, unsigned int page_size); 35 | void *storage_init_config(struct settings *s); 36 | int storage_read_config(void *conf, char **subopt); 37 | int storage_check_config(void *conf); 38 | void *storage_init(void *conf); 39 | 40 | // Ignore pointers and header bits from the CRC 41 | #define STORE_OFFSET offsetof(item, nbytes) 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /t/00-startup.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | 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 | # Maximum connections must be greater than 0. 79 | eval { 80 | my $server = new_memcached("-c 0"); 81 | }; 82 | ok($@, "Died with invalid maximum connections 0."); 83 | eval { 84 | my $server = new_memcached("-c -1"); 85 | }; 86 | ok($@, "Died with invalid maximum connections -1."); 87 | 88 | # Should not allow -t 0 89 | eval { 90 | my $server = new_memcached("-t 0"); 91 | }; 92 | ok($@, "Died with illegal 0 thread count"); 93 | 94 | { 95 | my $exit_code = run_help(); 96 | is($exit_code, 0, "Help defaults are fine."); 97 | }; 98 | -------------------------------------------------------------------------------- /t/64bit.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/README-TLS.md: -------------------------------------------------------------------------------- 1 | # Certificates creation process 2 | 3 | The following certificates are used in tests, that assume expiration date 4 | to always be in the future, so instead of a normal cert validity of 1-5 years. 5 | we use 500 years here. 6 | 7 | ## Create certificate authority key and certificate 8 | ``` 9 | $ openssl genrsa -out cakey.pem 2048 10 | $ openssl req -x509 -new -nodes -key cakey.pem -sha256 -days 182500 -out cacert.pem \ 11 | -subj "/CN=Test Root Certificate Authority/ST=CA/C=US/emailAddress=root@test.com/O=Test/OU=Test Department" 12 | ``` 13 | ## Create server key and certificate 14 | ``` 15 | $ openssl genrsa -out server_key.pem 2048 16 | $ openssl req -new -sha256 -key server_key.pem \ 17 | -subj "/C=US/ST=CA/O=Test/OU=Subunit of Test Organization/CN=test.com/emailAddress=root@test.com" \ 18 | -addext "subjectAltName=DNS:test.com,DNS:alt.test.com" \ 19 | -out server_crt.csr 20 | $ openssl x509 -req -in server_crt.csr -CA cacert.pem -CAkey cakey.pem \ 21 | -extfile <(printf "subjectAltName=DNS:test.com,DNS:alt.test.com") \ 22 | -CAcreateserial -out server_crt.pem -days 182500 -sha256 -text 23 | ``` 24 | ## Create client key and certificate 25 | ``` 26 | $ openssl genrsa -out client_key.pem 2048 27 | $ openssl req -new -sha256 -key client_key.pem \ 28 | -subj "/C=US/ST=CA/O=Test Client/OU=Subunit of Test Organization/CN=client.test.com/emailAddress=root@client.test.com" \ 29 | -addext "subjectAltName=DNS:client.test.com,DNS:alt.client.test.com" \ 30 | -out client_crt.csr 31 | $ openssl x509 -req -in client_crt.csr -CA cacert.pem -CAkey cakey.pem \ 32 | -extfile <(printf "subjectAltName=DNS:client.test.com,DNS:alt.client.test.com") \ 33 | -CAcreateserial -out client_crt.pem -days 182500 -sha256 -text 34 | ``` 35 | 36 | **NOTES**: *.csr files are certificate signing requests which are needed in order to sign certificates with signing authority. 37 | -CAcreateserial option creates one file which we do not need but openssl does. You can delete it after you are done. 38 | -------------------------------------------------------------------------------- /t/ascii-auth.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | # FIXME: Some tests are forcing UDP to be enabled via MemcachedTest.pm - need 10 | # to audit and fix. 11 | my $server = new_memcached("-Y $Bin/authfile -U 0"); 12 | my $sock = $server->sock; 13 | 14 | # Test unauthenticated modes 15 | print $sock "set foo 0 0 2\r\nhi\r\n"; 16 | like(scalar <$sock>, qr/CLIENT_ERROR/, "failed to do a write"); 17 | print $sock "get foo\r\n"; 18 | like(scalar <$sock>, qr/CLIENT_ERROR/, "failed to do a read"); 19 | 20 | # Fail to authenticate. 21 | print $sock "set foo 0 0 7\r\nfoo bab\r\n"; 22 | like(scalar <$sock>, qr/CLIENT_ERROR/, "failed to authenticate"); 23 | 24 | # Try for real. 25 | print $sock "set foo 0 0 7\r\nfoo bar\r\n"; 26 | like(scalar <$sock>, qr/STORED/, "authenticated?"); 27 | 28 | print $sock "set toast 0 0 2\r\nhi\r\n"; 29 | like(scalar <$sock>, qr/STORED/, "stored an item that didn't look like user/pass"); 30 | 31 | mem_get_is($sock, "toast", "hi"); 32 | 33 | # Create a second socket, try to authenticate against the second token. 34 | 35 | my $sock2 = $server->new_sock; 36 | 37 | print $sock2 "set foo 0 0 10\r\nbaaaz quux\r\n"; 38 | like(scalar <$sock2>, qr/STORED/, "authenticated a second socket?"); 39 | 40 | print $sock2 "set toast2 0 0 2\r\nho\r\n"; 41 | like(scalar <$sock2>, qr/STORED/, "stored an item that didn't look like user/pass"); 42 | 43 | mem_get_is($sock2, "toast2", "ho"); 44 | 45 | # TODO: tests for reloads. 46 | -------------------------------------------------------------------------------- /t/authfile: -------------------------------------------------------------------------------- 1 | foo:bar 2 | baaaz:quux 3 | -------------------------------------------------------------------------------- /t/binary-get.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/env 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/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIID8DCCAtigAwIBAgIJAOCrnjDZoOG/MA0GCSqGSIb3DQEBCwUAMIGLMSgwJgYD 3 | VQQDDB9UZXN0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQswCQYDVQQIDAJD 4 | QTELMAkGA1UEBhMCVVMxHDAaBgkqhkiG9w0BCQEWDXJvb3RAdGVzdC5jb20xDTAL 5 | BgNVBAoMBFRlc3QxGDAWBgNVBAsMD1Rlc3QgRGVwYXJ0bWVudDAgFw0yMDEwMjcw 6 | MzA0NTVaGA8yNTIwMDYyODAzMDQ1NVowgYsxKDAmBgNVBAMMH1Rlc3QgUm9vdCBD 7 | ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxCzAJBgNVBAgMAkNBMQswCQYDVQQGEwJVUzEc 8 | MBoGCSqGSIb3DQEJARYNcm9vdEB0ZXN0LmNvbTENMAsGA1UECgwEVGVzdDEYMBYG 9 | A1UECwwPVGVzdCBEZXBhcnRtZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 10 | CgKCAQEAvz238GzKt1UpaxgIxv76hlty5hCZKmD5B952MCWBRi7OyYatl0sMjv38 11 | pgsICIT68wGqQaTmV4W0XQQ54+NZ3o5ujJaDQX2kLs8x0sjuNkmt5Wt+orqdxO99 12 | lb7f4hpKyGq0OGeqz4nBHCZeqEt37OLxUgGWQtKGVZI0X/m/m4LudmWwZpLgAp0w 13 | 5mY+FLaPavLoXQ95+f3htUzgRdzsFWMXi0piH/D4ua2i+ekajf8owxmR9AIKpAYY 14 | oV1J+XexlzVzXG1OPUEkGvbxnoDIHUoL6IM4i/QEI52UL0b+sVVV0fmJw08mkN95 15 | XhW+OhS52lSBdqiC3/uRRF/g8/5nowIDAQABo1MwUTAdBgNVHQ4EFgQUcqmERx2+ 16 | c9d5Qd04od22AYhwY/AwHwYDVR0jBBgwFoAUcqmERx2+c9d5Qd04od22AYhwY/Aw 17 | DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAFYdGLXwXTU4p/CIF 18 | bVRzS+RTEMc6cwd/CC+q0iVZT5Yfhu+MvdlaLMEimYTO44qu32hZHO6ovvSoSNJ1 19 | fofv5crljHyyQY8GoZIq2jm7DcUyWJLuanqIcVXM9o0xR2CdOhvvDo/M0vRpR6EB 20 | ASZ7uV+w/tuc05YLdQOsXJyiXk80H7emJ0bRl1m/H2wxZRnL2jN2Iz1xeY8tGiP0 21 | UU9x0hNS7eNzC1IdRUGVcfstyXznKs37F0ZyNHF1TOTZT80mdJxn0D1alVLqkiN3 22 | RsIZpo1aZ0SShdtIx6luPU2wmGo5kLL2wN6Md18XSHViGA9dx2Mz00GxxDbiQl+F 23 | 6qzVDA== 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /t/cakey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAvz238GzKt1UpaxgIxv76hlty5hCZKmD5B952MCWBRi7OyYat 3 | l0sMjv38pgsICIT68wGqQaTmV4W0XQQ54+NZ3o5ujJaDQX2kLs8x0sjuNkmt5Wt+ 4 | orqdxO99lb7f4hpKyGq0OGeqz4nBHCZeqEt37OLxUgGWQtKGVZI0X/m/m4LudmWw 5 | ZpLgAp0w5mY+FLaPavLoXQ95+f3htUzgRdzsFWMXi0piH/D4ua2i+ekajf8owxmR 6 | 9AIKpAYYoV1J+XexlzVzXG1OPUEkGvbxnoDIHUoL6IM4i/QEI52UL0b+sVVV0fmJ 7 | w08mkN95XhW+OhS52lSBdqiC3/uRRF/g8/5nowIDAQABAoIBAQCEOGi3GpChZojn 8 | WUugQeP2Br4O4IT1LuyeiylNqnCRlIU2Ah58zs+VAXevWUspkL9sScdr8VWsR23n 9 | LafUrniRDgrSurFvEy3zix8YbAF+mTxYtRRt7/ZqlWZPpRNnYOQ5YsTNhqcrLEPO 10 | cZzaYKs+pb1RBHXeJ23nNdluTd8y5YYwhAj3pO5Q+A9KlDsXy2ZoJPB+1G7JWKLs 11 | Ptub5Qe9FkaeBVIDiyBqiCyilwmqtRX6cefTHb1/1sTSl8fey/MZZWf+vTFN80oR 12 | 09cZ5CxRBm/NX9g4on2T8k9RrjwidTki1ovVMpTr/OcT+j6EnrQFc+VqVcCfEsPk 13 | xhN5SVSZAoGBAOeSE1nQTABzlFwNj/8U57OMDChBFpb3fqbpLTaeMPTW5MGt4OJm 14 | 1/oNRNNTuSVS+z8DHRbErzG8CYgdFvE8t136XbYKzM8AcBTDqAU+sR6IkvWqLITN 15 | EVWDyTh04/UN19pstBkb7icsGSv8Rjm4QIis1cPm9eHVlj6v3Lp0zxuPAoGBANNq 16 | e0TDeeWweryGyAKlwhEFdxmr/5byejBBYyxqk44OADXCxI6mk5/3k8WGptoM8qeT 17 | dHFZLhvJJ2Rpd/GoiBDqEUxDwtvvCCsajOcK1gRvVlQjezgnE2qdXehqM4A8eAsj 18 | drPwciq1MJemYYSdgZZ5DQbfU2DtW+vis11fJritAoGANO1jNNzx8gx4tU14QwIB 19 | Lz1sLesqgZjRvs5+wgQIBn3zTP0z8Cs3Y/SSbYauQ4Si3jUU+fY8YK6sswqLCtoo 20 | wvcNYiU3yHw+IdDSX1iD3Qm0JSgfAr9oHdaAMEIvM/xVAJxl0XNbzGI84pvHZTvi 21 | rBpsxVjNjC9oJq3FUmyORasCgYB8HDF7P6nqrfyPeiwmabqQlX3Cg1dK/NPASlJ2 22 | +6Xdqdhztv/+okldilCkj5SZwyocOBMOi4Tm6OKILuYr5nziORaoh8r3o85Ox4pa 23 | A+F5vwaV5DJs13uthe5gByBuspDiMRJ5Nbk/2xhInnL3ECPH+w4rr6TZ7N2ej3ou 24 | YTJxDQKBgQCowcfZm4N5s1UynZ9CKXCeZ+lhIUBhz2rYSwDJhGaxRaJgq7ABrsT1 25 | 2SIZZUOXJ5tAFjLQ5FM6/RQMu1MGEUoJ/whxEn37Zvy/6dk8T1uxpFvNBbCE+QlX 26 | ENz3N5p1hTB16+ETumeLH42U2PZMhKpaEyYZHpVun5p/6TtxX3Sf3A== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /t/client_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA1WbJye9krQ/H/nT75HWzdmB/u1446SXkcIyZY612JrgGZfUO 3 | tm8iZrdLeoxPOvsOrMEObnbbtZWD1KKG63hjxdNHl9luDznuzVG2r80BLKDAu1jr 4 | I2qqCeNpMS6DSYhCcX0lqvFjXpOlRBlHsjscevHyJHVRbxN+miQsQuMjMbbd+lA2 5 | ku/DwkD+RbmAedlkYgL03oxY+nS5+mUIj0Z5qMZp34juGNhNkKYwTz23eFYepyQg 6 | SDvlq+eOQW6f8T009hqj760/hwC5iA+/zsROSvwekENAsd+GzN0eNU+VnChl5F66 7 | DQs/x18os0q+0tdPONrwPWg8QOfb/Xp5iUxD0QIDAQABAoIBAFbHF6NwH8ZoNnF4 8 | 7L14R2QJ1adBp27Xo89IvU9VSEhyaNepETSKeLjALIdG6ykW0l7Zmp684pcyl4su 9 | FCNIr3nQcrqDBkhWzI7dfaZ7fNDDBdypSeT7CO5AdZQ0T9rk9+/ibiDXTAs3iuro 10 | RMlrVCeCn27H7jd2+/J+ZylD6BncfpgF6wAz80Ac6PzhlE28tjl8JhDV3AQtoSXp 11 | 1oFFT1OIvg6+0FcrxocGsM61akEKdJYmWP8mvQvBWIDFKib9VQuIQ3UEJrwG8H6A 12 | at1H1hFIZpgYTXJnU1n34xxfml16kSpp61Sap531vwLrfi6jrsCgCppdfpX3HEdl 13 | ApGed0kCgYEA9MgV1cB3W/18zALmVf3LRfwVT2oAikeIYr34AcdQ+09TYo20iFIi 14 | +s993M5LOIRkXuL1a3zQYzQplp5hwyXy0+4v6NVHDiEothbOcViCE6pxesHxRlty 15 | ujud2taUIXo3z+fod0TBW19MOzC2EZFzuy6XOle2uTe785NzmmGtKpsCgYEA3y6I 16 | z53b1S4uXtqm494pPxhYvQTZJZND5XeIIW/L/WjWZaLlXtwqv/x94PyX9a3BjLvC 17 | NHuAJDV54KOXZDLn5d+nuqYhwx6DQ6eARTRkqcbsMxqBpbOxHo1CInUWkbCAzeI6 18 | JiHa6+mbfyF1j9waxUcLLVCvI7/chYGCwEZojAMCgYAZOe2WI8mpP0x3Me1O+2LR 19 | iZMgpgn8NjcGBhHjDQl5fMlZJMVwLxbPxkZwVQpHfs1hQf6M30YSkzOeBCCLHgQS 20 | dz+UvTYfbKdnbJ5F7Bsrr6sWPZqB57bBHpe8D9UgRhouYPYCJKHQqteOWgzJbrYA 21 | mmKXbqa0G9xQ0+dcB6jy9QKBgQDcuRoHMxkJ8b1chCSPsdTbGMoSTmwvlECO/bN9 22 | ViLJwVhgdkUgluAUtbMMwuPKzwhflgXQf9/Qb67UsxXzu8DLAHHSz6EOZuvtCgh6 23 | 6QExQ5GwTOOxrFBcZdnlWCV0+rm4ZKebtNndfVES31V0bHtxZfyw2V4NHiALTXWx 24 | kTCuXwKBgQDfDWjNsvHjHtY2RDO6OwI/X9sME49l9MmyaFlr7eCTvuGpTvvaZSvD 25 | vJV2J9aBqlJlowpoH0UCy2TWjVSlh3GLFqlKVGOEVIT3FaR2QLHESzNhMCLoYKfd 26 | 819yNk6np4WYrN7RpcVoG1XR8sj4NJJQZJy0Iu1N8tnPm9AZEu8jxQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /t/conn-limits.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # Test connection memory limits. 3 | 4 | use strict; 5 | use warnings; 6 | use Test::More; 7 | use FindBin qw($Bin); 8 | use lib "$Bin/lib"; 9 | use MemcachedTest; 10 | 11 | my $server = new_memcached('-o read_buf_mem_limit=1 -t 32 -R 500'); 12 | my $sock = $server->sock; 13 | 14 | # The minimum limit is 1 megabyte. This is then split between each of the 15 | # worker threads, which ends up being a lot of memory for a quick test. 16 | # So we use a high worker thread count to split them down more. 17 | 18 | { 19 | # easiest method is an ascii multiget. 20 | my $key = 'foo'; 21 | my @keys = (); 22 | for (1 .. 500) { 23 | push(@keys, $key); 24 | } 25 | my $keylist = join(' ', @keys); 26 | chop($keylist); 27 | print $sock "get ", $keylist, "\r\n"; 28 | like(<$sock>, qr/SERVER_ERROR out of memory writing/, "OOM'ed multiget"); 29 | my $stats = mem_stats($sock); 30 | isnt(0, $stats->{'response_obj_oom'}, 'non zero response object OOM counter: ' . $stats->{'response_obj_oom'}); 31 | } 32 | 33 | { 34 | # stacked ascii responses, which should cause a connection close. 35 | my $s = $server->new_sock; 36 | my @keys = (); 37 | for (1 .. 500) { 38 | push(@keys, "mg foo v\r\n"); 39 | } 40 | my $cmd = join('', @keys); 41 | print $s $cmd; 42 | ok(!defined <$s>, 'sock disconnected after overflow'); 43 | 44 | my $stats = mem_stats($sock); 45 | cmp_ok($stats->{'response_obj_oom'}, '>', 1, 'another OOM recorded'); 46 | } 47 | 48 | SKIP: { 49 | skip "read_buf test borks on travis CI. don't have patience to fix.", 1; 50 | # test read buffer limits. 51 | # spam connections with a partial command.. a set in this case is easy. 52 | my @conns = (); 53 | for (1 .. 128) { 54 | my $s = $server->new_sock; 55 | #if (!defined($s)) { 56 | # Don't need the spam of every individual conn made. 57 | #} 58 | ok(defined($s), 'new conn made'); 59 | # Partial set command, should attach a read buffer but not release it. 60 | print $s "set foo 0 0 2\r\n"; 61 | push(@conns, $s); 62 | } 63 | # Close everything so we have a red buffer available to get stats. 64 | for my $s (@conns) { 65 | $s->close(); 66 | } 67 | my $stats = mem_stats($sock); 68 | cmp_ok($stats->{'read_buf_oom'}, '>', 1, 'read buffer based OOM recorded'); 69 | } 70 | 71 | done_testing(); 72 | -------------------------------------------------------------------------------- /t/daemonize.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/env 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 2'); 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 + 2; 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 | my $stats = mem_stats($sock); 33 | is($stats->{"store_no_memory"}, 2, 34 | "recorded store failures due to no memory"); 35 | 36 | for($key = 0; $key < $max_stored; $key++) { 37 | mem_get_is $sock, "dash$key", $value, "Failed at dash$key"; 38 | } 39 | -------------------------------------------------------------------------------- /t/dyn-maxbytes.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # Test the 'stats items' evictions counters. 3 | 4 | use strict; 5 | use Test::More tests => 309; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | 10 | my $server = new_memcached("-m 3 -o modern,slab_automove_window=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 | my $stats = mem_stats($sock); 22 | my $evicted = $stats->{evictions}; 23 | isnt($evicted, "0", "check evicted"); 24 | 25 | # We're past the memory limit. Try adjusting maxbytes upward. 26 | $stats = mem_stats($sock, "settings"); 27 | my $pre_maxbytes = $stats->{"maxbytes"}; 28 | print $sock "cache_memlimit 8\r\n"; 29 | is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 3m to 8m"); 30 | 31 | # Confirm maxbytes updated. 32 | $stats = mem_stats($sock, "settings"); 33 | isnt($stats->{"maxbytes"}, $pre_maxbytes, "stats settings maxbytes updated"); 34 | 35 | # Check for total_malloced increasing as new memory is added 36 | $stats = mem_stats($sock, "slabs"); 37 | my $t_malloc = $stats->{"total_malloced"}; 38 | 39 | print $sock "set toast 0 0 66560\r\n$value\r\n"; 40 | is(scalar <$sock>, "STORED\r\n", "stored toast"); 41 | $stats = mem_stats($sock, "slabs"); 42 | cmp_ok($stats->{"total_malloced"}, '>', $t_malloc, "stats slabs total_malloced increased"); 43 | 44 | $stats = mem_stats($sock); 45 | my $new_evicted = $stats->{evictions}; 46 | cmp_ok($new_evicted, '==', $evicted, "no new evictions"); 47 | 48 | # Bump up to 16, fill a bit more, then delete everything. 49 | print $sock "cache_memlimit 16\r\n"; 50 | is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 8m to 16m"); 51 | for (;$key < 150; $key++) { 52 | print $sock "set key$key 0 0 66560\r\n$value\r\n"; 53 | is(scalar <$sock>, "STORED\r\n", "stored key$key"); 54 | } 55 | 56 | # Grab total_malloced after filling everything up. 57 | $stats = mem_stats($sock, "slabs"); 58 | $t_malloc = $stats->{"total_malloced"}; 59 | print $sock "cache_memlimit 8\r\n"; 60 | is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 16m to 8m"); 61 | 62 | # Remove all of the keys, allowing the slab rebalancer to push pages toward 63 | # the global page pool. 64 | for ($key = 0; $key < 150; $key++) { 65 | print $sock "delete key$key\r\n"; 66 | like(scalar <$sock>, qr/(DELETED|NOT_FOUND)\r\n/, "deleted key$key"); 67 | } 68 | 69 | # If memory limit is lower, it should free those pages back to the OS. 70 | my $reduced = 0; 71 | for (my $tries = 0; $tries < 6; $tries++) { 72 | sleep 1; 73 | $stats = mem_stats($sock, "slabs"); 74 | $reduced = $stats->{"total_malloced"} if ($t_malloc > $stats->{"total_malloced"}); 75 | last if $reduced; 76 | } 77 | 78 | isnt($reduced, 0, "total_malloced reduced to $reduced"); 79 | -------------------------------------------------------------------------------- /t/evictions.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/extstore-buckets.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | use Data::Dumper qw/Dumper/; 10 | 11 | my $ext_path; 12 | 13 | if (!supports_extstore()) { 14 | plan skip_all => 'extstore not enabled'; 15 | exit 0; 16 | } 17 | 18 | $ext_path = "/tmp/extstore.$$"; 19 | 20 | my $server = new_memcached("-m 256 -U 0 -o ext_page_size=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0,ext_path=$ext_path:64m,ext_low_ttl=60,slab_automove=1"); 21 | my $sock = $server->sock; 22 | 23 | my $value; 24 | { 25 | my @chars = ("C".."Z"); 26 | for (1 .. 20000) { 27 | $value .= $chars[rand @chars]; 28 | } 29 | } 30 | 31 | # fill some larger objects 32 | { 33 | # interleave sets with 0 ttl vs long ttl's. 34 | my $keycount = 1200; 35 | for (1 .. $keycount) { 36 | print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n"; 37 | print $sock "set lfoo$_ 0 5 20000 noreply\r\n$value\r\n"; 38 | } 39 | # wait for a flush 40 | sleep 10; 41 | print $sock "lru_crawler crawl all\r\n"; 42 | <$sock>; 43 | sleep 2; 44 | # fetch 45 | mem_get_is($sock, "nfoo1", $value); 46 | # check extstore counters 47 | my $stats = mem_stats($sock); 48 | cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated'); 49 | cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written'); 50 | cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written'); 51 | cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched'); 52 | cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read'); 53 | cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read'); 54 | cmp_ok($stats->{extstore_page_reclaims}, '>', 1, 'at least two pages reclaimed'); 55 | } 56 | 57 | done_testing(); 58 | 59 | END { 60 | unlink $ext_path if $ext_path; 61 | } 62 | -------------------------------------------------------------------------------- /t/extstore-jbod.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | use Data::Dumper qw/Dumper/; 10 | 11 | my $ext_path; 12 | my $ext_path2; 13 | 14 | if (!supports_extstore()) { 15 | plan skip_all => 'extstore not enabled'; 16 | exit 0; 17 | } 18 | 19 | $ext_path = "/tmp/extstore1.$$"; 20 | $ext_path2 = "/tmp/extstore2.$$"; 21 | 22 | my $server = new_memcached("-m 256 -U 0 -o ext_page_size=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path:64m,ext_path=$ext_path2:96m,slab_automove=1,ext_max_sleep=100000"); 23 | my $sock = $server->sock; 24 | 25 | my $value; 26 | { 27 | my @chars = ("C".."Z"); 28 | for (1 .. 20000) { 29 | $value .= $chars[rand @chars]; 30 | } 31 | } 32 | 33 | # fill some larger objects 34 | { 35 | # interleave sets with 0 ttl vs long ttl's. 36 | my $keycount = 3700; 37 | for (1 .. $keycount) { 38 | print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n"; 39 | print $sock "set lfoo$_ 0 0 20000 noreply\r\n$value\r\n"; 40 | } 41 | # wait for a flush 42 | wait_ext_flush($sock); 43 | # delete half 44 | mem_get_is($sock, "nfoo1", $value); 45 | for (1 .. $keycount) { 46 | print $sock "delete lfoo$_ noreply\r\n"; 47 | } 48 | print $sock "lru_crawler crawl all\r\n"; 49 | <$sock>; 50 | sleep 10; 51 | # fetch 52 | # check extstore counters 53 | my $stats = mem_stats($sock); 54 | is($stats->{evictions}, 0, 'no RAM evictions'); 55 | cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated'); 56 | cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written'); 57 | cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written'); 58 | cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched'); 59 | cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read'); 60 | cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read'); 61 | cmp_ok($stats->{extstore_page_reclaims}, '>', 1, 'at least two pages reclaimed'); 62 | } 63 | 64 | done_testing(); 65 | 66 | END { 67 | unlink $ext_path if $ext_path; 68 | unlink $ext_path2 if $ext_path2; 69 | } 70 | -------------------------------------------------------------------------------- /t/flags.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | # set foo (and should get it) 13 | for my $flags (0, 123, 2**16-1, 2**31) { 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/getandtouch.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More tests => 15; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | 10 | 11 | my $server = new_memcached(); 12 | my $sock = $server->sock; 13 | 14 | # cache miss 15 | print $sock "gat 10 foo1\r\n"; 16 | is(scalar <$sock>, "END\r\n", "cache miss"); 17 | 18 | # set foo1 and foo2 (and should get it) 19 | print $sock "set foo1 0 2 7\r\nfooval1\r\n"; 20 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 21 | 22 | print $sock "set foo2 0 2 7\r\nfooval2\r\n"; 23 | is(scalar <$sock>, "STORED\r\n", "stored foo2"); 24 | 25 | # get and touch it with cas 26 | print $sock "gats 10 foo1 foo2\r\n"; 27 | ok(scalar <$sock> =~ /VALUE foo1 0 7 (\d+)\r\n/, "get and touch foo1 with cas regexp success"); 28 | is(scalar <$sock>, "fooval1\r\n","value"); 29 | ok(scalar <$sock> =~ /VALUE foo2 0 7 (\d+)\r\n/, "get and touch foo2 with cas regexp success"); 30 | is(scalar <$sock>, "fooval2\r\n","value"); 31 | is(scalar <$sock>, "END\r\n", "end"); 32 | 33 | # get and touch it without cas 34 | print $sock "gat 10 foo1 foo2\r\n"; 35 | ok(scalar <$sock> =~ /VALUE foo1 0 7\r\n/, "get and touch foo1 without cas regexp success"); 36 | is(scalar <$sock>, "fooval1\r\n","value"); 37 | ok(scalar <$sock> =~ /VALUE foo2 0 7\r\n/, "get and touch foo2 without cas regexp success"); 38 | is(scalar <$sock>, "fooval2\r\n","value"); 39 | is(scalar <$sock>, "END\r\n", "end"); 40 | 41 | sleep 2; 42 | mem_get_is($sock, "foo1", "fooval1"); 43 | mem_get_is($sock, "foo2", "fooval2"); 44 | -------------------------------------------------------------------------------- /t/idle-timeout.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 11; 7 | 8 | use FindBin qw($Bin); 9 | use lib "$Bin/lib"; 10 | use MemcachedTest; 11 | 12 | # start up a server with 10 maximum connections 13 | my $server = new_memcached("-o idle_timeout=3 -l 127.0.0.1"); 14 | my $sock = $server->sock; 15 | 16 | # Make sure we can talk to start with 17 | my $stats = mem_stats($sock); 18 | is($stats->{idle_kicks}, "0", "check stats initial"); 19 | isnt($sock->connected(), undef, "check connected"); 20 | 21 | # Make sure we don't timeout when active 22 | for (my $i = 0; $i < 6; $i++) { 23 | $stats = mem_stats($sock); 24 | isnt($stats->{version}, undef, "check active $i"); 25 | } 26 | $stats = mem_stats($sock); 27 | is($stats->{idle_kicks}, "0", "check stats 2"); 28 | 29 | # Make sure we do timeout when not 30 | sleep(10); 31 | mem_stats($sock); # Network activity, so socket code will see dead socket 32 | sleep(1); 33 | # we run SSL tests over TCP; hence IO::Socket::SSL returns 34 | # '' upon disconnecting with the server. 35 | if (enabled_tls_testing()) { 36 | is($sock->connected(),'', "check disconnected"); 37 | } else { 38 | is($sock->connected(),undef, "check disconnected"); 39 | } 40 | 41 | $sock = $server->sock; 42 | $stats = mem_stats($sock); 43 | isnt($stats->{idle_kicks}, 0, "check stats timeout"); 44 | -------------------------------------------------------------------------------- /t/incrdecr.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/env 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/env 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/env 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/env 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/env 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/env 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 amount"); 38 | -------------------------------------------------------------------------------- /t/issue_183.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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("-o no_modern"); 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_192.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | 12 | ok($server->new_sock, "opened new socket"); 13 | 14 | print $sock "\x80\x12\x00\x01\x08\x00\x00\x00\xff\xff\xff\xe8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; 15 | 16 | sleep 0.5; 17 | ok($server->new_sock, "failed to open new socket"); 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /t/issue_22.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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_260.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # Issue #260 is a terrible bug. 3 | # In order to run this test: 4 | # * checkout 1.4.15 5 | # * change TAIL_REPAIR_TIME from (3 * 3600) to 3 6 | # Now it should cause an assert. Patches can be tested to fix it. 7 | 8 | use strict; 9 | use Test::More; 10 | use FindBin qw($Bin); 11 | use lib "$Bin/lib"; 12 | use MemcachedTest; 13 | 14 | plan skip_all => "Only possible to test #260 under artificial conditions"; 15 | exit 0; 16 | plan tests => 11074; 17 | # assuming max slab is 1M and default mem is 64M 18 | my $server = new_memcached(); 19 | my $sock = $server->sock; 20 | 21 | # create a big value for the largest slab 22 | my $max = 1024 * 1024; 23 | my $big = 'x' x (1024 * 1024 - 250); 24 | 25 | ok(length($big) > 512 * 1024); 26 | ok(length($big) < 1024 * 1024); 27 | 28 | # set the big value 29 | my $len = length($big); 30 | print $sock "set big 0 0 $len\r\n$big\r\n"; 31 | is(scalar <$sock>, "STORED\r\n", "stored big"); 32 | mem_get_is($sock, "big", $big); 33 | 34 | # no evictions yet 35 | my $stats = mem_stats($sock); 36 | is($stats->{"evictions"}, "0", "no evictions to start"); 37 | 38 | # set many big items, enough to get evictions 39 | for (my $i = 0; $i < 100; $i++) { 40 | print $sock "set item_$i 0 0 $len\r\n$big\r\n"; 41 | is(scalar <$sock>, "STORED\r\n", "stored item_$i"); 42 | } 43 | 44 | # some evictions should have happened 45 | my $stats = mem_stats($sock); 46 | my $evictions = int($stats->{"evictions"}); 47 | ok($evictions == 37, "some evictions happened"); 48 | 49 | # the first big value should be gone 50 | mem_get_is($sock, "big", undef); 51 | 52 | # the earliest items should be gone too 53 | for (my $i = 0; $i < $evictions - 1; $i++) { 54 | mem_get_is($sock, "item_$i", undef); 55 | } 56 | 57 | # check that the non-evicted are the right ones 58 | for (my $i = $evictions - 1; $i < $evictions + 4; $i++) { 59 | mem_get_is($sock, "item_$i", $big); 60 | } 61 | 62 | # Now we fill a slab with incrementable items... 63 | for (my $i = 0; $i < 10923; $i++) { 64 | print $sock "set sitem_$i 0 0 1\r\n1\r\n"; 65 | is(scalar <$sock>, "STORED\r\n", "stored sitem_$i"); 66 | } 67 | 68 | my $stats = mem_stats($sock); 69 | my $evictions = int($stats->{"evictions"}); 70 | ok($evictions == 38, "one more eviction happened: $evictions"); 71 | 72 | # That evicted item was the first one we put in. 73 | mem_get_is($sock, "sitem_0", undef); 74 | 75 | sleep 15; 76 | 77 | # Now we increment the item which should be on the tail. 78 | # THIS asserts the memcached-debug binary. 79 | print $sock "incr sitem_1 1\r\n"; 80 | is(scalar <$sock>, "2\r\n", "incremented to two"); 81 | 82 | #my $stats = mem_stats($sock, "slabs"); 83 | #is($stats->{"1:free_chunks"}, 0, "free chunks should still be 0"); 84 | -------------------------------------------------------------------------------- /t/issue_29.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/env 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/env 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/env 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("-o no_modern"); 10 | my $sock = $server->sock; 11 | my $value = "B"x10; 12 | my $key = 0; 13 | my $key_count = 10; 14 | 15 | for ($key = 0; $key < $key_count; $key++) { 16 | print $sock "set key$key 0 0 10\r\n$value\r\n"; 17 | is (scalar <$sock>, "STORED\r\n", "stored key$key"); 18 | } 19 | 20 | my $stats = mem_stats($sock, "slabs"); 21 | my $stats2 = mem_stats($sock, "items"); 22 | my $req = $stats2->{"items:1:mem_requested"}; 23 | my $top = $stats->{"1:chunk_size"} * $key_count; 24 | # unreasonable for the result to be < 500 bytes (min item header is 48), but 25 | # should be less than the maximum potential number. 26 | ok ($req > 500 && $req < $top, "Check allocated size: $req $top"); 27 | -------------------------------------------------------------------------------- /t/issue_50.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/env 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 | cmp_ok ($stats->{"conn_yields"}, ">=", "5", "Got a decent number of yields"); 21 | -------------------------------------------------------------------------------- /t/issue_67.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | use Carp qw(croak); 9 | use Socket qw(sockaddr_in INADDR_ANY PF_INET SOCK_STREAM); 10 | 11 | use Cwd; 12 | my $builddir = getcwd; 13 | 14 | $ENV{'MEMCACHED_PORT_FILENAME'} = "/tmp/ports.$$"; 15 | 16 | sub read_ports { 17 | my %rv = (); 18 | open(my $f, "/tmp/ports.$$") || die("Can't open ports file."); 19 | while(<$f>) { 20 | my ($type, $port) = split(/:\s+/); 21 | $rv{$type} = $port + 0; 22 | } 23 | unlink "/tmp/ports.$$"; 24 | return %rv; 25 | } 26 | 27 | sub validate_port { 28 | my ($name, $got, $expected) = @_; 29 | # diag "Wanted $expected, got $got"; 30 | if ($expected == -1) { 31 | ok(!defined($got), "$name expected no port, got $got"); 32 | } elsif ($expected == 0) { 33 | ok(defined($got) && $got != 11211, "$name expected random port (got $got)"); 34 | } else { 35 | is($got, $expected, "$name"); 36 | } 37 | } 38 | 39 | sub skip_if_default_addr_in_use(&) { 40 | my ($block) = @_; 41 | 42 | socket(my $socket, PF_INET, SOCK_STREAM, 0) or die $!; 43 | my $addr_in_use = !bind($socket, sockaddr_in(11211, INADDR_ANY)); 44 | close($socket); 45 | 46 | SKIP: { 47 | skip 'Default address is in use. Do you have a running instance?', 2 if $addr_in_use; 48 | return $block->(); 49 | } 50 | } 51 | 52 | sub run_server { 53 | my ($args) = @_; 54 | 55 | my $exe = "$builddir/memcached-debug"; 56 | croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe; 57 | 58 | my $childpid = fork(); 59 | 60 | my $root = ''; 61 | $root = "-u root" if ($< == 0); 62 | 63 | # test build requires more privileges 64 | $args .= " -o relaxed_privileges"; 65 | 66 | my $cmd = "$builddir/timedrun 10 $exe $root $args"; 67 | 68 | unless($childpid) { 69 | exec $cmd; 70 | exit; # NOTREACHED 71 | } 72 | 73 | for (1..20) { 74 | if (-f "/tmp/ports.$$") { 75 | return Memcached::Handle->new(pid => $childpid); 76 | } 77 | select undef, undef, undef, 0.10; 78 | } 79 | croak "Failed to start server."; 80 | } 81 | 82 | sub when { 83 | my ($name, $params, $expected_tcp, $expected_udp) = @_; 84 | 85 | my $server = run_server($params); 86 | my %ports = read_ports(); 87 | 88 | validate_port($name, $ports{'TCP INET'}, $expected_tcp); 89 | validate_port($name, $ports{'UDP INET'}, $expected_udp); 90 | } 91 | 92 | skip_if_default_addr_in_use { when('no arguments', '', 11211, -1) }; 93 | when('specifying tcp port', '-p 11212', 11212, -1); 94 | when('specifying udp port', '-U 11222', 11222, 11222); 95 | when('specifying tcp ephemeral port', '-p -1', 0, -1); 96 | when('specifying udp ephemeral port', '-U -1', 0, 0); 97 | when('tcp port disabled', '-p 0', -1, -1); 98 | skip_if_default_addr_in_use { when('udp port disabled', '-U 0', 11211, -1) }; 99 | when('specifying tcp and udp ports', '-p 11232 -U 11233', 11232, 11233); 100 | when('specifying tcp and disabling udp', '-p 11242 -U 0', 11242, -1); 101 | when('specifying udp and disabling tcp', '-p -1 -U 11252', 0, 11252); 102 | when('specifying tcp and ephemeral udp', '-p 11262 -U -1', 11262, 0); 103 | when('specifying udp and ephemeral tcp', '-p -1 -U 11272', 0, 11272); 104 | -------------------------------------------------------------------------------- /t/issue_68.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/env 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/env 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 -o slab_chunk_max=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 2097152'); 37 | my $stats = mem_stats($server->sock, ' settings'); 38 | is($stats->{item_size_max}, 2097152); 39 | $server->stop(); 40 | 41 | # Suffix kilobytes. 42 | $server = new_memcached('-I 512k -o slab_chunk_max=16384'); 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('-m 256 -I 32m'); 49 | my $stats = mem_stats($server->sock, ' settings'); 50 | is($stats->{item_size_max}, 33554432); 51 | $server->stop(); 52 | 53 | -------------------------------------------------------------------------------- /t/line-lengths.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/env 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('-o no_modern'); 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/malicious-commands.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # These command strings are always expected to be malicious and as such we 4 | # should just hang up on them. 5 | 6 | use strict; 7 | use Test::More tests => 3; 8 | use FindBin qw($Bin); 9 | use lib "$Bin/lib"; 10 | use MemcachedTest; 11 | 12 | my @strs = ( 13 | "GET / HTTP/1.0", 14 | "PUT /asdf/asd/fasdfasdf/sadf HTTP/1.1", 15 | "DELETE HTTP/1.1" 16 | ); 17 | 18 | for my $str (@strs) { 19 | my $server = new_memcached(); 20 | my $sock = $server->sock; 21 | 22 | print $sock "$str\r\n"; 23 | 24 | # Five seconds ought to be enough to get hung up on. 25 | my $oldalarmt = alarm(5); 26 | 27 | # Verify we can't read anything. 28 | my $bytesread = -1; 29 | eval { 30 | local $SIG{'ALRM'} = sub { die "timeout" }; 31 | my $data = ""; 32 | $bytesread = sysread($sock, $data, 24), 33 | }; 34 | is($bytesread, 0, $str); 35 | 36 | # Restore signal stuff. 37 | alarm($oldalarmt); 38 | } 39 | -------------------------------------------------------------------------------- /t/maxconns.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | use FindBin qw($Bin); 8 | use lib "$Bin/lib"; 9 | use MemcachedTest; 10 | 11 | my $server = new_memcached(); 12 | test_maxconns($server); 13 | 14 | my $ext_path; 15 | if (supports_extstore()) { 16 | $ext_path = "/tmp/extstore.$$"; 17 | 18 | my $server = new_memcached("-m 64 -U 0 -o ext_path=$ext_path:64m"); 19 | test_maxconns($server); 20 | } 21 | 22 | sub test_maxconns { 23 | my $server = shift; 24 | 25 | my $stat_sock = $server->sock; 26 | my @sockets = (); 27 | my $num_sockets; 28 | my $rejected_conns = 0; 29 | my $stats; 30 | for (1 .. 1024) { 31 | my $sock = $server->new_sock; 32 | if (defined($sock)) { 33 | push(@sockets, $sock); 34 | $stats = mem_stats($stat_sock); 35 | if ($stats->{rejected_connections} > $rejected_conns) { 36 | $rejected_conns = $stats->{rejected_connections}; 37 | my $buffer = ""; 38 | my $length = 31; 39 | my $res = recv($sock, $buffer, $length, 0); 40 | if (not $buffer eq '') { 41 | is($buffer, "ERROR Too many open connections", "Got expected response from the server"); 42 | } 43 | } 44 | } 45 | } 46 | 47 | my $failed = 1; 48 | for (1 .. 5) { 49 | $stats = mem_stats($stat_sock); 50 | if ($stats->{rejected_connections} != 0) { 51 | $failed = 0; 52 | last; 53 | } 54 | sleep 1; 55 | } 56 | is($failed, 0, "rejected connections were observed."); 57 | 58 | for my $s (@sockets) { 59 | $s->close(); 60 | } 61 | 62 | $server->stop; 63 | $stat_sock->close(); 64 | } 65 | 66 | done_testing(); 67 | 68 | END { 69 | unlink $ext_path if $ext_path; 70 | } 71 | -------------------------------------------------------------------------------- /t/misbehave.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use Test::More; 5 | use FindBin qw($Bin); 6 | use Socket qw(MSG_PEEK MSG_DONTWAIT); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | 10 | if (!enabled_tls_testing() && supports_drop_priv()) { 11 | plan tests => 1; 12 | } else { 13 | plan skip_all => 'Privilege drop not supported'; 14 | exit 0; 15 | } 16 | 17 | my $server = new_memcached('-o drop_privileges'); 18 | my $sock = $server->sock; 19 | 20 | print $sock "misbehave\r\n"; 21 | sleep(1); 22 | 23 | # check if the socket is dead now 24 | my $buff; 25 | my $ret = recv($sock, $buff, 1, MSG_PEEK | MSG_DONTWAIT); 26 | # ret = 0 means read 0 bytes, which means a closed socket 27 | ok($ret == 0, "did not allow misbehaving"); 28 | 29 | $server->DESTROY(); 30 | -------------------------------------------------------------------------------- /t/multiversioning.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | my $read = 0; 28 | my $to_read = $size / 2; 29 | while ($read < $to_read) { 30 | my $read_bytes = $sock->sysread($buf, 31 | ($to_read - $read > MemcachedTest::MAX_READ_WRITE_SIZE ? 32 | MemcachedTest::MAX_READ_WRITE_SIZE : $to_read - $read), 33 | $read); 34 | last if ($read_bytes <= 0); 35 | $read += $read_bytes; 36 | } 37 | is($read, $size / 2, "read half the answer back"); 38 | like($buf, qr/VALUE big/, "buf has big value header in it"); 39 | like($buf, qr/abcdef/, "buf has some data in it"); 40 | unlike($buf, qr/abcde\]/, "buf doesn't yet close"); 41 | 42 | # sock2 interrupts (maybe sock1 is slow) and deletes stuff: 43 | print $sock2 "delete big\r\n"; 44 | is(scalar <$sock2>, "DELETED\r\n", "deleted big from sock2 while sock1's still reading it"); 45 | mem_get_is($sock2, "big", undef, "nothing from sock2 now. gone from namespace."); 46 | print $sock2 "set big 0 0 $size\r\n$bigval2\r\n"; 47 | is(scalar <$sock2>, "STORED\r\n", "stored big w/ val2"); 48 | mem_get_is($sock2, "big", $bigval2, "big value2 got correctly"); 49 | 50 | # sock1 resumes reading... 51 | $buf .= <$sock>; 52 | $buf .= <$sock>; 53 | like($buf, qr/abcde\]/, "buf now closes"); 54 | 55 | # and if sock1 reads again, it's the uppercase version: 56 | mem_get_is($sock, "big", $bigval2, "big value2 got correctly from sock1"); 57 | -------------------------------------------------------------------------------- /t/noreply.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | -------------------------------------------------------------------------------- /t/quit.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | { 13 | print $sock "quit\r\n"; 14 | 15 | # Five seconds ought to be enough to get hung up on. 16 | my $oldalarmt = alarm(5); 17 | 18 | # Verify we can't read anything. 19 | my $bytesread = -1; 20 | eval { 21 | local $SIG{'ALRM'} = sub { die "timeout" }; 22 | my $data = ""; 23 | $bytesread = sysread($sock, $data, 24), 24 | }; 25 | is($bytesread, 0, "Read after quit."); 26 | 27 | # Restore signal stuff. 28 | alarm($oldalarmt); 29 | } 30 | -------------------------------------------------------------------------------- /t/refhang.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | 8 | use FindBin qw($Bin); 9 | use lib "$Bin/lib"; 10 | use MemcachedTest; 11 | 12 | plan skip_all => 'Test is flaky. Needs special hooks.'; 13 | 14 | plan tests => 127; 15 | 16 | # start up a server with 10 maximum connections 17 | my $server = new_memcached("-m 6"); 18 | my $sock = $server->sock; 19 | my $hangsock = $server->new_sock; 20 | my $hangsock2 = $server->new_sock; 21 | my $value = "B"x66560; 22 | my $key = 0; 23 | 24 | # These aren't set to expire. 25 | my $mget = ''; 26 | for ($key = 0; $key < 120; $key++) { 27 | $mget .= "key$key " if $key < 115; 28 | print $sock "set key$key 0 0 66560\r\n$value\r\n"; 29 | is(scalar <$sock>, "STORED\r\n", "stored key$key"); 30 | } 31 | chop $mget; 32 | 33 | my $stats = mem_stats($sock, "items"); 34 | isnt($stats->{"items:31:evicted"}, "0", "check evicted"); 35 | 36 | my $lrutail_reflocked = $stats->{"items:31:lrutail_reflocked"}; 37 | is($lrutail_reflocked, "0", "check no slab lrutail_reflocked"); 38 | 39 | $stats = mem_stats($sock); 40 | is($stats->{"lrutail_reflocked"}, "0", "check no total lrutail_reflocked"); 41 | 42 | # Don't intend to read the results, need to fill the socket. 43 | # TODO: This test would be smarter if we cranked down the socket buffers 44 | # first? Or perhaps used a unix domain socket. 45 | print $hangsock "get $mget\r\n"; 46 | #sleep 8; 47 | # Now we try a bunch of sets again, and see if they start coming back as OOM's 48 | for ($key = 121; $key < 240; $key++) { 49 | print $sock "set key$key 0 0 66560\r\n$value\r\n"; 50 | my $res = scalar <$sock>; 51 | } 52 | 53 | $stats = mem_stats($sock, "items"); 54 | # Some items will OOM since we only clear a handful per alloc attempt. 55 | ok($stats->{"items:31:outofmemory"} > 0, "some ooms happened"); 56 | ok($stats->{"items:31:outofmemory"} < 20, "fewer than 20 ooms"); 57 | isnt($stats->{"items:31:lrutail_reflocked"}, "0", "nonzero lrutail_reflocked"); 58 | 59 | $stats = mem_stats($sock); 60 | isnt($stats->{"lrutail_reflocked"}, "0", "nonzero total lrutail_reflocked"); 61 | -------------------------------------------------------------------------------- /t/sasl/memcached.conf: -------------------------------------------------------------------------------- 1 | mech_list: plain cram-md5 2 | log_level: 5 3 | sasldb_path: /tmp/test-memcached.sasldb 4 | -------------------------------------------------------------------------------- /t/server_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAyJ//TaBunqtvD1t6Z7ctC+tpa58Zem71spFQcdS0QoS54IyY 3 | YYktkMINaNTqHG9t0yhSKQShTSiOODWYG1vzx9pBF4PI1TJAfpC8aYgplpO00UVl 4 | rHyITFTcsLEGgn4N06YtUODNawvDdMSWjK+HNNc71zNg4xaZAQZek3eLasM/zp/g 5 | RDy8trzoZbNylO7kmWxfqUPkCo0TOEjdCJxRbdbTa9xEsRAPfYeX/KtB5REospvh 6 | S41klO4+X7WaA6xk4bg8UXgFuZj4QQs2fx52ompRdS3i+g0A0rmWUOutM8OrjM5J 7 | CYJ/aXe3OHCQ5xaca/LAfmGrxDQEqjjcSgBQ7QIDAQABAoIBACmp4HEUgiR9YaEE 8 | 1FS5m6dACjKJZdchN/EPcG9TRuQRgDB7wiFvRYEsa3B71up00Y/qbbWK+px1caOG 9 | rcHwxJ2aW64wdgKgXvhpwlcAKfLVVdWn3ceGTR/c97/R45Ix71kmx35mUQKL/NlB 10 | AirRQPjeQdUdHF/Mj5XA5t8lElTnPSbxTHqTsS4ktwZDz5szMyrMYTIj2Pwe9VJv 11 | Q6HpmaWhiMBDtc03yM+v2M4kXrtjYM0z3PGQsH77Of87Cq1E2gtxfroZC7lfsH5G 12 | lDffmW6fVGKeRnXya0WKRB58gJh/wF6rdhSbDjtOMl1UuObuFaz7hArI0LLFjiLp 13 | hkxt5EECgYEA6tHGsTdW3w3NBSihjqyqJdhHXnRyekJy/LBTyoUtW2yXSP5Dy/Iq 14 | ZVkAjxiGnOOuPKfDSCm6eKHKaB4+mhXD1ySclDdiurUg6zfbPjK9nzIuETJAPpR7 15 | Wbl4AzrTdpbFct4svcZa4zx1wbzjlLgKzTonmZTLufMmt7VoypNlxykCgYEA2rii 16 | zAnY94uO66WZNaxAHG1DBmz20scpmQGuwqm08Pp0Wecim91gaonJWTV0RLRNXNB8 17 | Y+lTlDCdhGzIaOtSUJBjIVtbaz/X6FuNvuuWRi+No/QtsA35dZeg3vNSlAp/IHKE 18 | tqLf7ep9DkheLqNPIv2OMHZNw0BmjeqMjqXvSCUCgYBrayNVllcrGokbPwcI2XvM 19 | bC/ZybNEsnkflxn9nwast/RM8+PXvCQg0KIs069gvdbK8IOo0032OSz6jDtfCW3Z 20 | UWo/c0a62nkAoUCuJ1APL0lbnH0/I3V6ChoYgCSUL0yLy470EXUqVlIYGmyRb8+h 21 | KmVLIIJBwjWH/Hi/ksYQYQKBgQCD1FejXagdhEtvcw+GTz0RJTYJ0HFwl8RVybLY 22 | 98rsmDi621p70ZHEhSoMD/D1XCto7uyal87defPnFl4xBu1FS9HEEDloykFLdqtX 23 | 0M1xnkXj9U/4VmPuYab/2m8CddUr7HpbTo5j6zrW+f1yZNJVB7jFMvkp304w6Zcw 24 | f4la1QKBgCc2ksKWuoSt3Kfv6s2dXRsO4p/osPjQc3bnVK2GiAJAjLf7fqadRdNL 25 | jCMzgA+lxRP4dzA0KD6Z8J6ePr6KmGa8b+lbCvQFKvJCWvsLLKqkz/lnfljp8VvA 26 | ntXBBQr+46peeLGehVvcprwc4jANagkiQq5DoVDONN+lobRbCM21 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /t/shutdown.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | 10 | # Disabled shutdown (default) 11 | { 12 | my $server = new_memcached(); 13 | my $sock = $server->sock; 14 | print $sock "shutdown\r\n"; 15 | is(scalar <$sock>, "ERROR: shutdown not enabled\r\n", 16 | "error when shutdown is not enabled"); 17 | } 18 | 19 | # Shutdown command error 20 | { 21 | my$server = new_memcached("-A"); 22 | my $sock = $server->sock; 23 | print $sock "shutdown foo\r\n"; 24 | like(scalar <$sock>, qr/CLIENT_ERROR/, "rejected invalid shutdown mode"); 25 | } 26 | 27 | # Normal shutdown 28 | { 29 | my $server = new_memcached("-A"); 30 | my $sock = $server->sock; 31 | print $sock "version\r\n"; 32 | like(scalar <$sock>, qr/VERSION/, "server is initially alive"); 33 | print $sock "shutdown\r\n"; 34 | still_going($server); 35 | } 36 | 37 | # Graceful shutdown 38 | { 39 | my $server = new_memcached("-A"); 40 | my $sock = $server->sock; 41 | print $sock "version\r\n"; 42 | like(scalar <$sock>, qr/VERSION/, "server is initially alive"); 43 | print $sock "shutdown graceful\r\n"; 44 | still_going($server); 45 | } 46 | 47 | sub still_going { 48 | my $server = shift; 49 | for (1..10) { 50 | if ($server->is_running) { 51 | sleep 1; 52 | } else { 53 | ok(!$server->is_running, "server stopped"); 54 | return; 55 | } 56 | } 57 | 58 | ok(0, "server failed to stop"); 59 | } 60 | 61 | done_testing(); 62 | -------------------------------------------------------------------------------- /t/slabhang.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | 8 | use FindBin qw($Bin); 9 | use lib "$Bin/lib"; 10 | use MemcachedTest; 11 | 12 | plan skip_all => 'Test is flaky. Needs special hooks.'; 13 | 14 | plan tests => 74; 15 | 16 | # start up a server with 10 maximum connections 17 | my $server = new_memcached("-m 16 -o modern"); 18 | my $sock = $server->sock; 19 | my $hangsock = $server->new_sock; 20 | my $value = "B"x260144; 21 | my $key = 0; 22 | 23 | # disable the normal automover. 24 | print $sock "slabs automove 0\r\n"; 25 | is(scalar <$sock>, "OK\r\n", "automover disabled"); 26 | 27 | # These aren't set to expire. 28 | my $mget = ''; 29 | for ($key = 0; $key < 70; $key++) { 30 | $mget .= "key$key "; 31 | print $sock "set key$key 0 0 260144\r\n$value\r\n"; 32 | is(scalar <$sock>, "STORED\r\n", "stored key$key"); 33 | } 34 | chop $mget; 35 | 36 | # Don't intend to read the results, need to fill the socket. 37 | print $hangsock "get $mget\r\n"; 38 | #sleep 8; 39 | my $stats = mem_stats($sock, "slabs"); 40 | my $source = 0; 41 | for my $key (keys %$stats) { 42 | if ($key =~ m/^(\d+):total_pages/) { 43 | my $sid = $1; 44 | if ($stats->{$key} > 10) { 45 | $source = $sid; 46 | last; 47 | } 48 | } 49 | } 50 | isnt($source, 0, "found the source slab: $source"); 51 | 52 | my $busy; 53 | my $tomove = 4; 54 | my $reassign = "slabs reassign $source 1\r\n"; 55 | while ($tomove) { 56 | $busy = 0; 57 | print $sock $reassign; 58 | my $res = scalar <$sock>; 59 | while ($res =~ m/^BUSY/) { 60 | if ($hangsock && $busy > 5) { 61 | # unjam the pipeline 62 | $hangsock->close; 63 | } 64 | last if ($busy > 10); 65 | sleep 1; 66 | $busy++; 67 | print $sock $reassign; 68 | $res = scalar <$sock>; 69 | } 70 | last if ($busy > 10); 71 | $tomove--; 72 | } 73 | 74 | ok($busy <= 10, "didn't time out moving pages"); 75 | 76 | $stats = mem_stats($sock); 77 | isnt($stats->{"slab_reassign_busy_deletes"}, "0", "deleted some busy items"); 78 | -------------------------------------------------------------------------------- /t/slabs_reassign.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | 37 | # Invalid argument test 38 | print $sock "slabs reassign invalid1 invalid2\r\n"; 39 | is(scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); 40 | 41 | # Move a large slab to the smaller slab 42 | print $sock "slabs reassign 31 25\r\n"; 43 | is(scalar <$sock>, "OK\r\n", "slab rebalancer started"); 44 | 45 | # Still working out how/if to signal the thread. For now, just sleep. 46 | sleep 2; 47 | 48 | # Check that stats counters increased 49 | my $slabs_after = mem_stats($sock, "slabs"); 50 | $stats = mem_stats($sock); 51 | 52 | isnt($stats->{slabs_moved}, 0, "slabs moved is nonzero"); 53 | 54 | # Check that slab stats reflect the change 55 | ok($slabs_before->{"31:total_pages"} != $slabs_after->{"31:total_pages"}, 56 | "slab 31 pagecount changed"); 57 | ok($slabs_before->{"25:total_pages"} != $slabs_after->{"25:total_pages"}, 58 | "slab 25 pagecount changed"); 59 | 60 | # Try to move another slab, see that you can move two in a row 61 | print $sock "slabs reassign 31 25\r\n"; 62 | like(scalar <$sock>, qr/^OK/, "Cannot re-run against class with empty space"); 63 | 64 | # Try to move a page backwards. Should complain that source class isn't "safe" 65 | # to move from. 66 | # TODO: Wait until the above command completes, then try to move it back? 67 | # Seems pointless... 68 | #print $sock "slabs reassign 25 31\r\n"; 69 | #like(scalar <$sock>, qr/^UNSAFE/, "Cannot move an unsafe slab back"); 70 | 71 | # Try to insert items into both slabs 72 | print $sock "set bfoo51 0 0 70000\r\n", $bigdata, "\r\n"; 73 | is(scalar <$sock>, "STORED\r\n", "stored key"); 74 | 75 | print $sock "set sfoo51 0 0 20000\r\n", $smalldata, "\r\n"; 76 | is(scalar <$sock>, "STORED\r\n", "stored key"); 77 | 78 | # Do need to come up with better automated tests for this. 79 | -------------------------------------------------------------------------------- /t/ssl_cert_refresh.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use File::Copy; 6 | use File::Temp; 7 | use Test::More; 8 | use FindBin qw($Bin); 9 | use lib "$Bin/lib"; 10 | use MemcachedTest; 11 | 12 | if (!enabled_tls_testing()) { 13 | plan skip_all => 'SSL testing is not enabled'; 14 | exit 0; 15 | } 16 | 17 | my $ca_cert = File::Temp->new()->filename; 18 | my $cert = File::Temp->new()->filename; 19 | my $key = File::Temp->new()->filename; 20 | my $new_cert_key = File::Temp->new()->filename; 21 | my $ca_cert_back = $ca_cert . ".bak"; 22 | my $cert_back = $cert . ".bak"; 23 | my $key_back = $key . ".bak"; 24 | 25 | copy("t/" . MemcachedTest::CA_CRT, $ca_cert); 26 | copy("t/" . MemcachedTest::SRV_CRT, $cert); 27 | copy("t/" . MemcachedTest::SRV_KEY, $key); 28 | copy("t/server.pem", $new_cert_key); 29 | 30 | my $default_crt_ou = "OU=Subunit of Test Organization"; 31 | 32 | my $server = new_memcached("-o ssl_ca_cert=$ca_cert -o ssl_chain_cert=$cert -o ssl_key=$key"); 33 | my $stats = mem_stats($server->sock); 34 | my $sock = $server->sock; 35 | 36 | # This connection should return the default server certificate 37 | # memcached was started with. 38 | my $cert_details =$sock->dump_peer_certificate(); 39 | $cert_details =~ m/(OU=([^\/\n]*))/; 40 | is($1, $default_crt_ou, 'Got the default cert'); 41 | 42 | # Swap a new certificate with a key 43 | copy($ca_cert, $ca_cert_back) or die "CA cert backup failed: $!"; 44 | copy($cert, $cert_back) or die "Cert backup failed: $!"; 45 | copy($key, $key_back) or die "Key backup failed: $!"; 46 | copy($new_cert_key, $ca_cert) or die "New CA cert copy failed: $!"; 47 | copy($new_cert_key, $cert) or die "New Cert copy failed: $!"; 48 | copy($new_cert_key, $key) or die "New key copy failed: $!"; 49 | 50 | # Ask server to refresh certificates 51 | print $sock "refresh_certs\r\n"; 52 | is(scalar <$sock>, "OK\r\n", "refreshed certificates"); 53 | 54 | # New connections should use the new certificate 55 | $cert_details = $server->new_sock->dump_peer_certificate(); 56 | $cert_details =~ m/(OU=([^\/]*))/; 57 | is($1, 'OU=FOR TESTING PURPOSES ONLY','Got the new cert'); 58 | # Old connection should use the previous certificate 59 | $cert_details = $sock->dump_peer_certificate(); 60 | $cert_details =~ m/(OU=([^\/\n]*))/; 61 | is($1, $default_crt_ou, 'Old connection still has the old cert'); 62 | 63 | # Just sleep a while to test the time_since_server_cert_refresh as it's counted 64 | # in seconds. 65 | sleep 5; 66 | $stats = mem_stats($sock); 67 | 68 | # Restore and ensure previous certificate is back for new connections. 69 | move($ca_cert_back, $ca_cert) or die "CA cert restore failed: $!"; 70 | move($cert_back, $cert) or die "Cert restore failed: $!"; 71 | move($key_back, $key) or die "Key restore failed: $!"; 72 | print $sock "refresh_certs\r\n"; 73 | is(scalar <$sock>, "OK\r\n", "refreshed certificates"); 74 | 75 | 76 | $cert_details = $server->new_sock->dump_peer_certificate(); 77 | $cert_details =~ m/(OU=([^\/\n]*))/; 78 | is($1, $default_crt_ou, 'Got the old cert back'); 79 | 80 | my $stats_after = mem_stats($sock); 81 | 82 | # We should see last refresh time is reset; hence the new 83 | # time_since_server_cert_refresh should be less. 84 | cmp_ok($stats_after->{time_since_server_cert_refresh}, '<', 85 | $stats->{time_since_server_cert_refresh}, 'Certs refreshed'); 86 | 87 | done_testing(); 88 | 89 | END { 90 | unlink $ca_cert if $ca_cert; 91 | unlink $cert if $cert; 92 | unlink $key if $key; 93 | unlink $new_cert_key if $new_cert_key; 94 | } 95 | -------------------------------------------------------------------------------- /t/ssl_ports.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | 10 | 11 | if (!enabled_tls_testing()) { 12 | plan skip_all => 'SSL testing is not enabled'; 13 | exit 0; 14 | } 15 | 16 | my $tcp_port = free_port(); 17 | my $ssl_port = free_port(); 18 | 19 | my $server = new_memcached("-l notls:127.0.0.1:$tcp_port,127.0.0.1:$ssl_port", $ssl_port); 20 | my $sock = $server->sock; 21 | 22 | # Make sure we can talk over SSL 23 | print $sock "set foo:123 0 0 16\r\nfoo set over SSL\r\n"; 24 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 25 | 26 | 27 | #.. and TCP 28 | my $tcp_sock = IO::Socket::INET->new(PeerAddr => "127.0.0.1:$tcp_port"); 29 | mem_get_is($tcp_sock, "foo:123", "foo set over SSL"); 30 | 31 | done_testing() 32 | -------------------------------------------------------------------------------- /t/ssl_proto_version.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use Test::More; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | if (!enabled_tls_testing()) { 10 | plan skip_all => 'SSL testing is not enabled'; 11 | exit 0; 12 | } 13 | 14 | my $server; 15 | my $is_tls_13_available = 0; 16 | 17 | eval { 18 | # ssl_min_version=3 is not recognized when compiled with OpenSSL < 1.1.1 19 | $server = new_memcached('-o ssl_min_version=3'); 20 | $is_tls_13_available = 1; 21 | }; 22 | 23 | SKIP: { 24 | skip 'TLS v1.3 not available', 1 if !$is_tls_13_available; 25 | # Unsupported protocol version 26 | $sock = $server->new_sock(undef, 'TLSv1_2'); 27 | is(undef, $sock, "handshake failure on unsupported proto version"); 28 | } 29 | 30 | $server = new_memcached('-o ssl_min_version=2'); 31 | 32 | # Minimum supported protocol version 33 | $sock = $server->new_sock(undef, 'TLSv1_2'); 34 | print $sock "version\r\n"; 35 | like(scalar <$sock>, qr/VERSION/, "handshake with minimum proto version"); 36 | 37 | SKIP: { 38 | skip 'TLS v1.3 not available', 1 if !$is_tls_13_available; 39 | # Above minimum supported protocol version 40 | $sock = $server->new_sock(undef, 'TLSv1_3'); 41 | print $sock "version\r\n"; 42 | like(scalar <$sock>, qr/VERSION/, "handshake above minimum proto version"); 43 | } 44 | 45 | done_testing(); 46 | -------------------------------------------------------------------------------- /t/ssl_session_resumption.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use Test::More; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | if (!enabled_tls_testing()) { 10 | plan skip_all => 'SSL testing is not enabled'; 11 | exit 0; 12 | } 13 | 14 | my $server; 15 | my $sock; 16 | my $stats; 17 | 18 | my $session_cache = eval qq{ IO::Socket::SSL::Session_Cache->new(1); }; 19 | 20 | ### Disabled SSL session cache 21 | 22 | $server = new_memcached(); 23 | $stats = mem_stats($server->sock); 24 | is($stats->{ssl_new_sessions}, undef, 25 | "new SSL sessions not recorded when session cache is disabled"); 26 | my $disabled_initial_total_conns = $stats->{total_connections}; 27 | 28 | $sock = $server->new_sock($session_cache, 'TLSv1_2'); 29 | $stats = mem_stats($sock); 30 | cmp_ok($stats->{total_connections}, '>', $disabled_initial_total_conns, 31 | "client-side session cache is noop in establishing a new connection"); 32 | is($sock->get_session_reused(), 0, "client-side session cache is unused"); 33 | 34 | ### Enabled SSL session cache 35 | 36 | $server = new_memcached("-o ssl_session_cache"); 37 | # Support for session caching in IO::Socket::SSL for TLS v1.3 is incomplete. 38 | # Here, we will deliberately force TLS v1.2 to test session caching. 39 | $sock = $server->new_sock($session_cache, 'TLSv1_2'); 40 | $stats = mem_stats($sock); 41 | cmp_ok($stats->{total_connections}, '>', 0, "initial connection is established"); 42 | SKIP: { 43 | skip "sessions counter accuracy requires OpenSSL 1.1.1 or newer", 1; 44 | cmp_ok($stats->{ssl_new_sessions}, '>', 0, "successful new SSL session"); 45 | } 46 | my $enabled_initial_ssl_sessions = $stats->{ssl_new_sessions}; 47 | my $enabled_initial_total_conns = $stats->{total_connections}; 48 | 49 | # Create a new client with the same session cache 50 | $sock = $server->new_sock($session_cache, 'TLSv1_2'); 51 | $stats = mem_stats($sock); 52 | cmp_ok($stats->{total_connections}, '>', $enabled_initial_total_conns, 53 | "new connection is established"); 54 | is($stats->{ssl_new_sessions}, $enabled_initial_ssl_sessions, 55 | "no new SSL sessions are created on the server"); 56 | is($sock->get_session_reused(), 1, 57 | "client-persisted session is reused"); 58 | 59 | done_testing(); 60 | -------------------------------------------------------------------------------- /t/ssl_settings.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | use Cwd; 10 | 11 | if (!enabled_tls_testing()) { 12 | plan skip_all => 'SSL testing is not enabled'; 13 | exit 0; 14 | } 15 | 16 | my $server = new_memcached(); 17 | my $settings = mem_stats($server->sock, ' settings'); 18 | 19 | my $cert = getcwd ."/t/". MemcachedTest::SRV_CRT; 20 | my $key = getcwd ."/t/". MemcachedTest::SRV_KEY; 21 | 22 | is($settings->{'ssl_enabled'}, 'yes'); 23 | is($settings->{'ssl_session_cache'}, 'no'); 24 | is($settings->{'ssl_kernel_tls'}, 'no'); 25 | is($settings->{'ssl_chain_cert'}, $cert); 26 | is($settings->{'ssl_key'}, $key); 27 | is($settings->{'ssl_verify_mode'}, 0); 28 | is($settings->{'ssl_keyformat'}, 1); 29 | is($settings->{'ssl_ciphers'}, 'NULL'); 30 | is($settings->{'ssl_ca_cert'}, 'NULL'); 31 | is($settings->{'ssl_wbuf_size'}, 16384); 32 | is($settings->{'ssl_min_version'}, 'tlsv1.2'); 33 | 34 | $server->DESTROY(); 35 | $server = new_memcached("-o ssl_wbuf_size=64"); 36 | $settings = mem_stats($server->sock, ' settings'); 37 | is($settings->{'ssl_wbuf_size'}, 65536); 38 | 39 | done_testing(); 40 | -------------------------------------------------------------------------------- /t/ssl_verify_modes.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | use FindBin qw($Bin); 7 | use lib "$Bin/lib"; 8 | use MemcachedTest; 9 | use Cwd; 10 | 11 | if (!enabled_tls_testing()) { 12 | plan skip_all => 'SSL testing is not enabled'; 13 | exit 0; 14 | } 15 | 16 | my $ca_crt = getcwd() . "/t/" . MemcachedTest::CA_CRT; 17 | my $server = new_memcached("-o ssl_verify_mode=2 -o ssl_ca_cert=$ca_crt"); 18 | # just using stats to make sure everything is working fine. 19 | my $stats = mem_stats($server->sock); 20 | is($stats->{accepting_conns}, 1, "client cert is verified"); 21 | 22 | done_testing(); 23 | -------------------------------------------------------------------------------- /t/stats-conns.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use Test::More; 5 | use FindBin qw($Bin); 6 | use lib "$Bin/lib"; 7 | use MemcachedTest; 8 | 9 | ## First make sure we report UNIX-domain sockets correctly 10 | if (supports_unix_socket()) { 11 | plan tests => 12; 12 | 13 | my $filename = "/tmp/memcachetest$$"; 14 | 15 | my $server = new_memcached("-s $filename"); 16 | my $sock = $server->sock; 17 | my $stats_sock = $server->new_sock; 18 | 19 | ok(-S $filename, "creating unix domain socket $filename"); 20 | 21 | print $sock "set foo 0 0 6\r\n"; 22 | sleep(1); # so we can test secs_since_last_cmd is nonzero 23 | print $stats_sock "stats conns\r\n"; 24 | 25 | my $stats; 26 | while (<$stats_sock>) { 27 | last if /^(\.|END)/; 28 | $stats .= $_; 29 | } 30 | 31 | like($stats, qr/STAT \d+:addr /); 32 | $stats =~ m/STAT (\d+):addr unix:(.*[^\r\n])/g; 33 | my $listen_fd = $1; 34 | my $socket_path = $2; 35 | # getsockname(2) doesn't return socket path on GNU/Hurd (and maybe others) 36 | SKIP: { 37 | skip "socket path checking on GNU kernel", 1 if ($^O eq 'gnu'); 38 | is($socket_path, $filename, "unix domain socket path reported correctly"); 39 | }; 40 | $stats =~ m/STAT (\d+):state conn_listening\r\n/g; 41 | is($1, $listen_fd, "listen socket fd reported correctly"); 42 | 43 | like($stats, qr/STAT \d+:state conn_nread/, 44 | "one client is sending data"); 45 | like($stats, qr/STAT \d+:state conn_parse_cmd/, 46 | "one client is in command processing"); 47 | like($stats, qr/STAT \d+:secs_since_last_cmd [1-9]\r/, 48 | "nonzero secs_since_last_cmd"); 49 | like($stats, qr/STAT \d+:listen_addr unix:\/tmp\/memcachetest\d+\r/, 50 | "found listen_addr for the UNIX-domain socket"); 51 | 52 | $server->stop; 53 | unlink($filename); 54 | } else { 55 | plan tests => 4; 56 | } 57 | 58 | ## Now look at TCP 59 | 60 | my $server = new_memcached("-l 0.0.0.0"); 61 | my $sock = $server->sock; 62 | my $stats_sock = $server->new_sock; 63 | 64 | print $sock "set foo 0 0 6\r\n"; 65 | print $stats_sock "stats conns\r\n"; 66 | 67 | my $stats = ''; 68 | while (<$stats_sock>) { 69 | last if /^(\.|END)/; 70 | $stats .= $_; 71 | } 72 | 73 | like($stats, qr/STAT \d+:state conn_listen/, "there is a listen socket"); 74 | $stats =~ m/STAT \d+:addr udp:0.0.0.0:(\d+)/; 75 | is($1, $server->udpport, "udp port number is correct"); 76 | $stats =~ m/STAT \d+:addr tcp:0.0.0.0:(\d+)/; 77 | print STDERR "PORT: ", $server->port, "\n"; 78 | is($1, $server->port, "tcp port number is correct"); 79 | 80 | $stats =~ m/STAT \d+:listen_addr tcp:0.0.0.0:(\d+)/; 81 | is($1, $server->port, "listen_addr is correct for the tcp port"); 82 | -------------------------------------------------------------------------------- /t/stats-detail.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/stress-memcached.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/strtol-testing.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | 12 | print $sock "verbosity invalid\r\n"; 13 | is(scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); 14 | 15 | print $sock "slabs automove invalid\r\n"; 16 | is(scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); 17 | -------------------------------------------------------------------------------- /t/touch.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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/env 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 $filename = "/tmp/memcachetest$$"; 10 | 11 | if (supports_unix_socket()) { 12 | plan tests => 3; 13 | 14 | my $server = new_memcached("-s $filename"); 15 | my $sock = $server->sock; 16 | 17 | ok(-S $filename, "creating unix domain socket $filename"); 18 | 19 | # set foo (and should get it) 20 | print $sock "set foo 0 0 6\r\nfooval\r\n"; 21 | 22 | is(scalar <$sock>, "STORED\r\n", "stored foo"); 23 | mem_get_is($sock, "foo", "fooval"); 24 | 25 | unlink($filename); 26 | 27 | ## Just some basic stuff for now... 28 | } else { 29 | plan tests => 1; 30 | 31 | eval { 32 | my $server = new_memcached("-s $filename"); 33 | }; 34 | ok($@, "Died connecting to unsupported unix socket."); 35 | } 36 | -------------------------------------------------------------------------------- /t/watcher_connid.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # Test for adding connection id to the output when watching fetchers 3 | # and mutations. 4 | # Note that this test relies on the order of connection establishments. This 5 | # could be improved if there's a way for a client to retrieve its connection id. 6 | use strict; 7 | use warnings; 8 | use Socket qw/SO_RCVBUF/; 9 | 10 | use Test::More tests => 4; 11 | use FindBin qw($Bin); 12 | use lib "$Bin/lib"; 13 | use MemcachedTest; 14 | 15 | my $server = new_memcached('-m 60 -o watcher_logbuf_size=8'); 16 | my $client_first = $server->sock; 17 | 18 | my $stats; 19 | 20 | # get the first client's connection id 21 | print $client_first "stats conns\r\n"; 22 | while (<$client_first>) { 23 | last if /^(\.|END)/; 24 | $stats = $_; 25 | } 26 | my $cfd_first =(split(':', $stats))[0]; 27 | $cfd_first =~ s/[^0-9]//g; 28 | 29 | # start watching fetchers and mutations 30 | my $watcher = $server->new_sock; 31 | print $watcher "watch fetchers mutations\n"; 32 | my $res = <$watcher>; 33 | is($res, "OK\r\n", "watching enabled for fetchers and mutations"); 34 | 35 | # first client does a set, which will result in a get and a set 36 | print $client_first "set foo 0 0 5 noreply\r\nhello\r\n"; 37 | 38 | # ensure client's connection id is correct 39 | $res = <$watcher>; 40 | print $res; 41 | like($res, qr/ts=\d+\.\d+\ gid=\d+ type=item_get key=foo status=not_found clsid=\d+ cfd=$cfd_first/, 42 | "Saw a miss with the connection id $cfd_first"); 43 | $res = <$watcher>; 44 | print $res; 45 | like($res, qr/ts=\d+\.\d+\ gid=\d+ type=item_store key=foo status=stored cmd=set ttl=\d+ clsid=\d+ cfd=$cfd_first/, 46 | "Saw a set with the connection id $cfd_first"); 47 | 48 | # get the second client's connection id 49 | my $client_second = $server->new_sock; 50 | print $client_second "stats conns\r\n"; 51 | while (<$client_second>) { 52 | last if /^(\.|END)/; 53 | $stats = $_; 54 | } 55 | my $cfd_second =(split(':', $stats))[0]; 56 | $cfd_second =~ s/[^0-9]//g; 57 | 58 | # second client does a get 59 | print $client_second "get foo\r\n"; 60 | 61 | # now we should see second client's connection id 62 | $res = <$watcher>; 63 | print $res; 64 | like($res, qr/ts=\d+\.\d+\ gid=\d+ type=item_get key=foo status=found clsid=\d+ cfd=$cfd_second/, 65 | "Saw a get with the connection id $cfd_second"); 66 | 67 | -------------------------------------------------------------------------------- /t/whitespace.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use FindBin qw($Bin); 4 | our @files; 5 | 6 | BEGIN { 7 | chdir "$Bin/.." or die; 8 | unless (-d "$Bin/../.git") { 9 | use Test::More; 10 | plan skip_all => "Skipping test because this does not appear to be a memcached git working directory"; 11 | exit 0; 12 | } 13 | 14 | my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md compile_commands.json md5.c md5.h); 15 | push(@exempted, glob("doc/*.xml")); 16 | push(@exempted, glob("doc/*.full")); 17 | push(@exempted, glob("doc/xml2rfc/*.xsl")); 18 | push(@exempted, glob("doc/xml2rfc/*.dtd")); 19 | push(@exempted, glob("m4/*backport*m4")); 20 | push(@exempted, glob("*.orig")); 21 | push(@exempted, glob(".*.swp")); 22 | push(@exempted, glob("queue.h")); 23 | my %exempted_hash = map { $_ => 1 } @exempted; 24 | 25 | my @stuff = split /\0/, `git ls-files -z -c -m -o --exclude-standard`; 26 | @files = grep { ! $exempted_hash{$_} && $_ !~ m/^vendor/ } @stuff; 27 | 28 | # We won't find any files if git isn't installed. If git isn't 29 | # installed, they're probably not doing any useful development, or 30 | # at the very least am will clean up whitespace when we receive 31 | # their patch. 32 | unless (@files) { 33 | use Test::More; 34 | plan skip_all => "Skipping tests probably because you don't have git."; 35 | exit 0; 36 | } 37 | } 38 | 39 | use Test::More tests => scalar(@files); 40 | 41 | foreach my $f (@files) { 42 | open(my $fh, $f) or die "Cannot open file $f: $!"; 43 | my $before = do { local $/; <$fh>; }; 44 | close ($fh); 45 | my $after = $before; 46 | $after =~ s/\t/ /g; 47 | $after =~ s/ +$//mg; 48 | $after .= "\n" unless $after =~ /\n$/; 49 | ok ($after eq $before, "$f (see devtools/clean-whitespace.pl)"); 50 | } 51 | -------------------------------------------------------------------------------- /timedrun.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | volatile sig_atomic_t caught_sig = 0; 12 | 13 | static void signal_handler(int which) 14 | { 15 | caught_sig = which; 16 | } 17 | 18 | static int wait_for_process(pid_t pid) 19 | { 20 | int rv = EX_SOFTWARE; 21 | int status = 0; 22 | int i = 0; 23 | struct sigaction sig_handler; 24 | 25 | memset(&sig_handler, 0, sizeof(struct sigaction)); 26 | sig_handler.sa_handler = signal_handler; 27 | sig_handler.sa_flags = 0; 28 | 29 | sigaction(SIGALRM, &sig_handler, NULL); 30 | sigaction(SIGHUP, &sig_handler, NULL); 31 | sigaction(SIGINT, &sig_handler, NULL); 32 | sigaction(SIGUSR1, &sig_handler, NULL); 33 | sigaction(SIGTERM, &sig_handler, NULL); 34 | sigaction(SIGPIPE, &sig_handler, NULL); 35 | 36 | /* Loop forever waiting for the process to quit */ 37 | for (i = 0; ;i++) { 38 | pid_t p = waitpid(pid, &status, 0); 39 | if (p == pid) { 40 | /* child exited. Let's get out of here */ 41 | rv = WIFEXITED(status) ? 42 | WEXITSTATUS(status) : 43 | (0x80 | WTERMSIG(status)); 44 | break; 45 | } else { 46 | int sig = 0; 47 | switch (i) { 48 | case 0: 49 | /* On the first iteration, pass the signal through */ 50 | sig = caught_sig > 0 ? caught_sig : SIGTERM; 51 | if (caught_sig == SIGALRM) { 52 | fprintf(stderr, "Timeout.. killing the process\n"); 53 | } 54 | break; 55 | case 1: 56 | sig = SIGTERM; 57 | break; 58 | default: 59 | sig = SIGKILL; 60 | break; 61 | } 62 | if (kill(pid, sig) < 0) { 63 | /* Kill failed. Must have lost the process. :/ */ 64 | perror("lost child when trying to kill"); 65 | } 66 | /* Wait up to 5 seconds for the pid */ 67 | alarm(5); 68 | } 69 | } 70 | return rv; 71 | } 72 | 73 | static int spawn_and_wait(char **argv) 74 | { 75 | int rv = EX_SOFTWARE; 76 | pid_t pid = fork(); 77 | 78 | switch (pid) { 79 | case -1: 80 | perror("fork"); 81 | rv = EX_OSERR; 82 | break; /* NOTREACHED */ 83 | case 0: 84 | execvp(argv[0], argv); 85 | perror("exec"); 86 | rv = EX_SOFTWARE; 87 | break; /* NOTREACHED */ 88 | default: 89 | rv = wait_for_process(pid); 90 | } 91 | return rv; 92 | } 93 | 94 | static void usage(void) { 95 | fprintf(stderr, "./timedrun args...\n"); 96 | exit(-1); 97 | } 98 | 99 | int main(int argc, char **argv) 100 | { 101 | int naptime = 0; 102 | if (argc < 3) 103 | usage(); 104 | 105 | naptime = atoi(argv[1]); 106 | assert(naptime > 0 && naptime < 1800); 107 | 108 | alarm(naptime); 109 | 110 | return spawn_and_wait(argv+2); 111 | } 112 | -------------------------------------------------------------------------------- /tls.h: -------------------------------------------------------------------------------- 1 | #ifndef TLS_H 2 | #define TLS_H 3 | 4 | /* constant session ID context for application-level SSL session scoping. 5 | * used in server-side SSL session caching, when enabled. */ 6 | #define SESSION_ID_CONTEXT "memcached" 7 | 8 | void SSL_LOCK(void); 9 | void SSL_UNLOCK(void); 10 | ssize_t ssl_read(conn *c, void *buf, size_t count); 11 | ssize_t ssl_sendmsg(conn *c, struct msghdr *msg, int flags); 12 | ssize_t ssl_write(conn *c, void *buf, size_t count); 13 | 14 | int ssl_init(void); 15 | bool refresh_certs(char **errmsg); 16 | void ssl_callback(const SSL *s, int where, int ret); 17 | int ssl_new_session_callback(SSL *s, SSL_SESSION *sess); 18 | const char *ssl_proto_text(int version); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /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) 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) 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.h: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | /* fast-enough functions for uriencoding strings. */ 4 | void uriencode_init(void); 5 | bool uriencode(const char *src, char *dst, const size_t srclen, const size_t dstlen); 6 | 7 | /* 8 | * Wrappers around strtoull/strtoll that are safer and easier to 9 | * use. For tests and assumptions, see internal_tests.c. 10 | * 11 | * str a NULL-terminated base decimal 10 unsigned integer 12 | * out out parameter, if conversion succeeded 13 | * 14 | * returns true if conversion succeeded. 15 | */ 16 | bool safe_strtoull(const char *str, uint64_t *out); 17 | bool safe_strtoull_hex(const char *str, uint64_t *out); 18 | bool safe_strtoll(const char *str, int64_t *out); 19 | bool safe_strtoul(const char *str, uint32_t *out); 20 | bool safe_strtol(const char *str, int32_t *out); 21 | bool safe_strtod(const char *str, double *out); 22 | bool safe_strcpy(char *dst, const char *src, const size_t dstmax); 23 | bool safe_memcmp(const void *a, const void *b, size_t len); 24 | 25 | #ifndef HAVE_HTONLL 26 | extern uint64_t htonll(uint64_t); 27 | extern uint64_t ntohll(uint64_t); 28 | #endif 29 | 30 | #ifdef __GCC 31 | # define __gcc_attribute__ __attribute__ 32 | #else 33 | # define __gcc_attribute__(x) 34 | #endif 35 | 36 | /** 37 | * Vararg variant of perror that makes for more useful error messages 38 | * when reporting with parameters. 39 | * 40 | * @param fmt a printf format 41 | */ 42 | void vperror(const char *fmt, ...) 43 | __gcc_attribute__ ((format (printf, 1, 2))); 44 | -------------------------------------------------------------------------------- /vendor/.gitignore: -------------------------------------------------------------------------------- 1 | README.md 2 | /mcmc/example 3 | !/mcmc/Makefile 4 | !/Makefile 5 | -------------------------------------------------------------------------------- /vendor/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | cd lua && $(MAKE) all CFLAGS="-g" && cd .. 3 | cd mcmc && $(MAKE) all && cd .. 4 | 5 | clean: 6 | cd lua && $(MAKE) clean && cd .. 7 | cd mcmc && $(MAKE) clean && cd .. 8 | 9 | install: ; 10 | 11 | dist: clean 12 | distdir: clean 13 | -------------------------------------------------------------------------------- /vendor/fetch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | HASH="44a55dee1d41c3ae92524df9f0dd8a747db79f04" 3 | wget https://github.com/memcached/memcached-vendor/archive/${HASH}.tar.gz 4 | tar -zxf ./${HASH}.tar.gz --strip-components=1 5 | rm ${HASH}.tar.gz 6 | -------------------------------------------------------------------------------- /vendor/lua/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /vendor/mcmc/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Cache Forge LLC. 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 | -------------------------------------------------------------------------------- /vendor/mcmc/Makefile: -------------------------------------------------------------------------------- 1 | # gcc -g -Wall -Werror -pedantic -o example example.c mcmc.c 2 | PREFIX=/usr/local 3 | 4 | all: 5 | gcc -g -O2 -Wall -Werror -pedantic -o example example.c mcmc.c 6 | gcc -g -O2 -Wall -Werror -pedantic -c mcmc.c 7 | 8 | clean: 9 | rm -f example mcmc.o 10 | 11 | dist: clean 12 | 13 | distdir: 14 | -------------------------------------------------------------------------------- /vendor/mcmc/README.md: -------------------------------------------------------------------------------- 1 | # Minimal (C) Client for MemCached 2 | 3 | WARNING: WORK IN PROGRESS. Missing features or testing! 4 | 5 | MCMC is a minimalistic allocation-free modern client for memcached. It uses a 6 | generic response parser, allowing a single code path regardless of the command 7 | sent to memcached. It has no 3rd party dependencies and is designed to 8 | integrate as a building block into full clients. 9 | 10 | MCMC does not (yet) include a typical memcached "selector". Meaning the 11 | ability to add many servers to a hash table of some kind and routing keys to 12 | specific servers. The MCMC base client is designed to be an object that 13 | selector objects hold and then issue commands against. 14 | 15 | Allocation-free (aside from a call to `getaddrinfo()`) means it does not 16 | _internally_ do any allocations, relying only on the stack. It requires you 17 | malloc a small structure and some buffers, but you are then free to manage 18 | them yourselves. Clients do not hold onto buffers when idle, cutting their 19 | memory overhead to a handful of bytes plus the TCP socket. 20 | 21 | MCMC is designed to be a building block for users designing full clients. 22 | For example: 23 | 24 | * A client author wants to implement the "get" command 25 | * They write a function in their native language's wrapper which accepts the 26 | key to fetch and embeds that into a text buffer to look like `get [key]\r\n` 27 | * They then call mcmc's functions to send and read the response, parsing and 28 | returning it to the client. 29 | 30 | This should be the same, if not less, code than wrapping a full C client with 31 | every possible command broken out. It also means 3rd party clients can (and 32 | should!) embed mcmc.c/mcmc.h (and any selector code they want) rather than be 33 | dependent on system distribution of a more complex client. 34 | 35 | The allocation-free nature also makes unit testing the client code easier, 36 | hopefully leading to higher quality. 37 | 38 | Caveats: 39 | 40 | * Care should be taken when handling the buffers mcmc requires to operate. 41 | Since there are few operators you should only have to pay attention once :) 42 | * It does not support the various maintenance/settings commands (ie; `lru_crawler`). 43 | It may gain some generic support for this, but those commands were not 44 | designed with consistent response codes and are hard to implement. 45 | * Does not support the binary protocol, which has been deprecated as of 1.6.0. 46 | 47 | As of this writing the code is being released _early_ (perhaps too early?). It 48 | may not have proper makefiles, tests, or a fully implemented API. The code has 49 | been posted so client authors and users can give early feedback on the API in 50 | hopes of prodiving something high quality and stable. 51 | 52 | Again, looking for feedback! Open an issue or let me know what you think. 53 | -------------------------------------------------------------------------------- /vendor/mcmc/mcmc.h: -------------------------------------------------------------------------------- 1 | #ifndef MCMC_HEADER 2 | #define MCMC_HEADER 3 | 4 | #include 5 | #include 6 | 7 | #define MCMC_OK 0 8 | #define MCMC_ERR -1 9 | #define MCMC_NOT_CONNECTED 1 10 | #define MCMC_CONNECTED 2 11 | #define MCMC_CONNECTING 3 // nonblock mode. 12 | #define MCMC_WANT_WRITE 4 13 | #define MCMC_WANT_READ 5 14 | // TODO: either internally set a flag for "ok" or "not ok" and use a func, 15 | // or use a bitflag here (1<<6) for "OK", (1<<5) for "FAIL", etc. 16 | // or, we directly return "OK" or "FAIL" and you can ask for specific error. 17 | #define MCMC_CODE_STORED 8 18 | #define MCMC_CODE_EXISTS 9 19 | #define MCMC_CODE_DELETED 10 20 | #define MCMC_CODE_TOUCHED 11 21 | #define MCMC_CODE_VERSION 12 22 | #define MCMC_CODE_NOT_FOUND 13 23 | #define MCMC_CODE_NOT_STORED 14 24 | #define MCMC_CODE_OK 15 25 | #define MCMC_CODE_NOP 16 26 | #define MCMC_CODE_END 17 27 | #define MCMC_ERR_SHORT 18 28 | #define MCMC_ERR_PARSE 19 29 | #define MCMC_ERR_VALUE 20 30 | 31 | // response types 32 | #define MCMC_RESP_GET 100 33 | #define MCMC_RESP_META 101 34 | #define MCMC_RESP_STAT 102 35 | #define MCMC_RESP_GENERIC 104 36 | #define MCMC_RESP_END 105 37 | #define MCMC_RESP_VERSION 106 38 | #define MCMC_RESP_NUMERIC 107 // for weird incr/decr syntax. 39 | 40 | #define MCMC_OPTION_BLANK 0 41 | #define MCMC_OPTION_NONBLOCK 1 42 | #define MCMC_OPTION_TCP_KEEPALIVE (1<<1) 43 | 44 | // convenience defines. if you want to save RAM you can set these smaller and 45 | // error handler will only copy what you ask for. 46 | #define MCMC_ERROR_CODE_MAX 32 47 | #define MCMC_ERROR_MSG_MAX 512 48 | 49 | typedef struct { 50 | short type; 51 | short code; 52 | char *value; // pointer to start of value in buffer. 53 | size_t reslen; // full length of the response line 54 | size_t vlen_read; // amount of value that was in supplied buffer. 55 | size_t vlen; // reslen + vlen is the full length of the response. 56 | union { 57 | // META response 58 | struct { 59 | char *rline; // start of meta response line. 60 | size_t rlen; 61 | }; 62 | // GET response 63 | struct { 64 | char *key; 65 | size_t klen; 66 | uint32_t flags; 67 | uint64_t cas; 68 | // TODO: value info 69 | }; 70 | // STAT response 71 | struct { 72 | char *stat; 73 | size_t slen; 74 | }; 75 | }; 76 | } mcmc_resp_t; 77 | 78 | int mcmc_fd(void *c); 79 | size_t mcmc_size(int options); 80 | size_t mcmc_min_buffer_size(int options); 81 | int mcmc_parse_buf(void *c, char *buf, size_t read, mcmc_resp_t *r); 82 | int mcmc_connect(void *c, char *host, char *port, int options); 83 | int mcmc_check_nonblock_connect(void *c, int *err); 84 | int mcmc_send_request(void *c, const char *request, int len, int count); 85 | int mcmc_request_writev(void *c, const struct iovec *iov, int iovcnt, ssize_t *sent, int count); 86 | //int mcmc_read(void *c, char *buf, size_t bufsize, mcmc_resp_t *r); 87 | //int mcmc_read_value(void *c, char *val, mcmc_resp_t *r, int *read); 88 | int mcmc_disconnect(void *c); 89 | void mcmc_get_error(void *c, char *code, size_t clen, char *msg, size_t mlen); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /version.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # If you think this is stupid/overkill, blame dormando 3 | 4 | use warnings; 5 | use strict; 6 | 7 | my $version = `git describe`; 8 | chomp $version; 9 | # Test the various versions. 10 | #my $version = 'foob'; 11 | #my $version = '1.4.2-30-gf966dba'; 12 | #my $version = '1.4.3-rc1'; 13 | #my $version = '1.4.3'; 14 | unless ($version =~ m/^\d+\.\d+\.\d+/) { 15 | write_file('version.m4', "m4_define([VERSION_NUMBER], [UNKNOWN])\n"); 16 | exit; 17 | } 18 | 19 | $version =~ s/-/_/g; 20 | write_file('version.m4', "m4_define([VERSION_NUMBER], [$version])\n"); 21 | my ($VERSION, $FULLVERSION, $RELEASE); 22 | 23 | if ($version =~ m/^(\d+\.\d+\.\d+)_rc(\d+)$/) { 24 | $VERSION = $1; 25 | $FULLVERSION = $version; 26 | $RELEASE = '0.1.rc' . $2; 27 | } elsif ($version =~ m/^(\d+\.\d+\.\d+)_(.+)$/) { 28 | $VERSION = $1; 29 | $FULLVERSION = $version; 30 | $RELEASE = '1.' . $2; 31 | } elsif ($version =~ m/^(\d+\.\d+\.\d+)$/) { 32 | $VERSION = $1; 33 | $FULLVERSION = $version; 34 | $RELEASE = '1'; 35 | } 36 | 37 | my $spec = read_file('memcached.spec.in'); 38 | $spec =~ s/\@VERSION\@/$VERSION/gm; 39 | $spec =~ s/\@FULLVERSION\@/$FULLVERSION/gm; 40 | $spec =~ s/\@RELEASE\@/$RELEASE/gm; 41 | 42 | write_file('memcached.spec', $spec); 43 | 44 | sub write_file { 45 | my $file = shift; 46 | my $data = shift; 47 | open(my $fh, "> $file") or die "Can't open $file: $!"; 48 | print $fh $data; 49 | close($fh); 50 | } 51 | 52 | sub read_file { 53 | my $file = shift; 54 | local $/ = undef; 55 | open(my $fh, "< $file") or die "Can't open $file: $!"; 56 | my $data = <$fh>; 57 | close($fh); 58 | return $data; 59 | } 60 | -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if git describe | sed s/-/_/g > version.num.tmp 4 | then 5 | mv version.num.tmp version.num 6 | echo "m4_define([VERSION_NUMBER], [`tr -d '\n' < version.num`])" \ 7 | > version.m4 8 | sed s/@VERSION@/`cat version.num`/ < memcached.spec.in > memcached.spec 9 | else 10 | rm version.num.tmp 11 | fi 12 | --------------------------------------------------------------------------------