Acquire/release the GIL only once for set_multi operations, and add add_multi/incr_multi operations
This commit is contained in:
parent
4674638327
commit
eb21e83e2c
752
_pylibmcmodule.c
752
_pylibmcmodule.c
@ -35,6 +35,7 @@
|
|||||||
#ifdef USE_ZLIB
|
#ifdef USE_ZLIB
|
||||||
# include <zlib.h>
|
# include <zlib.h>
|
||||||
# define ZLIB_BUFSZ (1 << 14)
|
# define ZLIB_BUFSZ (1 << 14)
|
||||||
|
/* only callable while holding the GIL */
|
||||||
# define _ZLIB_ERR(s, rc) \
|
# define _ZLIB_ERR(s, rc) \
|
||||||
PyErr_Format(PylibMCExc_MemcachedError, "zlib error %d in " s, rc);
|
PyErr_Format(PylibMCExc_MemcachedError, "zlib error %d in " s, rc);
|
||||||
#endif
|
#endif
|
||||||
@ -162,35 +163,33 @@ error:
|
|||||||
|
|
||||||
/* {{{ Compression helpers */
|
/* {{{ Compression helpers */
|
||||||
#ifdef USE_ZLIB
|
#ifdef USE_ZLIB
|
||||||
static PyObject *_PylibMC_Deflate(PyObject *value) {
|
static int _PylibMC_Deflate(char* value, size_t value_len,
|
||||||
int rc;
|
char** result, size_t *result_len) {
|
||||||
char *out;
|
/* todo: failures in here are entirely silent. this should probably
|
||||||
z_stream strm;
|
be fixed */
|
||||||
Py_ssize_t length, out_sz;
|
|
||||||
|
|
||||||
if (!PyString_Check(value)) {
|
z_stream strm;
|
||||||
return NULL;
|
*result = NULL;
|
||||||
}
|
*result_len = 0;
|
||||||
|
|
||||||
/* Don't ask me about this one. Got it from zlibmodule.c in Python 2.6. */
|
/* Don't ask me about this one. Got it from zlibmodule.c in Python 2.6. */
|
||||||
length = PyString_GET_SIZE(value);
|
size_t out_sz = value_len + value_len / 1000 + 12 + 1;
|
||||||
out_sz = length + length / 1000 + 12 + 1;
|
|
||||||
|
|
||||||
if ((out = PyMem_New(char, out_sz)) == NULL) {
|
if ((*result = malloc(sizeof(char) * out_sz)) == NULL) {
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
strm.avail_in = length;
|
strm.avail_in = value_len;
|
||||||
strm.avail_out = out_sz;
|
strm.avail_out = out_sz;
|
||||||
strm.next_in = (Byte *)PyString_AS_STRING(value);
|
strm.next_in = (Bytef*)value;
|
||||||
strm.next_out = (Byte *)out;
|
strm.next_out = (Bytef*)*result;
|
||||||
|
|
||||||
|
/* we just pre-allocated all of it up front */
|
||||||
strm.zalloc = (alloc_func)NULL;
|
strm.zalloc = (alloc_func)NULL;
|
||||||
strm.zfree = (free_func)Z_NULL;
|
strm.zfree = (free_func)Z_NULL;
|
||||||
|
|
||||||
/* TODO Expose compression level somehow. */
|
/* TODO Expose compression level somehow. */
|
||||||
if ((rc = deflateInit((z_streamp)&strm, Z_BEST_SPEED)) != Z_OK) {
|
if (deflateInit((z_streamp)&strm, Z_BEST_SPEED) != Z_OK) {
|
||||||
_ZLIB_ERR("deflateInit", rc);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,15 +202,29 @@ static PyObject *_PylibMC_Deflate(PyObject *value) {
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((rc = deflateEnd((z_streamp)&strm)) != Z_OK) {
|
if (deflateEnd((z_streamp)&strm) != Z_OK) {
|
||||||
_ZLIB_ERR("deflateEnd", rc);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PyString_FromStringAndSize(out, strm.total_out);
|
if(strm.total_out >= value_len) {
|
||||||
|
/* if we didn't actually save anything, don't bother storing it
|
||||||
|
compressed */
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *result should already be populated since that's the address we
|
||||||
|
passed into the z_stream */
|
||||||
|
*result_len = strm.total_out;
|
||||||
|
|
||||||
|
return 1;
|
||||||
error:
|
error:
|
||||||
PyMem_Del(out);
|
/* if any error occurred, we'll just use the original value
|
||||||
return NULL;
|
instead of trying to compress it */
|
||||||
|
if(*result != NULL) {
|
||||||
|
free(*result);
|
||||||
|
*result = NULL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *_PylibMC_Inflate(char *value, size_t size) {
|
static PyObject *_PylibMC_Inflate(char *value, size_t size) {
|
||||||
@ -339,9 +352,7 @@ static PyObject *_PylibMC_parse_memcached_value(char *value, size_t size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if USE_ZLIB
|
#if USE_ZLIB
|
||||||
if (inflated != NULL) {
|
Py_XDECREF(inflated);
|
||||||
Py_DECREF(inflated);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
@ -365,6 +376,7 @@ static PyObject *PylibMC_Client_get(PylibMC_Client *self, PyObject *arg) {
|
|||||||
PyString_AS_STRING(arg), PyString_GET_SIZE(arg),
|
PyString_AS_STRING(arg), PyString_GET_SIZE(arg),
|
||||||
&val_size, &flags, &error);
|
&val_size, &flags, &error);
|
||||||
Py_END_ALLOW_THREADS;
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
if (mc_val != NULL) {
|
if (mc_val != NULL) {
|
||||||
PyObject *r = _PylibMC_parse_memcached_value(mc_val, val_size, flags);
|
PyObject *r = _PylibMC_parse_memcached_value(mc_val, val_size, flags);
|
||||||
free(mc_val);
|
free(mc_val);
|
||||||
@ -382,26 +394,20 @@ static PyObject *PylibMC_Client_get(PylibMC_Client *self, PyObject *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* {{{ Set commands (set, replace, add, prepend, append) */
|
/* {{{ Set commands (set, replace, add, prepend, append) */
|
||||||
static PyObject *_PylibMC_RunSetCommand(PylibMC_Client *self,
|
static PyObject *_PylibMC_RunSetCommandSingle(PylibMC_Client *self,
|
||||||
_PylibMC_SetCommand f, char *fname, PyObject *args,
|
_PylibMC_SetCommand f, char *fname, PyObject *args,
|
||||||
PyObject *kwds) {
|
PyObject *kwds) {
|
||||||
char *key;
|
/* function called by the set/add/etc commands */
|
||||||
Py_ssize_t key_sz;
|
|
||||||
memcached_return rc;
|
|
||||||
PyObject *val, *tmp;
|
|
||||||
PyObject *retval = NULL;
|
|
||||||
PyObject *store_val = NULL;
|
|
||||||
unsigned int time = 0;
|
|
||||||
unsigned int min_compress = 0;
|
|
||||||
uint32_t store_flags = 0;
|
|
||||||
|
|
||||||
static char *kws[] = { "key", "val", "time", "min_compress_len", NULL };
|
static char *kws[] = { "key", "val", "time", "min_compress_len", NULL };
|
||||||
|
PyObject *key;
|
||||||
|
PyObject *value;
|
||||||
|
unsigned int time = 0; /* this will be turned into a time_t */
|
||||||
|
unsigned int min_compress = 0;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#O|II", kws,
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "SO|II", kws,
|
||||||
&key, &key_sz, &val, &time, &min_compress)) {
|
&key, &value,
|
||||||
return NULL;
|
&time, &min_compress)) {
|
||||||
}
|
|
||||||
if (!_PylibMC_CheckKeyStringAndSize(key, key_sz)) {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,51 +418,284 @@ static PyObject *_PylibMC_RunSetCommand(PylibMC_Client *self,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Adapt val to a str. */
|
pylibmc_mset serialized = { NULL, 0,
|
||||||
if (PyString_Check(val)) {
|
NULL, 0,
|
||||||
store_val = val;
|
0, PYLIBMC_FLAG_NONE,
|
||||||
Py_INCREF(store_val);
|
NULL, NULL, NULL,
|
||||||
} else if (PyBool_Check(val)) {
|
false };
|
||||||
store_flags |= PYLIBMC_FLAG_BOOL;
|
|
||||||
tmp = PyNumber_Int(val);
|
success = _PylibMC_SerializeValue(key, NULL, value, time, &serialized);
|
||||||
store_val = PyObject_Str(tmp);
|
|
||||||
Py_DECREF(tmp);
|
if(!success) goto cleanup;
|
||||||
} else if (PyInt_Check(val)) {
|
|
||||||
store_flags |= PYLIBMC_FLAG_INTEGER;
|
success = _PylibMC_RunSetCommand(self, f, fname,
|
||||||
tmp = PyNumber_Int(val);
|
&serialized, 1,
|
||||||
store_val = PyObject_Str(tmp);
|
min_compress);
|
||||||
Py_DECREF(tmp);
|
|
||||||
} else if (PyLong_Check(val)) {
|
cleanup:
|
||||||
store_flags |= PYLIBMC_FLAG_LONG;
|
_PylibMC_FreeMset(&serialized);
|
||||||
tmp = PyNumber_Long(val);
|
|
||||||
store_val = PyObject_Str(tmp);
|
if(PyErr_Occurred() != NULL) {
|
||||||
Py_DECREF(tmp);
|
return NULL;
|
||||||
|
} else if(success) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
} else {
|
} else {
|
||||||
Py_INCREF(val);
|
Py_RETURN_FALSE;
|
||||||
store_flags |= PYLIBMC_FLAG_PICKLE;
|
|
||||||
store_val = _PylibMC_Pickle(val);
|
|
||||||
Py_DECREF(val);
|
|
||||||
}
|
}
|
||||||
if (store_val == NULL) {
|
}
|
||||||
|
|
||||||
|
static PyObject *_PylibMC_RunSetCommandMulti(PylibMC_Client* self,
|
||||||
|
_PylibMC_SetCommand f, char *fname, PyObject* args,
|
||||||
|
PyObject* kwds) {
|
||||||
|
/* function called by the set/add/incr/etc commands */
|
||||||
|
static char *kws[] = { "keys", "key_prefix", "time", "min_compress_len", NULL };
|
||||||
|
PyObject* keys = NULL;
|
||||||
|
PyObject* key_prefix = NULL;
|
||||||
|
unsigned int time = 0;
|
||||||
|
unsigned int min_compress = 0;
|
||||||
|
PyObject * retval = NULL;
|
||||||
|
size_t idx = 0;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!II", kws,
|
||||||
|
&PyDict_Type, &keys,
|
||||||
|
&PyString_Type, &key_prefix,
|
||||||
|
&time, &min_compress)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ZLIB
|
#ifndef USE_ZLIB
|
||||||
if (min_compress && PyString_GET_SIZE(store_val) > min_compress) {
|
if (min_compress) {
|
||||||
PyObject *deflated = _PylibMC_Deflate(val);
|
PyErr_SetString(PyExc_TypeError, "min_compress_len without zlib");
|
||||||
|
return NULL;
|
||||||
if (deflated != NULL) {
|
|
||||||
Py_DECREF(store_val);
|
|
||||||
store_val = deflated;
|
|
||||||
store_flags |= PYLIBMC_FLAG_ZLIB;
|
|
||||||
} else {
|
|
||||||
/* XXX What to do here, really? */
|
|
||||||
PyErr_Print();
|
|
||||||
PyErr_Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
PyObject *curr_key, *curr_value;
|
||||||
|
size_t nkeys = (size_t)PyDict_Size(keys);
|
||||||
|
|
||||||
|
pylibmc_mset* serialized = PyMem_New(pylibmc_mset, nkeys);
|
||||||
|
if(serialized == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(idx = 0; idx < nkeys; idx++) {
|
||||||
|
/* init them all with NULL pointers so that we can reliably detect
|
||||||
|
and free the ones that get allocated */
|
||||||
|
serialized[idx].key = NULL;
|
||||||
|
serialized[idx].key_len = 0;
|
||||||
|
serialized[idx].value = NULL;
|
||||||
|
serialized[idx].value_len = 0;
|
||||||
|
serialized[idx].time = 0;
|
||||||
|
serialized[idx].flags = PYLIBMC_FLAG_NONE;
|
||||||
|
serialized[idx].key_obj = NULL;
|
||||||
|
serialized[idx].prefixed_key_obj = NULL;
|
||||||
|
serialized[idx].value_obj = NULL;
|
||||||
|
serialized[idx].success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we're pointing into existing Python memory with the 'key' members
|
||||||
|
of pylibmc_mset (extracted using PyDict_Next) and during
|
||||||
|
_PylibMC_RunSetCommand (which uses those same 'key' params, and
|
||||||
|
potentially points into value string objects too), so we don't
|
||||||
|
want to go around decrementing any references that risk
|
||||||
|
destroying the pointed objects until we're done, especially since
|
||||||
|
we're going to release the GIL while we do the I/O that accesses
|
||||||
|
that memory. We're assuming that this is safe because Python
|
||||||
|
strings are immutable */
|
||||||
|
|
||||||
|
Py_ssize_t pos = 0; /* PyDict_Next's 'pos' isn't an incrementing index */
|
||||||
|
idx = 0;
|
||||||
|
while(PyDict_Next(keys, &pos, &curr_key, &curr_value)) {
|
||||||
|
int success = _PylibMC_SerializeValue(curr_key, key_prefix,
|
||||||
|
curr_value, time,
|
||||||
|
&serialized[idx]);
|
||||||
|
if(!success || PyErr_Occurred() != NULL) {
|
||||||
|
/* exception should already be on the stack */
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(PyErr_Occurred() != NULL) {
|
||||||
|
/* an iteration error of some sort */
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allsuccess = _PylibMC_RunSetCommand(self, f, fname, serialized, nkeys, time);
|
||||||
|
|
||||||
|
if(PyErr_Occurred() != NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're building the return value for set_multi, which is the list
|
||||||
|
of keys that were not successfully set */
|
||||||
|
retval = PyList_New(0);
|
||||||
|
if(retval == NULL || PyErr_Occurred() != NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if(!allsuccess) {
|
||||||
|
for(idx = 0; idx < nkeys; idx++) {
|
||||||
|
if(!serialized[idx].success) {
|
||||||
|
if(PyList_Append(retval, serialized[idx].key_obj) != 0) {
|
||||||
|
/* Ugh */
|
||||||
|
|
||||||
|
Py_DECREF(retval);
|
||||||
|
retval = NULL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if(serialized != NULL) {
|
||||||
|
for(pos = 0; pos < nkeys; pos++) {
|
||||||
|
_PylibMC_FreeMset(&serialized[pos]);
|
||||||
|
}
|
||||||
|
PyMem_Free(serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _PylibMC_FreeMset(pylibmc_mset* mset) {
|
||||||
|
mset->key = NULL;
|
||||||
|
mset->key_len = 0;
|
||||||
|
mset->value = NULL;
|
||||||
|
mset->value_len = 0;
|
||||||
|
|
||||||
|
/* if this isn't NULL then we incred it */
|
||||||
|
Py_XDECREF(mset->key_obj);
|
||||||
|
mset->key_obj = NULL;
|
||||||
|
|
||||||
|
/* if this isn't NULL then we built it */
|
||||||
|
Py_XDECREF(mset->prefixed_key_obj);
|
||||||
|
mset->prefixed_key_obj = NULL;
|
||||||
|
|
||||||
|
/* this is either a string that we created, or a string that we
|
||||||
|
passed to us. in the latter case, we incred it ourselves, so this
|
||||||
|
should be safe */
|
||||||
|
Py_XDECREF(mset->value_obj);
|
||||||
|
mset->value_obj = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _PylibMC_SerializeValue(PyObject* key_obj,
|
||||||
|
PyObject* key_prefix,
|
||||||
|
PyObject* value_obj,
|
||||||
|
time_t time,
|
||||||
|
pylibmc_mset* serialized) {
|
||||||
|
/* do the easy bits first */
|
||||||
|
serialized->time = time;
|
||||||
|
serialized->success = false;
|
||||||
|
serialized->flags = PYLIBMC_FLAG_NONE;
|
||||||
|
|
||||||
|
if(!_PylibMC_CheckKey(key_obj)
|
||||||
|
|| PyString_AsStringAndSize(key_obj, &serialized->key,
|
||||||
|
&serialized->key_len) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we need to incr our reference here so that it's guaranteed to
|
||||||
|
exist while we release the GIL. Even if we fail after this it
|
||||||
|
should be decremeneted by pylib_mset_free */
|
||||||
|
serialized->key_obj = key_obj;
|
||||||
|
Py_INCREF(key_obj);
|
||||||
|
|
||||||
|
/* make the prefixed key if appropriate */
|
||||||
|
if(key_prefix != NULL) {
|
||||||
|
if(!_PylibMC_CheckKey(key_prefix)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we can safely ignore an empty prefix */
|
||||||
|
if(PyString_Size(key_prefix) > 0) {
|
||||||
|
PyObject* prefixed_key_obj = NULL; /* freed by _PylibMC_FreeMset */
|
||||||
|
prefixed_key_obj = PyString_FromFormat("%s%s",
|
||||||
|
PyString_AS_STRING(key_prefix),
|
||||||
|
PyString_AS_STRING(key_obj));
|
||||||
|
if(prefixed_key_obj == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check the key and overwrite the C string */
|
||||||
|
if(!_PylibMC_CheckKey(prefixed_key_obj)
|
||||||
|
|| PyString_AsStringAndSize(prefixed_key_obj, &serialized->key,
|
||||||
|
&serialized->key_len) == -1) {
|
||||||
|
Py_DECREF(prefixed_key_obj);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
serialized->prefixed_key_obj = prefixed_key_obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* key/key_size should be copasetic, now onto the value */
|
||||||
|
|
||||||
|
PyObject* store_val = NULL;
|
||||||
|
|
||||||
|
/* first, build store_val, a Python String object, out of the object
|
||||||
|
we were passed */
|
||||||
|
if (PyString_Check(value_obj)) {
|
||||||
|
store_val = value_obj;
|
||||||
|
Py_INCREF(store_val); /* because we'll be decring it again in
|
||||||
|
pylibmc_mset_free*/
|
||||||
|
} else if (PyBool_Check(value_obj)) {
|
||||||
|
serialized->flags |= PYLIBMC_FLAG_BOOL;
|
||||||
|
PyObject* tmp = PyNumber_Int(value_obj);
|
||||||
|
store_val = PyObject_Str(tmp);
|
||||||
|
Py_DECREF(tmp);
|
||||||
|
} else if (PyInt_Check(value_obj)) {
|
||||||
|
serialized->flags |= PYLIBMC_FLAG_INTEGER;
|
||||||
|
PyObject* tmp = PyNumber_Int(value_obj);
|
||||||
|
store_val = PyObject_Str(tmp);
|
||||||
|
Py_DECREF(tmp);
|
||||||
|
} else if (PyLong_Check(value_obj)) {
|
||||||
|
serialized->flags |= PYLIBMC_FLAG_LONG;
|
||||||
|
PyObject* tmp = PyNumber_Long(value_obj);
|
||||||
|
store_val = PyObject_Str(tmp);
|
||||||
|
Py_DECREF(tmp);
|
||||||
|
} else if(value_obj != NULL) {
|
||||||
|
/* we have no idea what it is, so we'll store it pickled */
|
||||||
|
Py_INCREF(value_obj);
|
||||||
|
serialized->flags |= PYLIBMC_FLAG_PICKLE;
|
||||||
|
store_val = _PylibMC_Pickle(value_obj);
|
||||||
|
Py_DECREF(value_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (store_val == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(PyString_AsStringAndSize(store_val, &serialized->value,
|
||||||
|
&serialized->value_len) == -1) {
|
||||||
|
if(serialized->flags == PYLIBMC_FLAG_NONE) {
|
||||||
|
/* for some reason we weren't able to extract the value/size
|
||||||
|
from a string that we were explicitly passed, that we
|
||||||
|
INCREF'd above */
|
||||||
|
Py_DECREF(store_val);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* so now we have a reference to a string that we may have
|
||||||
|
created. we need that to keep existing while we release the HIL,
|
||||||
|
so we need to hold the reference, but we need to free it up when
|
||||||
|
we're done */
|
||||||
|
serialized->value_obj = store_val;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* {{{ Set commands (set, replace, add, prepend, append) */
|
||||||
|
static bool _PylibMC_RunSetCommand(PylibMC_Client* self,
|
||||||
|
_PylibMC_SetCommand f, char *fname,
|
||||||
|
pylibmc_mset* msets, size_t nkeys,
|
||||||
|
size_t min_compress) {
|
||||||
|
memcached_st* mc = self->mc;
|
||||||
|
memcached_return rc = MEMCACHED_SUCCESS;
|
||||||
|
int pos;
|
||||||
|
bool error = false;
|
||||||
|
bool allsuccess = true;
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
Py_BEGIN_ALLOW_THREADS;
|
||||||
rc = f(self->mc, key, key_sz,
|
rc = f(self->mc, key, key_sz,
|
||||||
PyString_AS_STRING(store_val), PyString_GET_SIZE(store_val),
|
PyString_AS_STRING(store_val), PyString_GET_SIZE(store_val),
|
||||||
@ -464,54 +703,111 @@ static PyObject *_PylibMC_RunSetCommand(PylibMC_Client *self,
|
|||||||
Py_END_ALLOW_THREADS;
|
Py_END_ALLOW_THREADS;
|
||||||
Py_DECREF(store_val);
|
Py_DECREF(store_val);
|
||||||
|
|
||||||
switch (rc) {
|
for(pos=0; pos < nkeys && !error; pos++) {
|
||||||
|
pylibmc_mset* mset = &msets[pos];
|
||||||
|
|
||||||
|
char* value = mset->value;
|
||||||
|
size_t value_len = mset->value_len;
|
||||||
|
uint32_t flags = mset->flags;
|
||||||
|
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
char* compressed_value = NULL;
|
||||||
|
size_t compressed_len = 0;
|
||||||
|
|
||||||
|
if(min_compress && value_len >= min_compress) {
|
||||||
|
_PylibMC_Deflate(value, value_len, &compressed_value, &compressed_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(compressed_value != NULL) {
|
||||||
|
/* will want to change this if this function needs to get back
|
||||||
|
at the old *value at some point */
|
||||||
|
value = compressed_value;
|
||||||
|
value_len = compressed_len;
|
||||||
|
flags |= PYLIBMC_FLAG_ZLIB;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* finally go and call the actual libmemcached function */
|
||||||
|
if(mset->key_len == 0) {
|
||||||
|
/* most other implementations ignore zero-length keys, so
|
||||||
|
we'll just do that */
|
||||||
|
rc = MEMCACHED_NOTSTORED;
|
||||||
|
} else {
|
||||||
|
rc = f(mc,
|
||||||
|
mset->key, mset->key_len,
|
||||||
|
value, value_len,
|
||||||
|
mset->time, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
if(compressed_value != NULL) {
|
||||||
|
free(compressed_value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch(rc) {
|
||||||
case MEMCACHED_SUCCESS:
|
case MEMCACHED_SUCCESS:
|
||||||
retval = Py_True;
|
mset->success = true;
|
||||||
break;
|
break;
|
||||||
case MEMCACHED_FAILURE:
|
case MEMCACHED_FAILURE:
|
||||||
case MEMCACHED_NO_KEY_PROVIDED:
|
case MEMCACHED_NO_KEY_PROVIDED:
|
||||||
case MEMCACHED_BAD_KEY_PROVIDED:
|
case MEMCACHED_BAD_KEY_PROVIDED:
|
||||||
case MEMCACHED_MEMORY_ALLOCATION_FAILURE:
|
case MEMCACHED_MEMORY_ALLOCATION_FAILURE:
|
||||||
|
case MEMCACHED_DATA_EXISTS:
|
||||||
case MEMCACHED_NOTSTORED:
|
case MEMCACHED_NOTSTORED:
|
||||||
retval = Py_False;
|
mset->success = false;
|
||||||
|
allsuccess = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PylibMC_ErrFromMemcached(self, fname, rc);
|
mset->success = false;
|
||||||
}
|
allsuccess = false;
|
||||||
|
error = true; /* will break the for loop */
|
||||||
|
} /* switch */
|
||||||
|
|
||||||
Py_XINCREF(retval);
|
} /* for */
|
||||||
return retval;
|
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
/* we only return the last return value, even for a _multi
|
||||||
|
operation, but we do set the success on the mset */
|
||||||
|
if(error) {
|
||||||
|
PylibMC_ErrFromMemcached(self, fname, rc);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return allsuccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* These all just call _PylibMC_RunSetCommand with the appropriate arguments.
|
/* These all just call _PylibMC_RunSetCommand with the appropriate
|
||||||
* In other words: bulk. */
|
* arguments. In other words: bulk. */
|
||||||
static PyObject *PylibMC_Client_set(PylibMC_Client *self, PyObject *args,
|
static PyObject *PylibMC_Client_set(PylibMC_Client *self, PyObject *args,
|
||||||
PyObject *kwds) {
|
PyObject *kwds) {
|
||||||
return _PylibMC_RunSetCommand(
|
PyObject* retval = _PylibMC_RunSetCommandSingle(
|
||||||
self, memcached_set, "memcached_set", args, kwds);
|
self, memcached_set, "memcached_set", args, kwds);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *PylibMC_Client_replace(PylibMC_Client *self, PyObject *args,
|
static PyObject *PylibMC_Client_replace(PylibMC_Client *self, PyObject *args,
|
||||||
PyObject *kwds) {
|
PyObject *kwds) {
|
||||||
return _PylibMC_RunSetCommand(
|
return _PylibMC_RunSetCommandSingle(
|
||||||
self, memcached_replace, "memcached_replace", args, kwds);
|
self, memcached_replace, "memcached_replace", args, kwds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *PylibMC_Client_add(PylibMC_Client *self, PyObject *args,
|
static PyObject *PylibMC_Client_add(PylibMC_Client *self, PyObject *args,
|
||||||
PyObject *kwds) {
|
PyObject *kwds) {
|
||||||
return _PylibMC_RunSetCommand(
|
return _PylibMC_RunSetCommandSingle(
|
||||||
self, memcached_add, "memcached_add", args, kwds);
|
self, memcached_add, "memcached_add", args, kwds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *PylibMC_Client_prepend(PylibMC_Client *self, PyObject *args,
|
static PyObject *PylibMC_Client_prepend(PylibMC_Client *self, PyObject *args,
|
||||||
PyObject *kwds) {
|
PyObject *kwds) {
|
||||||
return _PylibMC_RunSetCommand(
|
return _PylibMC_RunSetCommandSingle(
|
||||||
self, memcached_prepend, "memcached_prepend", args, kwds);
|
self, memcached_prepend, "memcached_prepend", args, kwds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *PylibMC_Client_append(PylibMC_Client *self, PyObject *args,
|
static PyObject *PylibMC_Client_append(PylibMC_Client *self, PyObject *args,
|
||||||
PyObject *kwds) {
|
PyObject *kwds) {
|
||||||
return _PylibMC_RunSetCommand(
|
return _PylibMC_RunSetCommandSingle(
|
||||||
self, memcached_append, "memcached_append", args, kwds);
|
self, memcached_append, "memcached_append", args, kwds);
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
@ -547,46 +843,211 @@ static PyObject *PylibMC_Client_delete(PylibMC_Client *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* {{{ Increment & decrement */
|
/* {{{ Increment & decrement */
|
||||||
static PyObject *_PylibMC_IncDec(PylibMC_Client *self, uint8_t dir,
|
static PyObject *_PylibMC_IncrSingle(PylibMC_Client *self,
|
||||||
|
_PylibMC_IncrCommand incr_func,
|
||||||
PyObject *args) {
|
PyObject *args) {
|
||||||
PyObject *retval;
|
|
||||||
char *key;
|
char *key;
|
||||||
Py_ssize_t key_sz;
|
Py_ssize_t key_len;
|
||||||
unsigned int delta;
|
unsigned int delta = 1;
|
||||||
uint64_t result;
|
|
||||||
memcached_return rc;
|
|
||||||
memcached_return (*incdec)(memcached_st *, const char *, size_t,
|
|
||||||
unsigned int, uint64_t *);
|
|
||||||
|
|
||||||
retval = NULL;
|
if (!PyArg_ParseTuple(args, "s#|I", &key, &key_len, &delta)) {
|
||||||
delta = 1;
|
return NULL;
|
||||||
if (!PyArg_ParseTuple(args, "s#|I:incr", &key, &key_sz, &delta)) {
|
} else if (!_PylibMC_CheckKeyStringAndSize(key, key_len)) {
|
||||||
return retval;
|
return NULL;
|
||||||
} else if (!_PylibMC_CheckKeyStringAndSize(key, key_sz)) {
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
incdec = (dir == PYLIBMC_INC) ? memcached_increment : memcached_decrement;
|
pylibmc_incr incr = { key, key_len,
|
||||||
Py_BEGIN_ALLOW_THREADS;
|
incr_func, delta,
|
||||||
rc = incdec(self->mc, key, key_sz, delta, &result);
|
0 };
|
||||||
Py_END_ALLOW_THREADS;
|
|
||||||
if (rc == MEMCACHED_SUCCESS) {
|
_PylibMC_IncrDecr(self, &incr, 1);
|
||||||
retval = PyLong_FromUnsignedLong((unsigned long)result);
|
|
||||||
} else {
|
if(PyErr_Occurred() != NULL) {
|
||||||
char *fname = (dir == PYLIBMC_INC) ? "memcached_increment"
|
/* exception already on the stack */
|
||||||
: "memcached_decrement";
|
return NULL;
|
||||||
PylibMC_ErrFromMemcached(self, fname, rc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* might be NULL, but if that's true then it's the right return value */
|
||||||
|
return PyLong_FromUnsignedLong((unsigned long)incr.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *_PylibMC_IncrMulti(PylibMC_Client *self,
|
||||||
|
_PylibMC_IncrCommand incr_func,
|
||||||
|
PyObject *args, PyObject *kwds) {
|
||||||
|
/* takes an iterable of keys and a single delta (that defaults to 1)
|
||||||
|
to be applied to all of them. Consider the return value and
|
||||||
|
exception behaviour to be undocumented: for now it returns None
|
||||||
|
and throws an exception on an error incrementing any key */
|
||||||
|
PyObject* keys = NULL;
|
||||||
|
PyObject* key_prefix = NULL;
|
||||||
|
PyObject* prefixed_keys = NULL;
|
||||||
|
PyObject* retval = NULL;
|
||||||
|
PyObject* iterator = NULL;
|
||||||
|
unsigned int delta = 1;
|
||||||
|
|
||||||
|
static char *kws[] = { "keys", "key_prefix", "delta", NULL };
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|SI", kws,
|
||||||
|
&keys, &key_prefix, &delta)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nkeys = (size_t)PySequence_Size(keys);
|
||||||
|
if(nkeys == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if(key_prefix == NULL || PyString_Size(key_prefix) < 1) {
|
||||||
|
/* if it's 0-length, we can safely pretend it doesn't exist */
|
||||||
|
key_prefix = NULL;
|
||||||
|
}
|
||||||
|
if(key_prefix != NULL) {
|
||||||
|
if(!_PylibMC_CheckKey(key_prefix)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixed_keys = PyList_New(nkeys);
|
||||||
|
if(prefixed_keys == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pylibmc_incr* incrs = PyMem_New(pylibmc_incr, nkeys);
|
||||||
|
if(incrs == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator = PyObject_GetIter(keys);
|
||||||
|
if(iterator == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject* key = NULL;
|
||||||
|
size_t idx = 0;
|
||||||
|
|
||||||
|
/* build our list of keys, prefixed as appropriate, and turn that
|
||||||
|
into a list of pylibmc_incr objects that can be incred in one
|
||||||
|
go. We're not going to own references to the prefixed keys: so
|
||||||
|
that we can free them all at once, we'll give ownership to a list
|
||||||
|
of them (prefixed_keys) which we'll DECR once at the end */
|
||||||
|
while((key = PyIter_Next(iterator)) != NULL) {
|
||||||
|
if(!_PylibMC_CheckKey(key)) goto loopcleanup;
|
||||||
|
|
||||||
|
if(key_prefix != NULL) {
|
||||||
|
|
||||||
|
PyObject* newkey = NULL;
|
||||||
|
|
||||||
|
newkey = PyString_FromFormat("%s%s",
|
||||||
|
PyString_AS_STRING(key_prefix),
|
||||||
|
PyString_AS_STRING(key));
|
||||||
|
if(!_PylibMC_CheckKey(newkey)) {
|
||||||
|
Py_XDECREF(newkey);
|
||||||
|
goto loopcleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* steals our reference */
|
||||||
|
if(PyList_SetItem(prefixed_keys, idx, newkey) == -1) {
|
||||||
|
/* it wasn't stolen before the error */
|
||||||
|
Py_DECREF(newkey);
|
||||||
|
goto loopcleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the list stole our reference to it, and we're going to rely
|
||||||
|
on that list to maintain it while we release the GIL, but
|
||||||
|
since we DECREF the key in loopcleanup we need to INCREF it
|
||||||
|
here */
|
||||||
|
Py_DECREF(key);
|
||||||
|
Py_INCREF(newkey);
|
||||||
|
key = newkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(PyString_AsStringAndSize(key, &incrs[idx].key, &incrs[idx].key_len) == -1)
|
||||||
|
goto loopcleanup;
|
||||||
|
|
||||||
|
incrs[idx].delta = delta;
|
||||||
|
incrs[idx].incr_func = incr_func;
|
||||||
|
incrs[idx].result = 0; /* after incring we have no way of knowing
|
||||||
|
whether the real result is 0 or if the
|
||||||
|
incr wasn't successful (or if noreply is
|
||||||
|
set), but since we're not actually
|
||||||
|
returning the result that's okay for
|
||||||
|
now */
|
||||||
|
|
||||||
|
loopcleanup:
|
||||||
|
Py_DECREF(key);
|
||||||
|
|
||||||
|
if(PyErr_Occurred())
|
||||||
|
break;
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iteration error */
|
||||||
|
if (PyErr_Occurred()) goto cleanup;
|
||||||
|
|
||||||
|
_PylibMC_IncrDecr(self, incrs, nkeys);
|
||||||
|
|
||||||
|
/* if that failed, there's an exception on the stack */
|
||||||
|
if(PyErr_Occurred()) goto cleanup;
|
||||||
|
|
||||||
|
retval = Py_None;
|
||||||
|
Py_INCREF(retval);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if(incrs != NULL) {
|
||||||
|
PyMem_Free(incrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(prefixed_keys);
|
||||||
|
Py_XDECREF(iterator);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject *PylibMC_Client_incr(PylibMC_Client *self, PyObject *args) {
|
static PyObject *PylibMC_Client_incr(PylibMC_Client *self, PyObject *args) {
|
||||||
return _PylibMC_IncDec(self, PYLIBMC_INC, args);
|
return _PylibMC_IncrSingle(self, memcached_increment, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *PylibMC_Client_decr(PylibMC_Client *self, PyObject *args) {
|
static PyObject *PylibMC_Client_decr(PylibMC_Client *self, PyObject *args) {
|
||||||
return _PylibMC_IncDec(self, PYLIBMC_DEC, args);
|
return _PylibMC_IncrSingle(self, memcached_decrement, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *PylibMC_Client_incr_multi(PylibMC_Client *self, PyObject *args,
|
||||||
|
PyObject *kwds) {
|
||||||
|
return _PylibMC_IncrMulti(self, memcached_increment, args, kwds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _PylibMC_IncrDecr(PylibMC_Client *self, pylibmc_incr *incrs,
|
||||||
|
size_t nkeys) {
|
||||||
|
|
||||||
|
bool error = false;
|
||||||
|
memcached_return rc = MEMCACHED_SUCCESS;
|
||||||
|
_PylibMC_IncrCommand f = NULL;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
for(i = 0; i < nkeys && !error; i++) {
|
||||||
|
pylibmc_incr *incr = &incrs[i];
|
||||||
|
uint64_t result = 0;
|
||||||
|
f = incr->incr_func;
|
||||||
|
rc = f(self->mc, incr->key, incr->key_len, incr->delta, &result);
|
||||||
|
if (rc == MEMCACHED_SUCCESS) {
|
||||||
|
incr->result = result;
|
||||||
|
} else {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
char *fname = (f == memcached_decrement) ? "memcached_decrement"
|
||||||
|
: "memcached_increment";
|
||||||
|
PylibMC_ErrFromMemcached(self, fname, rc);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
@ -598,6 +1059,11 @@ memcached_return pylibmc_memcached_fetch_multi(memcached_st* mc,
|
|||||||
size_t* nresults,
|
size_t* nresults,
|
||||||
char** err_func) {
|
char** err_func) {
|
||||||
|
|
||||||
|
/* the part of PylibMC_Client_get_multi that does the blocking I/O
|
||||||
|
and can be called while not holding the GIL. Builds an
|
||||||
|
intermediate result set into 'results' that is turned into a
|
||||||
|
PyDict before being returned to the caller */
|
||||||
|
|
||||||
memcached_return rc;
|
memcached_return rc;
|
||||||
char curr_key[MEMCACHED_MAX_KEY];
|
char curr_key[MEMCACHED_MAX_KEY];
|
||||||
size_t curr_key_len = 0;
|
size_t curr_key_len = 0;
|
||||||
@ -667,7 +1133,7 @@ static PyObject *PylibMC_Client_get_multi(PylibMC_Client *self, PyObject *args,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this is probably over-allocating in the majority of cases */
|
/* this is over-allocating in the majority of cases */
|
||||||
results = PyMem_New(pylibmc_mget_result, nkeys);
|
results = PyMem_New(pylibmc_mget_result, nkeys);
|
||||||
|
|
||||||
/* Populate keys and key_lens. */
|
/* Populate keys and key_lens. */
|
||||||
@ -777,9 +1243,8 @@ earlybird:
|
|||||||
}
|
}
|
||||||
if(results != NULL){
|
if(results != NULL){
|
||||||
for (i = 0; i < nresults; i++) {
|
for (i = 0; i < nresults; i++) {
|
||||||
/* I know Python says we can't call malloc/free, but
|
/* libmemcached mallocs, so we need to free its memory in
|
||||||
libmemcached does, so we need to free its memory in the
|
the same way */
|
||||||
same way */
|
|
||||||
free(results[i].value);
|
free(results[i].value);
|
||||||
}
|
}
|
||||||
PyMem_Free(results);
|
PyMem_Free(results);
|
||||||
@ -812,8 +1277,9 @@ cleanup:
|
|||||||
*/
|
*/
|
||||||
static PyObject *_PylibMC_DoMulti(PyObject *values, PyObject *func,
|
static PyObject *_PylibMC_DoMulti(PyObject *values, PyObject *func,
|
||||||
PyObject *prefix, PyObject *extra_args) {
|
PyObject *prefix, PyObject *extra_args) {
|
||||||
/* TODO: acquire/release the GIL only only once per DoMulti rather
|
/* TODO: acquire/release the GIL only once per DoMulti rather than
|
||||||
than once per action */
|
once per action; fortunately this is only used for
|
||||||
|
delete_multi, which isn't used very often */
|
||||||
|
|
||||||
PyObject *retval = PyList_New(0);
|
PyObject *retval = PyList_New(0);
|
||||||
PyObject *iter = NULL;
|
PyObject *iter = NULL;
|
||||||
@ -902,36 +1368,14 @@ error:
|
|||||||
|
|
||||||
static PyObject *PylibMC_Client_set_multi(PylibMC_Client *self, PyObject *args,
|
static PyObject *PylibMC_Client_set_multi(PylibMC_Client *self, PyObject *args,
|
||||||
PyObject *kwds) {
|
PyObject *kwds) {
|
||||||
PyObject *prefix = NULL;
|
return _PylibMC_RunSetCommandMulti(self, memcached_set, "memcached_set_multi",
|
||||||
PyObject *time = NULL;
|
args, kwds);
|
||||||
PyObject *set = NULL;
|
}
|
||||||
PyObject *map;
|
|
||||||
PyObject *call_args;
|
|
||||||
PyObject *retval;
|
|
||||||
|
|
||||||
static char *kws[] = { "mapping", "time", "key_prefix", NULL };
|
static PyObject *PylibMC_Client_add_multi(PylibMC_Client *self, PyObject *args,
|
||||||
|
PyObject *kwds) {
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O!S:set_multi", kws,
|
return _PylibMC_RunSetCommandMulti(self, memcached_add, "memcached_add_multi",
|
||||||
&map, &PyInt_Type, &time, &prefix))
|
args, kwds);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if ((set = PyObject_GetAttrString((PyObject *)self, "set")) == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (time == NULL) {
|
|
||||||
retval = _PylibMC_DoMulti(map, set, prefix, NULL);
|
|
||||||
} else {
|
|
||||||
if ((call_args = PyTuple_Pack(1, time)) == NULL)
|
|
||||||
goto error;
|
|
||||||
retval = _PylibMC_DoMulti(map, set, prefix, call_args);
|
|
||||||
Py_DECREF(call_args);
|
|
||||||
}
|
|
||||||
Py_DECREF(set);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
error:
|
|
||||||
Py_XDECREF(set);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *PylibMC_Client_delete_multi(PylibMC_Client *self,
|
static PyObject *PylibMC_Client_delete_multi(PylibMC_Client *self,
|
||||||
@ -1238,6 +1682,7 @@ static PyObject *_PylibMC_GetPickles(const char *attname) {
|
|||||||
pickle_attr = PyObject_GetAttrString(pickle, attname);
|
pickle_attr = PyObject_GetAttrString(pickle, attname);
|
||||||
Py_DECREF(pickle);
|
Py_DECREF(pickle);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pickle_attr;
|
return pickle_attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1259,7 +1704,6 @@ static PyObject *_PylibMC_Pickle(PyObject *val) {
|
|||||||
PyObject *pickle_dump;
|
PyObject *pickle_dump;
|
||||||
PyObject *retval = NULL;
|
PyObject *retval = NULL;
|
||||||
|
|
||||||
retval = NULL;
|
|
||||||
pickle_dump = _PylibMC_GetPickles("dumps");
|
pickle_dump = _PylibMC_GetPickles("dumps");
|
||||||
if (pickle_dump != NULL) {
|
if (pickle_dump != NULL) {
|
||||||
retval = PyObject_CallFunction(pickle_dump, "Oi", val, -1);
|
retval = PyObject_CallFunction(pickle_dump, "Oi", val, -1);
|
||||||
|
@ -68,11 +68,10 @@ typedef ssize_t Py_ssize_t;
|
|||||||
#define PYLIBMC_FLAG_ZLIB (1 << 3)
|
#define PYLIBMC_FLAG_ZLIB (1 << 3)
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
#define PYLIBMC_INC (1 << 0)
|
|
||||||
#define PYLIBMC_DEC (1 << 1)
|
|
||||||
|
|
||||||
typedef memcached_return (*_PylibMC_SetCommand)(memcached_st *, const char *,
|
typedef memcached_return (*_PylibMC_SetCommand)(memcached_st *, const char *,
|
||||||
size_t, const char *, size_t, time_t, uint32_t);
|
size_t, const char *, size_t, time_t, uint32_t);
|
||||||
|
typedef memcached_return (*_PylibMC_IncrCommand)(memcached_st *,
|
||||||
|
const char *, size_t, unsigned int, uint64_t*);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char key[MEMCACHED_MAX_KEY];
|
char key[MEMCACHED_MAX_KEY];
|
||||||
@ -81,14 +80,32 @@ typedef struct {
|
|||||||
size_t value_len;
|
size_t value_len;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
} pylibmc_mget_result;
|
} pylibmc_mget_result;
|
||||||
memcached_return pylibmc_memcached_fetch_multi(memcached_st* mc,
|
|
||||||
char** keys,
|
|
||||||
size_t nkeys,
|
|
||||||
size_t* key_lengths,
|
|
||||||
pylibmc_mget_result* results,
|
|
||||||
size_t* nresults,
|
|
||||||
char** err_func);
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *key;
|
||||||
|
Py_ssize_t key_len;
|
||||||
|
char* value;
|
||||||
|
Py_ssize_t value_len;
|
||||||
|
time_t time;
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
|
/* the objects that must be freed after the mset is executed */
|
||||||
|
PyObject* key_obj;
|
||||||
|
PyObject* prefixed_key_obj;
|
||||||
|
PyObject* value_obj;
|
||||||
|
|
||||||
|
/* the success of executing the mset afterwards */
|
||||||
|
int success;
|
||||||
|
|
||||||
|
} pylibmc_mset;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* key;
|
||||||
|
Py_ssize_t key_len;
|
||||||
|
_PylibMC_IncrCommand incr_func;
|
||||||
|
unsigned int delta;
|
||||||
|
uint64_t result;
|
||||||
|
} pylibmc_incr;
|
||||||
|
|
||||||
/* {{{ Exceptions */
|
/* {{{ Exceptions */
|
||||||
static PyObject *PylibMCExc_MemcachedError;
|
static PyObject *PylibMCExc_MemcachedError;
|
||||||
@ -221,8 +238,10 @@ static PyObject *PylibMC_Client_append(PylibMC_Client *, PyObject *, PyObject *)
|
|||||||
static PyObject *PylibMC_Client_delete(PylibMC_Client *, PyObject *);
|
static PyObject *PylibMC_Client_delete(PylibMC_Client *, PyObject *);
|
||||||
static PyObject *PylibMC_Client_incr(PylibMC_Client *, PyObject *);
|
static PyObject *PylibMC_Client_incr(PylibMC_Client *, PyObject *);
|
||||||
static PyObject *PylibMC_Client_decr(PylibMC_Client *, PyObject *);
|
static PyObject *PylibMC_Client_decr(PylibMC_Client *, PyObject *);
|
||||||
|
static PyObject *PylibMC_Client_incr_multi(PylibMC_Client*, PyObject*, PyObject*);
|
||||||
static PyObject *PylibMC_Client_get_multi(PylibMC_Client *, PyObject *, PyObject *);
|
static PyObject *PylibMC_Client_get_multi(PylibMC_Client *, PyObject *, PyObject *);
|
||||||
static PyObject *PylibMC_Client_set_multi(PylibMC_Client *, PyObject *, PyObject *);
|
static PyObject *PylibMC_Client_set_multi(PylibMC_Client *, PyObject *, PyObject *);
|
||||||
|
static PyObject *PylibMC_Client_add_multi(PylibMC_Client *, PyObject *, PyObject *);
|
||||||
static PyObject *PylibMC_Client_delete_multi(PylibMC_Client *, PyObject *, PyObject *);
|
static PyObject *PylibMC_Client_delete_multi(PylibMC_Client *, PyObject *, PyObject *);
|
||||||
static PyObject *PylibMC_Client_get_behaviors(PylibMC_Client *);
|
static PyObject *PylibMC_Client_get_behaviors(PylibMC_Client *);
|
||||||
static PyObject *PylibMC_Client_set_behaviors(PylibMC_Client *, PyObject *);
|
static PyObject *PylibMC_Client_set_behaviors(PylibMC_Client *, PyObject *);
|
||||||
@ -236,6 +255,24 @@ static PyObject *_PylibMC_Unpickle(const char *, size_t);
|
|||||||
static PyObject *_PylibMC_Pickle(PyObject *);
|
static PyObject *_PylibMC_Pickle(PyObject *);
|
||||||
static int _PylibMC_CheckKey(PyObject *);
|
static int _PylibMC_CheckKey(PyObject *);
|
||||||
static int _PylibMC_CheckKeyStringAndSize(char *, Py_ssize_t);
|
static int _PylibMC_CheckKeyStringAndSize(char *, Py_ssize_t);
|
||||||
|
static int _PylibMC_SerializeValue(PyObject* key_obj,
|
||||||
|
PyObject* key_prefix,
|
||||||
|
PyObject* value_obj,
|
||||||
|
time_t time,
|
||||||
|
pylibmc_mset* serialized);
|
||||||
|
static void _PylibMC_FreeMset(pylibmc_mset*);
|
||||||
|
static PyObject *_PylibMC_RunSetCommandSingle(PylibMC_Client *self,
|
||||||
|
_PylibMC_SetCommand f, char *fname, PyObject *args, PyObject *kwds);
|
||||||
|
static PyObject *_PylibMC_RunSetCommandMulti(PylibMC_Client* self,
|
||||||
|
_PylibMC_SetCommand f, char *fname, PyObject *args, PyObject *kwds);
|
||||||
|
static bool _PylibMC_RunSetCommand(PylibMC_Client* self,
|
||||||
|
_PylibMC_SetCommand f, char *fname,
|
||||||
|
pylibmc_mset* msets, size_t nkeys,
|
||||||
|
size_t min_compress);
|
||||||
|
static int _PylibMC_Deflate(char* value, size_t value_len,
|
||||||
|
char** result, size_t *result_len);
|
||||||
|
static bool _PylibMC_IncrDecr(PylibMC_Client*, pylibmc_incr*, size_t);
|
||||||
|
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
/* {{{ Type's method table */
|
/* {{{ Type's method table */
|
||||||
@ -258,10 +295,14 @@ static PyMethodDef PylibMC_ClientType_methods[] = {
|
|||||||
"Increment a key by a delta."},
|
"Increment a key by a delta."},
|
||||||
{"decr", (PyCFunction)PylibMC_Client_decr, METH_VARARGS,
|
{"decr", (PyCFunction)PylibMC_Client_decr, METH_VARARGS,
|
||||||
"Decrement a key by a delta."},
|
"Decrement a key by a delta."},
|
||||||
|
{"incr_multi", (PyCFunction)PylibMC_Client_incr_multi, METH_VARARGS|METH_KEYWORDS,
|
||||||
|
"Increment more than one key by a delta."},
|
||||||
{"get_multi", (PyCFunction)PylibMC_Client_get_multi,
|
{"get_multi", (PyCFunction)PylibMC_Client_get_multi,
|
||||||
METH_VARARGS|METH_KEYWORDS, "Get multiple keys at once."},
|
METH_VARARGS|METH_KEYWORDS, "Get multiple keys at once."},
|
||||||
{"set_multi", (PyCFunction)PylibMC_Client_set_multi,
|
{"set_multi", (PyCFunction)PylibMC_Client_set_multi,
|
||||||
METH_VARARGS|METH_KEYWORDS, "Set multiple keys at once."},
|
METH_VARARGS|METH_KEYWORDS, "Set multiple keys at once."},
|
||||||
|
{"add_multi", (PyCFunction)PylibMC_Client_add_multi,
|
||||||
|
METH_VARARGS|METH_KEYWORDS, "Add multiple keys at once."},
|
||||||
{"delete_multi", (PyCFunction)PylibMC_Client_delete_multi,
|
{"delete_multi", (PyCFunction)PylibMC_Client_delete_multi,
|
||||||
METH_VARARGS|METH_KEYWORDS, "Delete multiple keys at once."},
|
METH_VARARGS|METH_KEYWORDS, "Delete multiple keys at once."},
|
||||||
{"get_behaviors", (PyCFunction)PylibMC_Client_get_behaviors, METH_NOARGS,
|
{"get_behaviors", (PyCFunction)PylibMC_Client_get_behaviors, METH_NOARGS,
|
||||||
|
10
pylibmc.py
10
pylibmc.py
@ -45,6 +45,16 @@ Batch operation::
|
|||||||
{'cats': ['on acid', 'furry'], 'dogs': True}
|
{'cats': ['on acid', 'furry'], 'dogs': True}
|
||||||
>>> mc.delete_multi(["cats", "dogs", "nonextant"])
|
>>> mc.delete_multi(["cats", "dogs", "nonextant"])
|
||||||
False
|
False
|
||||||
|
>>> mc.add_multi({"cats": ["on acid", "furry"], "dogs": True})
|
||||||
|
[]
|
||||||
|
>>> mc.get_multi(["cats", "dogs"])
|
||||||
|
{'cats': ['on acid', 'furry'], 'dogs': True}
|
||||||
|
>>> mc.add_multi({"cats": "not set", "dogs": "definitely not set", "bacon": "yummy"})
|
||||||
|
['cats', 'dogs']
|
||||||
|
>>> mc.get_multi(["cats", "dogs", "bacon"])
|
||||||
|
{'cats': ['on acid', 'furry'], 'bacon': 'yummy', 'dogs': True}
|
||||||
|
>>> mc.delete_multi(["cats", "dogs", "bacon"])
|
||||||
|
True
|
||||||
|
|
||||||
Further Reading
|
Further Reading
|
||||||
===============
|
===============
|
||||||
|
26
tests.py
26
tests.py
@ -95,7 +95,7 @@ Now for keys with funny types.
|
|||||||
>>> c.set(1, "hi")
|
>>> c.set(1, "hi")
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: argument 1 must be string or read-only buffer, not int
|
TypeError: argument 1 must be string, not int
|
||||||
>>> c.get(1)
|
>>> c.get(1)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
@ -193,6 +193,30 @@ True
|
|||||||
>>> c.delete("hello")
|
>>> c.delete("hello")
|
||||||
True
|
True
|
||||||
|
|
||||||
|
incr_multi
|
||||||
|
>>> c.add_multi({'a': 1, 'b': 0, 'c': 4})
|
||||||
|
[]
|
||||||
|
>>> c.incr_multi(('a', 'b', 'c'), delta=1)
|
||||||
|
>>> list(sorted(c.get_multi(('a', 'b', 'c')).items()))
|
||||||
|
[('a', 2), ('b', 1), ('c', 5)]
|
||||||
|
>>> c.delete_multi(('a', 'b', 'c'))
|
||||||
|
True
|
||||||
|
>>> c.add_multi({'a': 1, 'b': 0, 'c': 4}, key_prefix='x')
|
||||||
|
[]
|
||||||
|
>>> c.incr_multi(('a', 'b', 'c'), key_prefix='x', delta=5)
|
||||||
|
>>> list(sorted(c.get_multi(('a', 'b', 'c'), key_prefix='x').items()))
|
||||||
|
[('a', 6), ('b', 5), ('c', 9)]
|
||||||
|
>>> c.delete_multi(('a', 'b', 'c'), key_prefix='x')
|
||||||
|
True
|
||||||
|
>>> c.add('a', 1)
|
||||||
|
True
|
||||||
|
>>> c.incr_multi(('a', 'b', 'c'), key_prefix='x', delta=1)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
NotFound: error 16 from memcached_increment: NOT FOUND
|
||||||
|
>>> c.delete('xa')
|
||||||
|
True
|
||||||
|
|
||||||
Empty server lists are bad for your health.
|
Empty server lists are bad for your health.
|
||||||
>>> c = _pylibmc.client([])
|
>>> c = _pylibmc.client([])
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
|
Reference in New Issue
Block a user