Commits

Anonymous committed f582141

async methods exposed in python client. more robust error handling. async methods work

Comments (0)

Files changed (2)

     if (context->async_count >= context->async_limit) {		\
       PyErr_SetString(AsyncLimit, "async limit reached");	\
       return 0;							\
+    } else if (context->async.count == context->async.size) {	\
+      PyErr_SetString(AsyncLimit, "too many results pending");	\
+      return 0;							\
     }								\
   }
 
     return 0;					\
   }
 
+#define NEW_EXCEPTION(type, msg) {					\
+    return PyObject_CallObject(type, Py_BuildValue("(s)", msg));	\
+  }
+
 #define INTERNAL_EXCEPTION_HANDLER(x) {		\
     if (context->internal_exception == 1) {	\
       x;					\
     s = (s + 1) % x->size;
   }
   
-  free(x->buffer);
+  if (x->buffer)
+    free(x->buffer);
   x->buffer = new;
   x->size = size;
   x->count = o;
   return 0;
 }
 
+#define lcb_code(code, type)	  \
+  case code:			  \
+  e_type = type;		  \
+  e_msg = # code;		  \
+  break
+
+#define lcb_fail(code) lcb_code(code, Failure)
+
+PyObject *lcb_error(libcouchbase_error_t err, int context_error) {
+  PyObject *e_type = Failure;
+  char *e_msg = "unknown error passed to lcb_error";
+
+  switch (err) {
+    lcb_fail(LIBCOUCHBASE_SUCCESS);
+    lcb_fail(LIBCOUCHBASE_AUTH_CONTINUE);
+    lcb_fail(LIBCOUCHBASE_AUTH_ERROR);
+    lcb_fail(LIBCOUCHBASE_DELTA_BADVAL);
+    lcb_fail(LIBCOUCHBASE_E2BIG);
+    lcb_fail(LIBCOUCHBASE_EBUSY);
+    lcb_fail(LIBCOUCHBASE_EINTERNAL);
+    lcb_fail(LIBCOUCHBASE_EINVAL);
+    lcb_code(LIBCOUCHBASE_ENOMEM, OutOfMemory);
+    lcb_fail(LIBCOUCHBASE_ERANGE);
+    lcb_fail(LIBCOUCHBASE_ERROR);
+    lcb_fail(LIBCOUCHBASE_ETMPFAIL);
+    lcb_code(LIBCOUCHBASE_KEY_EEXISTS, KeyExists);
+    lcb_fail(LIBCOUCHBASE_KEY_ENOENT);
+    lcb_fail(LIBCOUCHBASE_LIBEVENT_ERROR);
+    lcb_code(LIBCOUCHBASE_NETWORK_ERROR, ConnectionFailure);
+    lcb_fail(LIBCOUCHBASE_NOT_MY_VBUCKET);
+    lcb_fail(LIBCOUCHBASE_NOT_STORED);
+    lcb_fail(LIBCOUCHBASE_NOT_SUPPORTED);
+    lcb_fail(LIBCOUCHBASE_UNKNOWN_COMMAND);
+    lcb_code(LIBCOUCHBASE_UNKNOWN_HOST, ConnectionFailure);
+    lcb_fail(LIBCOUCHBASE_PROTOCOL_ERROR);
+    lcb_code(LIBCOUCHBASE_ETIMEDOUT, Timeout);
+    lcb_code(LIBCOUCHBASE_CONNECT_ERROR, ConnectionFailure);
+  }
+
+  if (context_error)
+    CB_EXCEPTION(e_type, e_msg);
+  NEW_EXCEPTION(e_type, e_msg);
+}
+
+
 void *get_callback(libcouchbase_t instance,
 		   const void *cookie,
 		   libcouchbase_error_t error,
 		   libcouchbase_size_t nbytes,
 		   libcouchbase_uint32_t flags,
 		   libcouchbase_cas_t cas) {
+  int t = rip_ticket((int *) cookie);
+  if (context->async_mode) {
+    PyObject *rval;
+    --context->async_count;
 
-  if (rip_ticket((int *) cookie) != context->callback_ticket)
+    switch (error) {
+    case LIBCOUCHBASE_SUCCESS:
+      rval = Py_BuildValue("(s#k)", bytes, nbytes, (unsigned long) cas);
+      break;
+
+    case LIBCOUCHBASE_KEY_ENOENT:
+      Py_INCREF(Py_None);
+      rval = Py_None;
+      break;
+      
+    default:
+      rval = lcb_error(error, 0);
+    }
+
+    async_push(&context->async, t, rval);
+    return 0;
+  }
+
+  if (t != context->callback_ticket)
     return 0;
   context->result = error;
 
   case LIBCOUCHBASE_KEY_ENOENT:
     context->succeeded = 1;
     return 0;
-  case LIBCOUCHBASE_ENOMEM:
-    CB_EXCEPTION(OutOfMemory, "libcouchbase_enomem");
-  case LIBCOUCHBASE_EINTERNAL:
-    CB_EXCEPTION(Failure, "libcouchbase_einternal");
   default:
-    CB_EXCEPTION(Failure, "mystery condition in get callback");
+    return lcb_error(error, 1);
   }
   
   context->returned_value = Py_BuildValue("s#", bytes, nbytes);
 		   const void *key,
 		   libcouchbase_size_t nkey,
 		   libcouchbase_cas_t cas) {
-  if (rip_ticket((int *) cookie) != context->callback_ticket)
+  int t = rip_ticket((int *) cookie);
+  if (context->async_mode) {
+    PyObject *rval;
+    --context->async_count;
+    
+    switch(error) {
+    case LIBCOUCHBASE_SUCCESS:
+      Py_INCREF(Py_True);
+      rval = Py_True;
+      break;
+    default:
+      rval = lcb_error(error, 0);
+    }
+
+    async_push(&context->async, t, rval);
+    return 0;
+  }
+
+  if (t != context->callback_ticket)
     return 0;
   context->result = error;
 
   switch (error) {
   case LIBCOUCHBASE_SUCCESS:
     break;
-  case LIBCOUCHBASE_KEY_EEXISTS:
-    CB_EXCEPTION(KeyExists, "key already present");
   default:
-    CB_EXCEPTION(Failure, "mystery condition in set callback");
+    return lcb_error(error, 1);
   }
 
   return 0;
 		      libcouchbase_error_t error,
 		      const void *key,
 		      libcouchbase_size_t nkey) {
-  if (rip_ticket((int *) cookie) != context->callback_ticket)
+  int t = rip_ticket((int *) cookie);
+  if (context->async_mode) {
+    PyObject *rval;
+    --context->async_count;
+    
+    switch (error) {
+    case LIBCOUCHBASE_SUCCESS:
+      Py_INCREF(Py_True);
+      rval = Py_True;
+      break;
+    default:
+      rval = lcb_error(error, 0);
+    }
+
+    async_push(&context->async, t, rval);
+    return 0; 
+  }
+
+  if (t != context->callback_ticket)
     return 0;
   context->result = error;
 
   case LIBCOUCHBASE_SUCCESS:
     break;
   default:
-    CB_EXCEPTION(Failure, "mystery condition in set callback");
+    return lcb_error(error, 1);
   }  
 
   return 0;
 
   set_context(cb);
 
