init
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user