[infinispan-commits] Infinispan SVN: r2025 - in branches/4.1.x/server/memcached/src: test and 1 other directories.
infinispan-commits at lists.jboss.org
infinispan-commits at lists.jboss.org
Mon Jul 12 14:07:32 EDT 2010
Author: manik.surtani at jboss.com
Date: 2010-07-12 14:07:31 -0400 (Mon, 12 Jul 2010)
New Revision: 2025
Added:
branches/4.1.x/server/memcached/src/main/resources/memcache.py
branches/4.1.x/server/memcached/src/main/resources/memcached_client.py
branches/4.1.x/server/memcached/src/test/resources/
branches/4.1.x/server/memcached/src/test/resources/memcache.py
branches/4.1.x/server/memcached/src/test/resources/test_memcached_read.py
branches/4.1.x/server/memcached/src/test/resources/test_memcached_write.py
Removed:
branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_client.py
branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_reader.py
branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_writer.py
branches/4.1.x/server/memcached/src/test/resources/memcache.py
branches/4.1.x/server/memcached/src/test/resources/test_memcached_read.py
branches/4.1.x/server/memcached/src/test/resources/test_memcached_write.py
Log:
Refactored memcached clients and tests in Python
Copied: branches/4.1.x/server/memcached/src/main/resources/memcache.py (from rev 2022, trunk/server/memcached/src/main/resources/memcache.py)
===================================================================
--- branches/4.1.x/server/memcached/src/main/resources/memcache.py (rev 0)
+++ branches/4.1.x/server/memcached/src/main/resources/memcache.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -0,0 +1,1241 @@
+#!/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(key, 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 os
+import re
+try:
+ import cPickle as pickle
+except ImportError:
+ import pickle
+
+from binascii import crc32 # zlib version is not cross-platform
+def cmemcache_hash(key):
+ return((((crc32(key) & 0xffffffff) >> 16) & 0x7fff) or 1)
+serverHashFunction = cmemcache_hash
+
+def useOldServerHashFunction():
+ """Use the old python-memcache server hash function."""
+ serverHashFunction = crc32
+
+try:
+ from zlib import compress, decompress
+ _supports_compress = True
+except ImportError:
+ _supports_compress = False
+ # quickly define a decompress just in case we recv compressed data.
+ def decompress(val):
+ raise _Error("received compressed data but I don't support compession (import error)")
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+
+__author__ = "Evan Martin <martine at danga.com>"
+__version__ = "1.45"
+__copyright__ = "Copyright (C) 2003 Danga Interactive"
+__license__ = "Python"
+
+SERVER_MAX_KEY_LENGTH = 250
+# Storing values larger than 1MB requires recompiling memcached. If you do,
+# this value can be changed by doing "memcache.SERVER_MAX_VALUE_LENGTH = N"
+# after importing this module.
+SERVER_MAX_VALUE_LENGTH = 1024*1024
+
+class _Error(Exception):
+ pass
+
+try:
+ # Only exists in Python 2.4+
+ from threading import local
+except ImportError:
+ # TODO: add the pure-python local implementation
+ class local(object):
+ pass
+
+
+class Client(local):
+ """
+ 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, set_multi
+ @group Retrieval: get, get_multi
+ @group Integers: incr, decr
+ @group Removal: delete, delete_multi
+ @sort: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog,\
+ set, set_multi, add, replace, get, get_multi, incr, decr, delete, delete_multi
+ """
+ _FLAG_PICKLE = 1<<0
+ _FLAG_INTEGER = 1<<1
+ _FLAG_LONG = 1<<2
+ _FLAG_COMPRESSED = 1<<3
+
+ _SERVER_RETRIES = 10 # how many times to try finding a free server.
+
+ # exceptions for Client
+ class MemcachedKeyError(Exception):
+ pass
+ class MemcachedKeyLengthError(MemcachedKeyError):
+ pass
+ class MemcachedKeyCharacterError(MemcachedKeyError):
+ pass
+ class MemcachedKeyNoneError(MemcachedKeyError):
+ pass
+ class MemcachedKeyTypeError(MemcachedKeyError):
+ pass
+ class MemcachedStringEncodingError(Exception):
+ pass
+
+ def __init__(self, servers, debug=0, pickleProtocol=0,
+ pickler=pickle.Pickler, unpickler=pickle.Unpickler,
+ pload=None, pid=None, server_max_key_length=SERVER_MAX_KEY_LENGTH,
+ server_max_value_length=SERVER_MAX_VALUE_LENGTH):
+ """
+ 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.
+ @param pickleProtocol: number to mandate protocol used by (c)Pickle.
+ @param pickler: optional override of default Pickler to allow subclassing.
+ @param unpickler: optional override of default Unpickler to allow subclassing.
+ @param pload: optional persistent_load function to call on pickle loading.
+ Useful for cPickle since subclassing isn't allowed.
+ @param pid: optional persistent_id function to call on pickle storing.
+ Useful for cPickle since subclassing isn't allowed.
+ """
+ local.__init__(self)
+ self.debug = debug
+ self.set_servers(servers)
+ self.stats = {}
+ self.cas_ids = {}
+
+ # Allow users to modify pickling/unpickling behavior
+ self.pickleProtocol = pickleProtocol
+ self.pickler = pickler
+ self.unpickler = unpickler
+ self.persistent_load = pload
+ self.persistent_id = pid
+ self.server_max_key_length = server_max_key_length
+ self.server_max_value_length = server_max_value_length
+
+ # figure out the pickler style
+ file = StringIO()
+ try:
+ pickler = self.pickler(file, protocol = self.pickleProtocol)
+ self.picklerIsKeyword = True
+ except TypeError:
+ self.picklerIsKeyword = False
+
+ 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.debug) for s in servers]
+ self._init_buckets()
+
+ def get_stats(self):
+ '''Get statistics from each of the servers.
+
+ @return: A list of tuples ( server_identifier, stats_dictionary ).
+ The dictionary contains a number of name/value pairs specifying
+ the name of the status field and the string value associated with
+ it. The values are not converted from strings.
+ '''
+ data = []
+ for s in self.servers:
+ if not s.connect(): continue
+ if s.family == socket.AF_INET:
+ name = '%s:%s (%s)' % ( s.ip, s.port, s.weight )
+ else:
+ name = 'unix:%s (%s)' % ( s.address, s.weight )
+ s.send_cmd('stats')
+ serverData = {}
+ data.append(( name, serverData ))
+ readline = s.readline
+ while 1:
+ line = readline()
+ if not line or line.strip() == 'END': break
+ stats = line.split(' ', 2)
+ serverData[stats[1]] = stats[2]
+
+ return(data)
+
+ def get_slabs(self):
+ data = []
+ for s in self.servers:
+ if not s.connect(): continue
+ if s.family == socket.AF_INET:
+ name = '%s:%s (%s)' % ( s.ip, s.port, s.weight )
+ else:
+ name = 'unix:%s (%s)' % ( s.address, s.weight )
+ serverData = {}
+ data.append(( name, serverData ))
+ s.send_cmd('stats items')
+ readline = s.readline
+ while 1:
+ line = readline()
+ if not line or line.strip() == 'END': break
+ item = line.split(' ', 2)
+ #0 = STAT, 1 = ITEM, 2 = Value
+ slab = item[1].split(':', 2)
+ #0 = items, 1 = Slab #, 2 = Name
+ if slab[1] not in serverData:
+ serverData[slab[1]] = {}
+ serverData[slab[1]][slab[2]] = item[2]
+ return data
+
+ def flush_all(self):
+ 'Expire all data currently in the memcache servers.'
+ for s in self.servers:
+ if not s.connect(): continue
+ s.send_cmd('flush_all')
+ s.expect("OK")
+
+ def debuglog(self, str):
+ if self.debug:
+ sys.stderr.write("MemCached: %s\n" % str)
+
+ def _statlog(self, func):
+ if func not in self.stats:
+ 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.deaduntil = 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 isinstance(key, tuple):
+ serverhash, key = key
+ else:
+ serverhash = serverHashFunction(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 = serverHashFunction(str(serverhash) + str(i))
+ return None, None
+
+ def disconnect_all(self):
+ for s in self.servers:
+ s.close_socket()
+
+ def delete_multi(self, keys, time=0, key_prefix=''):
+ '''
+ Delete multiple keys in the memcache doing just one query.
+
+ >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'})
+ >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', 'key2' : 'val2'}
+ 1
+ >>> mc.delete_multi(['key1', 'key2'])
+ 1
+ >>> mc.get_multi(['key1', 'key2']) == {}
+ 1
+
+
+ This method is recommended over iterated regular L{delete}s as it reduces total latency, since
+ your app doesn't have to wait for each round-trip of L{delete} before sending
+ the next one.
+
+ @param keys: An iterable of keys to clear
+ @param time: number of seconds any subsequent set / update commands should fail. Defaults to 0 for no delay.
+ @param key_prefix: Optional string to prepend to each key when sending to memcache.
+ See docs for L{get_multi} and L{set_multi}.
+
+ @return: 1 if no failure in communication with any memcacheds.
+ @rtype: int
+
+ '''
+
+ self._statlog('delete_multi')
+
+ server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(keys, key_prefix)
+
+ # send out all requests on each server before reading anything
+ dead_servers = []
+
+ rc = 1
+ for server in server_keys.iterkeys():
+ bigcmd = []
+ write = bigcmd.append
+ if time != None:
+ for key in server_keys[server]: # These are mangled keys
+ write("delete %s %d\r\n" % (key, time))
+ else:
+ for key in server_keys[server]: # These are mangled keys
+ write("delete %s\r\n" % key)
+ try:
+ server.send_cmds(''.join(bigcmd))
+ except socket.error, msg:
+ rc = 0
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ 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]
+
+ notstored = [] # original keys.
+ for server, keys in server_keys.iteritems():
+ try:
+ for key in keys:
+ server.expect("DELETED")
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ rc = 0
+ return rc
+
+ def delete(self, key, time=0):
+ '''Deletes a key from the memcache.
+
+ @return: Nonzero on success.
+ @param time: number of seconds any subsequent set / update commands
+ should fail. Defaults to 0 for no delay.
+ @rtype: int
+ '''
+ self.check_key(key)
+ 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:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ 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):
+ self.check_key(key)
+ 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()
+ if line.strip() =='NOT_FOUND': return None
+ return int(line)
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return None
+
+ def add(self, key, val, time = 0, min_compress_len = 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, min_compress_len)
+
+ def append(self, key, val, time=0, min_compress_len=0):
+ '''Append the value to the end of the existing key's value.
+
+ Only stores in memcache if key already exists.
+ Also see L{prepend}.
+
+ @return: Nonzero on success.
+ @rtype: int
+ '''
+ return self._set("append", key, val, time, min_compress_len)
+
+ def prepend(self, key, val, time=0, min_compress_len=0):
+ '''Prepend the value to the beginning of the existing key's value.
+
+ Only stores in memcache if key already exists.
+ Also see L{append}.
+
+ @return: Nonzero on success.
+ @rtype: int
+ '''
+ return self._set("prepend", key, val, time, min_compress_len)
+
+ def replace(self, key, val, time=0, min_compress_len=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, min_compress_len)
+
+ def set(self, key, val, time=0, min_compress_len=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 server hash value and the second being the key.
+ 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
+ @param time: Tells memcached the time which this value should expire, either
+ as a delta number of seconds, or an absolute unix time-since-the-epoch
+ value. See the memcached protocol docs section "Storage Commands"
+ for more info on <exptime>. We default to 0 == cache forever.
+ @param min_compress_len: The threshold length to kick in auto-compression
+ of the value using the zlib.compress() routine. If the value being cached is
+ a string, then the length of the string is measured, else if the value is an
+ object, then the length of the pickle result is measured. If the resulting
+ attempt at compression yeilds a larger string than the input, then it is
+ discarded. For backwards compatability, this parameter defaults to 0,
+ indicating don't ever try to compress.
+ '''
+ return self._set("set", key, val, time, min_compress_len)
+
+
+ def cas(self, key, val, time=0, min_compress_len=0):
+ '''Sets a key to a given value in the memcache if it hasn't been
+ altered since last fetched. (See L{gets}).
+
+ The C{key} can optionally be an tuple, with the first element
+ being the server hash value and the second being the key.
+ 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
+ @param time: Tells memcached the time which this value should expire,
+ either as a delta number of seconds, or an absolute unix
+ time-since-the-epoch value. See the memcached protocol docs section
+ "Storage Commands" for more info on <exptime>. We default to
+ 0 == cache forever.
+ @param min_compress_len: The threshold length to kick in
+ auto-compression of the value using the zlib.compress() routine. If
+ the value being cached is a string, then the length of the string is
+ measured, else if the value is an object, then the length of the
+ pickle result is measured. If the resulting attempt at compression
+ yeilds a larger string than the input, then it is discarded. For
+ backwards compatability, this parameter defaults to 0, indicating
+ don't ever try to compress.
+ '''
+ return self._set("cas", key, val, time, min_compress_len)
+
+
+ def _map_and_prefix_keys(self, key_iterable, key_prefix):
+ """Compute the mapping of server (_Host instance) -> list of keys to stuff onto that server, as well as the mapping of
+ prefixed key -> original key.
+
+
+ """
+ # Check it just once ...
+ key_extra_len=len(key_prefix)
+ if key_prefix:
+ self.check_key(key_prefix)
+
+ # server (_Host) -> list of unprefixed server keys in mapping
+ server_keys = {}
+
+ prefixed_to_orig_key = {}
+ # build up a list for each server of all the keys we want.
+ for orig_key in key_iterable:
+ if isinstance(orig_key, tuple):
+ # Tuple of hashvalue, key ala _get_server(). Caller is essentially telling us what server to stuff this on.
+ # Ensure call to _get_server gets a Tuple as well.
+ str_orig_key = str(orig_key[1])
+ server, key = self._get_server((orig_key[0], key_prefix + str_orig_key)) # Gotta pre-mangle key before hashing to a server. Returns the mangled key.
+ else:
+ str_orig_key = str(orig_key) # set_multi supports int / long keys.
+ server, key = self._get_server(key_prefix + str_orig_key)
+
+ # Now check to make sure key length is proper ...
+ self.check_key(str_orig_key, key_extra_len=key_extra_len)
+
+ if not server:
+ continue
+
+ if server not in server_keys:
+ server_keys[server] = []
+ server_keys[server].append(key)
+ prefixed_to_orig_key[key] = orig_key
+
+ return (server_keys, prefixed_to_orig_key)
+
+ def set_multi(self, mapping, time=0, key_prefix='', min_compress_len=0):
+ '''
+ Sets multiple keys in the memcache doing just one query.
+
+ >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'})
+ >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', 'key2' : 'val2'}
+ 1
+
+
+ This method is recommended over regular L{set} 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{set} before sending
+ the next one.
+
+ @param mapping: A dict of key/value pairs to set.
+ @param time: Tells memcached the time which this value should expire, either
+ as a delta number of seconds, or an absolute unix time-since-the-epoch
+ value. See the memcached protocol docs section "Storage Commands"
+ for more info on <exptime>. We default to 0 == cache forever.
+ @param key_prefix: Optional string to prepend to each key when sending to memcache. Allows you to efficiently stuff these keys into a pseudo-namespace in memcache:
+ >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'}, key_prefix='subspace_')
+ >>> len(notset_keys) == 0
+ True
+ >>> mc.get_multi(['subspace_key1', 'subspace_key2']) == {'subspace_key1' : 'val1', 'subspace_key2' : 'val2'}
+ True
+
+ Causes key 'subspace_key1' and 'subspace_key2' to be set. Useful in conjunction with a higher-level layer which applies namespaces to data in memcache.
+ In this case, the return result would be the list of notset original keys, prefix not applied.
+
+ @param min_compress_len: The threshold length to kick in auto-compression
+ of the value using the zlib.compress() routine. If the value being cached is
+ a string, then the length of the string is measured, else if the value is an
+ object, then the length of the pickle result is measured. If the resulting
+ attempt at compression yeilds a larger string than the input, then it is
+ discarded. For backwards compatability, this parameter defaults to 0,
+ indicating don't ever try to compress.
+ @return: List of keys which failed to be stored [ memcache out of memory, etc. ].
+ @rtype: list
+
+ '''
+
+ self._statlog('set_multi')
+
+
+
+ server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(mapping.iterkeys(), key_prefix)
+
+ # send out all requests on each server before reading anything
+ dead_servers = []
+
+ for server in server_keys.iterkeys():
+ bigcmd = []
+ write = bigcmd.append
+ try:
+ for key in server_keys[server]: # These are mangled keys
+ store_info = self._val_to_store_info(mapping[prefixed_to_orig_key[key]], min_compress_len)
+ write("set %s %d %d %d\r\n%s\r\n" % (key, store_info[0], time, store_info[1], store_info[2]))
+ server.send_cmds(''.join(bigcmd))
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ 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]
+
+ # short-circuit if there are no servers, just return all keys
+ if not server_keys: return(mapping.keys())
+
+ notstored = [] # original keys.
+ for server, keys in server_keys.iteritems():
+ try:
+ for key in keys:
+ line = server.readline()
+ if line == 'STORED':
+ continue
+ else:
+ notstored.append(prefixed_to_orig_key[key]) #un-mangle.
+ except (_Error, socket.error), msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return notstored
+
+ def _val_to_store_info(self, val, min_compress_len):
+ """
+ Transform val to a storable representation, returning a tuple of the flags, the length of the new value, and the new value itself.
+ """
+ flags = 0
+ if isinstance(val, str):
+ pass
+ elif isinstance(val, int):
+ flags |= Client._FLAG_INTEGER
+ val = "%d" % val
+ # force no attempt to compress this silly string.
+ min_compress_len = 0
+ elif isinstance(val, long):
+ flags |= Client._FLAG_LONG
+ val = "%d" % val
+ # force no attempt to compress this silly string.
+ min_compress_len = 0
+ else:
+ flags |= Client._FLAG_PICKLE
+ file = StringIO()
+ if self.picklerIsKeyword:
+ pickler = self.pickler(file, protocol = self.pickleProtocol)
+ else:
+ pickler = self.pickler(file, self.pickleProtocol)
+ if self.persistent_id:
+ pickler.persistent_id = self.persistent_id
+ pickler.dump(val)
+ val = file.getvalue()
+
+ lv = len(val)
+ # We should try to compress if min_compress_len > 0 and we could
+ # import zlib and this string is longer than our min threshold.
+ if min_compress_len and _supports_compress and lv > min_compress_len:
+ comp_val = compress(val)
+ # Only retain the result if the compression result is smaller
+ # than the original.
+ if len(comp_val) < lv:
+ flags |= Client._FLAG_COMPRESSED
+ val = comp_val
+
+ # silently do not store if value length exceeds maximum
+ if self.server_max_value_length != 0 and \
+ len(val) >= self.server_max_value_length: return(0)
+
+ return (flags, len(val), val)
+
+ def _set(self, cmd, key, val, time, min_compress_len = 0):
+ self.check_key(key)
+ server, key = self._get_server(key)
+ if not server:
+ return 0
+
+ self._statlog(cmd)
+
+ store_info = self._val_to_store_info(val, min_compress_len)
+ if not store_info: return(0)
+
+ if cmd == 'cas':
+ if key not in self.cas_ids:
+ return self._set('set', key, val, time, min_compress_len)
+ fullcmd = "%s %s %d %d %d %d\r\n%s" % (
+ cmd, key, store_info[0], time, store_info[1],
+ self.cas_ids[key], store_info[2])
+ else:
+ fullcmd = "%s %s %d %d %d\r\n%s" % (
+ cmd, key, store_info[0], time, store_info[1], store_info[2])
+
+ try:
+ server.send_cmd(fullcmd)
+ return(server.expect("STORED") == "STORED")
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return 0
+
+ def _get(self, cmd, key):
+ self.check_key(key)
+ server, key = self._get_server(key)
+ if not server:
+ return None
+
+ self._statlog(cmd)
+
+ try:
+ server.send_cmd("%s %s" % (cmd, key))
+ rkey = flags = rlen = cas_id = None
+ if cmd == 'gets':
+ rkey, flags, rlen, cas_id, = self._expect_cas_value(server)
+ if rkey:
+ self.cas_ids[rkey] = cas_id
+ else:
+ 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 isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return None
+ return value
+
+ def get(self, key):
+ '''Retrieves a key from the memcache.
+
+ @return: The value or None.
+ '''
+ return self._get('get', key)
+
+ def gets(self, key):
+ '''Retrieves a key from the memcache. Used in conjunction with 'cas'.
+
+ @return: The value or None.
+ '''
+ return self._get('gets', key)
+
+ def get_multi(self, keys, key_prefix=''):
+ '''
+ 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
+ >>> mc.set_multi({'k1' : 1, 'k2' : 2}, key_prefix='pfx_') == []
+ 1
+
+ This looks up keys 'pfx_k1', 'pfx_k2', ... . Returned dict will just have unprefixed keys 'k1', 'k2'.
+ >>> mc.get_multi(['k1', 'k2', 'nonexist'], key_prefix='pfx_') == {'k1' : 1, 'k2' : 2}
+ 1
+
+ get_mult [ and L{set_multi} ] can take str()-ables like ints / longs as keys too. Such as your db pri key fields.
+ They're rotored through str() before being passed off to memcache, with or without the use of a key_prefix.
+ In this mode, the key_prefix could be a table name, and the key itself a db primary key number.
+
+ >>> mc.set_multi({42: 'douglass adams', 46 : 'and 2 just ahead of me'}, key_prefix='numkeys_') == []
+ 1
+ >>> mc.get_multi([46, 42], key_prefix='numkeys_') == {42: 'douglass adams', 46 : 'and 2 just ahead of me'}
+ 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.
+
+ See also L{set_multi}.
+
+ @param keys: An array of keys.
+ @param key_prefix: A string to prefix each key when we communicate with memcache.
+ Facilitates pseudo-namespaces within memcache. Returned dictionary keys will not have this prefix.
+ @return: A dictionary of key/value pairs that were available. If key_prefix was provided, the keys in the retured dictionary will not have it present.
+
+ '''
+
+ self._statlog('get_multi')
+
+ server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(keys, key_prefix)
+
+ # send out all requests on each server before reading anything
+ dead_servers = []
+ for server in server_keys.iterkeys():
+ try:
+ server.send_cmd("get %s" % " ".join(server_keys[server]))
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ 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.iterkeys():
+ try:
+ line = server.readline()
+ while line and line != 'END':
+ rkey, flags, rlen = self._expectvalue(server, line)
+ # Bo Yang reports that this can sometimes be None
+ if rkey is not None:
+ val = self._recv_value(server, flags, rlen)
+ retvals[prefixed_to_orig_key[rkey]] = val # un-prefix returned key.
+ line = server.readline()
+ except (_Error, socket.error), msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return retvals
+
+ def _expect_cas_value(self, server, line=None):
+ if not line:
+ line = server.readline()
+
+ if line[:5] == 'VALUE':
+ resp, rkey, flags, len, cas_id = line.split()
+ return (rkey, int(flags), int(len), int(cas_id))
+ else:
+ return (None, None, None, None)
+
+ 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 & Client._FLAG_COMPRESSED:
+ buf = decompress(buf)
+
+ if flags == 0 or flags == Client._FLAG_COMPRESSED:
+ # Either a bare string or a compressed string now decompressed...
+ val = buf
+ elif flags & Client._FLAG_INTEGER:
+ val = int(buf)
+ elif flags & Client._FLAG_LONG:
+ val = long(buf)
+ elif flags & Client._FLAG_PICKLE:
+ try:
+ file = StringIO(buf)
+ unpickler = self.unpickler(file)
+ if self.persistent_load:
+ unpickler.persistent_load = self.persistent_load
+ val = unpickler.load()
+ except Exception, e:
+ self.debuglog('Pickle error: %s\n' % e)
+ val = None
+ else:
+ self.debuglog("unknown flags on get: %x\n" % flags)
+
+ return val
+
+ def check_key(self, key, key_extra_len=0):
+ """Checks sanity of key. Fails if:
+ Key length is > SERVER_MAX_KEY_LENGTH (Raises MemcachedKeyLength).
+ Contains control characters (Raises MemcachedKeyCharacterError).
+ Is not a string (Raises MemcachedStringEncodingError)
+ Is an unicode string (Raises MemcachedStringEncodingError)
+ Is not a string (Raises MemcachedKeyError)
+ Is None (Raises MemcachedKeyError)
+ """
+ if isinstance(key, tuple): key = key[1]
+ if not key:
+ raise Client.MemcachedKeyNoneError("Key is None")
+ if isinstance(key, unicode):
+ raise Client.MemcachedStringEncodingError(
+ "Keys must be str()'s, not unicode. Convert your unicode "
+ "strings using mystring.encode(charset)!")
+ if not isinstance(key, str):
+ raise Client.MemcachedKeyTypeError("Key must be str()'s")
+
+ if isinstance(key, basestring):
+ if self.server_max_key_length != 0 and \
+ len(key) + key_extra_len > self.server_max_key_length:
+ raise Client.MemcachedKeyLengthError("Key length is > %s"
+ % self.server_max_key_length)
+ for char in key:
+ if ord(char) < 33 or ord(char) == 127:
+ raise Client.MemcachedKeyCharacterError(
+ "Control characters not allowed")
+
+
+class _Host(object):
+ _DEAD_RETRY = 30 # number of seconds before retrying a dead server.
+ _SOCKET_TIMEOUT = 3 # number of seconds before sockets timeout.
+
+ def __init__(self, host, debug=0):
+ self.debug = debug
+ if isinstance(host, tuple):
+ host, self.weight = host
+ else:
+ self.weight = 1
+
+ # parse the connection string
+ m = re.match(r'^(?P<proto>unix):(?P<path>.*)$', host)
+ if not m:
+ m = re.match(r'^(?P<proto>inet):'
+ r'(?P<host>[^:]+)(:(?P<port>[0-9]+))?$', host)
+ if not m: m = re.match(r'^(?P<host>[^:]+):(?P<port>[0-9]+)$', host)
+ if not m:
+ raise ValueError('Unable to parse connection string: "%s"' % host)
+
+ hostData = m.groupdict()
+ if hostData.get('proto') == 'unix':
+ self.family = socket.AF_UNIX
+ self.address = hostData['path']
+ else:
+ self.family = socket.AF_INET
+ self.ip = hostData['host']
+ self.port = int(hostData.get('port', 11211))
+ self.address = ( self.ip, self.port )
+
+ self.deaduntil = 0
+ self.socket = None
+
+ self.buffer = ''
+
+ def debuglog(self, str):
+ if self.debug:
+ sys.stderr.write("MemCached: %s\n" % str)
+
+ 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):
+ self.debuglog("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(self.family, socket.SOCK_STREAM)
+ if hasattr(s, 'settimeout'): s.settimeout(self._SOCKET_TIMEOUT)
+ try:
+ s.connect(self.address)
+ except socket.timeout, msg:
+ self.mark_dead("connect: %s" % msg)
+ return None
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ self.mark_dead("connect: %s" % msg[1])
+ return None
+ self.socket = s
+ self.buffer = ''
+ 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 send_cmds(self, cmds):
+ """ cmds already has trailing \r\n's applied """
+ self.socket.sendall(cmds)
+
+ def readline(self):
+ buf = self.buffer
+ recv = self.socket.recv
+ while True:
+ index = buf.find('\r\n')
+ if index >= 0:
+ break
+ data = recv(4096)
+ if not data:
+ self.mark_dead('Connection closed while reading from %s'
+ % repr(self))
+ self.buffer = ''
+ return None
+ buf += data
+ self.buffer = buf[index+2:]
+ return buf[:index]
+
+ 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):
+ self_socket_recv = self.socket.recv
+ buf = self.buffer
+ while len(buf) < rlen:
+ foo = self_socket_recv(max(rlen - len(buf), 4096))
+ buf += foo
+ if not foo:
+ raise _Error( 'Read %d bytes, expecting %d, '
+ 'read returned 0 length bytes' % ( len(buf), rlen ))
+ self.buffer = buf[rlen:]
+ return buf[:rlen]
+
+ def __str__(self):
+ d = ''
+ if self.deaduntil:
+ d = " (dead until %d)" % self.deaduntil
+
+ if self.family == socket.AF_INET:
+ return "inet:%s:%d%s" % (self.address[0], self.address[1], d)
+ else:
+ return "unix:%s%s" % (self.address, 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__":
+ failures = 0
+ print "Testing docstrings..."
+ _doctest()
+ print "Running tests:"
+ print
+ serverList = [["127.0.0.1:11211"]]
+ if '--do-unix' in sys.argv:
+ serverList.append([os.path.join(os.getcwd(), 'memcached.socket')])
+
+ for servers in serverList:
+ mc = Client(servers, debug=1)
+
+ def to_s(val):
+ if not isinstance(val, basestring):
+ 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"; failures = failures + 1
+ return 0
+
+
+ class FooStruct(object):
+ 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"; failures = failures + 1
+ 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"; failures = failures + 1
+
+ print "Testing decr ...",
+ x = mc.decr("an_integer", 1)
+ if x == 42:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+ sys.stdout.flush()
+
+ # sanity tests
+ print "Testing sending spaces...",
+ sys.stdout.flush()
+ try:
+ x = mc.set("this has spaces", 1)
+ except Client.MemcachedKeyCharacterError, msg:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing sending control characters...",
+ try:
+ x = mc.set("this\x10has\x11control characters\x02", 1)
+ except Client.MemcachedKeyCharacterError, msg:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing using insanely long key...",
+ try:
+ x = mc.set('a'*SERVER_MAX_KEY_LENGTH + 'aaaa', 1)
+ except Client.MemcachedKeyLengthError, msg:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing sending a unicode-string key...",
+ try:
+ x = mc.set(u'keyhere', 1)
+ except Client.MemcachedStringEncodingError, msg:
+ print "OK",
+ else:
+ print "FAIL",; failures = failures + 1
+ try:
+ x = mc.set((u'a'*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1)
+ except:
+ print "FAIL",; failures = failures + 1
+ else:
+ print "OK",
+ import pickle
+ s = pickle.loads('V\\u4f1a\np0\n.')
+ try:
+ x = mc.set((s*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1)
+ except Client.MemcachedKeyLengthError:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing using a value larger than the memcached value limit...",
+ x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH)
+ if mc.get('keyhere') == None:
+ print "OK",
+ else:
+ print "FAIL",; failures = failures + 1
+ x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH + 'aaa')
+ if mc.get('keyhere') == None:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing set_multi() with no memcacheds running",
+ mc.disconnect_all()
+ errors = mc.set_multi({'keyhere' : 'a', 'keythere' : 'b'})
+ if errors != []:
+ print "FAIL"; failures = failures + 1
+ else:
+ print "OK"
+
+ print "Testing delete_multi() with no memcacheds running",
+ mc.disconnect_all()
+ ret = mc.delete_multi({'keyhere' : 'a', 'keythere' : 'b'})
+ if ret != 1:
+ print "FAIL"; failures = failures + 1
+ else:
+ print "OK"
+
+ if failures > 0:
+ print '*** THERE WERE FAILED TESTS'
+ sys.exit(1)
+ sys.exit(0)
+
+
+# vim: ts=4 sw=4 et :
Copied: branches/4.1.x/server/memcached/src/main/resources/memcached_client.py (from rev 2022, trunk/server/memcached/src/main/resources/memcached_client.py)
===================================================================
--- branches/4.1.x/server/memcached/src/main/resources/memcached_client.py (rev 0)
+++ branches/4.1.x/server/memcached/src/main/resources/memcached_client.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+
+"""
+Sample Python-based memcached command-line application. Makes use of Danga's 'memcache'
+Python module to communicate with memcached.
+
+Overview
+========
+
+See U{the MemCached homepage<http://www.danga.com/memcached>} for more about memcached, and
+U{Infinispan's project page<http://www.infinispan.org>} for more information about Infinispan.
+
+Usage summary
+=============
+
+This command-line script is operated as follows:
+
+ $ ./memcached_client.py GET <key>
+
+Would return an entry under key from the memcached server.
+
+ $ ./memcached_client.py PUT <key> <value>
+
+Places a value in the memcached server.
+
+ $ ./memcached_client.py REMOVE <key>
+
+Removes an entry on the server.
+
+Server endpoints
+================
+
+This script can connect to a number of server endpoints, and randomly selects an endpoint to use
+with each invocation. Please edit this script to edit the server endpoint list.
+"""
+
+__author__ = "Manik Surtani"
+__version__ = "Infinispan 4.1"
+__copyright__ = "Copyright (C) 2010 Red Hat Middleware LLC"
+__license__ = "LGPL"
+
+import memcache
+import math
+import random
+import sys
+
+endpoints = ["127.0.0.1:11211", "127.0.0.1:11212", "127.0.0.1:11213"]
+
+def connect():
+ ipaddress = choose_random_endpoint()
+ mc = memcache.Client([ipaddress], debug=1)
+ print "Connecting to endpoint {0}".format(ipaddress)
+ return mc
+
+def put(k, v):
+ conn = connect()
+ conn.set(k, v)
+
+def get(k):
+ return connect().get(k)
+
+def remove(k):
+ connect().delete(k)
+
+def usage(msg = None):
+ if msg:
+ print 'Error: %s' % msg
+ print '''
+ Usage:
+ memcached_client.py [GET|PUT|REMOVE] <key> <value>
+
+ GET: Retrieves a value from Infinispan. Key is required.
+ PUT: Stores a value in Infinispan. Key and Value are required.
+ REMOVE: Removes an entry from Infinispan. Key is required.
+
+'''
+ sys.exit(1)
+
+def main():
+ if len(sys.argv) < 3:
+ usage(msg = "Not enough parameters!")
+ cmd = sys.argv[1]
+ key = sys.argv[2]
+
+ cmd = cmd.strip().upper()
+ if cmd == "GET":
+ retval = get(key)
+ print "The value of [%s] is [%s]" % (key, retval)
+ elif cmd == "REMOVE":
+ remove(key)
+ print "Ok, removed entry under key [%s]" % key
+ elif cmd == "PUT":
+ if len(sys.argv) < 4:
+ usage(msg = "Not enough parameters!")
+ value = sys.argv[3]
+ put(key, value)
+ print "Ok, set value of [%s] as [%s]" % (key, value)
+ else:
+ usage(msg = "Unknown command %s" % cmd)
+
+def choose_random_endpoint():
+ return endpoints[random.randrange(0, len(endpoints))]
+
+if __name__ == "__main__":
+ main()
Deleted: branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_client.py
===================================================================
--- branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_client.py 2010-07-12 17:31:53 UTC (rev 2024)
+++ branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_client.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -1,72 +0,0 @@
-#!/usr/bin/python
-
-#
-# Sample python code using the standard memcached library to talk to Infinispan memcached server
-# To use it, make sure you install Python memcached client library
-#
-
-import memcache
-import time
-
-ip = "127.0.0.1"
-port = "11211"
-ipaddress = ip + ':' + port
-mc = memcache.Client([ipaddress], debug=1)
-
-print "Connecting to {0}".format(ipaddress)
-
-def setAndGet(mc, k, v):
- mc.set(k, v)
- get(mc, k, v)
-
-def get(mc, k, v):
- value = mc.get(k)
- if value == v:
- print "OK"
- else:
- print "FAIL"
-
-def setAndGetNone(mc, k, v, t):
- mc.set(k, v, t)
- time.sleep(t + 2)
- value = mc.get(k)
- if value == None:
- print "OK"
- else:
- print "FAIL"
-
-def delete(mc, k):
- ret = mc.delete(k)
- if ret != 0:
- print "OK"
- else:
- print "FAIL"
-
-print "Testing set/get ['{0}': {1}] ...".format("Simple_Key", "Simple value"),
-setAndGet(mc, "Simple_Key", "Simple value")
-
-print "Testing delete ['{0}'] ...".format("Simple_Key"),
-delete(mc, "Simple_Key")
-
-print "Testing set/get ['{0}' : {1} : {2}] ...".format("Expiring_Key", 999, 3),
-setAndGetNone(mc, "Expiring_Key", 999, 3)
-
-print "Testing increment 3 times ['{0}' : starting at {1} ] ...".format("Incr_Decr_Key", "1"),
-mc.set("Incr_Decr_Key", "1") # note that the key used for incr/decr must be a string.
-mc.incr("Incr_Decr_Key")
-mc.incr("Incr_Decr_Key")
-mc.incr("Incr_Decr_Key")
-get(mc, "Incr_Decr_Key", "4")
-
-print "Testing decrement 1 time ['{0}' : starting at {1} ] ...".format("Incr_Decr_Key", "4"),
-mc.decr("Incr_Decr_Key")
-get(mc, "Incr_Decr_Key", "3")
-
-print "Testing decrement 2 times in one call ['{0}' : {1} ] ...".format("Incr_Decr_Key", "3"),
-mc.decr("Incr_Decr_Key", 2)
-get(mc, "Incr_Decr_Key", "1")
-
-print "Finally, delete ['{0}'] ...".format("Incr_Decr_Key"),
-delete(mc, "Incr_Decr_Key")
-
-## For more information see http://community.jboss.org/wiki/UsingInfinispanMemcachedServer
\ No newline at end of file
Deleted: branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_reader.py
===================================================================
--- branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_reader.py 2010-07-12 17:31:53 UTC (rev 2024)
+++ branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_reader.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -1,58 +0,0 @@
-#!/usr/bin/python
-
-#
-# Sample python code using the standard memcached library to talk to Infinispan memcached server
-# To use it, make sure you install Python memcached client library
-# This particular script tests that it's reading from one of the clustered servers correctly
-#
-
-import memcache
-import time
-
-ip = "127.0.0.1"
-port = "13211"
-ipaddress = ip + ':' + port
-mc = memcache.Client([ipaddress], debug=1)
-
-print "Connecting to {0}".format(ipaddress)
-
-def get(mc, key, expected) :
- value = mc.get(key)
- if value == expected:
- print "OK"
- else:
- print "FAIL"
-
-def getNone(mc, key):
- value = mc.get(key)
- if value == None:
- print "OK"
- else:
- print "FAIL"
-
-key = "Simple_Key"
-expected = "Simple value"
-print "Testing get ['{0}'] should return {1} ...".format(key, expected),
-get(mc, key, expected)
-
-key = "Expiring_Key"
-print "Testing get ['{0}'] should return nothing...".format(key),
-time.sleep(5)
-getNone(mc, key)
-
-key = "Incr_Key"
-expected = "4"
-print "Testing get ['{0}'] should return {1} ...".format(key, expected),
-get(mc, key, expected)
-
-key = "Decr_Key"
-expected = "3"
-print "Testing get ['{0}'] should return {1} ...".format(key, expected),
-get(mc, key, expected)
-
-key = "Multi_Decr_Key"
-expected = "1"
-print "Testing get ['{0}'] should return {1} ...".format(key, expected),
-get(mc, key, expected)
-
-## For more information see http://community.jboss.org/wiki/UsingInfinispanMemcachedServer
\ No newline at end of file
Deleted: branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_writer.py
===================================================================
--- branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_writer.py 2010-07-12 17:31:53 UTC (rev 2024)
+++ branches/4.1.x/server/memcached/src/main/resources/sample_python_memcached_writer.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -1,79 +0,0 @@
-#!/usr/bin/python
-
-#
-# Sample python code using the standard memcached library to talk to Infinispan memcached server
-# To use it, make sure you install Python memcached client library
-# This particular script tests that it's writing to the one of the clustered servers correctly
-#
-
-import memcache
-import time
-
-ip = "127.0.0.1"
-port = "12211"
-ipaddress = ip + ':' + port
-mc = memcache.Client([ipaddress], debug=1)
-
-print "Connecting to {0}".format(ipaddress)
-
-def set(mc, key, val, time = 0):
- ret = mc.set(key, val, time)
- if ret != 0:
- print "OK"
- else:
- print "FAIL: returned {0}".format(ret)
-
-def incr(mc, expected, key, delta = 1):
- ret = mc.incr(key, delta)
- if ret == expected:
- print "OK"
- else:
- print "FAIL: returned {0}".format(ret)
-
-def decr(mc, expected, key, delta = 1):
- ret = mc.decr(key, delta)
- if ret == expected:
- print "OK"
- else:
- print "FAIL: returned {0}".format(ret)
-
-key = "Simple_Key"
-value = "Simple value"
-print "Testing set ['{0}': {1}] ...".format(key, value),
-set(mc, key, value)
-
-key = "Expiring_Key"
-value = 999
-expiry = 3
-print "Testing set ['{0}' : {1} : {2}] ...".format(key, value, expiry),
-set(mc, key, value, expiry)
-
-key = "Incr_Key"
-value = "1"
-print "Testing increment 3 times ['{0}' : starting at {1} ]".format(key, value)
-print "Initialise at {0} ...".format(value),
-set(mc, key, value) # note that the key used for incr/decr must be a string.
-print "Increment by one ...",
-incr(mc, 2, key)
-print "Increment again ...",
-incr(mc, 3, key)
-print "Increment yet again ...",
-incr(mc, 4, key)
-
-key = "Decr_Key"
-value = "4"
-print "Testing decrement 1 time ['{0}' : starting at {1} ]".format(key, value)
-print "Initialise at {0} ...".format(value),
-set(mc, key, value)
-print "Decrement by one ...",
-decr(mc, 3, key)
-
-key = "Multi_Decr_Key"
-value = "3"
-print "Testing decrement 2 times in one call ['{0}' : {1} ]".format(key, value)
-print "Initialise at {0} ...".format(value),
-set(mc, key, value)
-print "Decrement by 2 ...",
-decr(mc, 1, "Multi_Decr_Key", 2)
-
-## For more information see http://community.jboss.org/wiki/UsingInfinispanMemcachedServer
Copied: branches/4.1.x/server/memcached/src/test/resources (from rev 2022, trunk/server/memcached/src/test/resources)
Deleted: branches/4.1.x/server/memcached/src/test/resources/memcache.py
===================================================================
--- trunk/server/memcached/src/test/resources/memcache.py 2010-07-12 16:18:27 UTC (rev 2022)
+++ branches/4.1.x/server/memcached/src/test/resources/memcache.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -1,1241 +0,0 @@
-#!/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(key, 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 os
-import re
-try:
- import cPickle as pickle
-except ImportError:
- import pickle
-
-from binascii import crc32 # zlib version is not cross-platform
-def cmemcache_hash(key):
- return((((crc32(key) & 0xffffffff) >> 16) & 0x7fff) or 1)
-serverHashFunction = cmemcache_hash
-
-def useOldServerHashFunction():
- """Use the old python-memcache server hash function."""
- serverHashFunction = crc32
-
-try:
- from zlib import compress, decompress
- _supports_compress = True
-except ImportError:
- _supports_compress = False
- # quickly define a decompress just in case we recv compressed data.
- def decompress(val):
- raise _Error("received compressed data but I don't support compession (import error)")
-
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-
-
-__author__ = "Evan Martin <martine at danga.com>"
-__version__ = "1.45"
-__copyright__ = "Copyright (C) 2003 Danga Interactive"
-__license__ = "Python"
-
-SERVER_MAX_KEY_LENGTH = 250
-# Storing values larger than 1MB requires recompiling memcached. If you do,
-# this value can be changed by doing "memcache.SERVER_MAX_VALUE_LENGTH = N"
-# after importing this module.
-SERVER_MAX_VALUE_LENGTH = 1024*1024
-
-class _Error(Exception):
- pass
-
-try:
- # Only exists in Python 2.4+
- from threading import local
-except ImportError:
- # TODO: add the pure-python local implementation
- class local(object):
- pass
-
-
-class Client(local):
- """
- 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, set_multi
- @group Retrieval: get, get_multi
- @group Integers: incr, decr
- @group Removal: delete, delete_multi
- @sort: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog,\
- set, set_multi, add, replace, get, get_multi, incr, decr, delete, delete_multi
- """
- _FLAG_PICKLE = 1<<0
- _FLAG_INTEGER = 1<<1
- _FLAG_LONG = 1<<2
- _FLAG_COMPRESSED = 1<<3
-
- _SERVER_RETRIES = 10 # how many times to try finding a free server.
-
- # exceptions for Client
- class MemcachedKeyError(Exception):
- pass
- class MemcachedKeyLengthError(MemcachedKeyError):
- pass
- class MemcachedKeyCharacterError(MemcachedKeyError):
- pass
- class MemcachedKeyNoneError(MemcachedKeyError):
- pass
- class MemcachedKeyTypeError(MemcachedKeyError):
- pass
- class MemcachedStringEncodingError(Exception):
- pass
-
- def __init__(self, servers, debug=0, pickleProtocol=0,
- pickler=pickle.Pickler, unpickler=pickle.Unpickler,
- pload=None, pid=None, server_max_key_length=SERVER_MAX_KEY_LENGTH,
- server_max_value_length=SERVER_MAX_VALUE_LENGTH):
- """
- 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.
- @param pickleProtocol: number to mandate protocol used by (c)Pickle.
- @param pickler: optional override of default Pickler to allow subclassing.
- @param unpickler: optional override of default Unpickler to allow subclassing.
- @param pload: optional persistent_load function to call on pickle loading.
- Useful for cPickle since subclassing isn't allowed.
- @param pid: optional persistent_id function to call on pickle storing.
- Useful for cPickle since subclassing isn't allowed.
- """
- local.__init__(self)
- self.debug = debug
- self.set_servers(servers)
- self.stats = {}
- self.cas_ids = {}
-
- # Allow users to modify pickling/unpickling behavior
- self.pickleProtocol = pickleProtocol
- self.pickler = pickler
- self.unpickler = unpickler
- self.persistent_load = pload
- self.persistent_id = pid
- self.server_max_key_length = server_max_key_length
- self.server_max_value_length = server_max_value_length
-
- # figure out the pickler style
- file = StringIO()
- try:
- pickler = self.pickler(file, protocol = self.pickleProtocol)
- self.picklerIsKeyword = True
- except TypeError:
- self.picklerIsKeyword = False
-
- 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.debug) for s in servers]
- self._init_buckets()
-
- def get_stats(self):
- '''Get statistics from each of the servers.
-
- @return: A list of tuples ( server_identifier, stats_dictionary ).
- The dictionary contains a number of name/value pairs specifying
- the name of the status field and the string value associated with
- it. The values are not converted from strings.
- '''
- data = []
- for s in self.servers:
- if not s.connect(): continue
- if s.family == socket.AF_INET:
- name = '%s:%s (%s)' % ( s.ip, s.port, s.weight )
- else:
- name = 'unix:%s (%s)' % ( s.address, s.weight )
- s.send_cmd('stats')
- serverData = {}
- data.append(( name, serverData ))
- readline = s.readline
- while 1:
- line = readline()
- if not line or line.strip() == 'END': break
- stats = line.split(' ', 2)
- serverData[stats[1]] = stats[2]
-
- return(data)
-
- def get_slabs(self):
- data = []
- for s in self.servers:
- if not s.connect(): continue
- if s.family == socket.AF_INET:
- name = '%s:%s (%s)' % ( s.ip, s.port, s.weight )
- else:
- name = 'unix:%s (%s)' % ( s.address, s.weight )
- serverData = {}
- data.append(( name, serverData ))
- s.send_cmd('stats items')
- readline = s.readline
- while 1:
- line = readline()
- if not line or line.strip() == 'END': break
- item = line.split(' ', 2)
- #0 = STAT, 1 = ITEM, 2 = Value
- slab = item[1].split(':', 2)
- #0 = items, 1 = Slab #, 2 = Name
- if slab[1] not in serverData:
- serverData[slab[1]] = {}
- serverData[slab[1]][slab[2]] = item[2]
- return data
-
- def flush_all(self):
- 'Expire all data currently in the memcache servers.'
- for s in self.servers:
- if not s.connect(): continue
- s.send_cmd('flush_all')
- s.expect("OK")
-
- def debuglog(self, str):
- if self.debug:
- sys.stderr.write("MemCached: %s\n" % str)
-
- def _statlog(self, func):
- if func not in self.stats:
- 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.deaduntil = 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 isinstance(key, tuple):
- serverhash, key = key
- else:
- serverhash = serverHashFunction(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 = serverHashFunction(str(serverhash) + str(i))
- return None, None
-
- def disconnect_all(self):
- for s in self.servers:
- s.close_socket()
-
- def delete_multi(self, keys, time=0, key_prefix=''):
- '''
- Delete multiple keys in the memcache doing just one query.
-
- >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'})
- >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', 'key2' : 'val2'}
- 1
- >>> mc.delete_multi(['key1', 'key2'])
- 1
- >>> mc.get_multi(['key1', 'key2']) == {}
- 1
-
-
- This method is recommended over iterated regular L{delete}s as it reduces total latency, since
- your app doesn't have to wait for each round-trip of L{delete} before sending
- the next one.
-
- @param keys: An iterable of keys to clear
- @param time: number of seconds any subsequent set / update commands should fail. Defaults to 0 for no delay.
- @param key_prefix: Optional string to prepend to each key when sending to memcache.
- See docs for L{get_multi} and L{set_multi}.
-
- @return: 1 if no failure in communication with any memcacheds.
- @rtype: int
-
- '''
-
- self._statlog('delete_multi')
-
- server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(keys, key_prefix)
-
- # send out all requests on each server before reading anything
- dead_servers = []
-
- rc = 1
- for server in server_keys.iterkeys():
- bigcmd = []
- write = bigcmd.append
- if time != None:
- for key in server_keys[server]: # These are mangled keys
- write("delete %s %d\r\n" % (key, time))
- else:
- for key in server_keys[server]: # These are mangled keys
- write("delete %s\r\n" % key)
- try:
- server.send_cmds(''.join(bigcmd))
- except socket.error, msg:
- rc = 0
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- 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]
-
- notstored = [] # original keys.
- for server, keys in server_keys.iteritems():
- try:
- for key in keys:
- server.expect("DELETED")
- except socket.error, msg:
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- rc = 0
- return rc
-
- def delete(self, key, time=0):
- '''Deletes a key from the memcache.
-
- @return: Nonzero on success.
- @param time: number of seconds any subsequent set / update commands
- should fail. Defaults to 0 for no delay.
- @rtype: int
- '''
- self.check_key(key)
- 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:
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- 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):
- self.check_key(key)
- 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()
- if line.strip() =='NOT_FOUND': return None
- return int(line)
- except socket.error, msg:
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- return None
-
- def add(self, key, val, time = 0, min_compress_len = 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, min_compress_len)
-
- def append(self, key, val, time=0, min_compress_len=0):
- '''Append the value to the end of the existing key's value.
-
- Only stores in memcache if key already exists.
- Also see L{prepend}.
-
- @return: Nonzero on success.
- @rtype: int
- '''
- return self._set("append", key, val, time, min_compress_len)
-
- def prepend(self, key, val, time=0, min_compress_len=0):
- '''Prepend the value to the beginning of the existing key's value.
-
- Only stores in memcache if key already exists.
- Also see L{append}.
-
- @return: Nonzero on success.
- @rtype: int
- '''
- return self._set("prepend", key, val, time, min_compress_len)
-
- def replace(self, key, val, time=0, min_compress_len=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, min_compress_len)
-
- def set(self, key, val, time=0, min_compress_len=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 server hash value and the second being the key.
- 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
- @param time: Tells memcached the time which this value should expire, either
- as a delta number of seconds, or an absolute unix time-since-the-epoch
- value. See the memcached protocol docs section "Storage Commands"
- for more info on <exptime>. We default to 0 == cache forever.
- @param min_compress_len: The threshold length to kick in auto-compression
- of the value using the zlib.compress() routine. If the value being cached is
- a string, then the length of the string is measured, else if the value is an
- object, then the length of the pickle result is measured. If the resulting
- attempt at compression yeilds a larger string than the input, then it is
- discarded. For backwards compatability, this parameter defaults to 0,
- indicating don't ever try to compress.
- '''
- return self._set("set", key, val, time, min_compress_len)
-
-
- def cas(self, key, val, time=0, min_compress_len=0):
- '''Sets a key to a given value in the memcache if it hasn't been
- altered since last fetched. (See L{gets}).
-
- The C{key} can optionally be an tuple, with the first element
- being the server hash value and the second being the key.
- 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
- @param time: Tells memcached the time which this value should expire,
- either as a delta number of seconds, or an absolute unix
- time-since-the-epoch value. See the memcached protocol docs section
- "Storage Commands" for more info on <exptime>. We default to
- 0 == cache forever.
- @param min_compress_len: The threshold length to kick in
- auto-compression of the value using the zlib.compress() routine. If
- the value being cached is a string, then the length of the string is
- measured, else if the value is an object, then the length of the
- pickle result is measured. If the resulting attempt at compression
- yeilds a larger string than the input, then it is discarded. For
- backwards compatability, this parameter defaults to 0, indicating
- don't ever try to compress.
- '''
- return self._set("cas", key, val, time, min_compress_len)
-
-
- def _map_and_prefix_keys(self, key_iterable, key_prefix):
- """Compute the mapping of server (_Host instance) -> list of keys to stuff onto that server, as well as the mapping of
- prefixed key -> original key.
-
-
- """
- # Check it just once ...
- key_extra_len=len(key_prefix)
- if key_prefix:
- self.check_key(key_prefix)
-
- # server (_Host) -> list of unprefixed server keys in mapping
- server_keys = {}
-
- prefixed_to_orig_key = {}
- # build up a list for each server of all the keys we want.
- for orig_key in key_iterable:
- if isinstance(orig_key, tuple):
- # Tuple of hashvalue, key ala _get_server(). Caller is essentially telling us what server to stuff this on.
- # Ensure call to _get_server gets a Tuple as well.
- str_orig_key = str(orig_key[1])
- server, key = self._get_server((orig_key[0], key_prefix + str_orig_key)) # Gotta pre-mangle key before hashing to a server. Returns the mangled key.
- else:
- str_orig_key = str(orig_key) # set_multi supports int / long keys.
- server, key = self._get_server(key_prefix + str_orig_key)
-
- # Now check to make sure key length is proper ...
- self.check_key(str_orig_key, key_extra_len=key_extra_len)
-
- if not server:
- continue
-
- if server not in server_keys:
- server_keys[server] = []
- server_keys[server].append(key)
- prefixed_to_orig_key[key] = orig_key
-
- return (server_keys, prefixed_to_orig_key)
-
- def set_multi(self, mapping, time=0, key_prefix='', min_compress_len=0):
- '''
- Sets multiple keys in the memcache doing just one query.
-
- >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'})
- >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', 'key2' : 'val2'}
- 1
-
-
- This method is recommended over regular L{set} 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{set} before sending
- the next one.
-
- @param mapping: A dict of key/value pairs to set.
- @param time: Tells memcached the time which this value should expire, either
- as a delta number of seconds, or an absolute unix time-since-the-epoch
- value. See the memcached protocol docs section "Storage Commands"
- for more info on <exptime>. We default to 0 == cache forever.
- @param key_prefix: Optional string to prepend to each key when sending to memcache. Allows you to efficiently stuff these keys into a pseudo-namespace in memcache:
- >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'}, key_prefix='subspace_')
- >>> len(notset_keys) == 0
- True
- >>> mc.get_multi(['subspace_key1', 'subspace_key2']) == {'subspace_key1' : 'val1', 'subspace_key2' : 'val2'}
- True
-
- Causes key 'subspace_key1' and 'subspace_key2' to be set. Useful in conjunction with a higher-level layer which applies namespaces to data in memcache.
- In this case, the return result would be the list of notset original keys, prefix not applied.
-
- @param min_compress_len: The threshold length to kick in auto-compression
- of the value using the zlib.compress() routine. If the value being cached is
- a string, then the length of the string is measured, else if the value is an
- object, then the length of the pickle result is measured. If the resulting
- attempt at compression yeilds a larger string than the input, then it is
- discarded. For backwards compatability, this parameter defaults to 0,
- indicating don't ever try to compress.
- @return: List of keys which failed to be stored [ memcache out of memory, etc. ].
- @rtype: list
-
- '''
-
- self._statlog('set_multi')
-
-
-
- server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(mapping.iterkeys(), key_prefix)
-
- # send out all requests on each server before reading anything
- dead_servers = []
-
- for server in server_keys.iterkeys():
- bigcmd = []
- write = bigcmd.append
- try:
- for key in server_keys[server]: # These are mangled keys
- store_info = self._val_to_store_info(mapping[prefixed_to_orig_key[key]], min_compress_len)
- write("set %s %d %d %d\r\n%s\r\n" % (key, store_info[0], time, store_info[1], store_info[2]))
- server.send_cmds(''.join(bigcmd))
- except socket.error, msg:
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- 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]
-
- # short-circuit if there are no servers, just return all keys
- if not server_keys: return(mapping.keys())
-
- notstored = [] # original keys.
- for server, keys in server_keys.iteritems():
- try:
- for key in keys:
- line = server.readline()
- if line == 'STORED':
- continue
- else:
- notstored.append(prefixed_to_orig_key[key]) #un-mangle.
- except (_Error, socket.error), msg:
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- return notstored
-
- def _val_to_store_info(self, val, min_compress_len):
- """
- Transform val to a storable representation, returning a tuple of the flags, the length of the new value, and the new value itself.
- """
- flags = 0
- if isinstance(val, str):
- pass
- elif isinstance(val, int):
- flags |= Client._FLAG_INTEGER
- val = "%d" % val
- # force no attempt to compress this silly string.
- min_compress_len = 0
- elif isinstance(val, long):
- flags |= Client._FLAG_LONG
- val = "%d" % val
- # force no attempt to compress this silly string.
- min_compress_len = 0
- else:
- flags |= Client._FLAG_PICKLE
- file = StringIO()
- if self.picklerIsKeyword:
- pickler = self.pickler(file, protocol = self.pickleProtocol)
- else:
- pickler = self.pickler(file, self.pickleProtocol)
- if self.persistent_id:
- pickler.persistent_id = self.persistent_id
- pickler.dump(val)
- val = file.getvalue()
-
- lv = len(val)
- # We should try to compress if min_compress_len > 0 and we could
- # import zlib and this string is longer than our min threshold.
- if min_compress_len and _supports_compress and lv > min_compress_len:
- comp_val = compress(val)
- # Only retain the result if the compression result is smaller
- # than the original.
- if len(comp_val) < lv:
- flags |= Client._FLAG_COMPRESSED
- val = comp_val
-
- # silently do not store if value length exceeds maximum
- if self.server_max_value_length != 0 and \
- len(val) >= self.server_max_value_length: return(0)
-
- return (flags, len(val), val)
-
- def _set(self, cmd, key, val, time, min_compress_len = 0):
- self.check_key(key)
- server, key = self._get_server(key)
- if not server:
- return 0
-
- self._statlog(cmd)
-
- store_info = self._val_to_store_info(val, min_compress_len)
- if not store_info: return(0)
-
- if cmd == 'cas':
- if key not in self.cas_ids:
- return self._set('set', key, val, time, min_compress_len)
- fullcmd = "%s %s %d %d %d %d\r\n%s" % (
- cmd, key, store_info[0], time, store_info[1],
- self.cas_ids[key], store_info[2])
- else:
- fullcmd = "%s %s %d %d %d\r\n%s" % (
- cmd, key, store_info[0], time, store_info[1], store_info[2])
-
- try:
- server.send_cmd(fullcmd)
- return(server.expect("STORED") == "STORED")
- except socket.error, msg:
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- return 0
-
- def _get(self, cmd, key):
- self.check_key(key)
- server, key = self._get_server(key)
- if not server:
- return None
-
- self._statlog(cmd)
-
- try:
- server.send_cmd("%s %s" % (cmd, key))
- rkey = flags = rlen = cas_id = None
- if cmd == 'gets':
- rkey, flags, rlen, cas_id, = self._expect_cas_value(server)
- if rkey:
- self.cas_ids[rkey] = cas_id
- else:
- 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 isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- return None
- return value
-
- def get(self, key):
- '''Retrieves a key from the memcache.
-
- @return: The value or None.
- '''
- return self._get('get', key)
-
- def gets(self, key):
- '''Retrieves a key from the memcache. Used in conjunction with 'cas'.
-
- @return: The value or None.
- '''
- return self._get('gets', key)
-
- def get_multi(self, keys, key_prefix=''):
- '''
- 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
- >>> mc.set_multi({'k1' : 1, 'k2' : 2}, key_prefix='pfx_') == []
- 1
-
- This looks up keys 'pfx_k1', 'pfx_k2', ... . Returned dict will just have unprefixed keys 'k1', 'k2'.
- >>> mc.get_multi(['k1', 'k2', 'nonexist'], key_prefix='pfx_') == {'k1' : 1, 'k2' : 2}
- 1
-
- get_mult [ and L{set_multi} ] can take str()-ables like ints / longs as keys too. Such as your db pri key fields.
- They're rotored through str() before being passed off to memcache, with or without the use of a key_prefix.
- In this mode, the key_prefix could be a table name, and the key itself a db primary key number.
-
- >>> mc.set_multi({42: 'douglass adams', 46 : 'and 2 just ahead of me'}, key_prefix='numkeys_') == []
- 1
- >>> mc.get_multi([46, 42], key_prefix='numkeys_') == {42: 'douglass adams', 46 : 'and 2 just ahead of me'}
- 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.
-
- See also L{set_multi}.
-
- @param keys: An array of keys.
- @param key_prefix: A string to prefix each key when we communicate with memcache.
- Facilitates pseudo-namespaces within memcache. Returned dictionary keys will not have this prefix.
- @return: A dictionary of key/value pairs that were available. If key_prefix was provided, the keys in the retured dictionary will not have it present.
-
- '''
-
- self._statlog('get_multi')
-
- server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(keys, key_prefix)
-
- # send out all requests on each server before reading anything
- dead_servers = []
- for server in server_keys.iterkeys():
- try:
- server.send_cmd("get %s" % " ".join(server_keys[server]))
- except socket.error, msg:
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- 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.iterkeys():
- try:
- line = server.readline()
- while line and line != 'END':
- rkey, flags, rlen = self._expectvalue(server, line)
- # Bo Yang reports that this can sometimes be None
- if rkey is not None:
- val = self._recv_value(server, flags, rlen)
- retvals[prefixed_to_orig_key[rkey]] = val # un-prefix returned key.
- line = server.readline()
- except (_Error, socket.error), msg:
- if isinstance(msg, tuple): msg = msg[1]
- server.mark_dead(msg)
- return retvals
-
- def _expect_cas_value(self, server, line=None):
- if not line:
- line = server.readline()
-
- if line[:5] == 'VALUE':
- resp, rkey, flags, len, cas_id = line.split()
- return (rkey, int(flags), int(len), int(cas_id))
- else:
- return (None, None, None, None)
-
- 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 & Client._FLAG_COMPRESSED:
- buf = decompress(buf)
-
- if flags == 0 or flags == Client._FLAG_COMPRESSED:
- # Either a bare string or a compressed string now decompressed...
- val = buf
- elif flags & Client._FLAG_INTEGER:
- val = int(buf)
- elif flags & Client._FLAG_LONG:
- val = long(buf)
- elif flags & Client._FLAG_PICKLE:
- try:
- file = StringIO(buf)
- unpickler = self.unpickler(file)
- if self.persistent_load:
- unpickler.persistent_load = self.persistent_load
- val = unpickler.load()
- except Exception, e:
- self.debuglog('Pickle error: %s\n' % e)
- val = None
- else:
- self.debuglog("unknown flags on get: %x\n" % flags)
-
- return val
-
- def check_key(self, key, key_extra_len=0):
- """Checks sanity of key. Fails if:
- Key length is > SERVER_MAX_KEY_LENGTH (Raises MemcachedKeyLength).
- Contains control characters (Raises MemcachedKeyCharacterError).
- Is not a string (Raises MemcachedStringEncodingError)
- Is an unicode string (Raises MemcachedStringEncodingError)
- Is not a string (Raises MemcachedKeyError)
- Is None (Raises MemcachedKeyError)
- """
- if isinstance(key, tuple): key = key[1]
- if not key:
- raise Client.MemcachedKeyNoneError("Key is None")
- if isinstance(key, unicode):
- raise Client.MemcachedStringEncodingError(
- "Keys must be str()'s, not unicode. Convert your unicode "
- "strings using mystring.encode(charset)!")
- if not isinstance(key, str):
- raise Client.MemcachedKeyTypeError("Key must be str()'s")
-
- if isinstance(key, basestring):
- if self.server_max_key_length != 0 and \
- len(key) + key_extra_len > self.server_max_key_length:
- raise Client.MemcachedKeyLengthError("Key length is > %s"
- % self.server_max_key_length)
- for char in key:
- if ord(char) < 33 or ord(char) == 127:
- raise Client.MemcachedKeyCharacterError(
- "Control characters not allowed")
-
-
-class _Host(object):
- _DEAD_RETRY = 30 # number of seconds before retrying a dead server.
- _SOCKET_TIMEOUT = 3 # number of seconds before sockets timeout.
-
- def __init__(self, host, debug=0):
- self.debug = debug
- if isinstance(host, tuple):
- host, self.weight = host
- else:
- self.weight = 1
-
- # parse the connection string
- m = re.match(r'^(?P<proto>unix):(?P<path>.*)$', host)
- if not m:
- m = re.match(r'^(?P<proto>inet):'
- r'(?P<host>[^:]+)(:(?P<port>[0-9]+))?$', host)
- if not m: m = re.match(r'^(?P<host>[^:]+):(?P<port>[0-9]+)$', host)
- if not m:
- raise ValueError('Unable to parse connection string: "%s"' % host)
-
- hostData = m.groupdict()
- if hostData.get('proto') == 'unix':
- self.family = socket.AF_UNIX
- self.address = hostData['path']
- else:
- self.family = socket.AF_INET
- self.ip = hostData['host']
- self.port = int(hostData.get('port', 11211))
- self.address = ( self.ip, self.port )
-
- self.deaduntil = 0
- self.socket = None
-
- self.buffer = ''
-
- def debuglog(self, str):
- if self.debug:
- sys.stderr.write("MemCached: %s\n" % str)
-
- 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):
- self.debuglog("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(self.family, socket.SOCK_STREAM)
- if hasattr(s, 'settimeout'): s.settimeout(self._SOCKET_TIMEOUT)
- try:
- s.connect(self.address)
- except socket.timeout, msg:
- self.mark_dead("connect: %s" % msg)
- return None
- except socket.error, msg:
- if isinstance(msg, tuple): msg = msg[1]
- self.mark_dead("connect: %s" % msg[1])
- return None
- self.socket = s
- self.buffer = ''
- 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 send_cmds(self, cmds):
- """ cmds already has trailing \r\n's applied """
- self.socket.sendall(cmds)
-
- def readline(self):
- buf = self.buffer
- recv = self.socket.recv
- while True:
- index = buf.find('\r\n')
- if index >= 0:
- break
- data = recv(4096)
- if not data:
- self.mark_dead('Connection closed while reading from %s'
- % repr(self))
- self.buffer = ''
- return None
- buf += data
- self.buffer = buf[index+2:]
- return buf[:index]
-
- 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):
- self_socket_recv = self.socket.recv
- buf = self.buffer
- while len(buf) < rlen:
- foo = self_socket_recv(max(rlen - len(buf), 4096))
- buf += foo
- if not foo:
- raise _Error( 'Read %d bytes, expecting %d, '
- 'read returned 0 length bytes' % ( len(buf), rlen ))
- self.buffer = buf[rlen:]
- return buf[:rlen]
-
- def __str__(self):
- d = ''
- if self.deaduntil:
- d = " (dead until %d)" % self.deaduntil
-
- if self.family == socket.AF_INET:
- return "inet:%s:%d%s" % (self.address[0], self.address[1], d)
- else:
- return "unix:%s%s" % (self.address, 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__":
- failures = 0
- print "Testing docstrings..."
- _doctest()
- print "Running tests:"
- print
- serverList = [["127.0.0.1:11211"]]
- if '--do-unix' in sys.argv:
- serverList.append([os.path.join(os.getcwd(), 'memcached.socket')])
-
- for servers in serverList:
- mc = Client(servers, debug=1)
-
- def to_s(val):
- if not isinstance(val, basestring):
- 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"; failures = failures + 1
- return 0
-
-
- class FooStruct(object):
- 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"; failures = failures + 1
- 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"; failures = failures + 1
-
- print "Testing decr ...",
- x = mc.decr("an_integer", 1)
- if x == 42:
- print "OK"
- else:
- print "FAIL"; failures = failures + 1
- sys.stdout.flush()
-
- # sanity tests
- print "Testing sending spaces...",
- sys.stdout.flush()
- try:
- x = mc.set("this has spaces", 1)
- except Client.MemcachedKeyCharacterError, msg:
- print "OK"
- else:
- print "FAIL"; failures = failures + 1
-
- print "Testing sending control characters...",
- try:
- x = mc.set("this\x10has\x11control characters\x02", 1)
- except Client.MemcachedKeyCharacterError, msg:
- print "OK"
- else:
- print "FAIL"; failures = failures + 1
-
- print "Testing using insanely long key...",
- try:
- x = mc.set('a'*SERVER_MAX_KEY_LENGTH + 'aaaa', 1)
- except Client.MemcachedKeyLengthError, msg:
- print "OK"
- else:
- print "FAIL"; failures = failures + 1
-
- print "Testing sending a unicode-string key...",
- try:
- x = mc.set(u'keyhere', 1)
- except Client.MemcachedStringEncodingError, msg:
- print "OK",
- else:
- print "FAIL",; failures = failures + 1
- try:
- x = mc.set((u'a'*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1)
- except:
- print "FAIL",; failures = failures + 1
- else:
- print "OK",
- import pickle
- s = pickle.loads('V\\u4f1a\np0\n.')
- try:
- x = mc.set((s*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1)
- except Client.MemcachedKeyLengthError:
- print "OK"
- else:
- print "FAIL"; failures = failures + 1
-
- print "Testing using a value larger than the memcached value limit...",
- x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH)
- if mc.get('keyhere') == None:
- print "OK",
- else:
- print "FAIL",; failures = failures + 1
- x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH + 'aaa')
- if mc.get('keyhere') == None:
- print "OK"
- else:
- print "FAIL"; failures = failures + 1
-
- print "Testing set_multi() with no memcacheds running",
- mc.disconnect_all()
- errors = mc.set_multi({'keyhere' : 'a', 'keythere' : 'b'})
- if errors != []:
- print "FAIL"; failures = failures + 1
- else:
- print "OK"
-
- print "Testing delete_multi() with no memcacheds running",
- mc.disconnect_all()
- ret = mc.delete_multi({'keyhere' : 'a', 'keythere' : 'b'})
- if ret != 1:
- print "FAIL"; failures = failures + 1
- else:
- print "OK"
-
- if failures > 0:
- print '*** THERE WERE FAILED TESTS'
- sys.exit(1)
- sys.exit(0)
-
-
-# vim: ts=4 sw=4 et :
Copied: branches/4.1.x/server/memcached/src/test/resources/memcache.py (from rev 2022, trunk/server/memcached/src/test/resources/memcache.py)
===================================================================
--- branches/4.1.x/server/memcached/src/test/resources/memcache.py (rev 0)
+++ branches/4.1.x/server/memcached/src/test/resources/memcache.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -0,0 +1,1241 @@
+#!/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(key, 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 os
+import re
+try:
+ import cPickle as pickle
+except ImportError:
+ import pickle
+
+from binascii import crc32 # zlib version is not cross-platform
+def cmemcache_hash(key):
+ return((((crc32(key) & 0xffffffff) >> 16) & 0x7fff) or 1)
+serverHashFunction = cmemcache_hash
+
+def useOldServerHashFunction():
+ """Use the old python-memcache server hash function."""
+ serverHashFunction = crc32
+
+try:
+ from zlib import compress, decompress
+ _supports_compress = True
+except ImportError:
+ _supports_compress = False
+ # quickly define a decompress just in case we recv compressed data.
+ def decompress(val):
+ raise _Error("received compressed data but I don't support compession (import error)")
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+
+__author__ = "Evan Martin <martine at danga.com>"
+__version__ = "1.45"
+__copyright__ = "Copyright (C) 2003 Danga Interactive"
+__license__ = "Python"
+
+SERVER_MAX_KEY_LENGTH = 250
+# Storing values larger than 1MB requires recompiling memcached. If you do,
+# this value can be changed by doing "memcache.SERVER_MAX_VALUE_LENGTH = N"
+# after importing this module.
+SERVER_MAX_VALUE_LENGTH = 1024*1024
+
+class _Error(Exception):
+ pass
+
+try:
+ # Only exists in Python 2.4+
+ from threading import local
+except ImportError:
+ # TODO: add the pure-python local implementation
+ class local(object):
+ pass
+
+
+class Client(local):
+ """
+ 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, set_multi
+ @group Retrieval: get, get_multi
+ @group Integers: incr, decr
+ @group Removal: delete, delete_multi
+ @sort: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog,\
+ set, set_multi, add, replace, get, get_multi, incr, decr, delete, delete_multi
+ """
+ _FLAG_PICKLE = 1<<0
+ _FLAG_INTEGER = 1<<1
+ _FLAG_LONG = 1<<2
+ _FLAG_COMPRESSED = 1<<3
+
+ _SERVER_RETRIES = 10 # how many times to try finding a free server.
+
+ # exceptions for Client
+ class MemcachedKeyError(Exception):
+ pass
+ class MemcachedKeyLengthError(MemcachedKeyError):
+ pass
+ class MemcachedKeyCharacterError(MemcachedKeyError):
+ pass
+ class MemcachedKeyNoneError(MemcachedKeyError):
+ pass
+ class MemcachedKeyTypeError(MemcachedKeyError):
+ pass
+ class MemcachedStringEncodingError(Exception):
+ pass
+
+ def __init__(self, servers, debug=0, pickleProtocol=0,
+ pickler=pickle.Pickler, unpickler=pickle.Unpickler,
+ pload=None, pid=None, server_max_key_length=SERVER_MAX_KEY_LENGTH,
+ server_max_value_length=SERVER_MAX_VALUE_LENGTH):
+ """
+ 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.
+ @param pickleProtocol: number to mandate protocol used by (c)Pickle.
+ @param pickler: optional override of default Pickler to allow subclassing.
+ @param unpickler: optional override of default Unpickler to allow subclassing.
+ @param pload: optional persistent_load function to call on pickle loading.
+ Useful for cPickle since subclassing isn't allowed.
+ @param pid: optional persistent_id function to call on pickle storing.
+ Useful for cPickle since subclassing isn't allowed.
+ """
+ local.__init__(self)
+ self.debug = debug
+ self.set_servers(servers)
+ self.stats = {}
+ self.cas_ids = {}
+
+ # Allow users to modify pickling/unpickling behavior
+ self.pickleProtocol = pickleProtocol
+ self.pickler = pickler
+ self.unpickler = unpickler
+ self.persistent_load = pload
+ self.persistent_id = pid
+ self.server_max_key_length = server_max_key_length
+ self.server_max_value_length = server_max_value_length
+
+ # figure out the pickler style
+ file = StringIO()
+ try:
+ pickler = self.pickler(file, protocol = self.pickleProtocol)
+ self.picklerIsKeyword = True
+ except TypeError:
+ self.picklerIsKeyword = False
+
+ 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.debug) for s in servers]
+ self._init_buckets()
+
+ def get_stats(self):
+ '''Get statistics from each of the servers.
+
+ @return: A list of tuples ( server_identifier, stats_dictionary ).
+ The dictionary contains a number of name/value pairs specifying
+ the name of the status field and the string value associated with
+ it. The values are not converted from strings.
+ '''
+ data = []
+ for s in self.servers:
+ if not s.connect(): continue
+ if s.family == socket.AF_INET:
+ name = '%s:%s (%s)' % ( s.ip, s.port, s.weight )
+ else:
+ name = 'unix:%s (%s)' % ( s.address, s.weight )
+ s.send_cmd('stats')
+ serverData = {}
+ data.append(( name, serverData ))
+ readline = s.readline
+ while 1:
+ line = readline()
+ if not line or line.strip() == 'END': break
+ stats = line.split(' ', 2)
+ serverData[stats[1]] = stats[2]
+
+ return(data)
+
+ def get_slabs(self):
+ data = []
+ for s in self.servers:
+ if not s.connect(): continue
+ if s.family == socket.AF_INET:
+ name = '%s:%s (%s)' % ( s.ip, s.port, s.weight )
+ else:
+ name = 'unix:%s (%s)' % ( s.address, s.weight )
+ serverData = {}
+ data.append(( name, serverData ))
+ s.send_cmd('stats items')
+ readline = s.readline
+ while 1:
+ line = readline()
+ if not line or line.strip() == 'END': break
+ item = line.split(' ', 2)
+ #0 = STAT, 1 = ITEM, 2 = Value
+ slab = item[1].split(':', 2)
+ #0 = items, 1 = Slab #, 2 = Name
+ if slab[1] not in serverData:
+ serverData[slab[1]] = {}
+ serverData[slab[1]][slab[2]] = item[2]
+ return data
+
+ def flush_all(self):
+ 'Expire all data currently in the memcache servers.'
+ for s in self.servers:
+ if not s.connect(): continue
+ s.send_cmd('flush_all')
+ s.expect("OK")
+
+ def debuglog(self, str):
+ if self.debug:
+ sys.stderr.write("MemCached: %s\n" % str)
+
+ def _statlog(self, func):
+ if func not in self.stats:
+ 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.deaduntil = 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 isinstance(key, tuple):
+ serverhash, key = key
+ else:
+ serverhash = serverHashFunction(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 = serverHashFunction(str(serverhash) + str(i))
+ return None, None
+
+ def disconnect_all(self):
+ for s in self.servers:
+ s.close_socket()
+
+ def delete_multi(self, keys, time=0, key_prefix=''):
+ '''
+ Delete multiple keys in the memcache doing just one query.
+
+ >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'})
+ >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', 'key2' : 'val2'}
+ 1
+ >>> mc.delete_multi(['key1', 'key2'])
+ 1
+ >>> mc.get_multi(['key1', 'key2']) == {}
+ 1
+
+
+ This method is recommended over iterated regular L{delete}s as it reduces total latency, since
+ your app doesn't have to wait for each round-trip of L{delete} before sending
+ the next one.
+
+ @param keys: An iterable of keys to clear
+ @param time: number of seconds any subsequent set / update commands should fail. Defaults to 0 for no delay.
+ @param key_prefix: Optional string to prepend to each key when sending to memcache.
+ See docs for L{get_multi} and L{set_multi}.
+
+ @return: 1 if no failure in communication with any memcacheds.
+ @rtype: int
+
+ '''
+
+ self._statlog('delete_multi')
+
+ server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(keys, key_prefix)
+
+ # send out all requests on each server before reading anything
+ dead_servers = []
+
+ rc = 1
+ for server in server_keys.iterkeys():
+ bigcmd = []
+ write = bigcmd.append
+ if time != None:
+ for key in server_keys[server]: # These are mangled keys
+ write("delete %s %d\r\n" % (key, time))
+ else:
+ for key in server_keys[server]: # These are mangled keys
+ write("delete %s\r\n" % key)
+ try:
+ server.send_cmds(''.join(bigcmd))
+ except socket.error, msg:
+ rc = 0
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ 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]
+
+ notstored = [] # original keys.
+ for server, keys in server_keys.iteritems():
+ try:
+ for key in keys:
+ server.expect("DELETED")
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ rc = 0
+ return rc
+
+ def delete(self, key, time=0):
+ '''Deletes a key from the memcache.
+
+ @return: Nonzero on success.
+ @param time: number of seconds any subsequent set / update commands
+ should fail. Defaults to 0 for no delay.
+ @rtype: int
+ '''
+ self.check_key(key)
+ 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:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ 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):
+ self.check_key(key)
+ 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()
+ if line.strip() =='NOT_FOUND': return None
+ return int(line)
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return None
+
+ def add(self, key, val, time = 0, min_compress_len = 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, min_compress_len)
+
+ def append(self, key, val, time=0, min_compress_len=0):
+ '''Append the value to the end of the existing key's value.
+
+ Only stores in memcache if key already exists.
+ Also see L{prepend}.
+
+ @return: Nonzero on success.
+ @rtype: int
+ '''
+ return self._set("append", key, val, time, min_compress_len)
+
+ def prepend(self, key, val, time=0, min_compress_len=0):
+ '''Prepend the value to the beginning of the existing key's value.
+
+ Only stores in memcache if key already exists.
+ Also see L{append}.
+
+ @return: Nonzero on success.
+ @rtype: int
+ '''
+ return self._set("prepend", key, val, time, min_compress_len)
+
+ def replace(self, key, val, time=0, min_compress_len=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, min_compress_len)
+
+ def set(self, key, val, time=0, min_compress_len=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 server hash value and the second being the key.
+ 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
+ @param time: Tells memcached the time which this value should expire, either
+ as a delta number of seconds, or an absolute unix time-since-the-epoch
+ value. See the memcached protocol docs section "Storage Commands"
+ for more info on <exptime>. We default to 0 == cache forever.
+ @param min_compress_len: The threshold length to kick in auto-compression
+ of the value using the zlib.compress() routine. If the value being cached is
+ a string, then the length of the string is measured, else if the value is an
+ object, then the length of the pickle result is measured. If the resulting
+ attempt at compression yeilds a larger string than the input, then it is
+ discarded. For backwards compatability, this parameter defaults to 0,
+ indicating don't ever try to compress.
+ '''
+ return self._set("set", key, val, time, min_compress_len)
+
+
+ def cas(self, key, val, time=0, min_compress_len=0):
+ '''Sets a key to a given value in the memcache if it hasn't been
+ altered since last fetched. (See L{gets}).
+
+ The C{key} can optionally be an tuple, with the first element
+ being the server hash value and the second being the key.
+ 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
+ @param time: Tells memcached the time which this value should expire,
+ either as a delta number of seconds, or an absolute unix
+ time-since-the-epoch value. See the memcached protocol docs section
+ "Storage Commands" for more info on <exptime>. We default to
+ 0 == cache forever.
+ @param min_compress_len: The threshold length to kick in
+ auto-compression of the value using the zlib.compress() routine. If
+ the value being cached is a string, then the length of the string is
+ measured, else if the value is an object, then the length of the
+ pickle result is measured. If the resulting attempt at compression
+ yeilds a larger string than the input, then it is discarded. For
+ backwards compatability, this parameter defaults to 0, indicating
+ don't ever try to compress.
+ '''
+ return self._set("cas", key, val, time, min_compress_len)
+
+
+ def _map_and_prefix_keys(self, key_iterable, key_prefix):
+ """Compute the mapping of server (_Host instance) -> list of keys to stuff onto that server, as well as the mapping of
+ prefixed key -> original key.
+
+
+ """
+ # Check it just once ...
+ key_extra_len=len(key_prefix)
+ if key_prefix:
+ self.check_key(key_prefix)
+
+ # server (_Host) -> list of unprefixed server keys in mapping
+ server_keys = {}
+
+ prefixed_to_orig_key = {}
+ # build up a list for each server of all the keys we want.
+ for orig_key in key_iterable:
+ if isinstance(orig_key, tuple):
+ # Tuple of hashvalue, key ala _get_server(). Caller is essentially telling us what server to stuff this on.
+ # Ensure call to _get_server gets a Tuple as well.
+ str_orig_key = str(orig_key[1])
+ server, key = self._get_server((orig_key[0], key_prefix + str_orig_key)) # Gotta pre-mangle key before hashing to a server. Returns the mangled key.
+ else:
+ str_orig_key = str(orig_key) # set_multi supports int / long keys.
+ server, key = self._get_server(key_prefix + str_orig_key)
+
+ # Now check to make sure key length is proper ...
+ self.check_key(str_orig_key, key_extra_len=key_extra_len)
+
+ if not server:
+ continue
+
+ if server not in server_keys:
+ server_keys[server] = []
+ server_keys[server].append(key)
+ prefixed_to_orig_key[key] = orig_key
+
+ return (server_keys, prefixed_to_orig_key)
+
+ def set_multi(self, mapping, time=0, key_prefix='', min_compress_len=0):
+ '''
+ Sets multiple keys in the memcache doing just one query.
+
+ >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'})
+ >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', 'key2' : 'val2'}
+ 1
+
+
+ This method is recommended over regular L{set} 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{set} before sending
+ the next one.
+
+ @param mapping: A dict of key/value pairs to set.
+ @param time: Tells memcached the time which this value should expire, either
+ as a delta number of seconds, or an absolute unix time-since-the-epoch
+ value. See the memcached protocol docs section "Storage Commands"
+ for more info on <exptime>. We default to 0 == cache forever.
+ @param key_prefix: Optional string to prepend to each key when sending to memcache. Allows you to efficiently stuff these keys into a pseudo-namespace in memcache:
+ >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'}, key_prefix='subspace_')
+ >>> len(notset_keys) == 0
+ True
+ >>> mc.get_multi(['subspace_key1', 'subspace_key2']) == {'subspace_key1' : 'val1', 'subspace_key2' : 'val2'}
+ True
+
+ Causes key 'subspace_key1' and 'subspace_key2' to be set. Useful in conjunction with a higher-level layer which applies namespaces to data in memcache.
+ In this case, the return result would be the list of notset original keys, prefix not applied.
+
+ @param min_compress_len: The threshold length to kick in auto-compression
+ of the value using the zlib.compress() routine. If the value being cached is
+ a string, then the length of the string is measured, else if the value is an
+ object, then the length of the pickle result is measured. If the resulting
+ attempt at compression yeilds a larger string than the input, then it is
+ discarded. For backwards compatability, this parameter defaults to 0,
+ indicating don't ever try to compress.
+ @return: List of keys which failed to be stored [ memcache out of memory, etc. ].
+ @rtype: list
+
+ '''
+
+ self._statlog('set_multi')
+
+
+
+ server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(mapping.iterkeys(), key_prefix)
+
+ # send out all requests on each server before reading anything
+ dead_servers = []
+
+ for server in server_keys.iterkeys():
+ bigcmd = []
+ write = bigcmd.append
+ try:
+ for key in server_keys[server]: # These are mangled keys
+ store_info = self._val_to_store_info(mapping[prefixed_to_orig_key[key]], min_compress_len)
+ write("set %s %d %d %d\r\n%s\r\n" % (key, store_info[0], time, store_info[1], store_info[2]))
+ server.send_cmds(''.join(bigcmd))
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ 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]
+
+ # short-circuit if there are no servers, just return all keys
+ if not server_keys: return(mapping.keys())
+
+ notstored = [] # original keys.
+ for server, keys in server_keys.iteritems():
+ try:
+ for key in keys:
+ line = server.readline()
+ if line == 'STORED':
+ continue
+ else:
+ notstored.append(prefixed_to_orig_key[key]) #un-mangle.
+ except (_Error, socket.error), msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return notstored
+
+ def _val_to_store_info(self, val, min_compress_len):
+ """
+ Transform val to a storable representation, returning a tuple of the flags, the length of the new value, and the new value itself.
+ """
+ flags = 0
+ if isinstance(val, str):
+ pass
+ elif isinstance(val, int):
+ flags |= Client._FLAG_INTEGER
+ val = "%d" % val
+ # force no attempt to compress this silly string.
+ min_compress_len = 0
+ elif isinstance(val, long):
+ flags |= Client._FLAG_LONG
+ val = "%d" % val
+ # force no attempt to compress this silly string.
+ min_compress_len = 0
+ else:
+ flags |= Client._FLAG_PICKLE
+ file = StringIO()
+ if self.picklerIsKeyword:
+ pickler = self.pickler(file, protocol = self.pickleProtocol)
+ else:
+ pickler = self.pickler(file, self.pickleProtocol)
+ if self.persistent_id:
+ pickler.persistent_id = self.persistent_id
+ pickler.dump(val)
+ val = file.getvalue()
+
+ lv = len(val)
+ # We should try to compress if min_compress_len > 0 and we could
+ # import zlib and this string is longer than our min threshold.
+ if min_compress_len and _supports_compress and lv > min_compress_len:
+ comp_val = compress(val)
+ # Only retain the result if the compression result is smaller
+ # than the original.
+ if len(comp_val) < lv:
+ flags |= Client._FLAG_COMPRESSED
+ val = comp_val
+
+ # silently do not store if value length exceeds maximum
+ if self.server_max_value_length != 0 and \
+ len(val) >= self.server_max_value_length: return(0)
+
+ return (flags, len(val), val)
+
+ def _set(self, cmd, key, val, time, min_compress_len = 0):
+ self.check_key(key)
+ server, key = self._get_server(key)
+ if not server:
+ return 0
+
+ self._statlog(cmd)
+
+ store_info = self._val_to_store_info(val, min_compress_len)
+ if not store_info: return(0)
+
+ if cmd == 'cas':
+ if key not in self.cas_ids:
+ return self._set('set', key, val, time, min_compress_len)
+ fullcmd = "%s %s %d %d %d %d\r\n%s" % (
+ cmd, key, store_info[0], time, store_info[1],
+ self.cas_ids[key], store_info[2])
+ else:
+ fullcmd = "%s %s %d %d %d\r\n%s" % (
+ cmd, key, store_info[0], time, store_info[1], store_info[2])
+
+ try:
+ server.send_cmd(fullcmd)
+ return(server.expect("STORED") == "STORED")
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return 0
+
+ def _get(self, cmd, key):
+ self.check_key(key)
+ server, key = self._get_server(key)
+ if not server:
+ return None
+
+ self._statlog(cmd)
+
+ try:
+ server.send_cmd("%s %s" % (cmd, key))
+ rkey = flags = rlen = cas_id = None
+ if cmd == 'gets':
+ rkey, flags, rlen, cas_id, = self._expect_cas_value(server)
+ if rkey:
+ self.cas_ids[rkey] = cas_id
+ else:
+ 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 isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return None
+ return value
+
+ def get(self, key):
+ '''Retrieves a key from the memcache.
+
+ @return: The value or None.
+ '''
+ return self._get('get', key)
+
+ def gets(self, key):
+ '''Retrieves a key from the memcache. Used in conjunction with 'cas'.
+
+ @return: The value or None.
+ '''
+ return self._get('gets', key)
+
+ def get_multi(self, keys, key_prefix=''):
+ '''
+ 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
+ >>> mc.set_multi({'k1' : 1, 'k2' : 2}, key_prefix='pfx_') == []
+ 1
+
+ This looks up keys 'pfx_k1', 'pfx_k2', ... . Returned dict will just have unprefixed keys 'k1', 'k2'.
+ >>> mc.get_multi(['k1', 'k2', 'nonexist'], key_prefix='pfx_') == {'k1' : 1, 'k2' : 2}
+ 1
+
+ get_mult [ and L{set_multi} ] can take str()-ables like ints / longs as keys too. Such as your db pri key fields.
+ They're rotored through str() before being passed off to memcache, with or without the use of a key_prefix.
+ In this mode, the key_prefix could be a table name, and the key itself a db primary key number.
+
+ >>> mc.set_multi({42: 'douglass adams', 46 : 'and 2 just ahead of me'}, key_prefix='numkeys_') == []
+ 1
+ >>> mc.get_multi([46, 42], key_prefix='numkeys_') == {42: 'douglass adams', 46 : 'and 2 just ahead of me'}
+ 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.
+
+ See also L{set_multi}.
+
+ @param keys: An array of keys.
+ @param key_prefix: A string to prefix each key when we communicate with memcache.
+ Facilitates pseudo-namespaces within memcache. Returned dictionary keys will not have this prefix.
+ @return: A dictionary of key/value pairs that were available. If key_prefix was provided, the keys in the retured dictionary will not have it present.
+
+ '''
+
+ self._statlog('get_multi')
+
+ server_keys, prefixed_to_orig_key = self._map_and_prefix_keys(keys, key_prefix)
+
+ # send out all requests on each server before reading anything
+ dead_servers = []
+ for server in server_keys.iterkeys():
+ try:
+ server.send_cmd("get %s" % " ".join(server_keys[server]))
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ 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.iterkeys():
+ try:
+ line = server.readline()
+ while line and line != 'END':
+ rkey, flags, rlen = self._expectvalue(server, line)
+ # Bo Yang reports that this can sometimes be None
+ if rkey is not None:
+ val = self._recv_value(server, flags, rlen)
+ retvals[prefixed_to_orig_key[rkey]] = val # un-prefix returned key.
+ line = server.readline()
+ except (_Error, socket.error), msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ server.mark_dead(msg)
+ return retvals
+
+ def _expect_cas_value(self, server, line=None):
+ if not line:
+ line = server.readline()
+
+ if line[:5] == 'VALUE':
+ resp, rkey, flags, len, cas_id = line.split()
+ return (rkey, int(flags), int(len), int(cas_id))
+ else:
+ return (None, None, None, None)
+
+ 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 & Client._FLAG_COMPRESSED:
+ buf = decompress(buf)
+
+ if flags == 0 or flags == Client._FLAG_COMPRESSED:
+ # Either a bare string or a compressed string now decompressed...
+ val = buf
+ elif flags & Client._FLAG_INTEGER:
+ val = int(buf)
+ elif flags & Client._FLAG_LONG:
+ val = long(buf)
+ elif flags & Client._FLAG_PICKLE:
+ try:
+ file = StringIO(buf)
+ unpickler = self.unpickler(file)
+ if self.persistent_load:
+ unpickler.persistent_load = self.persistent_load
+ val = unpickler.load()
+ except Exception, e:
+ self.debuglog('Pickle error: %s\n' % e)
+ val = None
+ else:
+ self.debuglog("unknown flags on get: %x\n" % flags)
+
+ return val
+
+ def check_key(self, key, key_extra_len=0):
+ """Checks sanity of key. Fails if:
+ Key length is > SERVER_MAX_KEY_LENGTH (Raises MemcachedKeyLength).
+ Contains control characters (Raises MemcachedKeyCharacterError).
+ Is not a string (Raises MemcachedStringEncodingError)
+ Is an unicode string (Raises MemcachedStringEncodingError)
+ Is not a string (Raises MemcachedKeyError)
+ Is None (Raises MemcachedKeyError)
+ """
+ if isinstance(key, tuple): key = key[1]
+ if not key:
+ raise Client.MemcachedKeyNoneError("Key is None")
+ if isinstance(key, unicode):
+ raise Client.MemcachedStringEncodingError(
+ "Keys must be str()'s, not unicode. Convert your unicode "
+ "strings using mystring.encode(charset)!")
+ if not isinstance(key, str):
+ raise Client.MemcachedKeyTypeError("Key must be str()'s")
+
+ if isinstance(key, basestring):
+ if self.server_max_key_length != 0 and \
+ len(key) + key_extra_len > self.server_max_key_length:
+ raise Client.MemcachedKeyLengthError("Key length is > %s"
+ % self.server_max_key_length)
+ for char in key:
+ if ord(char) < 33 or ord(char) == 127:
+ raise Client.MemcachedKeyCharacterError(
+ "Control characters not allowed")
+
+
+class _Host(object):
+ _DEAD_RETRY = 30 # number of seconds before retrying a dead server.
+ _SOCKET_TIMEOUT = 3 # number of seconds before sockets timeout.
+
+ def __init__(self, host, debug=0):
+ self.debug = debug
+ if isinstance(host, tuple):
+ host, self.weight = host
+ else:
+ self.weight = 1
+
+ # parse the connection string
+ m = re.match(r'^(?P<proto>unix):(?P<path>.*)$', host)
+ if not m:
+ m = re.match(r'^(?P<proto>inet):'
+ r'(?P<host>[^:]+)(:(?P<port>[0-9]+))?$', host)
+ if not m: m = re.match(r'^(?P<host>[^:]+):(?P<port>[0-9]+)$', host)
+ if not m:
+ raise ValueError('Unable to parse connection string: "%s"' % host)
+
+ hostData = m.groupdict()
+ if hostData.get('proto') == 'unix':
+ self.family = socket.AF_UNIX
+ self.address = hostData['path']
+ else:
+ self.family = socket.AF_INET
+ self.ip = hostData['host']
+ self.port = int(hostData.get('port', 11211))
+ self.address = ( self.ip, self.port )
+
+ self.deaduntil = 0
+ self.socket = None
+
+ self.buffer = ''
+
+ def debuglog(self, str):
+ if self.debug:
+ sys.stderr.write("MemCached: %s\n" % str)
+
+ 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):
+ self.debuglog("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(self.family, socket.SOCK_STREAM)
+ if hasattr(s, 'settimeout'): s.settimeout(self._SOCKET_TIMEOUT)
+ try:
+ s.connect(self.address)
+ except socket.timeout, msg:
+ self.mark_dead("connect: %s" % msg)
+ return None
+ except socket.error, msg:
+ if isinstance(msg, tuple): msg = msg[1]
+ self.mark_dead("connect: %s" % msg[1])
+ return None
+ self.socket = s
+ self.buffer = ''
+ 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 send_cmds(self, cmds):
+ """ cmds already has trailing \r\n's applied """
+ self.socket.sendall(cmds)
+
+ def readline(self):
+ buf = self.buffer
+ recv = self.socket.recv
+ while True:
+ index = buf.find('\r\n')
+ if index >= 0:
+ break
+ data = recv(4096)
+ if not data:
+ self.mark_dead('Connection closed while reading from %s'
+ % repr(self))
+ self.buffer = ''
+ return None
+ buf += data
+ self.buffer = buf[index+2:]
+ return buf[:index]
+
+ 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):
+ self_socket_recv = self.socket.recv
+ buf = self.buffer
+ while len(buf) < rlen:
+ foo = self_socket_recv(max(rlen - len(buf), 4096))
+ buf += foo
+ if not foo:
+ raise _Error( 'Read %d bytes, expecting %d, '
+ 'read returned 0 length bytes' % ( len(buf), rlen ))
+ self.buffer = buf[rlen:]
+ return buf[:rlen]
+
+ def __str__(self):
+ d = ''
+ if self.deaduntil:
+ d = " (dead until %d)" % self.deaduntil
+
+ if self.family == socket.AF_INET:
+ return "inet:%s:%d%s" % (self.address[0], self.address[1], d)
+ else:
+ return "unix:%s%s" % (self.address, 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__":
+ failures = 0
+ print "Testing docstrings..."
+ _doctest()
+ print "Running tests:"
+ print
+ serverList = [["127.0.0.1:11211"]]
+ if '--do-unix' in sys.argv:
+ serverList.append([os.path.join(os.getcwd(), 'memcached.socket')])
+
+ for servers in serverList:
+ mc = Client(servers, debug=1)
+
+ def to_s(val):
+ if not isinstance(val, basestring):
+ 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"; failures = failures + 1
+ return 0
+
+
+ class FooStruct(object):
+ 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"; failures = failures + 1
+ 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"; failures = failures + 1
+
+ print "Testing decr ...",
+ x = mc.decr("an_integer", 1)
+ if x == 42:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+ sys.stdout.flush()
+
+ # sanity tests
+ print "Testing sending spaces...",
+ sys.stdout.flush()
+ try:
+ x = mc.set("this has spaces", 1)
+ except Client.MemcachedKeyCharacterError, msg:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing sending control characters...",
+ try:
+ x = mc.set("this\x10has\x11control characters\x02", 1)
+ except Client.MemcachedKeyCharacterError, msg:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing using insanely long key...",
+ try:
+ x = mc.set('a'*SERVER_MAX_KEY_LENGTH + 'aaaa', 1)
+ except Client.MemcachedKeyLengthError, msg:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing sending a unicode-string key...",
+ try:
+ x = mc.set(u'keyhere', 1)
+ except Client.MemcachedStringEncodingError, msg:
+ print "OK",
+ else:
+ print "FAIL",; failures = failures + 1
+ try:
+ x = mc.set((u'a'*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1)
+ except:
+ print "FAIL",; failures = failures + 1
+ else:
+ print "OK",
+ import pickle
+ s = pickle.loads('V\\u4f1a\np0\n.')
+ try:
+ x = mc.set((s*SERVER_MAX_KEY_LENGTH).encode('utf-8'), 1)
+ except Client.MemcachedKeyLengthError:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing using a value larger than the memcached value limit...",
+ x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH)
+ if mc.get('keyhere') == None:
+ print "OK",
+ else:
+ print "FAIL",; failures = failures + 1
+ x = mc.set('keyhere', 'a'*SERVER_MAX_VALUE_LENGTH + 'aaa')
+ if mc.get('keyhere') == None:
+ print "OK"
+ else:
+ print "FAIL"; failures = failures + 1
+
+ print "Testing set_multi() with no memcacheds running",
+ mc.disconnect_all()
+ errors = mc.set_multi({'keyhere' : 'a', 'keythere' : 'b'})
+ if errors != []:
+ print "FAIL"; failures = failures + 1
+ else:
+ print "OK"
+
+ print "Testing delete_multi() with no memcacheds running",
+ mc.disconnect_all()
+ ret = mc.delete_multi({'keyhere' : 'a', 'keythere' : 'b'})
+ if ret != 1:
+ print "FAIL"; failures = failures + 1
+ else:
+ print "OK"
+
+ if failures > 0:
+ print '*** THERE WERE FAILED TESTS'
+ sys.exit(1)
+ sys.exit(0)
+
+
+# vim: ts=4 sw=4 et :
Deleted: branches/4.1.x/server/memcached/src/test/resources/test_memcached_read.py
===================================================================
--- trunk/server/memcached/src/test/resources/test_memcached_read.py 2010-07-12 16:18:27 UTC (rev 2022)
+++ branches/4.1.x/server/memcached/src/test/resources/test_memcached_read.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -1,63 +0,0 @@
-#!/usr/bin/python
-
-#
-# Sample python code using the standard memcached library to talk to Infinispan memcached server
-# To use it, make sure you install Python memcached client library
-# This particular script tests that it's reading from one of the clustered servers correctly
-#
-
-__author__ = "Galder Zamarreno"
-__version__ = "Infinispan 4.1"
-__copyright__ = "Copyright (C) 2010 Red Hat Middleware LLC"
-__license__ = "LGPL"
-
-import memcache
-import time
-
-ip = "127.0.0.1"
-port = "13211"
-ipaddress = ip + ':' + port
-mc = memcache.Client([ipaddress], debug=1)
-
-print "Connecting to {0}".format(ipaddress)
-
-def get(mc, key, expected) :
- value = mc.get(key)
- if value == expected:
- print "OK"
- else:
- print "FAIL"
-
-def getNone(mc, key):
- value = mc.get(key)
- if value == None:
- print "OK"
- else:
- print "FAIL"
-
-key = "Simple_Key"
-expected = "Simple value"
-print "Testing get ['{0}'] should return {1} ...".format(key, expected),
-get(mc, key, expected)
-
-key = "Expiring_Key"
-print "Testing get ['{0}'] should return nothing...".format(key),
-time.sleep(5)
-getNone(mc, key)
-
-key = "Incr_Key"
-expected = "4"
-print "Testing get ['{0}'] should return {1} ...".format(key, expected),
-get(mc, key, expected)
-
-key = "Decr_Key"
-expected = "3"
-print "Testing get ['{0}'] should return {1} ...".format(key, expected),
-get(mc, key, expected)
-
-key = "Multi_Decr_Key"
-expected = "1"
-print "Testing get ['{0}'] should return {1} ...".format(key, expected),
-get(mc, key, expected)
-
-## For more information see http://community.jboss.org/wiki/UsingInfinispanMemcachedServer
\ No newline at end of file
Copied: branches/4.1.x/server/memcached/src/test/resources/test_memcached_read.py (from rev 2022, trunk/server/memcached/src/test/resources/test_memcached_read.py)
===================================================================
--- branches/4.1.x/server/memcached/src/test/resources/test_memcached_read.py (rev 0)
+++ branches/4.1.x/server/memcached/src/test/resources/test_memcached_read.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+
+#
+# Sample python code using the standard memcached library to talk to Infinispan memcached server
+# To use it, make sure you install Python memcached client library
+# This particular script tests that it's reading from one of the clustered servers correctly
+#
+
+__author__ = "Galder Zamarreno"
+__version__ = "Infinispan 4.1"
+__copyright__ = "Copyright (C) 2010 Red Hat Middleware LLC"
+__license__ = "LGPL"
+
+import memcache
+import time
+
+ip = "127.0.0.1"
+port = "13211"
+ipaddress = ip + ':' + port
+mc = memcache.Client([ipaddress], debug=1)
+
+print "Connecting to {0}".format(ipaddress)
+
+def get(mc, key, expected) :
+ value = mc.get(key)
+ if value == expected:
+ print "OK"
+ else:
+ print "FAIL"
+
+def getNone(mc, key):
+ value = mc.get(key)
+ if value == None:
+ print "OK"
+ else:
+ print "FAIL"
+
+key = "Simple_Key"
+expected = "Simple value"
+print "Testing get ['{0}'] should return {1} ...".format(key, expected),
+get(mc, key, expected)
+
+key = "Expiring_Key"
+print "Testing get ['{0}'] should return nothing...".format(key),
+time.sleep(5)
+getNone(mc, key)
+
+key = "Incr_Key"
+expected = "4"
+print "Testing get ['{0}'] should return {1} ...".format(key, expected),
+get(mc, key, expected)
+
+key = "Decr_Key"
+expected = "3"
+print "Testing get ['{0}'] should return {1} ...".format(key, expected),
+get(mc, key, expected)
+
+key = "Multi_Decr_Key"
+expected = "1"
+print "Testing get ['{0}'] should return {1} ...".format(key, expected),
+get(mc, key, expected)
+
+## For more information see http://community.jboss.org/wiki/UsingInfinispanMemcachedServer
\ No newline at end of file
Deleted: branches/4.1.x/server/memcached/src/test/resources/test_memcached_write.py
===================================================================
--- trunk/server/memcached/src/test/resources/test_memcached_write.py 2010-07-12 16:18:27 UTC (rev 2022)
+++ branches/4.1.x/server/memcached/src/test/resources/test_memcached_write.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -1,84 +0,0 @@
-#!/usr/bin/python
-
-#
-# Sample python code using the standard memcached library to talk to Infinispan memcached server
-# To use it, make sure you install Python memcached client library
-# This particular script tests that it's writing to the one of the clustered servers correctly
-#
-
-__author__ = "Galder Zamarreno"
-__version__ = "Infinispan 4.1"
-__copyright__ = "Copyright (C) 2010 Red Hat Middleware LLC"
-__license__ = "LGPL"
-
-import memcache
-import time
-
-ip = "127.0.0.1"
-port = "12211"
-ipaddress = ip + ':' + port
-mc = memcache.Client([ipaddress], debug=1)
-
-print "Connecting to {0}".format(ipaddress)
-
-def set(mc, key, val, time = 0):
- ret = mc.set(key, val, time)
- if ret != 0:
- print "OK"
- else:
- print "FAIL: returned {0}".format(ret)
-
-def incr(mc, expected, key, delta = 1):
- ret = mc.incr(key, delta)
- if ret == expected:
- print "OK"
- else:
- print "FAIL: returned {0}".format(ret)
-
-def decr(mc, expected, key, delta = 1):
- ret = mc.decr(key, delta)
- if ret == expected:
- print "OK"
- else:
- print "FAIL: returned {0}".format(ret)
-
-key = "Simple_Key"
-value = "Simple value"
-print "Testing set ['{0}': {1}] ...".format(key, value),
-set(mc, key, value)
-
-key = "Expiring_Key"
-value = 999
-expiry = 3
-print "Testing set ['{0}' : {1} : {2}] ...".format(key, value, expiry),
-set(mc, key, value, expiry)
-
-key = "Incr_Key"
-value = "1"
-print "Testing increment 3 times ['{0}' : starting at {1} ]".format(key, value)
-print "Initialise at {0} ...".format(value),
-set(mc, key, value) # note that the key used for incr/decr must be a string.
-print "Increment by one ...",
-incr(mc, 2, key)
-print "Increment again ...",
-incr(mc, 3, key)
-print "Increment yet again ...",
-incr(mc, 4, key)
-
-key = "Decr_Key"
-value = "4"
-print "Testing decrement 1 time ['{0}' : starting at {1} ]".format(key, value)
-print "Initialise at {0} ...".format(value),
-set(mc, key, value)
-print "Decrement by one ...",
-decr(mc, 3, key)
-
-key = "Multi_Decr_Key"
-value = "3"
-print "Testing decrement 2 times in one call ['{0}' : {1} ]".format(key, value)
-print "Initialise at {0} ...".format(value),
-set(mc, key, value)
-print "Decrement by 2 ...",
-decr(mc, 1, "Multi_Decr_Key", 2)
-
-## For more information see http://community.jboss.org/wiki/UsingInfinispanMemcachedServer
Copied: branches/4.1.x/server/memcached/src/test/resources/test_memcached_write.py (from rev 2022, trunk/server/memcached/src/test/resources/test_memcached_write.py)
===================================================================
--- branches/4.1.x/server/memcached/src/test/resources/test_memcached_write.py (rev 0)
+++ branches/4.1.x/server/memcached/src/test/resources/test_memcached_write.py 2010-07-12 18:07:31 UTC (rev 2025)
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+#
+# Sample python code using the standard memcached library to talk to Infinispan memcached server
+# To use it, make sure you install Python memcached client library
+# This particular script tests that it's writing to the one of the clustered servers correctly
+#
+
+__author__ = "Galder Zamarreno"
+__version__ = "Infinispan 4.1"
+__copyright__ = "Copyright (C) 2010 Red Hat Middleware LLC"
+__license__ = "LGPL"
+
+import memcache
+import time
+
+ip = "127.0.0.1"
+port = "12211"
+ipaddress = ip + ':' + port
+mc = memcache.Client([ipaddress], debug=1)
+
+print "Connecting to {0}".format(ipaddress)
+
+def set(mc, key, val, time = 0):
+ ret = mc.set(key, val, time)
+ if ret != 0:
+ print "OK"
+ else:
+ print "FAIL: returned {0}".format(ret)
+
+def incr(mc, expected, key, delta = 1):
+ ret = mc.incr(key, delta)
+ if ret == expected:
+ print "OK"
+ else:
+ print "FAIL: returned {0}".format(ret)
+
+def decr(mc, expected, key, delta = 1):
+ ret = mc.decr(key, delta)
+ if ret == expected:
+ print "OK"
+ else:
+ print "FAIL: returned {0}".format(ret)
+
+key = "Simple_Key"
+value = "Simple value"
+print "Testing set ['{0}': {1}] ...".format(key, value),
+set(mc, key, value)
+
+key = "Expiring_Key"
+value = 999
+expiry = 3
+print "Testing set ['{0}' : {1} : {2}] ...".format(key, value, expiry),
+set(mc, key, value, expiry)
+
+key = "Incr_Key"
+value = "1"
+print "Testing increment 3 times ['{0}' : starting at {1} ]".format(key, value)
+print "Initialise at {0} ...".format(value),
+set(mc, key, value) # note that the key used for incr/decr must be a string.
+print "Increment by one ...",
+incr(mc, 2, key)
+print "Increment again ...",
+incr(mc, 3, key)
+print "Increment yet again ...",
+incr(mc, 4, key)
+
+key = "Decr_Key"
+value = "4"
+print "Testing decrement 1 time ['{0}' : starting at {1} ]".format(key, value)
+print "Initialise at {0} ...".format(value),
+set(mc, key, value)
+print "Decrement by one ...",
+decr(mc, 3, key)
+
+key = "Multi_Decr_Key"
+value = "3"
+print "Testing decrement 2 times in one call ['{0}' : {1} ]".format(key, value)
+print "Initialise at {0} ...".format(value),
+set(mc, key, value)
+print "Decrement by 2 ...",
+decr(mc, 1, "Multi_Decr_Key", 2)
+
+## For more information see http://community.jboss.org/wiki/UsingInfinispanMemcachedServer
More information about the infinispan-commits
mailing list