-  if (context->async.count < limit)
-    async_size(&context->async, limit);
+  if (context->async.count * 2 < limit)
+    async_size(&context->async, limit * 2);
   context->async_limit = limit;
 
   Py_RETURN_NONE;
 
   context->async_mode = 1;
   context->async_count = 0;
-  if (context->async.count < context->async_limit)
-    async_size(&context->async, context->async_limit);
+  if (context->async.count < context->async_limit * 2)
+    async_size(&context->async, context->async_limit * 2);
 
   Py_RETURN_NONE;
 }
     return 0;
   }
 
-  int *ticket = new_ticket();
-  if (!ticket)
-    return 0;
+  if (usec) {
+    int *ticket = new_ticket();
+    if (!ticket)
+      return 0;
 
-  if (!create_timeout(usec, hand_out_ticket(ticket))) {
-    rip_ticket(ticket);
-    return 0;
+    if (!create_timeout(usec, hand_out_ticket(ticket))) {
+      rip_ticket(ticket);
+      return 0;
+    }
   }
 
   while (!context->timed_out && context->async_count && !context->internal_exception)
     libcouchbase_wait(context->cb);
+
   INTERNAL_EXCEPTION_HANDLER(return 0);
 
-  return get_async_results();
+  PyObject *r = get_async_results();
+  if (!r) {
+    PyErr_SetString(Failure, "get_async_results");
+    return 0;
+  } return r;
 }
 
 static PyMethodDef PylibcbMethods[] = {
   { "disable_async", disable_async, METH_VARARGS,
     "Disable asynchronous behavior" },
   { "async_wait", async_wait, METH_VARARGS,
-  "Execute eventloop for a given number of microseconds" },
+    "Execute eventloop for a given number of microseconds" },
   { 0, 0, 0, 0 }
 };
 
 
     def remove(self, key, cas=0):
         return _pylibcb.remove(self.instance, key)
+
+    def get_async_limit(self):
+        return _pylibcb.get_async_limit(self.instance)
+
+    def set_async_limit(self, limit):
+        return _pylibcb.set_async_limit(self.instance, limit)
+
+    def get_async_count(self):
+        return _pylibcb.get_async_count(self.instance)
+
+    def enable_async(self):
+        return _pylibcb.enable_async(self.instance)
+
+    def disable_async(self):
+        return _pylibcb.disable_async(self.instance)
+
+    def async_wait(self, timeout=None):
+        if timeout:
+            return _pylibcb.async_wait(self.instance, int(timeout * 1000))
+        return _pylibcb.async_wait(self.instance, self.timeout)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.