Add CAS support (client.gets, client.cas). Note that this is not

compatible with python-memcached's implementation
master
ketralnis 2010-09-28 14:30:28 -07:00
parent 725134931b
commit 0c49d98fb4
2 changed files with 136 additions and 2 deletions

View File

@ -394,6 +394,72 @@ static PyObject *PylibMC_Client_get(PylibMC_Client *self, PyObject *arg) {
return PylibMC_ErrFromMemcached(self, "memcached_get", error);
}
static PyObject *PylibMC_Client_gets(PylibMC_Client *self, PyObject *arg) {
if (!_PylibMC_CheckKey(arg)) {
return NULL;
} else if (!PySequence_Length(arg)) {
/* Others do this, so... */
Py_RETURN_NONE;
} else if (!memcached_behavior_get(self->mc, MEMCACHED_BEHAVIOR_SUPPORT_CAS)) {
PyErr_SetString(PyExc_ValueError, "gets without cas behavior");
return NULL;
}
/* we have to do this with mget because that's the only function
* that libmemcached exposes that returns a memcached_result_st,
* which is the only way to get at the returned cas value */
const char* keys[2] = {PyString_AS_STRING(arg), NULL};
size_t keylengths[2] = {(size_t)PyString_GET_SIZE(arg), 0};
memcached_result_st results_obj;
memcached_result_st *results = NULL;
memcached_return_t rc;
PyObject* ret = NULL;
Py_BEGIN_ALLOW_THREADS;
rc = memcached_mget(self->mc, keys, keylengths, 1);
if(rc == MEMCACHED_SUCCESS) {
/* we don't have to check if the allocation was successful
because it's right on our stack */
results = memcached_result_create(self->mc, &results_obj);
/* this will be NULL if the key wasn't found, or
memcached_result_st if it was */
results = memcached_fetch_result(self->mc, &results_obj, &rc);
}
Py_END_ALLOW_THREADS;
if (results != NULL) {
const char *mc_val = memcached_result_value(results);
size_t val_size = memcached_result_length(results);
uint32_t flags = memcached_result_flags(results);
uint64_t cas = memcached_result_cas(results);
PyObject *r = _PylibMC_parse_memcached_value((char*)mc_val, val_size, flags);
if(r != NULL) {
ret = Py_BuildValue("(OL)", r, cas);
/* we don't need our own reference to r, it just belongs to
the ret tuple */
Py_DECREF(r);
}
memcached_quit(self->mc);
} else if (rc == MEMCACHED_END) {
/* Since python-memcache returns None when the key doesn't exist,
* so shall we. */
ret = Py_None;
Py_INCREF(Py_None);
} else {
ret = PylibMC_ErrFromMemcached(self, "memcached_gets", rc);
}
/* deallocate any indirect buffers, even though the struct itself
is on our stack */
memcached_result_free(&results_obj);
return ret;
}
/* {{{ Set commands (set, replace, add, prepend, append) */
static PyObject *_PylibMC_RunSetCommandSingle(PylibMC_Client *self,
_PylibMC_SetCommand f, char *fname, PyObject *args,
@ -544,6 +610,65 @@ cleanup:
return retval;
}
static PyObject *_PylibMC_RunCasCommand(PylibMC_Client *self,
PyObject *args, PyObject *kwds) {
/* function called by the set/add/etc commands */
static char *kws[] = { "key", "val", "cas", "time", NULL };
PyObject *ret = NULL;
PyObject *key;
PyObject *value;
uint64_t cas = 0;
unsigned int time = 0; /* this will be turned into a time_t */
bool success = false;
memcached_return rc;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "SOL|I", kws,
&key, &value, &cas,
&time)) {
return NULL;
}
if (!memcached_behavior_get(self->mc, MEMCACHED_BEHAVIOR_SUPPORT_CAS)) {
PyErr_SetString(PyExc_ValueError, "cas without cas behavior");
return NULL;
}
pylibmc_mset mset = { NULL };
/* TODO: because it's RunSetCommand that does the zlib
compression, cas can't currently use compressed values. */
success = _PylibMC_SerializeValue(key, NULL, value, time, &mset);
if (!success || PyErr_Occurred() != NULL) {
goto cleanup;
}
Py_BEGIN_ALLOW_THREADS;
rc = memcached_cas(self->mc,
mset.key, mset.key_len,
mset.value, mset.value_len,
mset.time, mset.flags, cas);
Py_END_ALLOW_THREADS;
switch(rc) {
case MEMCACHED_SUCCESS:
Py_INCREF(Py_True);
ret = Py_True;
break;
case MEMCACHED_DATA_EXISTS:
Py_INCREF(Py_False);
ret = Py_False;
break;
default:
PylibMC_ErrFromMemcached(self, "memcached_cas", rc);
}
cleanup:
_PylibMC_FreeMset(&mset);
return ret;
}
static void _PylibMC_FreeMset(pylibmc_mset *mset) {
Py_XDECREF(mset->key_obj);
mset->key_obj = NULL;
@ -795,6 +920,11 @@ static PyObject *PylibMC_Client_append(PylibMC_Client *self, PyObject *args,
}
/* }}} */
static PyObject *PylibMC_Client_cas(PylibMC_Client *self, PyObject *args,
PyObject *kwds) {
return _PylibMC_RunCasCommand(self, args, kwds);
}
static PyObject *PylibMC_Client_delete(PylibMC_Client *self, PyObject *args) {
PyObject *retval;
char *key;
@ -988,8 +1118,6 @@ cleanup:
return retval;
}
static PyObject *PylibMC_Client_incr(PylibMC_Client *self, PyObject *args) {
return _PylibMC_IncrSingle(self, memcached_increment, args);
}

View File

@ -238,11 +238,13 @@ static PylibMC_Client *PylibMC_ClientType_new(PyTypeObject *, PyObject *,
static void PylibMC_ClientType_dealloc(PylibMC_Client *);
static int PylibMC_Client_init(PylibMC_Client *, PyObject *, PyObject *);
static PyObject *PylibMC_Client_get(PylibMC_Client *, PyObject *arg);
static PyObject *PylibMC_Client_gets(PylibMC_Client *, PyObject *arg);
static PyObject *PylibMC_Client_set(PylibMC_Client *, PyObject *, PyObject *);
static PyObject *PylibMC_Client_replace(PylibMC_Client *, PyObject *, PyObject *);
static PyObject *PylibMC_Client_add(PylibMC_Client *, PyObject *, PyObject *);
static PyObject *PylibMC_Client_prepend(PylibMC_Client *, PyObject *, PyObject *);
static PyObject *PylibMC_Client_append(PylibMC_Client *, PyObject *, PyObject *);
static PyObject *PylibMC_Client_cas(PylibMC_Client *, PyObject *, PyObject *);
static PyObject *PylibMC_Client_delete(PylibMC_Client *, PyObject *);
static PyObject *PylibMC_Client_incr(PylibMC_Client *, PyObject *);
static PyObject *PylibMC_Client_decr(PylibMC_Client *, PyObject *);
@ -287,6 +289,8 @@ static bool _PylibMC_IncrDecr(PylibMC_Client*, pylibmc_incr*, size_t);
static PyMethodDef PylibMC_ClientType_methods[] = {
{"get", (PyCFunction)PylibMC_Client_get, METH_O,
"Retrieve a key from a memcached."},
{"gets", (PyCFunction)PylibMC_Client_gets, METH_O,
"Retrieve a key and cas_id from a memcached."},
{"set", (PyCFunction)PylibMC_Client_set, METH_VARARGS|METH_KEYWORDS,
"Set a key unconditionally."},
{"replace", (PyCFunction)PylibMC_Client_replace, METH_VARARGS|METH_KEYWORDS,
@ -297,6 +301,8 @@ static PyMethodDef PylibMC_ClientType_methods[] = {
"Prepend data to a key."},
{"append", (PyCFunction)PylibMC_Client_append, METH_VARARGS|METH_KEYWORDS,
"Append data to a key."},
{"cas", (PyCFunction)PylibMC_Client_cas, METH_VARARGS|METH_KEYWORDS,
"Attempt to compare-and-store a key by CAS ID."},
{"delete", (PyCFunction)PylibMC_Client_delete, METH_VARARGS,
"Delete a key."},
{"incr", (PyCFunction)PylibMC_Client_incr, METH_VARARGS,