init
This commit is contained in:
2
wcmtools/memcached/AUTHORS
Executable file
2
wcmtools/memcached/AUTHORS
Executable file
@@ -0,0 +1,2 @@
|
||||
Anatoly Vorobey <mellon@pobox.com>
|
||||
Brad Fitzpatrick <brad@danga.com>
|
||||
37
wcmtools/memcached/BUILD
Executable file
37
wcmtools/memcached/BUILD
Executable file
@@ -0,0 +1,37 @@
|
||||
Ideally, you want to make a static binary, otherwise the dynamic
|
||||
linker pollutes your address space with shared libs right in the
|
||||
middle. (NOTE: actually, this shouldn't matter so much anymore, now
|
||||
that we only allocate huge, fixed-size slabs)
|
||||
|
||||
Make sure your libevent has epoll (Linux) or kqueue (BSD) support.
|
||||
Using poll or select only is slow, and works for testing, but
|
||||
shouldn't be used for high-traffic memcache installations.
|
||||
|
||||
To build libevent with epoll on Linux, you need two things. First,
|
||||
you need /usr/include/sys/epoll.h . To get it, you can install the
|
||||
userspace epoll library, epoll-lib. The link to the latest version
|
||||
is buried inside
|
||||
http://www.xmailserver.org/linux-patches/nio-improve.html ; currently
|
||||
it's http://www.xmailserver.org/linux-patches/epoll-lib-0.9.tar.gz .
|
||||
If you're having any trouble building/installing it, you can just copy
|
||||
epoll.h from that tarball to /usr/include/sys as that's the only thing
|
||||
from there that libevent really needs.
|
||||
|
||||
Secondly, you need to declare syscall numbers of epoll syscalls, so
|
||||
libevent can use them. Put these declarations somewhere
|
||||
inside <sys/epoll.h>:
|
||||
|
||||
#define __NR_epoll_create 254
|
||||
#define __NR_epoll_ctl 255
|
||||
#define __NR_epoll_wait 256
|
||||
|
||||
After this you should be able to build libevent with epoll support.
|
||||
Once you build/install libevent, you don't need <sys/epoll.h> to
|
||||
compile memcache or link it against libevent. Don't forget that for epoll
|
||||
support to actually work at runtime you need to use a kernel with epoll
|
||||
support patch applied, as explained in the README file.
|
||||
|
||||
BSD users are luckier, and will get kqueue support by default.
|
||||
|
||||
|
||||
|
||||
48
wcmtools/memcached/CONTRIBUTORS
Executable file
48
wcmtools/memcached/CONTRIBUTORS
Executable file
@@ -0,0 +1,48 @@
|
||||
Brad Fitzpatrick <brad@danga.com>
|
||||
-- design/protocol
|
||||
-- Perl client
|
||||
-- prototype Perl server
|
||||
-- memory allocator design
|
||||
-- small enhancements/changes to C server
|
||||
-- website
|
||||
|
||||
Anatoly Vorobey <mellon@pobox.com>
|
||||
-- C server
|
||||
-- memory allocator design
|
||||
-- revised setuid code
|
||||
|
||||
Evan Martin <martine@danga.com>
|
||||
-- automake/autoconf support
|
||||
-- Python client
|
||||
-- portability work to build on OS X
|
||||
|
||||
Ryan <hotrodder@rocketmail.com>
|
||||
-- PHP client
|
||||
|
||||
Jamie McCarthy <jamie@mccarthy.vg>
|
||||
-- Perl client fixes: Makefile.PL, stats, doc updates
|
||||
|
||||
Lisa Marie Seelye <lisa@gentoo.org>
|
||||
-- packaging for Gentoo Linux
|
||||
-- initial setuid code
|
||||
|
||||
Sean Chittenden <seanc@FreeBSD.org>
|
||||
-- packaging for FreeBSD
|
||||
|
||||
Stuart Herbert <stuart@gentoo.org>
|
||||
-- fix for: memcached's php client can run in an infinite loop
|
||||
http://bugs.gentoo.org/show_bug.cgi?id=25385
|
||||
|
||||
Brion Vibber <brion@pobox.com>
|
||||
-- debugging abstraction in PHP client
|
||||
-- debugging the failure of daemon mode on FreeBSD
|
||||
|
||||
Brad Whitaker <whitaker@danga.com>
|
||||
-- compression support for the Perl API
|
||||
|
||||
Richard Russo <russor@msoe.edu>
|
||||
-- Java API
|
||||
|
||||
Ryan T. Dean <rtdean@cytherianage.net>
|
||||
-- Second PHP client with correct parsing (based on Perl client)
|
||||
-- autoconf fixes for mallinfo.arena on BSD (don't just check malloc.h)
|
||||
30
wcmtools/memcached/COPYING
Executable file
30
wcmtools/memcached/COPYING
Executable file
@@ -0,0 +1,30 @@
|
||||
Copyright (c) 2003, Danga Interactive, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the Danga Interactive nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
178
wcmtools/memcached/ChangeLog
Executable file
178
wcmtools/memcached/ChangeLog
Executable file
@@ -0,0 +1,178 @@
|
||||
2005-05-25
|
||||
* patch from Peter van Dijk <peter@nextgear.nl> to make
|
||||
stderr unbuffered, for running under daemontools
|
||||
|
||||
2005-04-04
|
||||
* patch from Don MacAskill <don@smugmug.com> 'flush_all' doesn't
|
||||
seem to work properly. Basically, if you try to add a key which
|
||||
is present, but expired, the store fails but the old key is no
|
||||
longer expired.
|
||||
|
||||
* release 1.1.12
|
||||
|
||||
2005-01-14
|
||||
* Date: Thu, 18 Nov 2004 15:25:59 -0600
|
||||
From: David Phillips <electrum@gmail.com>
|
||||
Here is a patch to configure.ac and Makefile.am to put the man page in
|
||||
the correct location. Trying to install the man page from a
|
||||
subdirectory results in the subdirectory being used in the install
|
||||
path (it tries to install to doc/memcached.1). This is the correct
|
||||
thing to do:
|
||||
|
||||
- create a Makefile.am in the doc directory that installs the man page
|
||||
with man_MANS
|
||||
- modify Makefile.am in the base directory to reference the doc
|
||||
directory using SUBDIRS
|
||||
- modify the AC_CONFIG_FILES macro in configure.ac to output the
|
||||
Makefile in doc
|
||||
|
||||
|
||||
2005-01-14
|
||||
* pidfile saving support from Lisa Seelye <lisa@gentoo.org>, sent
|
||||
Jan 13, 2005
|
||||
|
||||
2005-01-14
|
||||
* don't delete libevent events that haven't been added (the deltimer)
|
||||
patch from Ted Schundler <tschundler@gmail.com>
|
||||
|
||||
2004-12-10
|
||||
* document -M and -r in manpage (Doug Porter <dsp@dsp.name>)
|
||||
|
||||
2004-07-22
|
||||
* fix buffer overflow in items.c with 250 byte keys along with
|
||||
other info on the same line going into a 256 byte char[].
|
||||
thanks to Andrei Nigmatulin <anight@monamour.ru>
|
||||
|
||||
2004-06-15
|
||||
* immediate deletes weren't being unlinked a few seconds,
|
||||
preventing "add" commands to the same key in that time period.
|
||||
thanks to Michael Alan Dorman <mdorman@debian.org> for the
|
||||
bug report and demo script.
|
||||
|
||||
2004-04-30
|
||||
* released 1.1.11
|
||||
|
||||
2004-04-24
|
||||
* Avva: Add a new command line option: -r , to maximize core file
|
||||
limit.
|
||||
|
||||
2004-03-31
|
||||
* Avva: Use getrlimit and setrlimit to set limits for number of
|
||||
simultaneously open file descriptors. Get the current limits and
|
||||
try to raise them if they're not enough for the specified (or the
|
||||
default) setting of max connections.
|
||||
|
||||
2004-02-24
|
||||
* Adds a '-M' flag to turn off tossing items from the cache.
|
||||
(Jason Titus <jtitus@postini.com>)
|
||||
|
||||
2004-02-19 (Evan)
|
||||
* Install manpage on "make install", etc.
|
||||
|
||||
2003-12-30 (Brad)
|
||||
* remove static build stuff. interferes with PAM setuid stuff
|
||||
and was only included as a possible fix with the old memory
|
||||
allocator. really shouldn't make a difference.
|
||||
* add Jay Bonci's Debian scripts and manpage
|
||||
* release version 1.1.10
|
||||
|
||||
2003-12-01 (Avva)
|
||||
* New command: flush_all, causes all existing items to
|
||||
be invalidated immediately (without deleting them from
|
||||
memory, merely causing memcached to no longer return them).
|
||||
2003-10-23
|
||||
* Shift init code around to fix daemon mode on FreeBSD,
|
||||
* and drop root only after creating the server socket (to
|
||||
* allow the use of privileged ports)
|
||||
* version 1.1.10pre
|
||||
|
||||
2003-10-09
|
||||
* BSD compile fixes from Ryan T. Dean
|
||||
* version 1.1.9
|
||||
|
||||
2003-09-29
|
||||
* ignore SIGPIPE at start instead of crashing in rare cases it
|
||||
comes up. no other code had to be modified, since everything
|
||||
else is already dead-connection-aware. (avva)
|
||||
|
||||
2003-09-09 (Avva, Lisa Marie Seelye <lisa@gentoo.org>)
|
||||
* setuid support
|
||||
|
||||
2003-09-05 (Avva)
|
||||
* accept all new connections in the same event (so we work with ET epoll)
|
||||
* mark all items as clsid=0 after slab page reassignment to please future
|
||||
asserts (on the road to making slab page reassignment work fully)
|
||||
|
||||
2003-08-12 (Brad Fitzpatrick)
|
||||
* use TCP_CORK on Linux or TCP_PUSH on BSD
|
||||
* only use TCP_NODELAY when we don't have alternatives
|
||||
|
||||
2003-08-10
|
||||
* disable Nagel's Algorithm (TCP_NODELAY) for better performance (avva)
|
||||
|
||||
2003-08-10
|
||||
* support multiple levels of verbosity (-vv)
|
||||
|
||||
2003-08-10 (Evan Martin)
|
||||
* Makefile.am: debug, optimization, and static flags are controlled
|
||||
by the configure script.
|
||||
* configure.ac:
|
||||
- allow specifying libevent directory with --with-libevent=DIR
|
||||
- check for malloc.h (unavailable on BSDs)
|
||||
- check for socklen_t (unavailable on OSX)
|
||||
* assoc.c, items.c, slabs.c: Remove some unused headers.
|
||||
* memcached.c: allow for nonexistence of malloc.h; #define a POSIX
|
||||
macro to import mlockall flags.
|
||||
|
||||
2003-07-29
|
||||
* version 1.1.7
|
||||
* big bug fix: item exptime 0 meant expire immediately, not never
|
||||
* version 1.1.8
|
||||
|
||||
2003-07-22
|
||||
* make 'delete' take second arg, of time to refuse new add/replace
|
||||
* set/add/replace/delete can all take abs or delta time (delta can't
|
||||
be larger than a month)
|
||||
|
||||
2003-07-21
|
||||
* added doc/protocol.txt
|
||||
|
||||
2003-07-01
|
||||
* report CPU usage in stats
|
||||
|
||||
2003-06-30
|
||||
* version 1.1.6
|
||||
* fix a number of obscure bugs
|
||||
* more stats reporting
|
||||
|
||||
2003-06-10
|
||||
* removing use of Judy; use a hash. (judy caused memory fragmentation)
|
||||
* shrink some structures
|
||||
* security improvements
|
||||
* version 1.1.0
|
||||
|
||||
2003-06-18
|
||||
* changing maxsize back to an unsigned int
|
||||
|
||||
2003-06-16
|
||||
* adding PHP support
|
||||
* added CONTRIBUTORS file
|
||||
* version 1.0.4
|
||||
|
||||
2003-06-15
|
||||
* forgot to distribute website/api (still learning auto*)
|
||||
* version 1.0.3
|
||||
|
||||
2003-06-15
|
||||
* update to version 1.0.2
|
||||
* autoconf/automake fixes for older versions
|
||||
* make stats report version number
|
||||
* change license from GPL to BSD
|
||||
|
||||
Fri, 13 Jun 2003 10:05:51 -0700 Evan Martin <martine@danga.com>
|
||||
|
||||
* configure.ac, autogen.sh, Makefile.am: Use autotools.
|
||||
* items.c, memcached.c: #include <time.h> for time(),
|
||||
printf time_t as %lu (is this correct?),
|
||||
minor warnings fixes.
|
||||
|
||||
30
wcmtools/memcached/LICENSE
Executable file
30
wcmtools/memcached/LICENSE
Executable file
@@ -0,0 +1,30 @@
|
||||
Copyright (c) 2003, Danga Interactive, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the Danga Interactive nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
10
wcmtools/memcached/Makefile.am
Executable file
10
wcmtools/memcached/Makefile.am
Executable file
@@ -0,0 +1,10 @@
|
||||
bin_PROGRAMS = memcached
|
||||
|
||||
memcached_SOURCES = memcached.c slabs.c items.c memcached.h assoc.c
|
||||
|
||||
SUBDIRS = doc
|
||||
DIST_DIRS = scripts
|
||||
EXTRA_DIST = doc scripts TODO
|
||||
|
||||
AM_CFLAGS=-DNDEBUG
|
||||
|
||||
1
wcmtools/memcached/NEWS
Executable file
1
wcmtools/memcached/NEWS
Executable file
@@ -0,0 +1 @@
|
||||
http://www.danga.com/memcached/news.bml
|
||||
22
wcmtools/memcached/README
Executable file
22
wcmtools/memcached/README
Executable file
@@ -0,0 +1,22 @@
|
||||
Dependencies:
|
||||
|
||||
-- libevent, http://www.monkey.org/~provos/libevent/ (libevent-dev)
|
||||
|
||||
If using Linux, you need a kernel with epoll. Sure, libevent will
|
||||
work with normal select, but it sucks.
|
||||
|
||||
epoll isn't in Linux 2.4 yet, but there's a backport at:
|
||||
|
||||
http://www.xmailserver.org/linux-patches/nio-improve.html
|
||||
|
||||
You want the epoll-lt patch (level-triggered).
|
||||
|
||||
Also, be warned that the -k (mlockall) option to memcached might be
|
||||
dangerous when using a large cache. Just make sure the memcached machines
|
||||
don't swap. memcached does non-blocking network I/O, but not disk. (it
|
||||
should never go to disk, or you've lost the whole point of it)
|
||||
|
||||
The memcached website is at:
|
||||
|
||||
http://www.danga.com/memcached/
|
||||
|
||||
8
wcmtools/memcached/TODO
Executable file
8
wcmtools/memcached/TODO
Executable file
@@ -0,0 +1,8 @@
|
||||
* slab class reassignment still buggy and can crash. once that's
|
||||
stable, server should re-assign pages every 60 seconds or so
|
||||
to keep all classes roughly equal. [Update: fixed now?, but
|
||||
not heavily tested. Future: make slab classes, with per-class
|
||||
cleaners functions.]
|
||||
|
||||
* calendar queue for early expirations of items, so they don't push
|
||||
out other objects with infinite expirations.
|
||||
56
wcmtools/memcached/api/java/CHANGELOG.txt
Executable file
56
wcmtools/memcached/api/java/CHANGELOG.txt
Executable file
@@ -0,0 +1,56 @@
|
||||
Version 0.9.1 - 12 Oct 2003
|
||||
-- Altered the SockIO helper class, so it no longer allows accessing
|
||||
the streams it contains directly, instead it has methods
|
||||
with identical signatures to the methods that were called
|
||||
on the streams... This makes the client code prettier.
|
||||
-- Changed looped non blocking read to blocking read, for getting
|
||||
items from the server. This probably reduces CPU usage in
|
||||
cases where the retrieval would block, and cleans up the
|
||||
code a bit. We're blocking on retrieval anyhow.
|
||||
-- Made get() not call get_multi(), and added single socket
|
||||
optimization. This parallels recent changes to the perl
|
||||
client
|
||||
-- Changed a few for loops to use iterators instead, since it's
|
||||
probably marginally more efficient, and it's probably
|
||||
better coding practice.
|
||||
-- Actually spell checked. :)
|
||||
|
||||
Version 0.9.0 - 29 Sep 2003
|
||||
-- Renumbered to reflect that it's not been realworld tested
|
||||
-- changed package to danga.com.MemCached (thanks)
|
||||
-- added dates to changelog
|
||||
-- added .txt to text files
|
||||
-- added to official memcached site :)
|
||||
|
||||
Version 1.0.0 - 28 Sep 2003
|
||||
-- Adjusted namespacing for SockIO, it shouldn't have been public; is now package level.
|
||||
As a bonus, this means I don't have to Javadoc it. :)
|
||||
-- Finished adding complete Javadoc to MemCachedClient.
|
||||
-- spellchecked
|
||||
-- added a couple versions of function variations that I missed. for example, some that
|
||||
didn't take an int directly as a hash value, and i missed a get_multi w/out hashes.
|
||||
-- removed java.net.Socket reference from MemCachedClient, SockIO has a new constructor which
|
||||
takes hostname and port number
|
||||
-- switched to three part version number
|
||||
|
||||
|
||||
|
||||
Version 0.3 - 27 Sep 2003
|
||||
-- Compression, for strings/stringified numbers, this is compatible w/ perl
|
||||
Serialized objects are incompatible w/ perl for obvious reasons. :)
|
||||
-- Added PORTABILITY file, to include information about using the client
|
||||
with various JVM's
|
||||
-- Updated string parsing to StreamTokenizer rather than regexp's in an
|
||||
effort to get sablevm to like the client
|
||||
|
||||
Version 0.2 - 24 Sep 2003
|
||||
-- Serialization works
|
||||
-- Possible BUG: Only the lower byte of the characters of keys are sent
|
||||
This is only a problem if the memcache server can handle
|
||||
unicode keys. (I haven't checked)
|
||||
-- Server Failures handled gracefully
|
||||
-- Partial Javadoc
|
||||
|
||||
Version 0.1 - 23 Sep 2003
|
||||
-- Initial Release
|
||||
-- Storing and Retrieving numbers and strings works
|
||||
18
wcmtools/memcached/api/java/PORTABILITY.txt
Executable file
18
wcmtools/memcached/api/java/PORTABILITY.txt
Executable file
@@ -0,0 +1,18 @@
|
||||
This file lists the portability status of this client. Please send me any
|
||||
additional information.
|
||||
|
||||
Richard 'toast' Russo <russor@msoe.edu>
|
||||
|
||||
|
||||
I have personally tested this on the following platforms, and it works to the
|
||||
best of my knowledge:
|
||||
|
||||
Sun's JRE 1.4.2 on Linux/i86
|
||||
kaffe 1.1.1 on Linux/i86
|
||||
|
||||
I have personally tested this on the following platforms, and it does not work:
|
||||
|
||||
sablevm 1.0.9: complains of todo in native_interface.c
|
||||
gjc(jdk) 3.3.2 20030908 (Debian prerelease): strange compiler errors
|
||||
gij(jre) 3.3.2 20030908 (Debian prerelease): does not get items from server properly
|
||||
|
||||
9
wcmtools/memcached/api/java/TODO.txt
Executable file
9
wcmtools/memcached/api/java/TODO.txt
Executable file
@@ -0,0 +1,9 @@
|
||||
This are the things left to do
|
||||
|
||||
Investigate threading issues (what needs to be synchronized)
|
||||
|
||||
Investigate 3rd party jvm incompatibility
|
||||
|
||||
Non deprecated stream input
|
||||
|
||||
Extensive testing
|
||||
1219
wcmtools/memcached/api/java/com/danga/MemCached/MemCachedClient.java
Executable file
1219
wcmtools/memcached/api/java/com/danga/MemCached/MemCachedClient.java
Executable file
File diff suppressed because it is too large
Load Diff
72
wcmtools/memcached/api/java/com/danga/MemCached/SockIO.java
Executable file
72
wcmtools/memcached/api/java/com/danga/MemCached/SockIO.java
Executable file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* MemCached Java client, utility class for Socket IO
|
||||
* Copyright (c) 2003
|
||||
* Richard 'toast' Russo <russor@msoe.edu>
|
||||
* http://people.msoe.edu/~russor/memcached
|
||||
*
|
||||
*
|
||||
* This module is Copyright (c) 2003 Richard Russo.
|
||||
* All rights reserved.
|
||||
* You may distribute under the terms of the GNU General Public License
|
||||
* This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
|
||||
*
|
||||
* @author Richard 'toast' Russo <russor@msoe.edu>
|
||||
* @version 0.9.1
|
||||
*/
|
||||
|
||||
|
||||
package com.danga.MemCached;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
|
||||
|
||||
|
||||
class SockIO {
|
||||
Socket sock;
|
||||
DataInputStream in;
|
||||
DataOutputStream out;
|
||||
boolean closed = false;
|
||||
|
||||
public SockIO(String host, int port) throws IOException {
|
||||
sock = new Socket(host,port);
|
||||
in = new DataInputStream(sock.getInputStream());
|
||||
out = new DataOutputStream(sock.getOutputStream());
|
||||
|
||||
}
|
||||
|
||||
public void close() {
|
||||
closed = true;
|
||||
try {
|
||||
in.close();
|
||||
out.close();
|
||||
sock.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
}
|
||||
public boolean isConnected() {
|
||||
return (closed && sock.isConnected());
|
||||
}
|
||||
|
||||
public void readFully(byte[] b) throws IOException {
|
||||
in.readFully(b);
|
||||
}
|
||||
|
||||
public String readLine() throws IOException {
|
||||
return in.readLine();
|
||||
}
|
||||
|
||||
public void writeBytes(String s) throws IOException {
|
||||
out.writeBytes(s);
|
||||
}
|
||||
public void flush() throws IOException {
|
||||
out.flush();
|
||||
}
|
||||
public void write(byte[] b) throws IOException {
|
||||
out.write(b);
|
||||
}
|
||||
|
||||
}
|
||||
79
wcmtools/memcached/api/java/dist.pl
Executable file
79
wcmtools/memcached/api/java/dist.pl
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Simplify Brad's life. I'm sure there's a Java-specific way
|
||||
# to do this (or there should be), but I don't have Java
|
||||
# installed.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
|
||||
my $opt_tar = 0;
|
||||
my $opt_upload = 0;
|
||||
exit 1 unless GetOptions("tar" => \$opt_tar,
|
||||
"upload" => \$opt_upload);
|
||||
|
||||
# chdir to the directory the script's at, so future
|
||||
# paths need only be relative
|
||||
use FindBin qw($Bin);
|
||||
chdir $Bin or die "Couldn't cd to $Bin\n";
|
||||
|
||||
die "Must use --tar or --upload\n" unless $opt_tar || $opt_upload;
|
||||
|
||||
# files to distribute
|
||||
my @manifest = qw(
|
||||
TODO.txt
|
||||
PORTABILITY.txt
|
||||
CHANGELOG.txt
|
||||
memcachetest.java
|
||||
com
|
||||
com/danga
|
||||
com/danga/MemCached
|
||||
com/danga/MemCached/MemCachedClient.java
|
||||
com/danga/MemCached/SockIO.java
|
||||
);
|
||||
|
||||
# figure out the version number
|
||||
|
||||
open (F, "com/danga/MemCached/MemCachedClient.java") or die;
|
||||
{ local $/ = undef; $_ = <F>; } # suck in the whole file
|
||||
close F;
|
||||
die "Can't find version number\n" unless
|
||||
/\@version\s+(\d[^\'\"\s]+)/s;
|
||||
my $ver = $1;
|
||||
my $dir = "java-memcached-$ver";
|
||||
my $dist = "$dir.tar.gz";
|
||||
|
||||
if ($opt_tar) {
|
||||
# make a fresh directory
|
||||
mkdir $dir or die "Couldn't make directory: $dir\n";
|
||||
|
||||
# copy files to fresh directory
|
||||
foreach my $file (@manifest) {
|
||||
if (-f $file) {
|
||||
system("cp", $file, "$dir/$file")
|
||||
and die "Error copying file $file\n";
|
||||
} elsif (-d $file) {
|
||||
mkdir "$dir/$file"
|
||||
or die "Error creating directory $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
# tar it up
|
||||
system("tar", "zcf", $dist, $dir)
|
||||
and die "Error running tar.\n";
|
||||
|
||||
# remove temp directory
|
||||
system("rm", "-rf", $dir)
|
||||
and die "Error cleaning up temp directory\n";
|
||||
|
||||
print "$dist created.\n";
|
||||
}
|
||||
|
||||
if ($opt_upload) {
|
||||
print "Uploading $dist...\n";
|
||||
system("scp", $dist, 'bradfitz@danga.com:memd/dist/')
|
||||
and die "Error uploading to memcached/dist\n";
|
||||
}
|
||||
|
||||
print "Done.\n";
|
||||
61
wcmtools/memcached/api/java/memcachetest.java
Executable file
61
wcmtools/memcached/api/java/memcachetest.java
Executable file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* memcachetest.java
|
||||
*
|
||||
* Created on September 23, 2003, 12:23 AM
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author toast
|
||||
*/
|
||||
import com.danga.MemCached.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** This is an example program using a MemCacheClient. */
|
||||
public class memcachetest {
|
||||
|
||||
/** Creates a new instance of memcachetest */
|
||||
public memcachetest() {
|
||||
}
|
||||
|
||||
/** This runs through some simple tests of the MemCacheClient.
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
MemCachedClient mcc = new MemCachedClient();
|
||||
String[] serverlist = { "localhost:12345"}; //, "localhost:12346"};
|
||||
mcc.set_compress_enable(true);
|
||||
mcc.set_compress_savings(0.0); // compress everthing
|
||||
mcc.set_compress_threshold(0); // compress everthing
|
||||
mcc.set_servers(serverlist);
|
||||
//mcc.set_serial(true);
|
||||
// Integer foo = new Integer(-2);
|
||||
mcc.set("foo", "Your mother eats army boots, in the day time, with her friends. " +
|
||||
"English text should be nice and compressible.");
|
||||
|
||||
Object tmp = mcc.get("foo");
|
||||
System.out.println(tmp);
|
||||
System.out.println("Sleeping ...");
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
while (mcc.get("foo") == null) {
|
||||
System.out.print(".");
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(mcc.get("foo"));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
164
wcmtools/memcached/api/perl/ChangeLog
Executable file
164
wcmtools/memcached/api/perl/ChangeLog
Executable file
@@ -0,0 +1,164 @@
|
||||
2004-07-27
|
||||
* release 1.14
|
||||
|
||||
2004-07-27
|
||||
* kill buggy, slow ord() _hashfunc, replace with crc32.
|
||||
this adds String::CRC32 as a dependency. thanks to
|
||||
everybody's feedback on the mailing list.
|
||||
|
||||
2004-07-19
|
||||
* don't use pos() because it doesn't seem to work in
|
||||
taint mode. use $+[0] instead. (Dave Evans <..@djce.org.uk>)
|
||||
|
||||
2004-06-19
|
||||
* support multiple paths to memcache nodes (Brad)
|
||||
see 'set_pref_ip'
|
||||
|
||||
2004-05-30
|
||||
* release version 1.13
|
||||
|
||||
2004-05-26 (Whitaker <whitaker@danga.com>)
|
||||
* quiet warning
|
||||
|
||||
2004-05-25 (Whitaker <whitaker@danga.com>)
|
||||
* get_multi shouldn't modify caller's @_
|
||||
|
||||
2004-05-18 (Michael <ged@danga.com>)
|
||||
* namespace support
|
||||
* use fields
|
||||
|
||||
2004-05-16 (Alexei Kozlov <kozlov@w-m.ru>)
|
||||
* remove warnings with vec
|
||||
|
||||
2004-04-09 (brad)
|
||||
* in perl 5.6, trap errors dethawing 5.8 storable objects
|
||||
and instead treat it like a cache miss
|
||||
|
||||
2004-04-01
|
||||
* use $! and not %! for perl 5.6 compat (Dave Evans <..@djce.org.uk>)
|
||||
* don't mark whole IP dead anymore when a node is down (Jason Titus <jtitus@postini.com>)
|
||||
* start version numbering (Jamie McCarthy <jamie@mccarthy.vg>)
|
||||
|
||||
2004-03-09 (Brad/Avva)
|
||||
* _oneline can return more than one line (but always on a line break),
|
||||
so caller must decide when it's really time to quit. had to modify
|
||||
run_command to know that. (which is used by stats)
|
||||
|
||||
2004-03-05 (Dave Evans <..@djce.org.uk>)
|
||||
* Here's a really trivial patch for the Perl binding,
|
||||
Cache::Memcached. The bug is that the module assumes that the
|
||||
currently select()ed filehandle is STDOUT, but this might not be
|
||||
the case. So this patch ensures that the select()ed filehandle is
|
||||
preserved, not forced to STDOUT.
|
||||
|
||||
2004-02-29 (Brad)
|
||||
* add readonly option
|
||||
|
||||
2004-02-27 (Avva)
|
||||
* Cleaner handling of the case when _oneline is called without a
|
||||
line parameter (i.e. not to send anything, just read a line from
|
||||
the socket). Make it depend on $line being defined only,
|
||||
regardless of its content (thanks Brad!).
|
||||
|
||||
2004-02-25 (Avva)
|
||||
* Asyncify all I/O, finally get rid of alarm() yuckiness, unify all
|
||||
one-liner command/responses into a single internal API.
|
||||
|
||||
2004-02-17
|
||||
* document in POD the delete method
|
||||
|
||||
2004-02-03
|
||||
* fix bug with 2k read boundaries falling in the middle
|
||||
of "VALUE ..." or "END" lines, thus halting future
|
||||
parsing and responses. (eek!)
|
||||
* version 1.0.12
|
||||
|
||||
2003-12-01
|
||||
* merge stats/stats_reset patch from Jamie McCarthy
|
||||
* trailing whitespace cleanup
|
||||
|
||||
2003-11-08
|
||||
* work on Solaris/BSD where there's no MSG_NOSIGNAL.
|
||||
the expense is extra syscalls to change the local
|
||||
SIGPIPE handler all the time. in the future, it'd
|
||||
be nice to have an option so Solaris/BSD callers
|
||||
can say, "Hey, I've turned off SIGPIPE globally,
|
||||
don't worry about it."
|
||||
|
||||
2003-10-26
|
||||
* add a test file, so automated CPAN test hosts are happy
|
||||
* check MSG_NOSIGNAL immediately on module load, not on use,
|
||||
so Solaris dies early. (still on TODO to fix, but better
|
||||
to fail loudly)
|
||||
* version 1.0.11
|
||||
|
||||
2003-10-25
|
||||
* version 1.0.10, rename to Cache::Memcached, upload to CPAN
|
||||
|
||||
2003-10-18
|
||||
* implement read/write timeouts everywhere. Now the client shouldn't
|
||||
hang if the server machine goes down unexpectedly. (avva)
|
||||
|
||||
2003-10-16
|
||||
* use Storable::nfreeze instead of freeze, so hosts from different
|
||||
architectures can all use the same data. (all must use Perl, though.
|
||||
the different memcache APIs all store/pickle/serialize data differently)
|
||||
Suggestion by Jason Titus <jtitus@postini.com>
|
||||
|
||||
2003-10-06
|
||||
* fix _incrdecr to return complete number, not just first
|
||||
digit (thanks to Ryan T. Dean)
|
||||
* release version 1.0.9
|
||||
|
||||
2003-10-04
|
||||
* document expiration times in POD (thanks to Tim Bunce
|
||||
for noting the omission)
|
||||
* release version 1.0.8
|
||||
|
||||
2003-10-03
|
||||
* add connect timeout of 0.25s, for dead host detection.
|
||||
We had 1 second a couple revs ago, but lost it when
|
||||
ditching IO::Socket module. (avva)
|
||||
|
||||
2003-10-02
|
||||
* fix _incrdecr with explicit-hashvalue keys (whitaker)
|
||||
|
||||
2003-10-01
|
||||
* add run_command API call. TODO: document, and document
|
||||
the $exptime on the setters
|
||||
|
||||
2003-09-30
|
||||
* use send instead of print, so we can set MSG_NOSIGNAL
|
||||
and not get SIGPIPES, which avoids 3 syscalls of localizing
|
||||
$SIG{PIPE} and sends everything at once, instead of 4k
|
||||
stdio chunks. in review: stdio buffered in, send unbuffered
|
||||
out. TODO: setvbuf so reads are buffered at more than 4k.
|
||||
|
||||
2003-09-29
|
||||
* yet faster parsing
|
||||
* switch to stdio/perlio instead of raw io: more correct,
|
||||
simpler parsing code.
|
||||
|
||||
2003-09-28
|
||||
* prevent some warnings
|
||||
* faster get() call that doesn't use get_multi()
|
||||
* optimizations for single-server case
|
||||
* use socket APIs directly, instead of uber-slow IO::* modules
|
||||
* new faster _load_items parsing
|
||||
|
||||
2003-09-04
|
||||
* emit debug when set/add/replace fails, in addition to succeed
|
||||
|
||||
Version 1.0.7
|
||||
-- compression support (Brad Whitaker)
|
||||
|
||||
Version 1.0.6
|
||||
-- incr/decr client support
|
||||
-- make delete optionally take second argument (server now supports
|
||||
a delay time on delete)
|
||||
-- doc updates from Jamie McCarthy
|
||||
-- better hashing after dead host detection: new requests go to different
|
||||
remaining hosts, instead of all to the same one.
|
||||
|
||||
Version 1.0.2
|
||||
-- initial release, about.
|
||||
8
wcmtools/memcached/api/perl/MANIFEST
Executable file
8
wcmtools/memcached/api/perl/MANIFEST
Executable file
@@ -0,0 +1,8 @@
|
||||
ChangeLog
|
||||
Memcached.pm
|
||||
Makefile.PL
|
||||
README
|
||||
MANIFEST
|
||||
TODO
|
||||
t/use.t
|
||||
|
||||
13
wcmtools/memcached/api/perl/Makefile.PL
Executable file
13
wcmtools/memcached/api/perl/Makefile.PL
Executable file
@@ -0,0 +1,13 @@
|
||||
use ExtUtils::MakeMaker;
|
||||
WriteMakefile( 'NAME' => 'Cache::Memcached',
|
||||
'VERSION_FROM' => 'Memcached.pm',
|
||||
'PREREQ_PM' => {
|
||||
'Storable' => 0,
|
||||
'Time::HiRes' => 0,
|
||||
'String::CRC32' => 0,
|
||||
},
|
||||
($] >= 5.005 ?
|
||||
(ABSTRACT_FROM => 'Memcached.pm',
|
||||
AUTHOR => 'Brad Fitzpatrick <brad@danga.com>') : ()),
|
||||
);
|
||||
|
||||
1164
wcmtools/memcached/api/perl/Memcached.pm
Executable file
1164
wcmtools/memcached/api/perl/Memcached.pm
Executable file
File diff suppressed because it is too large
Load Diff
14
wcmtools/memcached/api/perl/README
Executable file
14
wcmtools/memcached/api/perl/README
Executable file
@@ -0,0 +1,14 @@
|
||||
This is the Perl API for memcached, a distributed memory cache daemon.
|
||||
See the documentation within the module for details on its use.
|
||||
|
||||
Information on the big picture is available at:
|
||||
|
||||
http://www.danga.com/memcached/
|
||||
|
||||
Feel free to join the mailing list and ask any questions.
|
||||
|
||||
--
|
||||
Brad Fitzpatrick
|
||||
brad@danga.com
|
||||
|
||||
|
||||
3
wcmtools/memcached/api/perl/TODO
Executable file
3
wcmtools/memcached/api/perl/TODO
Executable file
@@ -0,0 +1,3 @@
|
||||
(currently empty)
|
||||
|
||||
|
||||
7
wcmtools/memcached/api/perl/t/use.t
Executable file
7
wcmtools/memcached/api/perl/t/use.t
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env perl -w
|
||||
use strict;
|
||||
use Test;
|
||||
BEGIN { plan tests => 1 }
|
||||
|
||||
use Cache::Memcached; ok(1);
|
||||
exit;
|
||||
45
wcmtools/memcached/api/php/ChangeLog
Executable file
45
wcmtools/memcached/api/php/ChangeLog
Executable file
@@ -0,0 +1,45 @@
|
||||
Release 1.0.10
|
||||
--------------
|
||||
* bug fix: changes hashing function to crc32, sprintf %u
|
||||
* feature: optional compression
|
||||
|
||||
Release 1.0.9
|
||||
-------------
|
||||
* protocol parsing bug
|
||||
|
||||
Release 1.0.8
|
||||
-------------
|
||||
* whitespace/punctuation/wording cleanups
|
||||
|
||||
Release 1.0.7
|
||||
-------------
|
||||
* added 3 functions which handle error reporting
|
||||
error() - returns error number of last error generated, else returns 0
|
||||
error_string() - returns a string description of error number retuned
|
||||
error_clear() - clears the last error number and error string
|
||||
* removed call to preg_match() in _loaditems()
|
||||
* only non-scalar values are serialize() before being
|
||||
sent to the server
|
||||
* added the optional timestamp argument for delete()
|
||||
read Documentation file for details
|
||||
* PHPDocs/PEAR style comments added
|
||||
* abstract debugging (Brion Vibber <brion@pobox.com>)
|
||||
|
||||
Release 1.0.6
|
||||
-------------
|
||||
* removed all array_push() calls
|
||||
* applied patch provided by Stuart Herbert<stuart@gentoo.org>
|
||||
corrects possible endless loop. Available at
|
||||
http://bugs.gentoo.org/show_bug.cgi?id=25385
|
||||
* fixed problem with storing large binary files
|
||||
* added more error checking, specifically on all socket functions
|
||||
* added support for the INCR and DECR commands
|
||||
which increment or decrement a value stored in MemCached
|
||||
* Documentation removed from source and is now available
|
||||
in the file Documentation
|
||||
|
||||
Release 1.0.4
|
||||
-------------
|
||||
* initial release, version numbers kept
|
||||
in sync with MemCached version
|
||||
* capable of storing any datatype in MemCached
|
||||
258
wcmtools/memcached/api/php/Documentation
Executable file
258
wcmtools/memcached/api/php/Documentation
Executable file
@@ -0,0 +1,258 @@
|
||||
Ryan Gilfether <hotrodder@rocketmail.com>
|
||||
http://www.gilfether.com
|
||||
This module is Copyright (c) 2003 Ryan Gilfether.
|
||||
All rights reserved.
|
||||
|
||||
You may distribute under the terms of the GNU General Public License
|
||||
This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
|
||||
|
||||
See the memcached website: http://www.danga.com/memcached/
|
||||
|
||||
|
||||
// Takes one parameter, a array of options. The most important key is
|
||||
// options["servers"], but that can also be set later with the set_servers()
|
||||
// method. The servers must be an array of hosts, each of which is
|
||||
// either a scalar of the form <10.0.0.10:11211> or an array of the
|
||||
// former and an integer weight value. (the default weight if
|
||||
// unspecified is 1.) It's recommended that weight values be kept as low
|
||||
// as possible, as this module currently allocates memory for bucket
|
||||
// distribution proportional to the total host weights.
|
||||
// $options["debug"] turns the debugging on if set to true
|
||||
MemCachedClient::MemCachedClient($options);
|
||||
|
||||
// sets up the list of servers and the ports to connect to
|
||||
// takes an array of servers in the same format as in the constructor
|
||||
MemCachedClient::set_servers($servers);
|
||||
|
||||
// Retrieves a key from the memcache. Returns the value (automatically
|
||||
// unserialized, if necessary) or FALSE if it fails.
|
||||
// The $key can optionally be an array, with the first element being the
|
||||
// hash value, if you want to avoid making this module calculate a hash
|
||||
// value. You may prefer, for example, to keep all of a given user's
|
||||
// objects on the same memcache server, so you could use the user's
|
||||
// unique id as the hash value.
|
||||
// Possible errors set are:
|
||||
// MC_ERR_GET
|
||||
MemCachedClient::get($key);
|
||||
|
||||
// just like get(), but takes an array of keys, returns FALSE on error
|
||||
// Possible errors set are:
|
||||
// MC_ERR_NOT_ACTIVE
|
||||
MemCachedClient::get_multi($keys)
|
||||
|
||||
// Unconditionally sets a key to a given value in the memcache. Returns true
|
||||
// if it was stored successfully.
|
||||
// The $key can optionally be an arrayref, with the first element being the
|
||||
// hash value, as described above.
|
||||
// returns TRUE on success else FALSE
|
||||
// Possible errors set are:
|
||||
// MC_ERR_NOT_ACTIVE
|
||||
// MC_ERR_GET_SOCK
|
||||
// MC_ERR_SOCKET_WRITE
|
||||
// MC_ERR_SOCKET_READ
|
||||
// MC_ERR_SET
|
||||
MemCachedClient::set($key, $value, $exptime);
|
||||
|
||||
// Like set(), but only stores in memcache if the key doesn't already exist.
|
||||
// returns TRUE on success else FALSE
|
||||
// Possible errors set are:
|
||||
// MC_ERR_NOT_ACTIVE
|
||||
// MC_ERR_GET_SOCK
|
||||
// MC_ERR_SOCKET_WRITE
|
||||
// MC_ERR_SOCKET_READ
|
||||
// MC_ERR_SET
|
||||
MemCachedClient::add($key, $value, $exptime);
|
||||
|
||||
// Like set(), but only stores in memcache if the key already exists.
|
||||
// returns TRUE on success else FALSE
|
||||
// Possible errors set are:
|
||||
// MC_ERR_NOT_ACTIVE
|
||||
// MC_ERR_GET_SOCK
|
||||
// MC_ERR_SOCKET_WRITE
|
||||
// MC_ERR_SOCKET_READ
|
||||
// MC_ERR_SET
|
||||
MemCachedClient::replace($key, $value, $exptime);
|
||||
|
||||
// removes the key from the MemCache
|
||||
// $time is the amount of time in seconds (or Unix time) until which
|
||||
// the client wishes the server to refuse "add" and "replace" commands
|
||||
// with this key. For this amount of item, the item is put into a
|
||||
// delete queue, which means that it won't possible to retrieve it by
|
||||
// the "get" command, but "add" and "replace" command with this key
|
||||
// will also fail (the "set" command will succeed, however). After the
|
||||
// time passes, the item is finally deleted from server memory.
|
||||
// The parameter $time is optional, and, if absent, defaults to 0
|
||||
// (which means that the item will be deleted immediately and further
|
||||
// storage commands with this key will succeed).
|
||||
// returns TRUE on success else returns FALSE
|
||||
// Possible errors set are:
|
||||
// MC_ERR_NOT_ACTIVE
|
||||
// MC_ERR_GET_SOCK
|
||||
// MC_ERR_SOCKET_WRITE
|
||||
// MC_ERR_SOCKET_READ
|
||||
// MC_ERR_DELETE
|
||||
MemCachedClient::delete($key, $time = 0);
|
||||
|
||||
// Sends a command to the server to atomically increment the value for
|
||||
// $key by $value, or by 1 if $value is undefined. Returns FALSE if $key
|
||||
// doesn't exist on server, otherwise it returns the new value after
|
||||
// incrementing. Value should be zero or greater. Overflow on server
|
||||
// is not checked. Be aware of values approaching 2**32. See decr.
|
||||
// Possible errors set are:
|
||||
// MC_ERR_NOT_ACTIVE
|
||||
// MC_ERR_GET_SOCK
|
||||
// MC_ERR_SOCKET_WRITE
|
||||
// MC_ERR_SOCKET_READ
|
||||
// returns new value on success, else returns FALSE
|
||||
// ONLY WORKS WITH NUMERIC VALUES
|
||||
MemCachedClient::incr($key[, $value]);
|
||||
|
||||
// Like incr, but decrements. Unlike incr, underflow is checked and new
|
||||
// values are capped at 0. If server value is 1, a decrement of 2
|
||||
// returns 0, not -1.
|
||||
// Possible errors set are:
|
||||
// MC_ERR_NOT_ACTIVE
|
||||
// MC_ERR_GET_SOCK
|
||||
// MC_ERR_SOCKET_WRITE
|
||||
// MC_ERR_SOCKET_READ
|
||||
// returns new value on success, else returns FALSE
|
||||
// ONLY WORKS WITH NUMERIC VALUES
|
||||
MemCachedClient::decr($key[, $value]);
|
||||
|
||||
// disconnects from all servers
|
||||
MemCachedClient::disconnect_all();
|
||||
|
||||
// if $do_debug is set to true, will print out
|
||||
// debugging info, else debug is turned off
|
||||
MemCachedClient::set_debug($do_debug);
|
||||
|
||||
// remove all cached hosts that are no longer good
|
||||
MemCachedClient::forget_dead_hosts();
|
||||
|
||||
// When a function returns FALSE, an error code is set.
|
||||
// This funtion will return the error code.
|
||||
// See error_string()
|
||||
// returns last error code set
|
||||
MemCachedClient::error()
|
||||
|
||||
// Returns a string describing the error set in error()
|
||||
// See error()
|
||||
// returns a string describing the error code given
|
||||
MemCachedClient::error_string()
|
||||
|
||||
// Resets the error number and error string
|
||||
MemCachedClient::error_clear()
|
||||
|
||||
Error codes are as follows:
|
||||
MC_ERR_NOT_ACTIVE // no active servers
|
||||
MC_ERR_SOCKET_WRITE // socket_write() failed
|
||||
MC_ERR_SOCKET_READ // socket_read() failed
|
||||
MC_ERR_SOCKET_CONNECT // failed to connect to host
|
||||
MC_ERR_DELETE // delete() did not recieve DELETED command
|
||||
MC_ERR_HOST_FORMAT // sock_to_host() invalid host format
|
||||
MC_ERR_HOST_DEAD // sock_to_host() host is dead
|
||||
MC_ERR_GET_SOCK // get_sock() failed to find a valid socket
|
||||
MC_ERR_SET // _set() failed to receive the STORED response
|
||||
MC_ERR_LOADITEM_HEADER // _load_items failed to receive valid data header
|
||||
MC_ERR_LOADITEM_END // _load_items failed to receive END response
|
||||
MC_ERR_LOADITEM_BYTES // _load_items bytes read larger than bytes available
|
||||
MC_ERR_GET // failed to get value associated with key
|
||||
|
||||
// Turns compression on or off; 0=off, 1=on
|
||||
MemCacheClient::set_compression($setting)
|
||||
|
||||
EXAMPLE:
|
||||
<?php
|
||||
require("MemCachedClient.inc.php");
|
||||
|
||||
// set the servers, with the last one having an interger weight value of 3
|
||||
$options["servers"] = array("10.0.0.15:11000","10.0.0.16:11001",array("10.0.0.17:11002", 3));
|
||||
$options["debug"] = false;
|
||||
|
||||
$memc = new MemCachedClient($options);
|
||||
|
||||
|
||||
/***********************
|
||||
* STORE AN ARRAY
|
||||
***********************/
|
||||
$myarr = array("one","two", 3);
|
||||
$memc->set("key_one", $myarr);
|
||||
$val = $memc->get("key_one");
|
||||
print $val[0]."\n"; // prints 'one'
|
||||
print $val[1]."\n"; // prints 'two'
|
||||
print $val[2]."\n"; // prints 3
|
||||
|
||||
|
||||
print "\n";
|
||||
|
||||
|
||||
/***********************
|
||||
* STORE A CLASS
|
||||
***********************/
|
||||
class tester
|
||||
{
|
||||
var $one;
|
||||
var $two;
|
||||
var $three;
|
||||
}
|
||||
|
||||
$t = new tester;
|
||||
$t->one = "one";
|
||||
$t->two = "two";
|
||||
$t->three = 3;
|
||||
$memc->set("key_two", $t);
|
||||
$val = $memc->get("key_two");
|
||||
print $val->one."\n";
|
||||
print $val->two."\n";
|
||||
print $val->three."\n";
|
||||
|
||||
|
||||
print "\n";
|
||||
|
||||
|
||||
/***********************
|
||||
* STORE A STRING
|
||||
***********************/
|
||||
$memc->set("key_three", "my string");
|
||||
$val = $memc->get("key_three");
|
||||
print $val; // prints 'my string'
|
||||
|
||||
$memc->delete("key_one");
|
||||
$memc->delete("key_two");
|
||||
$memc->delete("key_three");
|
||||
|
||||
$memc->disconnect_all();
|
||||
|
||||
|
||||
|
||||
print "\n";
|
||||
|
||||
|
||||
/***********************
|
||||
* STORE A BINARY FILE
|
||||
***********************/
|
||||
|
||||
// first read the file and save it in memcache
|
||||
$fp = fopen( "./image.jpg", "rb" ) ;
|
||||
if ( !$fp )
|
||||
{
|
||||
print "Could not open ./file.dat!\n" ;
|
||||
exit ;
|
||||
}
|
||||
$data = fread( $fp, filesize( "./image.jpg" ) ) ;
|
||||
fclose( $fp ) ;
|
||||
print "Data length is " . strlen( $data ) . "\n" ;
|
||||
$memc->set( "key", $data ) ;
|
||||
|
||||
// now open a file for writing and write the data
|
||||
// retrieved from memcache
|
||||
$fp = fopen("./test.jpg","wb");
|
||||
$data = $memc->get( "key" ) ;
|
||||
print "Data length is " . strlen( $data ) . "\n" ;
|
||||
fwrite($fp,$data,strlen( $data ));
|
||||
fclose($fp);
|
||||
|
||||
|
||||
?>
|
||||
|
||||
|
||||
1143
wcmtools/memcached/api/php/MemCachedClient.inc.php
Executable file
1143
wcmtools/memcached/api/php/MemCachedClient.inc.php
Executable file
File diff suppressed because it is too large
Load Diff
70
wcmtools/memcached/api/php/dist.pl
Executable file
70
wcmtools/memcached/api/php/dist.pl
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Simplify Brad's life. I'm sure there's a PHP-specific way
|
||||
# to do this (or there should be), but I don't do the PHP,
|
||||
# so this is my answer.
|
||||
#
|
||||
# Heavily documented for the PHP programmers who might be
|
||||
# reading this.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
|
||||
my $opt_tar = 0;
|
||||
my $opt_upload = 0;
|
||||
exit 1 unless GetOptions("tar" => \$opt_tar,
|
||||
"upload" => \$opt_upload);
|
||||
|
||||
# chdir to the directory the script's at, so future
|
||||
# paths need only be relative
|
||||
use FindBin qw($Bin);
|
||||
chdir $Bin or die "Couldn't cd to $Bin\n";
|
||||
|
||||
die "Must use --tar or --upload\n" unless $opt_tar || $opt_upload;
|
||||
|
||||
# files to distribute
|
||||
my @manifest = qw(
|
||||
ChangeLog
|
||||
Documentation
|
||||
MemCachedClient.inc.php
|
||||
);
|
||||
|
||||
# figure out the version number
|
||||
open (PHP, "MemCachedClient.inc.php") or die;
|
||||
{ local $/ = undef; $_ = <PHP>; } # suck in the whole file
|
||||
close PHP;
|
||||
die "Can't find version number\n" unless
|
||||
/MC_VERSION.+?(\d[^\'\"]+)/s;
|
||||
my $ver = $1;
|
||||
my $dir = "php-memcached-$ver";
|
||||
my $dist = "$dir.tar.gz";
|
||||
|
||||
if ($opt_tar) {
|
||||
# make a fresh directory
|
||||
mkdir $dir or die "Couldn't make directory: $dir\n";
|
||||
|
||||
# copy files to fresh directory
|
||||
foreach my $file (@manifest) {
|
||||
system("cp", $file, "$dir/$file")
|
||||
and die "Error copying file $file\n";
|
||||
}
|
||||
|
||||
# tar it up
|
||||
system("tar", "zcf", $dist, $dir)
|
||||
and die "Error running tar.\n";
|
||||
|
||||
# remove temp directory
|
||||
system("rm", "-rf", $dir)
|
||||
and die "Error cleaning up temp directory\n";
|
||||
|
||||
print "$dist created.\n";
|
||||
}
|
||||
|
||||
if ($opt_upload) {
|
||||
print "Uploading $dist...\n";
|
||||
system("scp", $dist, 'bradfitz@danga.com:memd/dist/')
|
||||
and die "Error uploading to memcached/dist\n";
|
||||
}
|
||||
|
||||
print "Done.\n";
|
||||
16
wcmtools/memcached/api/python/ChangeLog
Executable file
16
wcmtools/memcached/api/python/ChangeLog
Executable file
@@ -0,0 +1,16 @@
|
||||
Thu, 10 Aug 2003 12:17:50 -0700 Evan Martin <martine@danga.com>
|
||||
|
||||
* Slightly more verbose self-test output.
|
||||
* Fix mark_dead() to use proper classname.
|
||||
* Make pooltest.py run from the test directory.
|
||||
|
||||
Thu, 07 Aug 2003 16:32:32 -0700 Evan Martin <martine@danga.com>
|
||||
|
||||
* Add incr, decr, and delete.
|
||||
* Better Python (based on comments from Uriah Welcome).
|
||||
* Docs, using epydoc.
|
||||
|
||||
Thu, 07 Aug 2003 14:20:27 -0700 Evan Martin <martine@danga.com>
|
||||
|
||||
* Initial prerelease.
|
||||
|
||||
3
wcmtools/memcached/api/python/DISTNOTES
Executable file
3
wcmtools/memcached/api/python/DISTNOTES
Executable file
@@ -0,0 +1,3 @@
|
||||
Note for Brad, on how to make new releases:
|
||||
|
||||
$ python setup.py sdist
|
||||
2
wcmtools/memcached/api/python/MANIFEST
Executable file
2
wcmtools/memcached/api/python/MANIFEST
Executable file
@@ -0,0 +1,2 @@
|
||||
memcache.py
|
||||
setup.py
|
||||
10
wcmtools/memcached/api/python/PKG-INFO
Executable file
10
wcmtools/memcached/api/python/PKG-INFO
Executable file
@@ -0,0 +1,10 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: python-memcache
|
||||
Version: 1.1
|
||||
Summary: UNKNOWN
|
||||
Home-page: http://www.danga.com/memcached/
|
||||
Author: Evan Martin
|
||||
Author-email: martine@danga.com
|
||||
License: UNKNOWN
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
585
wcmtools/memcached/api/python/memcache.py
Executable file
585
wcmtools/memcached/api/python/memcache.py
Executable file
@@ -0,0 +1,585 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
client module for memcached (memory cache daemon)
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
See U{the MemCached homepage<http://www.danga.com/memcached>} for more about memcached.
|
||||
|
||||
Usage summary
|
||||
=============
|
||||
|
||||
This should give you a feel for how this module operates::
|
||||
|
||||
import memcache
|
||||
mc = memcache.Client(['127.0.0.1:11211'], debug=0)
|
||||
|
||||
mc.set("some_key", "Some value")
|
||||
value = mc.get("some_key")
|
||||
|
||||
mc.set("another_key", 3)
|
||||
mc.delete("another_key")
|
||||
|
||||
mc.set("key", "1") # note that the key used for incr/decr must be a string.
|
||||
mc.incr("key")
|
||||
mc.decr("key")
|
||||
|
||||
The standard way to use memcache with a database is like this::
|
||||
|
||||
key = derive_key(obj)
|
||||
obj = mc.get(key)
|
||||
if not obj:
|
||||
obj = backend_api.get(...)
|
||||
mc.set(obj)
|
||||
|
||||
# we now have obj, and future passes through this code
|
||||
# will use the object from the cache.
|
||||
|
||||
Detailed Documentation
|
||||
======================
|
||||
|
||||
More detailed documentation is available in the L{Client} class.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import socket
|
||||
import time
|
||||
import types
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
__author__ = "Evan Martin <martine@danga.com>"
|
||||
__version__ = "1.2"
|
||||
__copyright__ = "Copyright (C) 2003 Danga Interactive"
|
||||
__license__ = "Python"
|
||||
|
||||
class _Error(Exception):
|
||||
pass
|
||||
|
||||
class Client:
|
||||
"""
|
||||
Object representing a pool of memcache servers.
|
||||
|
||||
See L{memcache} for an overview.
|
||||
|
||||
In all cases where a key is used, the key can be either:
|
||||
1. A simple hashable type (string, integer, etc.).
|
||||
2. A tuple of C{(hashvalue, key)}. This is useful if you want to avoid
|
||||
making this module calculate a hash value. You may prefer, for
|
||||
example, to keep all of a given user's objects on the same memcache
|
||||
server, so you could use the user's unique id as the hash value.
|
||||
|
||||
@group Setup: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog
|
||||
@group Insertion: set, add, replace
|
||||
@group Retrieval: get, get_multi
|
||||
@group Integers: incr, decr
|
||||
@group Removal: delete
|
||||
@sort: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog,\
|
||||
set, add, replace, get, get_multi, incr, decr, delete
|
||||
"""
|
||||
_FLAG_PICKLE = 1<<0
|
||||
_FLAG_INTEGER = 1<<1
|
||||
_FLAG_LONG = 1<<2
|
||||
|
||||
_SERVER_RETRIES = 10 # how many times to try finding a free server.
|
||||
|
||||
def __init__(self, servers, debug=0):
|
||||
"""
|
||||
Create a new Client object with the given list of servers.
|
||||
|
||||
@param servers: C{servers} is passed to L{set_servers}.
|
||||
@param debug: whether to display error messages when a server can't be
|
||||
contacted.
|
||||
"""
|
||||
self.set_servers(servers)
|
||||
self.debug = debug
|
||||
self.stats = {}
|
||||
|
||||
def set_servers(self, servers):
|
||||
"""
|
||||
Set the pool of servers used by this client.
|
||||
|
||||
@param servers: an array of servers.
|
||||
Servers can be passed in two forms:
|
||||
1. Strings of the form C{"host:port"}, which implies a default weight of 1.
|
||||
2. Tuples of the form C{("host:port", weight)}, where C{weight} is
|
||||
an integer weight value.
|
||||
"""
|
||||
self.servers = [_Host(s, self.debuglog) for s in servers]
|
||||
self._init_buckets()
|
||||
|
||||
def debuglog(self, str):
|
||||
if self.debug:
|
||||
sys.stderr.write("MemCached: %s\n" % str)
|
||||
|
||||
def _statlog(self, func):
|
||||
if not self.stats.has_key(func):
|
||||
self.stats[func] = 1
|
||||
else:
|
||||
self.stats[func] += 1
|
||||
|
||||
def forget_dead_hosts(self):
|
||||
"""
|
||||
Reset every host in the pool to an "alive" state.
|
||||
"""
|
||||
for s in self.servers:
|
||||
s.dead_until = 0
|
||||
|
||||
def _init_buckets(self):
|
||||
self.buckets = []
|
||||
for server in self.servers:
|
||||
for i in range(server.weight):
|
||||
self.buckets.append(server)
|
||||
|
||||
def _get_server(self, key):
|
||||
if type(key) == types.TupleType:
|
||||
serverhash = key[0]
|
||||
key = key[1]
|
||||
else:
|
||||
serverhash = hash(key)
|
||||
|
||||
for i in range(Client._SERVER_RETRIES):
|
||||
server = self.buckets[serverhash % len(self.buckets)]
|
||||
if server.connect():
|
||||
#print "(using server %s)" % server,
|
||||
return server, key
|
||||
serverhash = hash(str(serverhash) + str(i))
|
||||
return None, None
|
||||
|
||||
def disconnect_all(self):
|
||||
for s in self.servers:
|
||||
s.close_socket()
|
||||
|
||||
def delete(self, key, time=0):
|
||||
'''Deletes a key from the memcache.
|
||||
|
||||
@return: Nonzero on success.
|
||||
@rtype: int
|
||||
'''
|
||||
server, key = self._get_server(key)
|
||||
if not server:
|
||||
return 0
|
||||
self._statlog('delete')
|
||||
if time != None:
|
||||
cmd = "delete %s %d" % (key, time)
|
||||
else:
|
||||
cmd = "delete %s" % key
|
||||
|
||||
try:
|
||||
server.send_cmd(cmd)
|
||||
server.expect("DELETED")
|
||||
except socket.error, msg:
|
||||
server.mark_dead(msg[1])
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def incr(self, key, delta=1):
|
||||
"""
|
||||
Sends a command to the server to atomically increment the value for C{key} by
|
||||
C{delta}, or by 1 if C{delta} is unspecified. Returns None if C{key} doesn't
|
||||
exist on server, otherwise it returns the new value after incrementing.
|
||||
|
||||
Note that the value for C{key} must already exist in the memcache, and it
|
||||
must be the string representation of an integer.
|
||||
|
||||
>>> mc.set("counter", "20") # returns 1, indicating success
|
||||
1
|
||||
>>> mc.incr("counter")
|
||||
21
|
||||
>>> mc.incr("counter")
|
||||
22
|
||||
|
||||
Overflow on server is not checked. Be aware of values approaching
|
||||
2**32. See L{decr}.
|
||||
|
||||
@param delta: Integer amount to increment by (should be zero or greater).
|
||||
@return: New value after incrementing.
|
||||
@rtype: int
|
||||
"""
|
||||
return self._incrdecr("incr", key, delta)
|
||||
|
||||
def decr(self, key, delta=1):
|
||||
"""
|
||||
Like L{incr}, but decrements. Unlike L{incr}, underflow is checked and
|
||||
new values are capped at 0. If server value is 1, a decrement of 2
|
||||
returns 0, not -1.
|
||||
|
||||
@param delta: Integer amount to decrement by (should be zero or greater).
|
||||
@return: New value after decrementing.
|
||||
@rtype: int
|
||||
"""
|
||||
return self._incrdecr("decr", key, delta)
|
||||
|
||||
def _incrdecr(self, cmd, key, delta):
|
||||
server, key = self._get_server(key)
|
||||
if not server:
|
||||
return 0
|
||||
self._statlog(cmd)
|
||||
cmd = "%s %s %d" % (cmd, key, delta)
|
||||
try:
|
||||
server.send_cmd(cmd)
|
||||
line = server.readline()
|
||||
return int(line)
|
||||
except socket.error, msg:
|
||||
server.mark_dead(msg[1])
|
||||
return None
|
||||
|
||||
def add(self, key, val, time=0):
|
||||
'''
|
||||
Add new key with value.
|
||||
|
||||
Like L{set}, but only stores in memcache if the key doesn't already exist.
|
||||
|
||||
@return: Nonzero on success.
|
||||
@rtype: int
|
||||
'''
|
||||
return self._set("add", key, val, time)
|
||||
def replace(self, key, val, time=0):
|
||||
'''Replace existing key with value.
|
||||
|
||||
Like L{set}, but only stores in memcache if the key already exists.
|
||||
The opposite of L{add}.
|
||||
|
||||
@return: Nonzero on success.
|
||||
@rtype: int
|
||||
'''
|
||||
return self._set("replace", key, val, time)
|
||||
def set(self, key, val, time=0):
|
||||
'''Unconditionally sets a key to a given value in the memcache.
|
||||
|
||||
The C{key} can optionally be an tuple, with the first element being the
|
||||
hash value, if you want to avoid making this module calculate a hash value.
|
||||
You may prefer, for example, to keep all of a given user's objects on the
|
||||
same memcache server, so you could use the user's unique id as the hash
|
||||
value.
|
||||
|
||||
@return: Nonzero on success.
|
||||
@rtype: int
|
||||
'''
|
||||
return self._set("set", key, val, time)
|
||||
|
||||
def _set(self, cmd, key, val, time):
|
||||
server, key = self._get_server(key)
|
||||
if not server:
|
||||
return 0
|
||||
|
||||
self._statlog(cmd)
|
||||
|
||||
flags = 0
|
||||
if isinstance(val, types.StringTypes):
|
||||
pass
|
||||
elif isinstance(val, int):
|
||||
flags |= Client._FLAG_INTEGER
|
||||
val = "%d" % val
|
||||
elif isinstance(val, long):
|
||||
flags |= Client._FLAG_LONG
|
||||
val = "%d" % val
|
||||
else:
|
||||
flags |= Client._FLAG_PICKLE
|
||||
val = pickle.dumps(val, 2)
|
||||
|
||||
fullcmd = "%s %s %d %d %d\r\n%s" % (cmd, key, flags, time, len(val), val)
|
||||
try:
|
||||
server.send_cmd(fullcmd)
|
||||
server.expect("STORED")
|
||||
except socket.error, msg:
|
||||
server.mark_dead(msg[1])
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def get(self, key):
|
||||
'''Retrieves a key from the memcache.
|
||||
|
||||
@return: The value or None.
|
||||
'''
|
||||
server, key = self._get_server(key)
|
||||
if not server:
|
||||
return None
|
||||
|
||||
self._statlog('get')
|
||||
|
||||
try:
|
||||
server.send_cmd("get %s" % key)
|
||||
rkey, flags, rlen, = self._expectvalue(server)
|
||||
if not rkey:
|
||||
return None
|
||||
value = self._recv_value(server, flags, rlen)
|
||||
server.expect("END")
|
||||
except (_Error, socket.error), msg:
|
||||
if type(msg) is types.TupleType:
|
||||
msg = msg[1]
|
||||
server.mark_dead(msg)
|
||||
return None
|
||||
return value
|
||||
|
||||
def get_multi(self, keys):
|
||||
'''
|
||||
Retrieves multiple keys from the memcache doing just one query.
|
||||
|
||||
>>> success = mc.set("foo", "bar")
|
||||
>>> success = mc.set("baz", 42)
|
||||
>>> mc.get_multi(["foo", "baz", "foobar"]) == {"foo": "bar", "baz": 42}
|
||||
1
|
||||
|
||||
This method is recommended over regular L{get} as it lowers the number of
|
||||
total packets flying around your network, reducing total latency, since
|
||||
your app doesn't have to wait for each round-trip of L{get} before sending
|
||||
the next one.
|
||||
|
||||
@param keys: An array of keys.
|
||||
@return: A dictionary of key/value pairs that were available.
|
||||
|
||||
'''
|
||||
|
||||
self._statlog('get_multi')
|
||||
|
||||
server_keys = {}
|
||||
|
||||
# build up a list for each server of all the keys we want.
|
||||
for key in keys:
|
||||
server, key = self._get_server(key)
|
||||
if not server:
|
||||
continue
|
||||
if not server_keys.has_key(server):
|
||||
server_keys[server] = []
|
||||
server_keys[server].append(key)
|
||||
|
||||
# send out all requests on each server before reading anything
|
||||
dead_servers = []
|
||||
for server in server_keys.keys():
|
||||
try:
|
||||
server.send_cmd("get %s" % " ".join(server_keys[server]))
|
||||
except socket.error, msg:
|
||||
server.mark_dead(msg[1])
|
||||
dead_servers.append(server)
|
||||
|
||||
# if any servers died on the way, don't expect them to respond.
|
||||
for server in dead_servers:
|
||||
del server_keys[server]
|
||||
|
||||
retvals = {}
|
||||
for server in server_keys.keys():
|
||||
try:
|
||||
line = server.readline()
|
||||
while line and line != 'END':
|
||||
rkey, flags, rlen = self._expectvalue(server, line)
|
||||
val = self._recv_value(server, flags, rlen)
|
||||
retvals[rkey] = val
|
||||
line = server.readline()
|
||||
except (_Error, socket.error), msg:
|
||||
server.mark_dead(msg)
|
||||
return retvals
|
||||
|
||||
def _expectvalue(self, server, line=None):
|
||||
if not line:
|
||||
line = server.readline()
|
||||
|
||||
if line[:5] == 'VALUE':
|
||||
resp, rkey, flags, len = line.split()
|
||||
flags = int(flags)
|
||||
rlen = int(len)
|
||||
return (rkey, flags, rlen)
|
||||
else:
|
||||
return (None, None, None)
|
||||
|
||||
def _recv_value(self, server, flags, rlen):
|
||||
rlen += 2 # include \r\n
|
||||
buf = server.recv(rlen)
|
||||
if len(buf) != rlen:
|
||||
raise _Error("received %d bytes when expecting %d" % (len(buf), rlen))
|
||||
|
||||
if len(buf) == rlen:
|
||||
buf = buf[:-2] # strip \r\n
|
||||
|
||||
if flags == 0:
|
||||
val = buf
|
||||
elif flags & Client._FLAG_INTEGER:
|
||||
val = int(buf)
|
||||
elif flags & Client._FLAG_LONG:
|
||||
val = long(buf)
|
||||
elif flags & Client._FLAG_PICKLE:
|
||||
val = pickle.loads(buf)
|
||||
else:
|
||||
self.debuglog("unknown flags on get: %x\n" % flags)
|
||||
|
||||
return val
|
||||
|
||||
|
||||
class _Host:
|
||||
_DEAD_RETRY = 30 # number of seconds before retrying a dead server.
|
||||
|
||||
def __init__(self, host, debugfunc=None):
|
||||
if isinstance(host, types.TupleType):
|
||||
host = host[0]
|
||||
self.weight = host[1]
|
||||
else:
|
||||
self.weight = 1
|
||||
|
||||
if host.find(":") > 0:
|
||||
self.ip, self.port = host.split(":")
|
||||
self.port = int(self.port)
|
||||
else:
|
||||
self.ip, self.port = host, 11211
|
||||
|
||||
if not debugfunc:
|
||||
debugfunc = lambda x: x
|
||||
self.debuglog = debugfunc
|
||||
|
||||
self.deaduntil = 0
|
||||
self.socket = None
|
||||
|
||||
def _check_dead(self):
|
||||
if self.deaduntil and self.deaduntil > time.time():
|
||||
return 1
|
||||
self.deaduntil = 0
|
||||
return 0
|
||||
|
||||
def connect(self):
|
||||
if self._get_socket():
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def mark_dead(self, reason):
|
||||
print "MemCache: %s: %s. Marking dead." % (self, reason)
|
||||
self.deaduntil = time.time() + _Host._DEAD_RETRY
|
||||
self.close_socket()
|
||||
|
||||
def _get_socket(self):
|
||||
if self._check_dead():
|
||||
return None
|
||||
if self.socket:
|
||||
return self.socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
# Python 2.3-ism: s.settimeout(1)
|
||||
try:
|
||||
s.connect((self.ip, self.port))
|
||||
except socket.error, msg:
|
||||
self.mark_dead("connect: %s" % msg[1])
|
||||
return None
|
||||
self.socket = s
|
||||
return s
|
||||
|
||||
def close_socket(self):
|
||||
if self.socket:
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
|
||||
def send_cmd(self, cmd):
|
||||
self.socket.sendall(cmd + "\r\n")
|
||||
|
||||
def readline(self):
|
||||
newlines = 0
|
||||
buf = ''
|
||||
while newlines < 2:
|
||||
char = self.socket.recv(1) # XXX does this buffer or is this slow?
|
||||
if len(char) == 0:
|
||||
# connection closed.
|
||||
print "MemCache: Connection closed while reading from %s. Marking dead." % self
|
||||
self.mark_dead
|
||||
return buf
|
||||
if char == '\r' and newlines == 0:
|
||||
newlines = 1
|
||||
elif char == '\n' and newlines == 1:
|
||||
newlines = 2
|
||||
else:
|
||||
newlines = 0
|
||||
buf = buf + char
|
||||
return buf
|
||||
|
||||
def expect(self, text):
|
||||
line = self.readline()
|
||||
if line != text:
|
||||
self.debuglog("while expecting '%s', got unexpected response '%s'" % (text, line))
|
||||
return line
|
||||
|
||||
def recv(self, rlen):
|
||||
buf = ''
|
||||
while len(buf) < rlen:
|
||||
buf = buf + self.socket.recv(rlen - len(buf))
|
||||
return buf
|
||||
|
||||
def __str__(self):
|
||||
d = ''
|
||||
if self.deaduntil:
|
||||
d = " (dead until %d)" % self.deaduntil
|
||||
return "%s:%d%s" % (self.ip, self.port, d)
|
||||
|
||||
def _doctest():
|
||||
import doctest, memcache
|
||||
servers = ["127.0.0.1:11211"]
|
||||
mc = Client(servers, debug=1)
|
||||
globs = {"mc": mc}
|
||||
return doctest.testmod(memcache, globs=globs)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print "Testing docstrings..."
|
||||
_doctest()
|
||||
print "Running tests:"
|
||||
print
|
||||
#servers = ["127.0.0.1:11211", "127.0.0.1:11212"]
|
||||
servers = ["127.0.0.1:11211"]
|
||||
mc = Client(servers, debug=1)
|
||||
|
||||
def to_s(val):
|
||||
if not isinstance(val, types.StringTypes):
|
||||
return "%s (%s)" % (val, type(val))
|
||||
return "%s" % val
|
||||
def test_setget(key, val):
|
||||
print "Testing set/get {'%s': %s} ..." % (to_s(key), to_s(val)),
|
||||
mc.set(key, val)
|
||||
newval = mc.get(key)
|
||||
if newval == val:
|
||||
print "OK"
|
||||
return 1
|
||||
else:
|
||||
print "FAIL"
|
||||
return 0
|
||||
|
||||
class FooStruct:
|
||||
def __init__(self):
|
||||
self.bar = "baz"
|
||||
def __str__(self):
|
||||
return "A FooStruct"
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, FooStruct):
|
||||
return self.bar == other.bar
|
||||
return 0
|
||||
|
||||
test_setget("a_string", "some random string")
|
||||
test_setget("an_integer", 42)
|
||||
if test_setget("long", long(1<<30)):
|
||||
print "Testing delete ...",
|
||||
if mc.delete("long"):
|
||||
print "OK"
|
||||
else:
|
||||
print "FAIL"
|
||||
print "Testing get_multi ...",
|
||||
print mc.get_multi(["a_string", "an_integer"])
|
||||
|
||||
print "Testing get(unknown value) ...",
|
||||
print to_s(mc.get("unknown_value"))
|
||||
|
||||
f = FooStruct()
|
||||
test_setget("foostruct", f)
|
||||
|
||||
print "Testing incr ...",
|
||||
x = mc.incr("an_integer", 1)
|
||||
if x == 43:
|
||||
print "OK"
|
||||
else:
|
||||
print "FAIL"
|
||||
|
||||
print "Testing decr ...",
|
||||
x = mc.decr("an_integer", 1)
|
||||
if x == 42:
|
||||
print "OK"
|
||||
else:
|
||||
print "FAIL"
|
||||
|
||||
|
||||
|
||||
# vim: ts=4 sw=4 et :
|
||||
12
wcmtools/memcached/api/python/setup.py
Executable file
12
wcmtools/memcached/api/python/setup.py
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
import memcache
|
||||
|
||||
setup(name="python-memcached",
|
||||
version=memcache.__version__,
|
||||
author="Evan Martin",
|
||||
author_email="martine@danga.com",
|
||||
url="http://www.danga.com/memcached/",
|
||||
py_modules=["memcache"])
|
||||
|
||||
28
wcmtools/memcached/api/python/tests/pooltest.py
Executable file
28
wcmtools/memcached/api/python/tests/pooltest.py
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""pooltest
|
||||
|
||||
Bring up two memcaches on :11211 and :11212. Try killing one or both.
|
||||
If this code raises any exceptions, it's a bug."""
|
||||
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
import memcache
|
||||
import time
|
||||
|
||||
mc = memcache.Client(["127.0.0.1:11211", "127.0.0.1:11212"], debug=1)
|
||||
|
||||
def test_setget(key, val):
|
||||
print "Testing set/get {'%s': %s} ..." % (key, val),
|
||||
mc.set(key, val)
|
||||
newval = mc.get(key)
|
||||
if newval == val:
|
||||
print "OK"
|
||||
else:
|
||||
print "FAIL"
|
||||
|
||||
i = 0
|
||||
while 1:
|
||||
test_setget("foo%d" % i, "bar%d" % i)
|
||||
time.sleep(1)
|
||||
i += 1
|
||||
186
wcmtools/memcached/assoc.c
Executable file
186
wcmtools/memcached/assoc.c
Executable file
@@ -0,0 +1,186 @@
|
||||
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* Hash table
|
||||
*
|
||||
* The hash function used here is by Bob Jenkins, 1996:
|
||||
* <http://burtleburtle.net/bob/hash/doobs.html>
|
||||
* "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net.
|
||||
* You may use this code any way you wish, private, educational,
|
||||
* or commercial. It's free."
|
||||
*
|
||||
* The rest of the file is licensed under the BSD license. See LICENSE.
|
||||
*
|
||||
* $Id: assoc.c,v 1.6 2003/08/11 05:44:26 bradfitz Exp $
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/resource.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "memcached.h"
|
||||
|
||||
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
|
||||
typedef unsigned char ub1; /* unsigned 1-byte quantities */
|
||||
|
||||
/* hard-code one million buckets, for now (2**20 == 4MB hash) */
|
||||
#define HASHPOWER 20
|
||||
|
||||
#define hashsize(n) ((ub4)1<<(n))
|
||||
#define hashmask(n) (hashsize(n)-1)
|
||||
|
||||
#define mix(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>13); \
|
||||
b -= c; b -= a; b ^= (a<<8); \
|
||||
c -= a; c -= b; c ^= (b>>13); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<16); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>3); \
|
||||
b -= c; b -= a; b ^= (a<<10); \
|
||||
c -= a; c -= b; c ^= (b>>15); \
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------------------------------------------------------
|
||||
hash() -- hash a variable-length key into a 32-bit value
|
||||
k : the key (the unaligned variable-length array of bytes)
|
||||
len : the length of the key, counting by bytes
|
||||
initval : can be any 4-byte value
|
||||
Returns a 32-bit value. Every bit of the key affects every bit of
|
||||
the return value. Every 1-bit and 2-bit delta achieves avalanche.
|
||||
About 6*len+35 instructions.
|
||||
|
||||
The best hash table sizes are powers of 2. There is no need to do
|
||||
mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
use a bitmask. For example, if you need only 10 bits, do
|
||||
h = (h & hashmask(10));
|
||||
In which case, the hash table should have hashsize(10) elements.
|
||||
|
||||
If you are hashing n strings (ub1 **)k, do it like this:
|
||||
for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
|
||||
|
||||
By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
|
||||
code any way you wish, private, educational, or commercial. It's free.
|
||||
|
||||
See http://burtleburtle.net/bob/hash/evahash.html
|
||||
Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||
acceptable. Do NOT use for cryptographic purposes.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
ub4 hash( k, length, initval)
|
||||
register ub1 *k; /* the key */
|
||||
register ub4 length; /* the length of the key */
|
||||
register ub4 initval; /* the previous hash, or an arbitrary value */
|
||||
{
|
||||
register ub4 a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||
c = initval; /* the previous hash value */
|
||||
|
||||
/*---------------------------------------- handle most of the key */
|
||||
while (len >= 12)
|
||||
{
|
||||
a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
|
||||
b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
|
||||
c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
|
||||
mix(a,b,c);
|
||||
k += 12; len -= 12;
|
||||
}
|
||||
|
||||
/*------------------------------------- handle the last 11 bytes */
|
||||
c += length;
|
||||
switch(len) /* all the case statements fall through */
|
||||
{
|
||||
case 11: c+=((ub4)k[10]<<24);
|
||||
case 10: c+=((ub4)k[9]<<16);
|
||||
case 9 : c+=((ub4)k[8]<<8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 8 : b+=((ub4)k[7]<<24);
|
||||
case 7 : b+=((ub4)k[6]<<16);
|
||||
case 6 : b+=((ub4)k[5]<<8);
|
||||
case 5 : b+=k[4];
|
||||
case 4 : a+=((ub4)k[3]<<24);
|
||||
case 3 : a+=((ub4)k[2]<<16);
|
||||
case 2 : a+=((ub4)k[1]<<8);
|
||||
case 1 : a+=k[0];
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
mix(a,b,c);
|
||||
/*-------------------------------------------- report the result */
|
||||
return c;
|
||||
}
|
||||
|
||||
static item** hashtable = 0;
|
||||
|
||||
void assoc_init(void) {
|
||||
unsigned int hash_size = hashsize(HASHPOWER) * sizeof(void*);
|
||||
hashtable = malloc(hash_size);
|
||||
if (! hashtable) {
|
||||
fprintf(stderr, "Failed to init hashtable.\n");
|
||||
exit(1);
|
||||
}
|
||||
memset(hashtable, 0, hash_size);
|
||||
}
|
||||
|
||||
item *assoc_find(char *key) {
|
||||
ub4 hv = hash(key, strlen(key), 0) & hashmask(HASHPOWER);
|
||||
item *it = hashtable[hv];
|
||||
|
||||
while (it) {
|
||||
if (strcmp(key, ITEM_key(it)) == 0)
|
||||
return it;
|
||||
it = it->h_next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns the address of the item pointer before the key. if *item == 0,
|
||||
the item wasn't found */
|
||||
|
||||
static item** _hashitem_before (char *key) {
|
||||
ub4 hv = hash(key, strlen(key), 0) & hashmask(HASHPOWER);
|
||||
item **pos = &hashtable[hv];
|
||||
|
||||
while (*pos && strcmp(key, ITEM_key(*pos))) {
|
||||
pos = &(*pos)->h_next;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* Note: this isn't an assoc_update. The key must not already exist to call this */
|
||||
int assoc_insert(char *key, item *it) {
|
||||
ub4 hv = hash(key, strlen(key), 0) & hashmask(HASHPOWER);
|
||||
it->h_next = hashtable[hv];
|
||||
hashtable[hv] = it;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void assoc_delete(char *key) {
|
||||
item **before = _hashitem_before(key);
|
||||
if (*before) {
|
||||
item *nxt = (*before)->h_next;
|
||||
(*before)->h_next = 0; /* probably pointless, but whatever. */
|
||||
*before = nxt;
|
||||
return;
|
||||
}
|
||||
/* Note: we never actually get here. the callers don't delete things
|
||||
they can't find. */
|
||||
assert(*before != 0);
|
||||
}
|
||||
|
||||
22
wcmtools/memcached/autogen.sh
Executable file
22
wcmtools/memcached/autogen.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This is hacky, because there are so many damn versions
|
||||
# of autoconf/automake. It works with Debian woody, at least.
|
||||
#
|
||||
|
||||
echo "aclocal..."
|
||||
ACLOCAL=${ACLOCAL:-aclocal-1.7}
|
||||
$ACLOCAL || aclocal-1.5 || aclocal || exit 1
|
||||
|
||||
echo "autoheader..."
|
||||
AUTOHEADER=${AUTOHEADER:-autoheader}
|
||||
$AUTOHEADER || exit 1
|
||||
|
||||
echo "automake..."
|
||||
AUTOMAKE=${AUTOMAKE:-automake-1.7}
|
||||
$AUTOMAKE --foreign --add-missing || automake --gnu --add-missing || exit 1
|
||||
|
||||
echo "autoconf..."
|
||||
AUTOCONF=${AUTOCONF:-autoconf}
|
||||
$AUTOCONF || exit 1
|
||||
|
||||
56
wcmtools/memcached/configure.ac
Executable file
56
wcmtools/memcached/configure.ac
Executable file
@@ -0,0 +1,56 @@
|
||||
AC_PREREQ(2.52)
|
||||
AC_INIT(memcached, 1.1.11, brad@danga.com)
|
||||
AC_CANONICAL_SYSTEM
|
||||
AC_CONFIG_SRCDIR(memcached.c)
|
||||
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
|
||||
AC_ARG_WITH(libevent,
|
||||
AC_HELP_STRING([--with-libevent=DIRECTORY],[base directory for libevent]))
|
||||
if test "$with_libevent" != "no"; then
|
||||
CFLAGS="$CFLAGS -I$with_libevent/include"
|
||||
LDFLAGS="$LDFLAGS -L$with_libevent/lib"
|
||||
fi
|
||||
|
||||
LIBEVENT_URL=http://www.monkey.org/~provos/libevent/
|
||||
AC_CHECK_LIB(event, event_set, ,
|
||||
[AC_MSG_ERROR(libevent is required. You can get it from $LIBEVENT_URL)])
|
||||
|
||||
AC_CHECK_HEADER(malloc.h, AC_DEFINE(HAVE_MALLOC_H,,[do we have malloc.h?]))
|
||||
AC_CHECK_MEMBER([struct mallinfo.arena], [
|
||||
AC_DEFINE(HAVE_STRUCT_MALLINFO,,[do we have stuct mallinfo?])
|
||||
], ,[
|
||||
# include <malloc.h>
|
||||
]
|
||||
)
|
||||
|
||||
dnl From licq: Copyright (c) 2000 Dirk Mueller
|
||||
dnl Check if the type socklen_t is defined anywhere
|
||||
AC_DEFUN(AC_C_SOCKLEN_T,
|
||||
[AC_CACHE_CHECK(for socklen_t, ac_cv_c_socklen_t,
|
||||
[
|
||||
AC_TRY_COMPILE([
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
],[
|
||||
socklen_t foo;
|
||||
],[
|
||||
ac_cv_c_socklen_t=yes
|
||||
],[
|
||||
ac_cv_c_socklen_t=no
|
||||
])
|
||||
])
|
||||
if test $ac_cv_c_socklen_t = no; then
|
||||
AC_DEFINE(socklen_t, int, [define to int if socklen_t not available])
|
||||
fi
|
||||
])
|
||||
|
||||
AC_C_SOCKLEN_T
|
||||
|
||||
AC_CHECK_FUNCS(mlockall)
|
||||
|
||||
AC_CONFIG_FILES(Makefile doc/Makefile)
|
||||
AC_OUTPUT
|
||||
3
wcmtools/memcached/doc/Makefile.am
Executable file
3
wcmtools/memcached/doc/Makefile.am
Executable file
@@ -0,0 +1,3 @@
|
||||
man_MANS = memcached.1
|
||||
|
||||
EXTRA_DIST = *.txt
|
||||
86
wcmtools/memcached/doc/memcached.1
Executable file
86
wcmtools/memcached/doc/memcached.1
Executable file
@@ -0,0 +1,86 @@
|
||||
.TH MEMCACHED 1 "April 11, 2005"
|
||||
.SH NAME
|
||||
memcached \- high-performance memory object caching system
|
||||
.SH SYNOPSIS
|
||||
.B memcached
|
||||
.RI [ options ]
|
||||
.br
|
||||
.SH DESCRIPTION
|
||||
This manual page documents briefly the
|
||||
.B memcached
|
||||
memory object caching daemon.
|
||||
.PP
|
||||
.B memcached
|
||||
is a flexible memory object caching daemon designed to alleviate database load
|
||||
in dynamic web applications by storing objects in memory. It's based on
|
||||
libevent to scale to any size needed, and is specifically optimized to avoid
|
||||
swapping and always use non-blocking I/O.
|
||||
.br
|
||||
.SH OPTIONS
|
||||
These programs follow the usual GNU command line syntax. A summary of options
|
||||
is included below.
|
||||
.TP
|
||||
.B \-l <ip_addr>
|
||||
Listen on <ip_addr>; default to INDRR_ANY. This is an important option to
|
||||
consider as there is no other way to secure the installation. Binding to an
|
||||
internal or firewalled network interface is suggested.
|
||||
.TP
|
||||
.B \-d
|
||||
Run memcached as a daemon.
|
||||
.TP
|
||||
.B \-u <username>
|
||||
Assume the identity of <username> (only when run as root).
|
||||
.TP
|
||||
.B \-m <num>
|
||||
Use <num> MB memory max to use for object storage; the default is 64 megabytes.
|
||||
.TP
|
||||
.B \-c <num>
|
||||
Use <num> max simultaneous connections; the default is 1024.
|
||||
.TP
|
||||
.B \-k
|
||||
Lock down all paged memory. This is a somewhat dangerous option with large
|
||||
caches, so consult the README and memcached homepage for configuration
|
||||
suggestions.
|
||||
.TP
|
||||
.B \-p <num>
|
||||
Listen on port <num>, the default is port 11211.
|
||||
.TP
|
||||
.B \-M
|
||||
Disable automatic removal of items from the cache when out of memory.
|
||||
Additions will not be possible until adequate space is freed up.
|
||||
.TP
|
||||
.B \-r
|
||||
Raise the core file size limit to the maximum allowable.
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version of memcached and a summary of options.
|
||||
.TP
|
||||
.B \-v
|
||||
Be verbose during the event loop; print out errors and warnings.
|
||||
.TP
|
||||
.B \-vv
|
||||
Be even more verbose; same as \-v but also print client commands and
|
||||
responses.
|
||||
.TP
|
||||
.B \-i
|
||||
Print memcached and libevent licenses.
|
||||
.TP
|
||||
.B \-P <filename>
|
||||
Print pidfile to <filename>, only used under -d option.
|
||||
.br
|
||||
.SH LICENSE
|
||||
The memcached daemon is copyright Danga Interactive and is distributed under
|
||||
the BSD license. Note that daemon clients are licensed separately.
|
||||
.br
|
||||
.SH SEE ALSO
|
||||
The README file that comes with memcached
|
||||
.br
|
||||
.B http://www.danga.com/memcached
|
||||
.SH AUTHOR
|
||||
The memcached daemon was written by Anatoly Vorobey
|
||||
.B <mellon@pobox.com>
|
||||
and Brad Fitzpatrick
|
||||
.B <brad@danga.com>
|
||||
and the rest of the crew of Danga Interactive
|
||||
.B http://www.danga.com
|
||||
.br
|
||||
83
wcmtools/memcached/doc/memory_management.txt
Executable file
83
wcmtools/memcached/doc/memory_management.txt
Executable file
@@ -0,0 +1,83 @@
|
||||
Date: Fri, 5 Sep 2003 20:31:03 +0300
|
||||
From: Anatoly Vorobey <mellon@pobox.com>
|
||||
To: memcached@lists.danga.com
|
||||
Subject: Re: Memory Management...
|
||||
|
||||
On Fri, Sep 05, 2003 at 12:07:48PM -0400, Kyle R. Burton wrote:
|
||||
> prefixing keys with a container identifier). We have just begun to
|
||||
> look at the implementation of the memory management sub-system with
|
||||
> regards to it's allocation, de-allocation and compaction approaches.
|
||||
> Is there any documentation or discussion of how this subsystem
|
||||
> operates? (slabs.c?)
|
||||
|
||||
There's no documentation yet, and it's worth mentioning that this
|
||||
subsystem is the most active area of memcached under development at the
|
||||
moment (however, all the changes to it won't modify the way memcached
|
||||
presents itself towards clients, they're primarily directed at making
|
||||
memcached use memory more efficiently).
|
||||
|
||||
Here's a quick recap of what it does now and what is being worked
|
||||
on.
|
||||
|
||||
The primary goal of the slabs subsystem in memcached was to eliminate
|
||||
memory fragmentation issues totally by using fixed-size memory chunks
|
||||
coming from a few predetermined size classes (early versions of
|
||||
memcached relied on malloc()'s handling of fragmentation which proved
|
||||
woefully inadequate for our purposes). For instance, suppose
|
||||
we decide at the outset that the list of possible sizes is: 64 bytes,
|
||||
128 bytes, 256 bytes, etc. - doubling all the way up to 1Mb. For each
|
||||
size class in this list (each possible size) we maintain a list of free
|
||||
chunks of this size. Whenever a request comes for a particular size,
|
||||
it is rounded up to the closest size class and a free chunk is taken
|
||||
from that size class. In the above example, if you request from the
|
||||
slabs subsystem 100 bytes of memory, you'll actually get a chunk 128
|
||||
bytes worth, from the 128-bytes size class. If there are no free chunks
|
||||
of the needed size at the moment, there are two ways to get one: 1) free
|
||||
an existing chunk in the same size class, using LRU queues to free the
|
||||
least needed objects; 2) get more memory from the system, which we
|
||||
currently always do in _slabs_ of 1Mb each; we malloc() a slab, divide
|
||||
it to chunks of the needed size, and use them.
|
||||
|
||||
The tradeoff is between memory fragmentation and memory utilisation. In
|
||||
the scheme we're now using, we have zero fragmentation, but a relatively
|
||||
high percentage of memory is wasted. The most efficient way to reduce
|
||||
the waste is to use a list of size classes that closely matches (if
|
||||
that's at all possible) common sizes of objects that the clients
|
||||
of this particular installation of memcached are likely to store.
|
||||
For example, if your installation is going to store hundreds of thousands of objects of the size exactly 120 bytes, you'd be much better
|
||||
off changing, in the "naive" list of sizes outlined above, the class
|
||||
of 128 bytes to something a bit higher (because the overhead of
|
||||
storing an item, while not large, will push those 120-bytes objects over
|
||||
128 bytes of storage internally, and will require using 256 bytes for
|
||||
each of them in the naive scheme, forcing you to waste almost 50% of
|
||||
memory). Such tinkering with the list of size classes is not currently
|
||||
possible with memcached, but enabling it is one of the immediate goals.
|
||||
|
||||
Ideally, the slabs subsystem would analyze at runtime the common sizes
|
||||
of objects that are being requested, and would be able to modify the
|
||||
list of sizes dynamically to improve memory utilisation. This is not
|
||||
planned for the immediate future, however. What is planned is the
|
||||
ability to reassign slabs to different classes. Here's what this means.
|
||||
Currently, the total amount of memory allocated for each size class is
|
||||
determined by how clients interact with memcached during the initial
|
||||
phase of its execution, when it keeps malloc()'ing more slabs and
|
||||
dividing them into chunks, until it hits the specified memory limit
|
||||
(say, 2Gb, or whatever else was specified). Once it hits the limit, to
|
||||
allocate a new chunk it'll always delete an existing chunk of the same
|
||||
size (using LRU queues), and will never malloc() or free() any memory
|
||||
from/to the system. So if, for example, during those initial few hours
|
||||
of memcached's execution your clients mainly wanted to store very small
|
||||
items, the bulk of memory allocated will be divided to small-sized
|
||||
chunks, and the large size classes will get fewer memory, therefore the
|
||||
life-cycle of large objects you'll store in memcached will henceforth
|
||||
always be much shorter, with this instance of memcached (their LRU
|
||||
queues will be shorter and they'll be pushed out much more often). In
|
||||
general, if your system starts producing a different pattern of common
|
||||
object sizes, the memcached servers will become less efficient, unless
|
||||
you restart them. Slabs reassignment, which is the next feature being
|
||||
worked on, will ensure the server's ability to reclaim a slab (1Mb of
|
||||
memory) from one size class and put it into another class size, where
|
||||
it's needed more.
|
||||
|
||||
--
|
||||
avva
|
||||
389
wcmtools/memcached/doc/protocol.txt
Executable file
389
wcmtools/memcached/doc/protocol.txt
Executable file
@@ -0,0 +1,389 @@
|
||||
Protocol
|
||||
--------
|
||||
|
||||
Clients of memcached communicate with server through TCP
|
||||
connections. A given running memcached server listens on some
|
||||
(configurable) port; clients connect to that port, send commands to
|
||||
the server, read responses, and eventually close the connection.
|
||||
|
||||
There is no need to send any command to end the session. A client may
|
||||
just close the connection at any moment it no longer needs it. Note,
|
||||
however, that clients are encouraged to cache their connections rather
|
||||
than reopen them every time they need to store or retrieve data. This
|
||||
is because memcached is especially designed to work very efficiently
|
||||
with a very large number (many hundreds, more than a thousand if
|
||||
necessary) of open connections. Caching connections will eliminate the
|
||||
overhead associated with establishing a TCP connection (the overhead
|
||||
of preparing for a new connection on the server side is insignificant
|
||||
compared to this).
|
||||
|
||||
There are two kinds of data sent in the memcache protocol: text lines
|
||||
and unstructured data. Text lines are used for commands from clients
|
||||
and responses from servers. Unstructured data is sent when a client
|
||||
wants to store or retrieve data. The server will transmit back
|
||||
unstructured data in exactly the same way it received it, as a byte
|
||||
stream. The server doesn't care about byte order issues in
|
||||
unstructured data and isn't aware of them. There are no limitations on
|
||||
characters that may appear in unstructured data; however, the reader
|
||||
of such data (either a client or a server) will always know, from a
|
||||
preceding text line, the exact length of the data block being
|
||||
transmitted.
|
||||
|
||||
Text lines are always terminated by \r\n. Unstructured data is _also_
|
||||
terminated by \r\n, even though \r, \n or any other 8-bit characters
|
||||
may also appear inside the data. Therefore, when a client retrieves
|
||||
data from a server, it must use the length of the data block (which it
|
||||
will be provided with) to determine where the data block ends, and not
|
||||
the fact that \r\n follows the end of the data block, even though it
|
||||
does.
|
||||
|
||||
Keys
|
||||
----
|
||||
|
||||
Data stored by memcached is identified with the help of a key. A key
|
||||
is a text string which should uniquely identify the data for clients
|
||||
that are interested in storing and retrieving it. Currently the
|
||||
length limit of a key is set at 250 characters (of course, normally
|
||||
clients wouldn't need to use such long keys); the key must not include
|
||||
control characters or whitespace.
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
There are three types of commands.
|
||||
|
||||
Storage commands (there are three: "set", "add" and "replace") ask the
|
||||
server to store some data identified by a key. The client sends a
|
||||
command line, and then a data block; after that the client expects one
|
||||
line of response, which will indicate success or faulure.
|
||||
|
||||
Retrieval commands (there is only one: "get") ask the server to
|
||||
retrieve data corresponding to a set of keys (one or more keys in one
|
||||
request). The client sends a command line, which includes all the
|
||||
requested keys; after that for each item the server finds it sends to
|
||||
the client one response line with information about the item, and one
|
||||
data block with the item's data; this continues until the server
|
||||
finished with the "END" response line.
|
||||
|
||||
All other commands don't involve unstructured data. In all of them,
|
||||
the client sends one command line, and expects (depending on the
|
||||
command) either one line of response, or several lines of response
|
||||
ending with "END" on the last line.
|
||||
|
||||
A command line always starts with the name of the command, followed by
|
||||
parameters (if any) delimited by whitespace. Command names are
|
||||
lower-case and are case-sensitive.
|
||||
|
||||
Expiration times
|
||||
----------------
|
||||
|
||||
Some commands involve a client sending some kind of expiration time
|
||||
(relative to an item or to an operation requested by the client) to
|
||||
the server. In all such cases, the actual value sent may either be
|
||||
Unix time (number of seconds since January 1, 1970, as a 32-bit
|
||||
value), or a number of seconds starting from current time. In the
|
||||
latter case, this number of seconds may not exceed 60*60*24*30 (number
|
||||
of seconds in 30 days); if the number sent by a client is larger than
|
||||
that, the server will consider it to be real Unix time value rather
|
||||
than an offset from current time.
|
||||
|
||||
|
||||
Error strings
|
||||
-------------
|
||||
|
||||
Each command sent by a client may be answered with an error string
|
||||
from the server. These error strings come in three types:
|
||||
|
||||
- "ERROR\r\n"
|
||||
|
||||
means the client sent a nonexistent command name.
|
||||
|
||||
- "CLIENT_ERROR <error>\r\n"
|
||||
|
||||
means some sort of client error in the input line, i.e. the input
|
||||
doesn't conform to the protocol in some way. <error> is a
|
||||
human-readable error string.
|
||||
|
||||
- "SERVER_ERROR <error>\r\n"
|
||||
|
||||
means some sort of server error prevents the server from carrying
|
||||
out the command. <error> is a human-readable error string. In cases
|
||||
of severe server errors, which make it impossible to continue
|
||||
serving the client (this shouldn't normally happen), the server will
|
||||
close the connection after sending the error line. This is the only
|
||||
case in which the server closes a connection to a client.
|
||||
|
||||
|
||||
In the descriptions of individual commands below, these error lines
|
||||
are not again specifically mentioned, but clients must allow for their
|
||||
possibility.
|
||||
|
||||
|
||||
Storage commands
|
||||
----------------
|
||||
|
||||
First, the client sends a command line which looks like this:
|
||||
|
||||
<command name> <key> <flags> <exptime> <bytes>\r\n
|
||||
|
||||
- <command name> is "set", "add" or "replace"
|
||||
|
||||
"set" means "store this data".
|
||||
|
||||
"add" means "store this data, but only if the server *doesn't* already
|
||||
hold data for this key".
|
||||
|
||||
"replace" means "store this data, but only if the server *does*
|
||||
already hold data for this key".
|
||||
|
||||
- <key> is the key under which the client asks to store the data
|
||||
|
||||
- <flags> is an arbitrary 16-bit unsigned integer (written out in
|
||||
decimal) that the server stores along with the data and sends back
|
||||
when the item is retrieved. Clients may use this as a bit field to
|
||||
store data-specific information; this field is opaque to the server.
|
||||
|
||||
- <exptime> is expiration time. If it's 0, the item never expires
|
||||
(although it may be deleted from the cache to make place for other
|
||||
items). If it's non-zero (either Unix time or offset in seconds from
|
||||
current time), it is guaranteed that clients will not be able to
|
||||
retrieve this item after the expiration time arrives (measured by
|
||||
server time).
|
||||
|
||||
- <bytes> is the number of bytes in the data block to follow, *not*
|
||||
including the delimiting \r\n. <bytes> may be zero (in which case
|
||||
it's followed by an empty data block).
|
||||
|
||||
After this line, the client sends the data block:
|
||||
|
||||
<data block>\r\n
|
||||
|
||||
- <data block> is a chunk of arbitrary 8-bit data of length <bytes>
|
||||
from the previous line.
|
||||
|
||||
After sending the command line and the data blockm the client awaits
|
||||
the reply, which may be:
|
||||
|
||||
- "STORED\r\n", to indicate success.
|
||||
|
||||
- "NOT_STORED\r\n" to indicate the data was not stored, but not
|
||||
because of an error. This normally means that either that the
|
||||
condition for an "add" or a "replace" command wasn't met, or that the
|
||||
item is in a delete queue (see the "delete" command below).
|
||||
|
||||
|
||||
Retrieval command:
|
||||
------------------
|
||||
|
||||
The retrieval command looks like this:
|
||||
|
||||
get <key>*\r\n
|
||||
|
||||
- <key>* means one or more key strings separated by whitespace.
|
||||
|
||||
After this command, the client expects zero or more items, each of
|
||||
which is received as a text line followed by a data block. After all
|
||||
the items have been transmitted, the server sends the string
|
||||
|
||||
"END\r\n"
|
||||
|
||||
to indicate the end of response.
|
||||
|
||||
Each item sent by the server looks like this:
|
||||
|
||||
VALUE <key> <flags> <bytes>\r\n
|
||||
<data block>\r\n
|
||||
|
||||
- <key> is the key for the item being sent
|
||||
|
||||
- <flags> is the flags value set by the storage command
|
||||
|
||||
- <bytes> is the length of the data block to follow, *not* including
|
||||
its delimiting \r\n
|
||||
|
||||
- <data block> is the data for this item.
|
||||
|
||||
If some of the keys appearing in a retrieval request are not sent back
|
||||
by the server in the item list this means that the server does not
|
||||
hold items with such keys (because they were never stored, or stored
|
||||
but deleted to make space for more items, or expired, or explicitly
|
||||
deleted by a client).
|
||||
|
||||
|
||||
|
||||
Deletion
|
||||
--------
|
||||
|
||||
The command "delete" allows for explicit deletion of items:
|
||||
|
||||
delete <key> <time>\r\n
|
||||
|
||||
- <key> is the key of the item the client wishes the server to delete
|
||||
|
||||
- <time> is the amount of time in seconds (or Unix time until which)
|
||||
the client wishes the server to refuse "add" and "replace" commands
|
||||
with this key. For this amount of item, the item is put into a
|
||||
delete queue, which means that it won't possible to retrieve it by
|
||||
the "get" command, but "add" and "replace" command with this key
|
||||
will also fail (the "set" command will succeed, however). After the
|
||||
time passes, the item is finally deleted from server memory.
|
||||
|
||||
The parameter <time> is optional, and, if absent, defaults to 0
|
||||
(which means that the item will be deleted immediately and further
|
||||
storage commands with this key will succeed).
|
||||
|
||||
The response line to this command can be one of:
|
||||
|
||||
- "DELETED\r\n" to indicate success
|
||||
|
||||
- "NOT_FOUND\r\n" to indicate that the item with this key was not
|
||||
found.
|
||||
|
||||
See the "flush_all" command below for immediate invalidation
|
||||
of all existing items.
|
||||
|
||||
|
||||
Increment/Decrement
|
||||
-------------------
|
||||
|
||||
Commands "incr" and "decr" are used to change data for some item
|
||||
in-place, incrementing or decrementing it. The data for the item is
|
||||
treated as decimal representation of a 32-bit unsigned integer. If the
|
||||
current data value does not conform to such a representation, the
|
||||
commands behave as if the value were 0. Also, the item must already
|
||||
exist for incr/decr to work; these commands won't pretend that a
|
||||
non-existent key exists with value 0; instead, they will fail.
|
||||
|
||||
The client sends the command line:
|
||||
|
||||
incr <key> <value>\r\n
|
||||
|
||||
or
|
||||
|
||||
decr <key> <value>\r\n
|
||||
|
||||
- <key> is the key of the item the client wishes to change
|
||||
|
||||
- <value> is the amount by which the client wants to increase/decrease
|
||||
the item. It is a decimal representation of a 32-bit unsigned integer.
|
||||
|
||||
The response will be one of:
|
||||
|
||||
- "NOT_FOUND\r\n" to indicate the item with this value was not found
|
||||
|
||||
- <value>\r\n , where <value> is the new value of the item's data,
|
||||
after the increment/decrement operation was carried out.
|
||||
|
||||
Note that underflow in the "decr" command is caught: if a client tries
|
||||
to decrease the value below 0, the new value will be 0. Overflow in
|
||||
the "incr" command is not checked.
|
||||
|
||||
|
||||
Statistics
|
||||
----------
|
||||
|
||||
The command "stats" is used to query the server about statistics it
|
||||
maintains and other internal data. It has two forms. Without
|
||||
arguments:
|
||||
|
||||
stats\r\n
|
||||
|
||||
it causes the server to output general-purpose statistics and
|
||||
settings, documented below. In the other form it has some arguments:
|
||||
|
||||
stats <args>\r\n
|
||||
|
||||
Depending on <args>, various internal data is sent by the server. The
|
||||
kinds of arguments and the data sent are not documented in this vesion
|
||||
of the protocol, and are subject to change for the convenience of
|
||||
memcache developers.
|
||||
|
||||
|
||||
General-purpose statistics
|
||||
--------------------------
|
||||
|
||||
Upon receiving the "stats" command without arguments, the server sents
|
||||
a number of lines which look like this:
|
||||
|
||||
STAT <name> <value>\r\n
|
||||
|
||||
The server terminates this list with the line
|
||||
|
||||
END\r\n
|
||||
|
||||
In each line of statistics, <name> is the name of this statistic, and
|
||||
<value> is the data. The following is the list of all names sent in
|
||||
response to the "stats" command, together with the type of the value
|
||||
sent for this name, and the meaning of the value.
|
||||
|
||||
In the type column below, "32u" means a 32-bit unsigned integer, "64u"
|
||||
means a 64-bit unsigner integer. '32u:32u' means two 32-but unsigned
|
||||
integers separated by a colon.
|
||||
|
||||
|
||||
Name Type Meaning
|
||||
----------------------------------
|
||||
pid 32u Process id of this server process
|
||||
uptime 32u Number of seconds this server has been running
|
||||
time 32u current UNIX time according to the server
|
||||
version string Version string of this server
|
||||
rusage_user 32u:32u Accumulated user time for this process
|
||||
(seconds:microseconds)
|
||||
rusage_system 32u:32u Accumulated system time for this process
|
||||
(seconds:microseconds)
|
||||
curr_items 32u Current number of items stored by the server
|
||||
total_items 32u Total number of items stored by this server
|
||||
ever since it started
|
||||
bytes 64u Current number of bytes used by this server
|
||||
to store items
|
||||
curr_connections 32u Number of open connections
|
||||
total_connections 32u Total number of connections opened since
|
||||
the server started running
|
||||
connection_structures 32u Number of connection structures allocated
|
||||
by the server
|
||||
cmd_get 32u Cumulative number of retrieval requests
|
||||
cmd_set 32u Cumulative number of storage requests
|
||||
get_hits 32u Number of keys that have been requested and
|
||||
found present
|
||||
get_misses 32u Number of items that have been requested
|
||||
and not found
|
||||
bytes_read 64u Total number of bytes read by this server
|
||||
from network
|
||||
bytes_written 64u Total number of bytes sent by this server to
|
||||
network
|
||||
limit_maxbytes 32u Number of bytes this server is allowed to
|
||||
use for storage.
|
||||
|
||||
|
||||
|
||||
Other commands
|
||||
--------------
|
||||
|
||||
"flush_all" is a command with no arguments. It always succeeds,
|
||||
and the server sends "OK\r\n" in response. Its effect is to immediately
|
||||
invalidate all existing items: none of them will be returned in
|
||||
response to a retrieval command (unless it's stored again under the
|
||||
same key *after* flush_all has been executed). flush_all doesn't
|
||||
actually free all the memory taken up by existing items; that will
|
||||
happen gradually as new items are stored. The most precise definition
|
||||
of what flush_all does is the following: it causes all items whose
|
||||
update time is earlier than the time at which flush_all was executed
|
||||
to be ignored for retrieval purposes.
|
||||
|
||||
"version" is a command with no arguments:
|
||||
|
||||
version\r\n
|
||||
|
||||
In response, the server sends
|
||||
|
||||
"VERSION <version>\r\n", where <version> is the version string for the
|
||||
server.
|
||||
|
||||
|
||||
"quit" is a command with no arguments:
|
||||
|
||||
quit\r\n
|
||||
|
||||
Upon receiving this command, the server closes the
|
||||
connection. However, the client may also simply close the connection
|
||||
when it no longer needs it, without issuing this command.
|
||||
2
wcmtools/memcached/frontends/gtk2-perl/README
Executable file
2
wcmtools/memcached/frontends/gtk2-perl/README
Executable file
@@ -0,0 +1,2 @@
|
||||
By Evan Martin <martine@danga.com>
|
||||
|
||||
121
wcmtools/memcached/frontends/gtk2-perl/memcachedclient
Executable file
121
wcmtools/memcached/frontends/gtk2-perl/memcachedclient
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/perl
|
||||
# vim: set ts=4 sw=4 et :
|
||||
|
||||
use strict;
|
||||
use Gtk2;
|
||||
use Gtk2::Gdk::Keysyms;
|
||||
use Cache::Memcached;
|
||||
use Data::Dumper;
|
||||
|
||||
$Data::Dumper::Terse = 1;
|
||||
|
||||
my @cmds = ();
|
||||
my $cmd_cur = -1;
|
||||
|
||||
my $mc = new Cache::Memcached {
|
||||
'servers' => [ "127.0.0.1:11211" ]
|
||||
};
|
||||
|
||||
Gtk2->init;
|
||||
my $win = Gtk2::Window->new("toplevel");
|
||||
$win->signal_connect(delete_event => sub { Gtk2->main_quit });
|
||||
$win->set_border_width(10);
|
||||
|
||||
my $vb = Gtk2::VBox->new(0, 5);
|
||||
my $display = Gtk2::TextView->new;
|
||||
my $buffer = $display->get_buffer;
|
||||
$display->set_editable(0);
|
||||
my $scroll = Gtk2::ScrolledWindow->new;
|
||||
$scroll->set_policy('automatic', 'automatic');
|
||||
$scroll->set_shadow_type('in');
|
||||
$scroll->add($display);
|
||||
$vb->pack_start($scroll, 1, 1, 0);
|
||||
|
||||
$display->modify_font(Gtk2::Pango::FontDescription->from_string("monospace"));
|
||||
$buffer->create_tag("command", "foreground", "blue");
|
||||
$buffer->create_tag("data", "foreground", "black");
|
||||
$buffer->create_tag("error", "foreground", "red");
|
||||
|
||||
my $entry = Gtk2::Entry->new();
|
||||
$entry->signal_connect(key_press_event => \&entry_keypress);
|
||||
$entry->signal_connect(activate => \&entry_activate);
|
||||
$vb->pack_start($entry, 0, 0, 0);
|
||||
|
||||
$win->add($vb);
|
||||
|
||||
$win->set_title("MemCachedClient");
|
||||
$win->set_default_size(400, 500);
|
||||
$win->signal_connect(show => sub { $entry->grab_focus });
|
||||
$win->show_all;
|
||||
|
||||
Gtk2->main;
|
||||
|
||||
sub display {
|
||||
my ($level, $text) = @_;
|
||||
$buffer->insert_with_tags_by_name($buffer->get_end_iter, "$text\n", $level);
|
||||
}
|
||||
|
||||
sub run_command {
|
||||
my ($text) = @_;
|
||||
|
||||
# if we're rerunning a history command, then
|
||||
# we should pull it out of its old spot in the history.
|
||||
splice(@cmds, $cmd_cur, 1)
|
||||
if $cmd_cur >= 0 and $cmds[$cmd_cur] eq $text;
|
||||
# and in any case, add this command to the history.
|
||||
unshift(@cmds, $text);
|
||||
$cmd_cur = -1;
|
||||
|
||||
display('command', $text);
|
||||
if ($text =~ /^get\s+(\S+)$/i) {
|
||||
my $str = $mc->get($1);
|
||||
if (ref $str) {
|
||||
$str = Dumper($str);
|
||||
$str =~ s/^ //gm;
|
||||
}
|
||||
if ($str) {
|
||||
display('data', $str);
|
||||
} else {
|
||||
display('error', "Not found.");
|
||||
}
|
||||
} elsif ($text =~ /^set\s+(\S+)\s+(.*)$/i) {
|
||||
my ($key, $val) = ($1, $2);
|
||||
if ($mc->set($key, $val)) {
|
||||
display('data', "Ok.");
|
||||
} else {
|
||||
display('error', "Not found.");
|
||||
}
|
||||
} elsif ($text =~ /^delete\s+(\S+)$/i) {
|
||||
$mc->delete($1);
|
||||
display('data', "Ok.");
|
||||
} else {
|
||||
display('error', "Unknown command '$text'.");
|
||||
}
|
||||
}
|
||||
|
||||
sub entry_keypress {
|
||||
my ($entry, $ev) = @_;
|
||||
if ($ev->keyval == $Gtk2::Gdk::Keysyms{'Up'}) {
|
||||
$cmd_cur++ if $cmd_cur < @cmds-1;
|
||||
$entry->set_text($cmds[$cmd_cur]) if $cmds[$cmd_cur];
|
||||
return 1;
|
||||
} elsif ($ev->keyval == $Gtk2::Gdk::Keysyms{'Down'}) {
|
||||
$cmd_cur-- if $cmd_cur >= 0;
|
||||
if ($cmd_cur >= 0 and $cmds[$cmd_cur]) {
|
||||
$entry->set_text($cmds[$cmd_cur]);
|
||||
} else {
|
||||
$entry->set_text('');
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub entry_activate {
|
||||
my $text = $entry->get_text;
|
||||
if ($text =~ /\w/) {
|
||||
run_command($entry->get_text);
|
||||
$entry->set_text("");
|
||||
}
|
||||
}
|
||||
|
||||
299
wcmtools/memcached/items.c
Executable file
299
wcmtools/memcached/items.c
Executable file
@@ -0,0 +1,299 @@
|
||||
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/* $Id: items.c,v 1.23 2004/09/13 22:31:53 avva Exp $ */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/resource.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <event.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "memcached.h"
|
||||
|
||||
|
||||
/*
|
||||
* NOTE: we assume here for simplicity that slab ids are <=32. That's true in
|
||||
* the powers-of-2 implementation, but if that changes this should be changed too
|
||||
*/
|
||||
|
||||
#define LARGEST_ID 32
|
||||
static item *heads[LARGEST_ID];
|
||||
static item *tails[LARGEST_ID];
|
||||
unsigned int sizes[LARGEST_ID];
|
||||
|
||||
void item_init(void) {
|
||||
int i;
|
||||
for(i=0; i<LARGEST_ID; i++) {
|
||||
heads[i]=0;
|
||||
tails[i]=0;
|
||||
sizes[i]=0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
item *item_alloc(char *key, int flags, time_t exptime, int nbytes) {
|
||||
int ntotal, len;
|
||||
item *it;
|
||||
unsigned int id;
|
||||
|
||||
len = strlen(key) + 1; if(len % 4) len += 4 - (len % 4);
|
||||
ntotal = sizeof(item) + len + nbytes;
|
||||
|
||||
id = slabs_clsid(ntotal);
|
||||
if (id == 0)
|
||||
return 0;
|
||||
|
||||
it = slabs_alloc(ntotal);
|
||||
if (it == 0) {
|
||||
int tries = 50;
|
||||
item *search;
|
||||
|
||||
/* If requested to not push old items out of cache when memory runs out,
|
||||
* we're out of luck at this point...
|
||||
*/
|
||||
|
||||
if (!settings.evict_to_free) return 0;
|
||||
|
||||
/*
|
||||
* try to get one off the right LRU
|
||||
* don't necessariuly unlink the tail because it may be locked: refcount>0
|
||||
* search up from tail an item with refcount==0 and unlink it; give up after 50
|
||||
* tries
|
||||
*/
|
||||
|
||||
if (id > LARGEST_ID) return 0;
|
||||
if (tails[id]==0) return 0;
|
||||
|
||||
for (search = tails[id]; tries>0 && search; tries--, search=search->prev) {
|
||||
if (search->refcount==0) {
|
||||
item_unlink(search);
|
||||
break;
|
||||
}
|
||||
}
|
||||
it = slabs_alloc(ntotal);
|
||||
if (it==0) return 0;
|
||||
}
|
||||
|
||||
assert(it->slabs_clsid == 0);
|
||||
|
||||
it->slabs_clsid = id;
|
||||
|
||||
assert(it != heads[it->slabs_clsid]);
|
||||
|
||||
it->next = it->prev = it->h_next = 0;
|
||||
it->refcount = 0;
|
||||
it->it_flags = 0;
|
||||
it->nkey = len;
|
||||
it->nbytes = nbytes;
|
||||
strcpy(ITEM_key(it), key);
|
||||
it->exptime = exptime;
|
||||
it->flags = flags;
|
||||
return it;
|
||||
}
|
||||
|
||||
void item_free(item *it) {
|
||||
unsigned int ntotal = ITEM_ntotal(it);
|
||||
assert((it->it_flags & ITEM_LINKED) == 0);
|
||||
assert(it != heads[it->slabs_clsid]);
|
||||
assert(it != tails[it->slabs_clsid]);
|
||||
assert(it->refcount == 0);
|
||||
|
||||
/* so slab size changer can tell later if item is already free or not */
|
||||
it->slabs_clsid = 0;
|
||||
it->it_flags |= ITEM_SLABBED;
|
||||
slabs_free(it, ntotal);
|
||||
}
|
||||
|
||||
void item_link_q(item *it) { /* item is the new head */
|
||||
item **head, **tail;
|
||||
assert(it->slabs_clsid <= LARGEST_ID);
|
||||
assert((it->it_flags & ITEM_SLABBED) == 0);
|
||||
|
||||
head = &heads[it->slabs_clsid];
|
||||
tail = &tails[it->slabs_clsid];
|
||||
assert(it != *head);
|
||||
assert((*head && *tail) || (*head == 0 && *tail == 0));
|
||||
it->prev = 0;
|
||||
it->next = *head;
|
||||
if (it->next) it->next->prev = it;
|
||||
*head = it;
|
||||
if (*tail == 0) *tail = it;
|
||||
sizes[it->slabs_clsid]++;
|
||||
return;
|
||||
}
|
||||
|
||||
void item_unlink_q(item *it) {
|
||||
item **head, **tail;
|
||||
assert(it->slabs_clsid <= LARGEST_ID);
|
||||
head = &heads[it->slabs_clsid];
|
||||
tail = &tails[it->slabs_clsid];
|
||||
|
||||
if (*head == it) {
|
||||
assert(it->prev == 0);
|
||||
*head = it->next;
|
||||
}
|
||||
if (*tail == it) {
|
||||
assert(it->next == 0);
|
||||
*tail = it->prev;
|
||||
}
|
||||
assert(it->next != it);
|
||||
assert(it->prev != it);
|
||||
|
||||
if (it->next) it->next->prev = it->prev;
|
||||
if (it->prev) it->prev->next = it->next;
|
||||
sizes[it->slabs_clsid]--;
|
||||
return;
|
||||
}
|
||||
|
||||
int item_link(item *it) {
|
||||
assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
|
||||
assert(it->nbytes < 1048576);
|
||||
it->it_flags |= ITEM_LINKED;
|
||||
it->time = time(0);
|
||||
assoc_insert(ITEM_key(it), it);
|
||||
|
||||
stats.curr_bytes += ITEM_ntotal(it);
|
||||
stats.curr_items += 1;
|
||||
stats.total_items += 1;
|
||||
|
||||
item_link_q(it);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void item_unlink(item *it) {
|
||||
if (it->it_flags & ITEM_LINKED) {
|
||||
it->it_flags &= ~ITEM_LINKED;
|
||||
stats.curr_bytes -= ITEM_ntotal(it);
|
||||
stats.curr_items -= 1;
|
||||
assoc_delete(ITEM_key(it));
|
||||
item_unlink_q(it);
|
||||
}
|
||||
if (it->refcount == 0) item_free(it);
|
||||
}
|
||||
|
||||
void item_remove(item *it) {
|
||||
assert((it->it_flags & ITEM_SLABBED) == 0);
|
||||
if (it->refcount) it->refcount--;
|
||||
assert((it->it_flags & ITEM_DELETED) == 0 || it->refcount);
|
||||
if (it->refcount == 0 && (it->it_flags & ITEM_LINKED) == 0) {
|
||||
item_free(it);
|
||||
}
|
||||
}
|
||||
|
||||
void item_update(item *it) {
|
||||
assert((it->it_flags & ITEM_SLABBED) == 0);
|
||||
|
||||
item_unlink_q(it);
|
||||
it->time = time(0);
|
||||
item_link_q(it);
|
||||
}
|
||||
|
||||
int item_replace(item *it, item *new_it) {
|
||||
assert((it->it_flags & ITEM_SLABBED) == 0);
|
||||
|
||||
item_unlink(it);
|
||||
return item_link(new_it);
|
||||
}
|
||||
|
||||
char *item_cachedump(unsigned int slabs_clsid, unsigned int limit, unsigned int *bytes) {
|
||||
|
||||
int memlimit = 2*1024*1024;
|
||||
char *buffer;
|
||||
int bufcurr;
|
||||
item *it;
|
||||
int len;
|
||||
int shown = 0;
|
||||
char temp[512];
|
||||
|
||||
if (slabs_clsid > LARGEST_ID) return 0;
|
||||
it = heads[slabs_clsid];
|
||||
|
||||
buffer = malloc(memlimit);
|
||||
if (buffer == 0) return 0;
|
||||
bufcurr = 0;
|
||||
|
||||
while (it && (!limit || shown < limit)) {
|
||||
len = sprintf(temp, "ITEM %s [%u b; %lu s]\r\n", ITEM_key(it), it->nbytes - 2, it->time);
|
||||
if (bufcurr + len + 6 > memlimit) /* 6 is END\r\n\0 */
|
||||
break;
|
||||
strcpy(buffer + bufcurr, temp);
|
||||
bufcurr+=len;
|
||||
shown++;
|
||||
it = it->next;
|
||||
}
|
||||
|
||||
strcpy(buffer+bufcurr, "END\r\n");
|
||||
bufcurr+=5;
|
||||
|
||||
*bytes = bufcurr;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void item_stats(char *buffer, int buflen) {
|
||||
int i;
|
||||
char *bufcurr = buffer;
|
||||
time_t now = time(0);
|
||||
|
||||
if (buflen < 4096) {
|
||||
strcpy(buffer, "SERVER_ERROR out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0; i<LARGEST_ID; i++) {
|
||||
if (tails[i])
|
||||
bufcurr += sprintf(bufcurr, "STAT items:%u:number %u\r\nSTAT items:%u:age %lu\r\n",
|
||||
i, sizes[i], i, now - tails[i]->time);
|
||||
}
|
||||
strcpy(bufcurr, "END");
|
||||
return;
|
||||
}
|
||||
|
||||
/* dumps out a list of objects of each size, with granularity of 32 bytes */
|
||||
char* item_stats_sizes(int *bytes) {
|
||||
int num_buckets = 32768; /* max 1MB object, divided into 32 bytes size buckets */
|
||||
unsigned int *histogram = (int*) malloc(num_buckets * sizeof(int));
|
||||
char *buf = (char*) malloc(1024*1024*2*sizeof(char));
|
||||
int i;
|
||||
|
||||
if (histogram == 0 || buf == 0) {
|
||||
if (histogram) free(histogram);
|
||||
if (buf) free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* build the histogram */
|
||||
memset(histogram, 0, num_buckets * sizeof(int));
|
||||
for (i=0; i<LARGEST_ID; i++) {
|
||||
item *iter = heads[i];
|
||||
while (iter) {
|
||||
int ntotal = ITEM_ntotal(iter);
|
||||
int bucket = ntotal / 32;
|
||||
if (ntotal % 32) bucket++;
|
||||
if (bucket < num_buckets) histogram[bucket]++;
|
||||
iter = iter->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* write the buffer */
|
||||
*bytes = 0;
|
||||
for (i=0; i<num_buckets; i++) {
|
||||
if (histogram[i]) {
|
||||
*bytes += sprintf(&buf[*bytes], "%u %u\r\n", i*32, histogram[i]);
|
||||
}
|
||||
}
|
||||
*bytes += sprintf(&buf[*bytes], "END\r\n");
|
||||
|
||||
free(histogram);
|
||||
return buf;
|
||||
}
|
||||
1554
wcmtools/memcached/memcached.c
Executable file
1554
wcmtools/memcached/memcached.c
Executable file
File diff suppressed because it is too large
Load Diff
212
wcmtools/memcached/memcached.h
Executable file
212
wcmtools/memcached/memcached.h
Executable file
@@ -0,0 +1,212 @@
|
||||
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/* $Id: memcached.h,v 1.21 2004/02/24 23:42:02 bradfitz Exp $ */
|
||||
|
||||
#define DATA_BUFFER_SIZE 2048
|
||||
|
||||
#if defined(TCP_CORK) && !defined(TCP_NOPUSH)
|
||||
#define TCP_NOPUSH TCP_CORK
|
||||
#endif
|
||||
|
||||
struct stats {
|
||||
unsigned int curr_items;
|
||||
unsigned int total_items;
|
||||
unsigned long long curr_bytes;
|
||||
unsigned int curr_conns;
|
||||
unsigned int total_conns;
|
||||
unsigned int conn_structs;
|
||||
unsigned int get_cmds;
|
||||
unsigned int set_cmds;
|
||||
unsigned int get_hits;
|
||||
unsigned int get_misses;
|
||||
time_t started; /* when the process was started */
|
||||
unsigned long long bytes_read;
|
||||
unsigned long long bytes_written;
|
||||
};
|
||||
|
||||
struct settings {
|
||||
unsigned int maxbytes;
|
||||
int maxconns;
|
||||
int port;
|
||||
struct in_addr interface;
|
||||
int verbose;
|
||||
time_t oldest_live; /* ignore existing items older than this */
|
||||
int evict_to_free;
|
||||
};
|
||||
|
||||
extern struct stats stats;
|
||||
extern struct settings settings;
|
||||
|
||||
#define ITEM_LINKED 1
|
||||
#define ITEM_DELETED 2
|
||||
|
||||
/* temp */
|
||||
#define ITEM_SLABBED 4
|
||||
|
||||
typedef struct _stritem {
|
||||
struct _stritem *next;
|
||||
struct _stritem *prev;
|
||||
struct _stritem *h_next; /* hash chain next */
|
||||
unsigned short refcount;
|
||||
unsigned short flags;
|
||||
int nbytes; /* size of data */
|
||||
time_t time; /* least recent access */
|
||||
time_t exptime; /* expire time */
|
||||
unsigned char it_flags; /* ITEM_* above */
|
||||
unsigned char slabs_clsid;
|
||||
unsigned char nkey; /* key length, with terminating null and padding */
|
||||
unsigned char dummy1;
|
||||
void * end[0];
|
||||
} item;
|
||||
|
||||
#define ITEM_key(item) ((char*)&((item)->end[0]))
|
||||
|
||||
/* warning: don't use these macros with a function, as it evals its arg twice */
|
||||
#define ITEM_data(item) ((char*) &((item)->end[0]) + (item)->nkey)
|
||||
#define ITEM_ntotal(item) (sizeof(struct _stritem) + (item)->nkey + (item)->nbytes)
|
||||
|
||||
enum conn_states {
|
||||
conn_listening, /* the socket which listens for connections */
|
||||
conn_read, /* reading in a command line */
|
||||
conn_write, /* writing out a simple response */
|
||||
conn_nread, /* reading in a fixed number of bytes */
|
||||
conn_swallow, /* swallowing unnecessary bytes w/o storing */
|
||||
conn_closing, /* closing this connection */
|
||||
conn_mwrite /* writing out many items sequentially */
|
||||
};
|
||||
|
||||
#define NREAD_ADD 1
|
||||
#define NREAD_SET 2
|
||||
#define NREAD_REPLACE 3
|
||||
|
||||
typedef struct {
|
||||
int sfd;
|
||||
int state;
|
||||
struct event event;
|
||||
short ev_flags;
|
||||
short which; /* which events were just triggered */
|
||||
|
||||
char *rbuf;
|
||||
int rsize;
|
||||
int rbytes;
|
||||
|
||||
char *wbuf;
|
||||
char *wcurr;
|
||||
int wsize;
|
||||
int wbytes;
|
||||
int write_and_go; /* which state to go into after finishing current write */
|
||||
void *write_and_free; /* free this memory after finishing writing */
|
||||
char is_corked; /* boolean, connection is corked */
|
||||
|
||||
char *rcurr;
|
||||
int rlbytes;
|
||||
|
||||
/* data for the nread state */
|
||||
|
||||
/*
|
||||
* item is used to hold an item structure created after reading the command
|
||||
* line of set/add/replace commands, but before we finished reading the actual
|
||||
* data. The data is read into ITEM_data(item) to avoid extra copying.
|
||||
*/
|
||||
|
||||
void *item; /* for commands set/add/replace */
|
||||
int item_comm; /* which one is it: set/add/replace */
|
||||
|
||||
/* data for the swallow state */
|
||||
int sbytes; /* how many bytes to swallow */
|
||||
|
||||
/* data for the mwrite state */
|
||||
item **ilist; /* list of items to write out */
|
||||
int isize;
|
||||
item **icurr;
|
||||
int ileft;
|
||||
int ipart; /* 1 if we're writing a VALUE line, 2 if we're writing data */
|
||||
char ibuf[300]; /* for VALUE lines */
|
||||
char *iptr;
|
||||
int ibytes;
|
||||
|
||||
} conn;
|
||||
|
||||
/* listening socket */
|
||||
extern int l_socket;
|
||||
|
||||
/* temporary hack */
|
||||
/* #define assert(x) if(!(x)) { printf("assert failure: %s\n", #x); pre_gdb(); }
|
||||
void pre_gdb (); */
|
||||
|
||||
/*
|
||||
* Functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* given time value that's either unix time or delta from current unix time, return
|
||||
* unix time. Use the fact that delta can't exceed one month (and real time value can't
|
||||
* be that low).
|
||||
*/
|
||||
|
||||
time_t realtime(time_t exptime);
|
||||
|
||||
/* slabs memory allocation */
|
||||
|
||||
/* Init the subsystem. The argument is the limit on no. of bytes to allocate, 0 if no limit */
|
||||
void slabs_init(unsigned int limit);
|
||||
|
||||
/* Given object size, return id to use when allocating/freeing memory for object */
|
||||
/* 0 means error: can't store such a large object */
|
||||
unsigned int slabs_clsid(unsigned int size);
|
||||
|
||||
/* Allocate object of given length. 0 on error */
|
||||
void *slabs_alloc(unsigned int size);
|
||||
|
||||
/* Free previously allocated object */
|
||||
void slabs_free(void *ptr, unsigned int size);
|
||||
|
||||
/* Fill buffer with stats */
|
||||
char* slabs_stats(int *buflen);
|
||||
|
||||
/* Request some slab be moved between classes
|
||||
1 = success
|
||||
0 = fail
|
||||
-1 = tried. busy. send again shortly. */
|
||||
int slabs_reassign(unsigned char srcid, unsigned char dstid);
|
||||
|
||||
/* event handling, network IO */
|
||||
void event_handler(int fd, short which, void *arg);
|
||||
conn *conn_new(int sfd, int init_state, int event_flags);
|
||||
void conn_close(conn *c);
|
||||
void conn_init(void);
|
||||
void drive_machine(conn *c);
|
||||
int new_socket(void);
|
||||
int server_socket(int port);
|
||||
int update_event(conn *c, int new_flags);
|
||||
int try_read_command(conn *c);
|
||||
int try_read_network(conn *c);
|
||||
void complete_nread(conn *c);
|
||||
void process_command(conn *c, char *command);
|
||||
|
||||
/* stats */
|
||||
void stats_reset(void);
|
||||
void stats_init(void);
|
||||
|
||||
/* defaults */
|
||||
void settings_init(void);
|
||||
|
||||
/* associative array */
|
||||
void assoc_init(void);
|
||||
item *assoc_find(char *key);
|
||||
int assoc_insert(char *key, item *item);
|
||||
void assoc_delete(char *key);
|
||||
|
||||
|
||||
void item_init(void);
|
||||
item *item_alloc(char *key, int flags, time_t exptime, int nbytes);
|
||||
void item_free(item *it);
|
||||
|
||||
int item_link(item *it); /* may fail if transgresses limits */
|
||||
void item_unlink(item *it);
|
||||
void item_remove(item *it);
|
||||
|
||||
void item_update(item *it); /* update LRU time to current and reposition */
|
||||
int item_replace(item *it, item *new_it);
|
||||
char *item_cachedump(unsigned int slabs_clsid, unsigned int limit, unsigned int *bytes);
|
||||
char *item_stats_sizes(int *bytes);
|
||||
void item_stats(char *buffer, int buflen);
|
||||
59
wcmtools/memcached/scripts/memcached-init
Executable file
59
wcmtools/memcached/scripts/memcached-init
Executable file
@@ -0,0 +1,59 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# skeleton example file to build /etc/init.d/ scripts.
|
||||
# This file should be used to construct scripts for /etc/init.d.
|
||||
#
|
||||
# Written by Miquel van Smoorenburg <miquels@cistron.nl>.
|
||||
# Modified for Debian
|
||||
# by Ian Murdock <imurdock@gnu.ai.mit.edu>.
|
||||
#
|
||||
# Version: @(#)skeleton 1.9 26-Feb-2001 miquels@cistron.nl
|
||||
#
|
||||
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
DAEMON=/usr/bin/memcached
|
||||
DAEMONBOOTSTRAP=/usr/share/memcached/scripts/start-memcached
|
||||
NAME=memcached
|
||||
DESC=memcached
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
|
||||
test -x $DAEMON || exit 0
|
||||
test -x $DAEMONBOOTSTRAP || exit 0
|
||||
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo -n "Starting $DESC: "
|
||||
start-stop-daemon --start --quiet --exec $DAEMONBOOTSTRAP
|
||||
echo "$NAME."
|
||||
;;
|
||||
stop)
|
||||
echo -n "Stopping $DESC: "
|
||||
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON
|
||||
echo "$NAME."
|
||||
rm -f $PIDFILE
|
||||
;;
|
||||
|
||||
restart|force-reload)
|
||||
#
|
||||
# If the "reload" option is implemented, move the "force-reload"
|
||||
# option to the "reload" entry above. If not, "force-reload" is
|
||||
# just the same as "restart".
|
||||
#
|
||||
echo -n "Restarting $DESC: "
|
||||
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
|
||||
rm -f $PIDFILE
|
||||
sleep 1
|
||||
start-stop-daemon --start --quiet --exec $DAEMONBOOTSTRAP
|
||||
echo "$NAME."
|
||||
;;
|
||||
*)
|
||||
N=/etc/init.d/$NAME
|
||||
# echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
|
||||
echo "Usage: $N {start|stop|restart|force-reload}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
111
wcmtools/memcached/scripts/memcached-tool
Executable file
111
wcmtools/memcached/scripts/memcached-tool
Executable file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# memcached-tool:
|
||||
# stats/management tool for memcached.
|
||||
#
|
||||
# Author:
|
||||
# Brad Fitzpatrick <brad@danga.com>
|
||||
#
|
||||
# License:
|
||||
# public domain. I give up all rights to this
|
||||
# tool. modify and copy at will.
|
||||
#
|
||||
|
||||
use strict;
|
||||
use IO::Socket::INET;
|
||||
|
||||
my $host = shift;
|
||||
my $mode = shift || "display";
|
||||
my ($from, $to);
|
||||
|
||||
if ($mode eq "display") {
|
||||
undef $mode if @ARGV;
|
||||
} elsif ($mode eq "move") {
|
||||
$from = shift;
|
||||
$to = shift;
|
||||
undef $mode if $from < 6 || $from > 17;
|
||||
undef $mode if $to < 6 || $to > 17;
|
||||
print STDERR "ERROR: parameters out of range\n\n" unless $mode;
|
||||
} else {
|
||||
undef $mode;
|
||||
}
|
||||
|
||||
undef $mode if @ARGV;
|
||||
|
||||
die
|
||||
"Usage: memcached-tool <host[:port]> [mode]\n
|
||||
memcached-tool 10.0.0.5:11211 display # shows slabs
|
||||
memcached-tool 10.0.0.5:11211 # same. (default is display)
|
||||
memcached-tool 10.0.0.5:11211 move 7 9 # takes 1MB slab from class #7
|
||||
# to class #9.
|
||||
|
||||
You can only move slabs around once memory is totally allocated, and only
|
||||
once the target class is full. (So you can't move from #6 to #9 and #7
|
||||
to #9 at the same itme, since you'd have to wait for #9 to fill from
|
||||
the first reassigned page)
|
||||
" unless $host && $mode;
|
||||
|
||||
$host .= ":11211" unless $host =~ /:\d+/;
|
||||
|
||||
my $sock = IO::Socket::INET->new(PeerAddr => $host,
|
||||
Proto => 'tcp');
|
||||
die "Couldn't connect to $host\n" unless $sock;
|
||||
|
||||
|
||||
if ($mode eq "move") {
|
||||
my $tries = 0;
|
||||
while (1) {
|
||||
print $sock "slabs reassign $from $to\r\n";
|
||||
my $res = <$sock>;
|
||||
$res =~ s/\s+//;
|
||||
if ($res eq "DONE") {
|
||||
print "Success.\n";
|
||||
exit 0;
|
||||
} elsif ($res eq "CANT") {
|
||||
print "Error: can't move from $from to $to. Destination not yet full? See usage docs.\n";
|
||||
exit;
|
||||
} elsif ($res eq "BUSY") {
|
||||
if (++$tries == 3) {
|
||||
print "Failed to move after 3 tries. Try again later.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
print "Page busy, retrying...\n";
|
||||
sleep 1;
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
# display mode:
|
||||
|
||||
my %items; # class -> { number, age, chunk_size, chunks_per_page,
|
||||
# total_pages, total_chunks, used_chunks,
|
||||
# free_chunks, free_chunks_end }
|
||||
|
||||
print $sock "stats items\r\n";
|
||||
while (<$sock>) {
|
||||
last if /^END/;
|
||||
if (/^STAT items:(\d+):(\w+) (\d+)/) {
|
||||
$items{$1}{$2} = $3;
|
||||
}
|
||||
}
|
||||
|
||||
print $sock "stats slabs\r\n";
|
||||
while (<$sock>) {
|
||||
last if /^END/;
|
||||
if (/^STAT (\d+):(\w+) (\d+)/) {
|
||||
$items{$1}{$2} = $3;
|
||||
}
|
||||
}
|
||||
|
||||
print " # Item_Size Max_age 1MB_pages Full?\n";
|
||||
foreach my $n (6..17) {
|
||||
my $it = $items{$n};
|
||||
my $size = $it->{chunk_size} < 1024 ? "$it->{chunk_size} B" :
|
||||
sprintf("%d kB", $it->{chunk_size} / 1024);
|
||||
my $full = $it->{free_chunks_end} == 0 ? "yes" : " no";
|
||||
printf "%3d %6s%7d s %7d $full\n", $n, $size, $it->{age}, $it->{total_pages};
|
||||
}
|
||||
|
||||
117
wcmtools/memcached/scripts/start-memcached
Executable file
117
wcmtools/memcached/scripts/start-memcached
Executable file
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# start-memcached
|
||||
# 2003/2004 - Jay Bonci <jaybonci@debian.org>
|
||||
# This script handles the parsing of the /etc/memcached.conf file
|
||||
# and was originally created for the Debian distribution.
|
||||
# Anyone may use this little script under the same terms as
|
||||
# memcached itself.
|
||||
|
||||
use strict;
|
||||
|
||||
if($> != 0 and $< != 0)
|
||||
{
|
||||
print STDERR "Only root wants to run start-memcached.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
my $params; my $etchandle; my $etcfile = "/etc/memcached.conf";
|
||||
|
||||
# This script assumes that memcached is located at /usr/bin/memcached, and
|
||||
# that the pidfile is writable at /var/run/memcached.pid
|
||||
|
||||
my $memcached = "/usr/bin/memcached";
|
||||
my $pidfile = "/var/run/memcached.pid";
|
||||
|
||||
# If we don't get a valid logfile parameter in the /etc/memcached.conf file,
|
||||
# we'll just throw away all of our in-daemon output. We need to re-tie it so
|
||||
# that non-bash shells will not hang on logout. Thanks to Michael Renner for
|
||||
# the tip
|
||||
my $fd_reopened = "/dev/null";
|
||||
|
||||
sub handle_logfile
|
||||
{
|
||||
my ($logfile) = @_;
|
||||
$fd_reopened = $logfile;
|
||||
}
|
||||
|
||||
sub reopen_logfile
|
||||
{
|
||||
my ($logfile) = @_;
|
||||
|
||||
open *STDERR, ">>$logfile";
|
||||
open *STDOUT, ">>$logfile";
|
||||
open *STDIN, ">>/dev/null";
|
||||
$fd_reopened = $logfile;
|
||||
}
|
||||
|
||||
# This is set up in place here to support other non -[a-z] directives
|
||||
|
||||
my $conf_directives = {
|
||||
"logfile" => \&handle_logfile,
|
||||
};
|
||||
|
||||
if(open $etchandle, $etcfile)
|
||||
{
|
||||
foreach my $line (<$etchandle>)
|
||||
{
|
||||
$line ||= "";
|
||||
$line =~ s/\#.*//g;
|
||||
$line =~ s/\s+$//g;
|
||||
$line =~ s/^\s+//g;
|
||||
next unless $line;
|
||||
next if $line =~ /^\-[dh]/;
|
||||
|
||||
if($line =~ /^[^\-]/)
|
||||
{
|
||||
my ($directive, $arg) = $line =~ /^(.*?)\s+(.*)/;
|
||||
$conf_directives->{$directive}->($arg);
|
||||
next;
|
||||
}
|
||||
|
||||
push @$params, $line;
|
||||
}
|
||||
|
||||
}else{
|
||||
$params = [];
|
||||
}
|
||||
|
||||
push @$params, "-u root" unless(grep "-u", @$params);
|
||||
$params = join " ", @$params;
|
||||
|
||||
if(-e $pidfile)
|
||||
{
|
||||
open PIDHANDLE, "$pidfile";
|
||||
my $localpid = <PIDHANDLE>;
|
||||
close PIDHANDLE;
|
||||
|
||||
chomp $localpid;
|
||||
if(-d "/proc/$localpid")
|
||||
{
|
||||
print STDERR "memcached is already running.\n";
|
||||
exit;
|
||||
}else{
|
||||
`rm -f $localpid`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
my $pid = fork();
|
||||
|
||||
if($pid == 0)
|
||||
{
|
||||
reopen_logfile($fd_reopened);
|
||||
exec "$memcached $params";
|
||||
exit(0);
|
||||
|
||||
}else{
|
||||
if(open PIDHANDLE,">$pidfile")
|
||||
{
|
||||
print PIDHANDLE $pid;
|
||||
close PIDHANDLE;
|
||||
}else{
|
||||
|
||||
print STDERR "Can't write pidfile to $pidfile.\n";
|
||||
}
|
||||
}
|
||||
|
||||
290
wcmtools/memcached/slabs.c
Executable file
290
wcmtools/memcached/slabs.c
Executable file
@@ -0,0 +1,290 @@
|
||||
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* Slabs memory allocation, based on powers-of-2
|
||||
*
|
||||
* $Id: slabs.c,v 1.15 2003/09/05 22:37:36 bradfitz Exp $
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/resource.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "memcached.h"
|
||||
|
||||
#define POWER_SMALLEST 3
|
||||
#define POWER_LARGEST 20
|
||||
#define POWER_BLOCK 1048576
|
||||
|
||||
/* powers-of-2 allocation structures */
|
||||
|
||||
typedef struct {
|
||||
unsigned int size; /* sizes of items */
|
||||
unsigned int perslab; /* how many items per slab */
|
||||
|
||||
void **slots; /* list of item ptrs */
|
||||
unsigned int sl_total; /* size of previous array */
|
||||
unsigned int sl_curr; /* first free slot */
|
||||
|
||||
void *end_page_ptr; /* pointer to next free item at end of page, or 0 */
|
||||
unsigned int end_page_free; /* number of items remaining at end of last alloced page */
|
||||
|
||||
unsigned int slabs; /* how many slabs were allocated for this class */
|
||||
|
||||
void **slab_list; /* array of slab pointers */
|
||||
unsigned int list_size; /* size of prev array */
|
||||
|
||||
unsigned int killing; /* index+1 of dying slab, or zero if none */
|
||||
} slabclass_t;
|
||||
|
||||
static slabclass_t slabclass[POWER_LARGEST+1];
|
||||
static unsigned int mem_limit = 0;
|
||||
static unsigned int mem_malloced = 0;
|
||||
|
||||
unsigned int slabs_clsid(unsigned int size) {
|
||||
int res = 1;
|
||||
|
||||
if(size==0)
|
||||
return 0;
|
||||
size--;
|
||||
while(size >>= 1)
|
||||
res++;
|
||||
if (res < POWER_SMALLEST)
|
||||
res = POWER_SMALLEST;
|
||||
if (res > POWER_LARGEST)
|
||||
res = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
void slabs_init(unsigned int limit) {
|
||||
int i;
|
||||
int size=1;
|
||||
|
||||
mem_limit = limit;
|
||||
for(i=0; i<=POWER_LARGEST; i++, size*=2) {
|
||||
slabclass[i].size = size;
|
||||
slabclass[i].perslab = POWER_BLOCK / size;
|
||||
slabclass[i].slots = 0;
|
||||
slabclass[i].sl_curr = slabclass[i].sl_total = slabclass[i].slabs = 0;
|
||||
slabclass[i].end_page_ptr = 0;
|
||||
slabclass[i].end_page_free = 0;
|
||||
slabclass[i].slab_list = 0;
|
||||
slabclass[i].list_size = 0;
|
||||
slabclass[i].killing = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int grow_slab_list (unsigned int id) {
|
||||
slabclass_t *p = &slabclass[id];
|
||||
if (p->slabs == p->list_size) {
|
||||
unsigned int new_size = p->list_size ? p->list_size * 2 : 16;
|
||||
void *new_list = realloc(p->slab_list, new_size*sizeof(void*));
|
||||
if (new_list == 0) return 0;
|
||||
p->list_size = new_size;
|
||||
p->slab_list = new_list;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int slabs_newslab(unsigned int id) {
|
||||
slabclass_t *p = &slabclass[id];
|
||||
int num = p->perslab;
|
||||
int len = POWER_BLOCK;
|
||||
char *ptr;
|
||||
|
||||
if (mem_limit && mem_malloced + len > mem_limit)
|
||||
return 0;
|
||||
|
||||
if (! grow_slab_list(id)) return 0;
|
||||
|
||||
ptr = malloc(len);
|
||||
if (ptr == 0) return 0;
|
||||
|
||||
memset(ptr, 0, len);
|
||||
p->end_page_ptr = ptr;
|
||||
p->end_page_free = num;
|
||||
|
||||
p->slab_list[p->slabs++] = ptr;
|
||||
mem_malloced += len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *slabs_alloc(unsigned int size) {
|
||||
slabclass_t *p;
|
||||
|
||||
unsigned char id = slabs_clsid(size);
|
||||
if (id < POWER_SMALLEST || id > POWER_LARGEST)
|
||||
return 0;
|
||||
|
||||
p = &slabclass[id];
|
||||
assert(p->sl_curr == 0 || ((item*)p->slots[p->sl_curr-1])->slabs_clsid == 0);
|
||||
|
||||
#ifdef USE_SYSTEM_MALLOC
|
||||
if (mem_limit && mem_malloced + size > mem_limit)
|
||||
return 0;
|
||||
mem_malloced += size;
|
||||
return malloc(size);
|
||||
#endif
|
||||
|
||||
/* fail unless we have space at the end of a recently allocated page,
|
||||
we have something on our freelist, or we could allocate a new page */
|
||||
if (! (p->end_page_ptr || p->sl_curr || slabs_newslab(id)))
|
||||
return 0;
|
||||
|
||||
/* return off our freelist, if we have one */
|
||||
if (p->sl_curr)
|
||||
return p->slots[--p->sl_curr];
|
||||
|
||||
/* if we recently allocated a whole page, return from that */
|
||||
if (p->end_page_ptr) {
|
||||
void *ptr = p->end_page_ptr;
|
||||
if (--p->end_page_free) {
|
||||
p->end_page_ptr += p->size;
|
||||
} else {
|
||||
p->end_page_ptr = 0;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return 0; /* shouldn't ever get here */
|
||||
}
|
||||
|
||||
void slabs_free(void *ptr, unsigned int size) {
|
||||
unsigned char id = slabs_clsid(size);
|
||||
slabclass_t *p;
|
||||
|
||||
assert(((item *)ptr)->slabs_clsid==0);
|
||||
assert(id >= POWER_SMALLEST && id <= POWER_LARGEST);
|
||||
if (id < POWER_SMALLEST || id > POWER_LARGEST)
|
||||
return;
|
||||
|
||||
p = &slabclass[id];
|
||||
|
||||
#ifdef USE_SYSTEM_MALLOC
|
||||
mem_malloced -= size;
|
||||
free(ptr);
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (p->sl_curr == p->sl_total) { /* need more space on the free list */
|
||||
int new_size = p->sl_total ? p->sl_total*2 : 16; /* 16 is arbitrary */
|
||||
void **new_slots = realloc(p->slots, new_size*sizeof(void *));
|
||||
if (new_slots == 0)
|
||||
return;
|
||||
p->slots = new_slots;
|
||||
p->sl_total = new_size;
|
||||
}
|
||||
p->slots[p->sl_curr++] = ptr;
|
||||
return;
|
||||
}
|
||||
|
||||
char* slabs_stats(int *buflen) {
|
||||
int i, total;
|
||||
char *buf = (char*) malloc(8192);
|
||||
char *bufcurr = buf;
|
||||
|
||||
*buflen = 0;
|
||||
if (!buf) return 0;
|
||||
|
||||
total = 0;
|
||||
for(i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {
|
||||
slabclass_t *p = &slabclass[i];
|
||||
if (p->slabs) {
|
||||
unsigned int perslab, slabs;
|
||||
|
||||
slabs = p->slabs;
|
||||
perslab = p->perslab;
|
||||
|
||||
bufcurr += sprintf(bufcurr, "STAT %d:chunk_size %u\r\n", i, p->size);
|
||||
bufcurr += sprintf(bufcurr, "STAT %d:chunks_per_page %u\r\n", i, perslab);
|
||||
bufcurr += sprintf(bufcurr, "STAT %d:total_pages %u\r\n", i, slabs);
|
||||
bufcurr += sprintf(bufcurr, "STAT %d:total_chunks %u\r\n", i, slabs*perslab);
|
||||
bufcurr += sprintf(bufcurr, "STAT %d:used_chunks %u\r\n", i, slabs*perslab - p->sl_curr);
|
||||
bufcurr += sprintf(bufcurr, "STAT %d:free_chunks %u\r\n", i, p->sl_curr);
|
||||
bufcurr += sprintf(bufcurr, "STAT %d:free_chunks_end %u\r\n", i, p->end_page_free);
|
||||
total++;
|
||||
}
|
||||
}
|
||||
bufcurr += sprintf(bufcurr, "STAT active_slabs %d\r\nSTAT total_malloced %u\r\n", total, mem_malloced);
|
||||
bufcurr += sprintf(bufcurr, "END\r\n");
|
||||
*buflen = bufcurr - buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* 1 = success
|
||||
0 = fail
|
||||
-1 = tried. busy. send again shortly. */
|
||||
int slabs_reassign(unsigned char srcid, unsigned char dstid) {
|
||||
void *slab, *slab_end;
|
||||
slabclass_t *p, *dp;
|
||||
void *iter;
|
||||
int was_busy = 0;
|
||||
|
||||
if (srcid < POWER_SMALLEST || srcid > POWER_LARGEST ||
|
||||
dstid < POWER_SMALLEST || dstid > POWER_LARGEST)
|
||||
return 0;
|
||||
|
||||
p = &slabclass[srcid];
|
||||
dp = &slabclass[dstid];
|
||||
|
||||
/* fail if src still populating, or no slab to give up in src */
|
||||
if (p->end_page_ptr || ! p->slabs)
|
||||
return 0;
|
||||
|
||||
/* fail if dst is still growing or we can't make room to hold its new one */
|
||||
if (dp->end_page_ptr || ! grow_slab_list(dstid))
|
||||
return 0;
|
||||
|
||||
if (p->killing == 0) p->killing = 1;
|
||||
|
||||
slab = p->slab_list[p->killing-1];
|
||||
slab_end = slab + POWER_BLOCK;
|
||||
|
||||
for (iter=slab; iter<slab_end; iter+=p->size) {
|
||||
item *it = (item *) iter;
|
||||
if (it->slabs_clsid) {
|
||||
if (it->refcount) was_busy = 1;
|
||||
item_unlink(it);
|
||||
}
|
||||
}
|
||||
|
||||
/* go through free list and discard items that are no longer part of this slab */
|
||||
{
|
||||
int fi;
|
||||
for (fi=p->sl_curr-1; fi>=0; fi--) {
|
||||
if (p->slots[fi] >= slab && p->slots[fi] < slab_end) {
|
||||
p->sl_curr--;
|
||||
if (p->sl_curr > fi) p->slots[fi] = p->slots[p->sl_curr];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (was_busy) return -1;
|
||||
|
||||
/* if good, now move it to the dst slab class */
|
||||
p->slab_list[p->killing-1] = p->slab_list[p->slabs-1];
|
||||
p->slabs--;
|
||||
p->killing = 0;
|
||||
dp->slab_list[dp->slabs++] = slab;
|
||||
dp->end_page_ptr = slab;
|
||||
dp->end_page_free = dp->perslab;
|
||||
|
||||
/* this isn't too critical, but other parts of the code do asserts to
|
||||
make sure this field is always 0. */
|
||||
for (iter=slab; iter<slab_end; iter+=dp->size) {
|
||||
((item *)iter)->slabs_clsid = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
101
wcmtools/memcached/test/stress-memcached.pl
Executable file
101
wcmtools/memcached/test/stress-memcached.pl
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
|
||||
use strict;
|
||||
use lib '../api/perl';
|
||||
use MemCachedClient;
|
||||
use Time::HiRes qw(time);
|
||||
|
||||
unless (@ARGV == 2) {
|
||||
die "Usage: stress-memcached.pl ip:port threads\n";
|
||||
}
|
||||
|
||||
my $host = shift;
|
||||
my $threads = shift;
|
||||
|
||||
my $memc = new MemCachedClient;
|
||||
$memc->set_servers([$host]);
|
||||
|
||||
unless ($memc->set("foo", "bar") &&
|
||||
$memc->get("foo") eq "bar") {
|
||||
die "memcached not running at $host ?\n";
|
||||
}
|
||||
$memc->disconnect_all();
|
||||
|
||||
|
||||
my $running = 0;
|
||||
while (1) {
|
||||
if ($running < $threads) {
|
||||
my $cpid = fork();
|
||||
if ($cpid) {
|
||||
$running++;
|
||||
#print "Launched $cpid. Running $running threads.\n";
|
||||
} else {
|
||||
stress();
|
||||
exit 0;
|
||||
}
|
||||
} else {
|
||||
wait();
|
||||
$running--;
|
||||
}
|
||||
}
|
||||
|
||||
sub stress {
|
||||
undef $memc;
|
||||
$memc = new MemCachedClient;
|
||||
$memc->set_servers([$host]);
|
||||
|
||||
my ($t1, $t2);
|
||||
my $start = sub { $t1 = time(); };
|
||||
my $stop = sub {
|
||||
my $op = shift;
|
||||
$t2 = time();
|
||||
my $td = sprintf("%0.3f", $t2 - $t1);
|
||||
if ($td > 0.25) { print "Took $td seconds for: $op\n"; }
|
||||
};
|
||||
|
||||
my $max = rand(50);
|
||||
my $sets = 0;
|
||||
|
||||
for (my $i = 0; $i < $max; $i++) {
|
||||
my $key = key($i);
|
||||
my $set = $memc->set($key, $key);
|
||||
$sets++ if $set;
|
||||
}
|
||||
|
||||
for (1..int(rand(500))) {
|
||||
my $rand = int(rand($max));
|
||||
my $key = key($rand);
|
||||
my $meth = int(rand(3));
|
||||
my $exp = int(rand(3));
|
||||
undef $exp unless $exp;
|
||||
$start->();
|
||||
if ($meth == 0) {
|
||||
$memc->add($key, $key, $exp);
|
||||
$stop->("add");
|
||||
} elsif ($meth == 1) {
|
||||
$memc->delete($key);
|
||||
$stop->("delete");
|
||||
} else {
|
||||
$memc->set($key, $key, $exp);
|
||||
$stop->("set");
|
||||
}
|
||||
$rand = int(rand($max));
|
||||
$key = key($rand);
|
||||
$start->();
|
||||
my $v = $memc->get($key);
|
||||
$stop->("get");
|
||||
if ($v && $v ne $key) { die "Bogus: $v for key $rand\n"; }
|
||||
}
|
||||
|
||||
$start->();
|
||||
my $multi = $memc->get_multi(map { key(int(rand($max))) } (1..$max));
|
||||
$stop->("get_multi");
|
||||
}
|
||||
|
||||
sub key {
|
||||
my $n = shift;
|
||||
$_ = sprintf("%04d", $n);
|
||||
if ($n % 2) { $_ .= "a"x20; }
|
||||
$_;
|
||||
}
|
||||
2
wcmtools/memcached/website/_config.bml
Executable file
2
wcmtools/memcached/website/_config.bml
Executable file
@@ -0,0 +1,2 @@
|
||||
LookRoot .
|
||||
ForceScheme memcached
|
||||
78
wcmtools/memcached/website/apis.bml
Executable file
78
wcmtools/memcached/website/apis.bml
Executable file
@@ -0,0 +1,78 @@
|
||||
<?page
|
||||
wintitle=>client apis
|
||||
body<=
|
||||
|
||||
<?h1 Perl API h1?>
|
||||
|
||||
<p> An object-oriented Perl module can be found on CPAN as <tt>Cache::Memcached</tt> or downloaded here.</p>
|
||||
<ul>
|
||||
<li><a href="dist/Cache-Memcached-1.14.tar.gz">Cache-Memcached-1.14.tar.gz</a>, GPL/Artistic.
|
||||
[<a href="http://cvs.danga.com/browse.cgi/wcmtools/memcached/api/perl/ChangeLog?rev=HEAD&content-type=text/plain">ChangeLog</a>]
|
||||
</ul>
|
||||
|
||||
<p>The API takes advantage of the server's opaque flag support and
|
||||
sets its "complex" flag whenever the object being stored or retrieved
|
||||
isn't a plain scalar. In that case, the <tt>Storable</tt> module is
|
||||
used to freeze and thaw the value automatically going in and out of
|
||||
the memcached.</p>
|
||||
|
||||
<?h1 PHP API h1?>
|
||||
|
||||
<p>Several PHP APIs are available:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://phpca.cytherianage.net/memcached/">http://phpca.cytherianage.net/memcached/</a></li>
|
||||
<li><a href="http://pecl.php.net/package/memcache/">http://pecl.php.net/package/memcache/</a> -- implemented in C, but doesn't support multiple memcached servers</li>
|
||||
<li>... (many others, send me links?)</li>
|
||||
</ul>
|
||||
|
||||
<?h1 Python API h1?>
|
||||
|
||||
<p>An object-oriented Python module modelled after the Perl one is also
|
||||
included in the distribution. It includes Pickle/cPickle support (like Perl's Storable).</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="dist/python-memcached-1.2.tar.gz">python-memcached-1.2.tar.gz</a>, Python license.
|
||||
[<a href="http://cvs.danga.com/browse.cgi/wcmtools/memcached/api/python/ChangeLog?rev=HEAD&content-type=text/plain">ChangeLog</a>]
|
||||
</ul>
|
||||
|
||||
<p>The Python API, while tested, has not experienced real-world production use.
|
||||
Also, its performance could be optimized (less syscalls parsing responses), if anybody
|
||||
out there would care to improve it.</p>
|
||||
|
||||
<p>Please contact us to take over its development.</p>
|
||||
|
||||
<?h1 Ruby API h1?>
|
||||
<p>A Ruby API is available from Michael Granger at:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://www.deveiate.org/code/Ruby-MemCache.html">http://www.deveiate.org/code/Ruby-MemCache.html</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<?h1 Java API h1?>
|
||||
<p>A Java API is maintained by <a href="mailto:greg@meetup.com">Greg Whalin</a> from <a href="http://www.meetup.com/">Meetup.com</a>. You can find that library here:
|
||||
|
||||
<ul>
|
||||
<li><a href="http://www.whalin.com/memcached/">http://www.whalin.com/memcached/</a> -- Java API for memcached
|
||||
</ul>
|
||||
|
||||
<?h1 C API h1?>
|
||||
<p>Multiple C libraries for memcached exist:</p>
|
||||
<ul>
|
||||
<li><a href="http://www.outoforder.cc/projects/libs/apr_memcache/">apr_memcache</a> by Paul Querna; Apache Software License version 2.0</li>
|
||||
|
||||
<li><a href="http://people.freebsd.org/~seanc/libmemcache/">libmemcache</a> by Sean Chittenden; BSD license.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<?h1 Protocol h1?> <p>To write a new client, check out the <a
|
||||
href="http://cvs.danga.com/browse.cgi/wcmtools/memcached/doc/protocol.txt?rev=HEAD&content-type=text/plain">protocol
|
||||
docs</a>. Be aware that the most important part of the client is the
|
||||
hashing across multiple servers, based on the key, or an optional
|
||||
caller-provided hashing value. Feel free to join the mailing list (or
|
||||
mail me directly) for help, inclusion in CVS, and/or a link to your
|
||||
client from this site.</p>
|
||||
|
||||
<=body
|
||||
page?>
|
||||
18
wcmtools/memcached/website/download.bml
Executable file
18
wcmtools/memcached/website/download.bml
Executable file
@@ -0,0 +1,18 @@
|
||||
<?page
|
||||
wintitle=>download
|
||||
body<=
|
||||
|
||||
<?h1 Download Server h1?>
|
||||
|
||||
<p>The server is licensed under the BSD License. Read the <a href="http://cvs.danga.com/browse.cgi/wcmtools/memcached/ChangeLog?rev=HEAD&content-type=text/plain">ChangeLog</a> for details on what's new.</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="dist/memcached-1.1.12.tar.gz">memcached-1.1.12.tar.gz</a> -- Apr 4, 2005
|
||||
</ul>
|
||||
|
||||
<?h1 Download Client APIs h1?>
|
||||
<p>The client APIs, licensed individually, are available for download on the <a href="apis.bml">Client APIs</a> page.</p>
|
||||
|
||||
|
||||
<=body
|
||||
page?>
|
||||
116
wcmtools/memcached/website/index.bml
Executable file
116
wcmtools/memcached/website/index.bml
Executable file
@@ -0,0 +1,116 @@
|
||||
<?page
|
||||
wintitle=>a distributed memory object caching system
|
||||
body<=
|
||||
|
||||
<?h1 What is <?memd?>? h1?>
|
||||
<p><?memd?> is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.</p>
|
||||
<p><a href="http://www.danga.com/">Danga Interactive</a> developed <?memd?> to enhance the speed of <a href="http://www.livejournal.com/">LiveJournal.com</a>, a site which was already doing 20 million+ dynamic page views per day for 1 million users with a bunch of webservers and a bunch of database servers. <?memd?> dropped the database load to almost nothing, yielding faster page load times for users, better resource utilization, and faster access to the databases on a memcache miss.</p>
|
||||
|
||||
<?h1 How it Works h1?>
|
||||
<p>First, you start up the <?memd?> daemon on as many spare machines as you have. The daemon has no configuration file, just a few command line options, only 3 or 4 of which you'll likely use:
|
||||
<pre class='example'># ./memcached -d -m 2048 -l 10.0.0.40 -p 11211</pre>
|
||||
<p>This starts <?memd?> up as a daemon, using 2GB of memory, and listening on IP 10.0.0.40, port 11211. Because a 32-bit process can only address 4GB of virtual memory (usually significantly less, depending on your operating system), if you have a 32-bit server with 4-64GB of memory using PAE you can just run multiple processes on the machine, each using 2 or 3GB of memory.</p>
|
||||
|
||||
<?h1 Porting the Application h1?>
|
||||
<p>Now, in your application, wherever you go to do a database query, first check the memcache. If the memcache returns an undefined object, then go to the database, get what you're looking for, and put it in the memcache:</p>
|
||||
<pre class='example'>
|
||||
<div class='exampletitle'>Perl Example (see <a href="apis.bml">APIs page</a>)</div>
|
||||
sub get_foo_object {
|
||||
my $foo_id = int(shift);
|
||||
my $obj = $::MemCache->get("foo:$foo_id");
|
||||
return $obj if $obj;
|
||||
|
||||
$obj = $::db->selectrow_hashref("SELECT .... FROM foo f, bar b ".
|
||||
"WHERE ... AND f.fooid=$foo_id");
|
||||
$::MemCache->set("foo:$foo_id", $obj);
|
||||
return $obj;
|
||||
}</pre>
|
||||
|
||||
<p>(If your internal API was already clean enough, you should only have to do this in a few spots. Start with the queries that kill your database the most, then move to doing as much as possible.)</p>
|
||||
|
||||
<p>You'll notice the data structure the server provides is just a dictionary. You assign values to keys, and you request values from keys.</p>
|
||||
|
||||
<p>Now, what actually happens is that the API hashes your key to a unique server. (You define all the available servers and their weightings when initializing the API) Alternatively, the APIs also let you provide your own hash value. A good hash value for user-related data is the user's ID number. Then, the API maps that hash value onto a server (modulus number of server buckets, one bucket for each server IP/port, but some can be weighted heigher if they have more memory available).</p>
|
||||
|
||||
<p>If a host goes down, the API re-maps that dead host's requests onto the servers that are available.</p>
|
||||
|
||||
<?h1 Shouldn't the database do this? h1?>
|
||||
<p>Regardless of what database you use (MS-SQL, Oracle, Postgres, MysQL-InnoDB, etc..), there's a lot of overhead in implementing <a href="http://www.wikipedia.org/wiki/ACID">ACID</a> properties in a RDBMS, especially when disks are involved, which means queries are going to block. For databases that aren't ACID-compliant (like MySQL-MyISAM), that overhead doesn't exist, but reading threads block on the writing threads.</p>
|
||||
<p><?memd?> never blocks. See the "Is memcached fast?" question below.</p>
|
||||
|
||||
<?h1 What about shared memory? h1?>
|
||||
|
||||
<p>The first thing people generally do is cache things within their
|
||||
web processes. But this means your cache is duplicated multiple
|
||||
times, once for each mod_perl/PHP/etc thread. This is a waste of
|
||||
memory and you'll get low cache hit rates. If you're using a
|
||||
multi-threaded language or a shared memory API (IPC::Shareable, etc),
|
||||
you can have a global cache for all threads, but it's per-machine. It doesn't scale to multiple machines.
|
||||
Once you have 20 webservers, those 20 independent caches start to look
|
||||
just as silly as when you had 20 threads with their own caches on a
|
||||
single box. (plus, shared memory is typically laden with limitations)</p>
|
||||
|
||||
<p>The <?memd?> server and clients work together to implement one
|
||||
global cache across as many machines as you have. In fact, it's
|
||||
recommended you run both web nodes (which are typically memory-lite
|
||||
and CPU-hungry) and memcached processes (which are memory-hungry and
|
||||
CPU-lite) on the same machines. This way you'll save network
|
||||
ports.</p>
|
||||
|
||||
<?h1 What about MySQL 4.x query caching? h1?>
|
||||
<p>MySQL query caching is less than ideal, for a number of reasons:</p>
|
||||
<ul class='spaced'>
|
||||
|
||||
<li>MySQL's query cache destroys the entire cache for a given table whenever that table is changed. On a high-traffic site with updates happening many times per second, this makes the the cache practically worthless. In fact, it's often harmful to have it on, since there's a overhead to maintain the cache.</li>
|
||||
|
||||
<li>On 32-bit architectures, the entire server (including the query cache) is limited to a 4 GB virtual address space. <?memd?> lets you run as many processes as you want, so you have no limit on memory cache size.</li>
|
||||
|
||||
<li>MySQL has a query cache, not an object cache. If your objects require extra expensive construction after the data retrieval step, MySQL's query cache can't help you there.</li>
|
||||
|
||||
</ul>
|
||||
<p>If the data you need to cache is small and you do infrequent updates, MySQL's query caching should work for you. If not, use <?memd?>.</p>
|
||||
|
||||
<?h1 What about database replication? h1?>
|
||||
|
||||
<p>You can spread your reads with replication, and that helps a lot,
|
||||
but you can't spread writes (they have to process on all machines) and
|
||||
they'll eventually consume all your resources. You'll find yourself
|
||||
adding replicated slaves at an ever-increasing rate to make up for the
|
||||
diminishing returns each addition slave provides.</p>
|
||||
|
||||
<p>The next logical step is to horizontally partition your dataset
|
||||
onto different master/slave clusters so you can spread your writes,
|
||||
and then teach your application to connect to the correct cluster
|
||||
depending on the data it needs.</p>
|
||||
|
||||
<p>While this strategy works, and is recommended, more databases (each
|
||||
with a bunch of disks) statistically leads to more frequent hardware
|
||||
failures, which are annoying.</p>
|
||||
|
||||
<p>With <?memd?> you can reduce your database reads to a mere
|
||||
fraction, leaving the databases to mainly do infrequent writes, and
|
||||
end up getting much more bang for your buck, since your databases
|
||||
won't be blocking themselves doing ACID bookkeeping or waiting on
|
||||
writing threads.</p>
|
||||
|
||||
<?h1 Is <?memd?> fast? h1?> <p>Very fast. It uses <a
|
||||
href="http://www.monkey.org/~provos/libevent/">libevent</a> to scale
|
||||
to any number of open connections</a> (using <a
|
||||
href="http://www.xmailserver.org/linux-patches/nio-improve.html">epoll</a>
|
||||
on Linux, if available at runtime), uses non-blocking network I/O, refcounts internal objects
|
||||
(so objects can be in multiple states to multiple clients), and uses
|
||||
its own slab allocator and hash table so virtual memory never gets
|
||||
externally fragmented and allocations are guaranteed O(1).</p>
|
||||
|
||||
<?h1 What about race conditions? h1?>
|
||||
<p>You might wonder: <i>"What if the <tt>get_foo()</tt> function adds a stale version of the Foo object to the cache right as/after the user updates their Foo object via update_foo()?"</i></p>
|
||||
<p>While the server and API only have one way to get data from the cache, there exists 3 ways to put data in:</p>
|
||||
<ul class='spaced'>
|
||||
<li><b>set</b> -- unconditionally sets a given key with a given value (<tt>update_foo()</tt> should use this)</li>
|
||||
<li><b>add</b> -- adds to the cache, only if it doesn't already exist (<tt>get_foo()</tt> should use this)</li>
|
||||
<li><b>replace</b> -- sets in the cache only if the key already exists (not as useful, only for completeness)</li>
|
||||
</ul>
|
||||
Additionally, all three support an expiration time.
|
||||
|
||||
<=body
|
||||
page?>
|
||||
59
wcmtools/memcached/website/memcached.css
Executable file
59
wcmtools/memcached/website/memcached.css
Executable file
@@ -0,0 +1,59 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
border: 5px solid #7070d0; margin: 0; padding: 30px;
|
||||
background: white;
|
||||
}
|
||||
a { color: #5050d0; }
|
||||
h1 {
|
||||
color: #7070d0;
|
||||
letter-spacing: 0.2em;
|
||||
text-align: center;
|
||||
width: auto;
|
||||
font-size: 20pt;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 13pt;
|
||||
font-style: italic;
|
||||
margin: 1em 0 0.5em -0.7em;
|
||||
border-bottom: 2px dotted blue;
|
||||
background: #e0e0ff;
|
||||
}
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.linkbar {
|
||||
width: auto;
|
||||
text-align: center;
|
||||
}
|
||||
div.pagetitle {
|
||||
width: auto;
|
||||
text-align: center;
|
||||
font-weight: bold; font-size: 17pt;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
pre.example {
|
||||
margin-left: 40px;
|
||||
background: #e0e0e0;
|
||||
}
|
||||
div.exampletitle {
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
background: #d0d0f0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ul.spaced li { margin-top: 0.7em; }
|
||||
|
||||
a.barlink:active { background: yellow; }
|
||||
|
||||
div.footer {
|
||||
margin: 30px -30px -30px -30px;
|
||||
padding: 5px;
|
||||
font-size: smaller;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
tt.mem { color: #000030; }
|
||||
30
wcmtools/memcached/website/memcached.look
Executable file
30
wcmtools/memcached/website/memcached.look
Executable file
@@ -0,0 +1,30 @@
|
||||
page<=
|
||||
{Fps}<html>
|
||||
<head>
|
||||
<title>memcached: %%wintitle%%</title>
|
||||
<link rel='stylesheet' type='text/css' href='memcached.css' />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>memcached</h1>
|
||||
<div class='linkbar'>
|
||||
<?link ./|About link?>
|
||||
<?link news.bml|News link?>
|
||||
<?link download.bml|Download link?>
|
||||
<?link apis.bml|Client APIs link?>
|
||||
<?link users.bml|Users link?>
|
||||
<?link http://lists.danga.com/mailman/listinfo/memcached|Mailing List link?>
|
||||
</div>
|
||||
|
||||
%%body%%
|
||||
|
||||
<div class='footer'>Brad Fitzpatrick <brad@danga.com></div>
|
||||
</body>
|
||||
</html>
|
||||
<=page
|
||||
|
||||
h1=>{D}<h2>%%data%%</h2>
|
||||
|
||||
memd=>{S}<tt class='mem'>memcached</tt>
|
||||
|
||||
link=>{RP}[<a href="%%data1%%" class='barlink'>%%data2%%</a>]
|
||||
44
wcmtools/memcached/website/news.bml
Executable file
44
wcmtools/memcached/website/news.bml
Executable file
@@ -0,0 +1,44 @@
|
||||
<?page
|
||||
wintitle=>news
|
||||
body<=
|
||||
|
||||
<?h1 News h1?>
|
||||
|
||||
<p><b>2004-04-30:</b> Version 1.1.11 <a
|
||||
href="download.bml">released</a>. New option (off by default) to not
|
||||
act as a cache, but hold items forever until deleted (or until machine
|
||||
goes down, rather). This release also installs the manual page, and
|
||||
raises the per-process file descriptor limit to match the specified
|
||||
max connections.
|
||||
|
||||
<p><b>2003-12-30:</b> Version 1.1.10 <a
|
||||
href="download.bml">released</a>. Now includes a command to wipe
|
||||
the entire cache.
|
||||
|
||||
<p><b>2003-10-09:</b> Version 1.1.9 <a
|
||||
href="download.bml">released</a>, now with better network performance,
|
||||
better portability, setuid support, and a fix for a rare crash
|
||||
(SIGPIPE) if a client dies.
|
||||
|
||||
<p><b>2003-08-08:</b> We now have <a href="apis.bml">Python support</a>.</p>
|
||||
|
||||
<p><b>2003-07-29:</b> Version 1.1.8 <a href="download.bml">released</a>.
|
||||
The client APIs are now distributed separately.</p>
|
||||
|
||||
<p><b>2003-06-30:</b> Version 1.1.6 <a
|
||||
href="download.bml">released</a>. Many great changes.</p>
|
||||
|
||||
<p><b>2003-06-16:</b> <a href="apis.bml">PHP support</a> now included.</p>
|
||||
|
||||
<p><b>2003-06-15:</b> Autoconf support added in <a
|
||||
href="download.bml">1.0.2 release</a>. Website text updated a bit.</p>
|
||||
|
||||
<p><b>2003-06-10:</b> Website not so bad now. Files licensed. Perl
|
||||
API separated and documented. <a href="download.bml">1.0 release</a>.</p>
|
||||
|
||||
<p><b>2003-05-30:</b> Server's stable. Perl API's still LJ-specific
|
||||
(just need to just change the package name, basically). Other APIs
|
||||
should be easy to bang out. This website needs work.</p>
|
||||
|
||||
<=body
|
||||
page?>
|
||||
52
wcmtools/memcached/website/users.bml
Executable file
52
wcmtools/memcached/website/users.bml
Executable file
@@ -0,0 +1,52 @@
|
||||
<?page
|
||||
wintitle=>users
|
||||
body<=
|
||||
|
||||
<?h1 Who's using <?memd?>? h1?>
|
||||
<p>This is an initial list of <?memd?> users that I've heard about. Please mail me if you're using it, optionally with a little description of how, and I'll add you to this page.</p>
|
||||
|
||||
<ul>
|
||||
<li><b><a href="http://www.livejournal.com/">LiveJournal</a></b> -- fully dynamic blogging site with insane number of unnecessary features, doing over 20 million hits per day. We made <?memd?> for LiveJournal and we hardly ever hit the databases anymore. A few APIs in our codebase still unconditionally hit our databases, but they're being rewritten to be <?memd?>-aware. <?memd?> made a night-and-day difference in the speed of our site.</li>
|
||||
|
||||
<li><b><a href="http://www.slashdot.org/">Slashdot</a></b> -- I showed Jamie McCarthy <?memd?> at OSCON 2003 and how we use it on LiveJournal (including our <?memd?>-farm stats page) and he started frothing at the mouth and began implementing it that night in his hotel room. Now Slashdot uses it for caching comments in their rendered form, saving both DB load and web CPU load. They're reportedly working on using <?memd?> in more parts of their code.</li>
|
||||
|
||||
<li><b><a href="http://www.wikipedia.org/">WikiPedia</a></b> -- Brion Vibber added support to WikiPedia's MediaWiki backend. ( <a href="http://mail.wikipedia.org/pipermail/wikitech-l/2003-August/005514.html">original announcement</a>).</li>
|
||||
|
||||
<li><b><a href="http://vampirefreaks.com">VampireFreaks</a></b>:
|
||||
<i>"Hey man. I just wanted to thank you for memcached, I just started
|
||||
using it on http://vampirefreaks.com , a site which gets over a
|
||||
million page hits a day and has been really slowing down the server.
|
||||
I've already implemented memcached in a few key spots which determine
|
||||
the number of users online as well as the number of current users, and
|
||||
it seems to have helped a lot, I am sure I will be putting it into
|
||||
more parts of the code as well. Feel free to put us on the memcached
|
||||
users page if you like."</i></li>
|
||||
|
||||
<li><b><a href="http://sourceforge.net">SourceForge</a></b></li>
|
||||
|
||||
<li><b><a href="http://www.revelex.com/">Revelex</a></b>: <i>"... We have tried using MySQL, NFS-mounted flat-files and even NFS-mounted RAM drives to no avail. To date, only memcached has been able to keep up with our needs. ..."</i></li>
|
||||
|
||||
<li><b><a href="http://www.howardstern.com/">HowardStern.com</a></b>: <i>"We've been struggling to keep the hardware matched to the traffic
|
||||
level and ever-growing database size. I've now implemented memcached
|
||||
across major sections of the site and the vBulletin-based forum. We're
|
||||
using three memcached servers to support the one large mySQL server.
|
||||
The performance improvement has been tremendous and it allows me to
|
||||
have an elegant memory caching solution always available instead of
|
||||
my own cache on the webservers' filesystems, as I had been doing."</i></li>
|
||||
|
||||
</ul>
|
||||
|
||||
<?h1 Coming soon... h1?>
|
||||
<p>These people are (or reportedly are, or were) working on memcache support to speed up their sites.</p>
|
||||
|
||||
<ul>
|
||||
<li><b><a href="http://www.everything2.com/">Everything2</a></b> -- <a href="http://lists.danga.com/pipermail/memcached/2003-August/000044.html">adding support</a> for <?memd?> to the ecore nodecache over at <a href="http://www.everydevel.com">everydevel.com</a>.</li>
|
||||
|
||||
<li><b><a href="http://www.sourceforge.net/">SourceForge</a></b> -- adding support, which is why the Python API for <?memd?> was created.</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
<=body
|
||||
page?>
|
||||
Reference in New Issue
Block a user