This commit is contained in:
2019-02-06 00:49:12 +03:00
commit 8dbb1bb605
4796 changed files with 506072 additions and 0 deletions

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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);
}
}

View 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";

View 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"));
}
}

View 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.

View File

@@ -0,0 +1,8 @@
ChangeLog
Memcached.pm
Makefile.PL
README
MANIFEST
TODO
t/use.t

View 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>') : ()),
);

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -0,0 +1,3 @@
(currently empty)

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env perl -w
use strict;
use Test;
BEGIN { plan tests => 1 }
use Cache::Memcached; ok(1);
exit;

View 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

View 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);
?>

File diff suppressed because it is too large Load Diff

View 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";

View 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.

View File

@@ -0,0 +1,3 @@
Note for Brad, on how to make new releases:
$ python setup.py sdist

View File

@@ -0,0 +1,2 @@
memcache.py
setup.py

View 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

View 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 :

View 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"])

View 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