{"id":1767,"date":"2014-12-16T18:41:50","date_gmt":"2014-12-16T18:41:50","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=1767"},"modified":"2024-09-12T02:16:54","modified_gmt":"2024-09-12T09:16:54","slug":"python-sdk-and-twisted","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/es\/python-sdk-and-twisted\/","title":{"rendered":"SDK de Python y Twisted"},"content":{"rendered":"<p class=\"p1\"><span style=\"font-family: inherit; font-size: 1em; line-height: 1.4375em;\">Estoy trabajando en una interfaz Twisted para el cliente Couchbase (<\/span><a href=\"https:\/\/github.com\/couchbase\/couchbase-python-client\" style=\"font-family: inherit; font-size: 1em; line-height: 1.4375em;\">https:\/\/github.com\/couchbase\/couchbase-python-client<\/a><span style=\"font-family: inherit; font-size: 1em; line-height: 1.4375em;\">). El enlace apunta a la interfaz s\u00edncrona. La rama trenzada experimental se encuentra en <\/span><a href=\"https:\/\/github.com\/couchbaselabs\/couchbase-twisted-client\" style=\"font-family: inherit; font-size: 1em; line-height: 1.4375em;\">https:\/\/github.com\/couchbaselabs\/couchbase-twisted-client<\/a><\/p>\n<p class=\"p1\">Para explicar c\u00f3mo funciona el cliente Twisted, explicar\u00e9 un poco c\u00f3mo funciona internamente la extensi\u00f3n C.<\/p>\n<h2 class=\"p1\">SDK interno b\u00e1sico<\/h2>\n<h2 class=\"p1\">libcouchbase: La biblioteca multiplataforma<\/h2>\n<p class=\"p1\">El propio cliente s\u00edncrono es una extensi\u00f3n de C que utiliza <em>libcouchbase<\/em> (https:\/\/github.com\/couchbase\/libcouchbase). libcouchbase act\u00faa como la capa de plataforma com\u00fan para una serie de otros clientes Couchbase escritos en una variedad de otros lenguajes (como Ruby, PHP, node.js, Perl, y varios otros).<\/p>\n<p class=\"p1\">\u00a0<\/p>\n<p class=\"p1\">La extensi\u00f3n Python es una extensi\u00f3n C que utiliza la API C de Python (por qu\u00e9 no se utilizan ffi, Cython, etc. es otro tema de discusi\u00f3n, pero s\u00f3lo dir\u00e9 que he estado muy contento con la API C de Python y con la estabilidad general y el mantenimiento de la extensi\u00f3n hasta ahora).<\/p>\n<p class=\"p1\">\u00a0<\/p>\n<p class=\"p1\">El funcionamiento de la extensi\u00f3n es bastante complejo. La API expuesta que se muestra a Python ofrece una variedad de m\u00e9todos de acceso a datos (p. ej. <em>configure<\/em>, <em>consiga<\/em>, <em>a\u00f1ada<\/em>, <em>a\u00f1adir<\/em>, <em>borrar<\/em>as\u00ed como variantes que pueden operar sobre m\u00faltiples valores, como <em>set_multi<\/em>, <em>get_multi<\/em> etc.).  Estos m\u00e9todos llaman directamente a la extensi\u00f3n Python, que realiza algunas validaciones de argumentos y luego programa estas operaciones en libcouchbase (utilizando, por ejemplo. <em>lcb_set()<\/em>, <em>lcb_get()<\/em>etc.). Una vez programadas estas operaciones, la extensi\u00f3n llama a <em>lcb_wait()<\/em> que se bloquea hasta que los resultados de las operaciones programadas se han completado (o han fallado con un error).<\/p>\n<p class=\"p1\">El c\u00f3digo general es el siguiente:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\">lcb_get_cmd_t comando<span style=\"color: #339933;\">;<\/span><br \/>lcb_get_cmd_t <span style=\"color: #339933;\">*<\/span>cmdp <span style=\"color: #339933;\">=<\/span> <span style=\"color: #339933;\">&#038;<\/span>comando<span style=\"color: #339933;\">;<\/span><br \/>mando.<span style=\"color: #202020;\">v<\/span>.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">clave<\/span> <span style=\"color: #339933;\">=<\/span> clave<span style=\"color: #339933;\">;<\/span> <span style=\"color: #808080; font-style: italic;\">\/* donde 'key' se extrae de alg\u00fan PyObject *\/<\/span><br \/>mando.<span style=\"color: #202020;\">v<\/span>.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">nkey<\/span> <span style=\"color: #339933;\">=<\/span> nkey<span style=\"color: #339933;\">;<\/span> <span style=\"color: #808080; font-style: italic;\">\/* longitud de la clave *\/<\/span><br \/>mando.<span style=\"color: #202020;\">v<\/span>.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">exptime<\/span> <span style=\"color: #339933;\">=<\/span> exptime<span style=\"color: #339933;\">;<\/span> <span style=\"color: #808080; font-style: italic;\">\/* opcional, extra\u00eddo del par\u00e1metro *\/<\/span><\/p>\n<p>errstatus <span style=\"color: #339933;\">=<\/span> lcb_get<span style=\"color: #009900;\">(<\/span>instancia<span style=\"color: #339933;\">,<\/span> <span style=\"color: #808080; font-style: italic;\">\/* asa de biblioteca *\/<\/span><span style=\"color: #339933;\">,<\/span><br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 galleta <span style=\"color: #808080; font-style: italic;\">\/* puntero opaco *\/<\/span><span style=\"color: #339933;\">,<\/span><br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #339933;\">&#038;<\/span>cmdp<span style=\"color: #339933;\">,<\/span> <span style=\"color: #808080; font-style: italic;\">\/* comando \"list\" *\/<\/span><br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span style=\"color: #0000dd;\">1<\/span> <span style=\"color: #808080; font-style: italic;\">\/* n\u00famero de elementos de la lista *\/<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><\/p>\n<p><span style=\"color: #808080; font-style: italic;\">\/** comprobar 'errstatus' *\/<\/span><br \/>errstatus <span style=\"color: #339933;\">=<\/span> lcb_wait<span style=\"color: #009900;\">(<\/span>instancia<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span> <span style=\"color: #808080; font-style: italic;\">\/* bloque *\/<\/span><\/div>\n<\/div>\n<p class=\"p1\">\u00a0<\/p>\n<p class=\"p1\"><em>lcb_wait<\/em> ejecuta un bucle de eventos interno implementado por libcouchbase, y eventualmente llama a los callbacks de resultado:<\/p>\n<p class=\"p1\">Los resultados reales no se entregan desde libcouchbase como un valor de retorno, sino que se pasan como una devoluci\u00f3n de llamada. La idea detr\u00e1s del uso de callbacks es que la estructura de resultados contiene punteros a buffers de red. Si los resultados fueran realmente \"devueltos\" por la librer\u00eda, ser\u00eda necesario que la librer\u00eda copiara los buffers de red temporales a la memoria asignada y que el usuario los liberara, lo que afectar\u00eda al rendimiento. La llamada de retorno del resultado es algo parecido a esto:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">void<\/span> get_callback<span style=\"color: #009900;\">(<\/span>lcb_t instancia<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">const<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span>galleta<span style=\"color: #339933;\">,<\/span> lcb_error_t estado<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">const<\/span> lcb_get_resp_t <span style=\"color: #339933;\">*<\/span>resp<span style=\"color: #009900;\">)<\/span><\/p>\n<p><span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #993333;\">char<\/span> <span style=\"color: #339933;\">*<\/span>clave <span style=\"color: #339933;\">=<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">clave<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #993333;\">char<\/span> <span style=\"color: #339933;\">*<\/span>valor <span style=\"color: #339933;\">=<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">bytes<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 <a href=\"https:\/\/www.opengroup.org\/onlinepubs\/009695399\/functions\/printf.html\"><span style=\"color: #000066;\">printf<\/span><\/a><span style=\"color: #009900;\">(<\/span><span style=\"color: #ff0000;\">\"Resultado obtenido para %.*s: %.*s<span style=\"color: #000099; font-weight: bold;\">n<\/span>&#8220;<\/span><span style=\"color: #339933;\">,<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">nkey<\/span><span style=\"color: #339933;\">,<\/span> clave<span style=\"color: #339933;\">,<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">nbytes<\/span><span style=\"color: #339933;\">,<\/span> valor<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 <a href=\"https:\/\/www.opengroup.org\/onlinepubs\/009695399\/functions\/printf.html\"><span style=\"color: #000066;\">printf<\/span><\/a><span style=\"color: #009900;\">(<\/span><span style=\"color: #ff0000;\">\"Banderas: %lu, CAS %llu<span style=\"color: #000099; font-weight: bold;\">n<\/span>&#8220;<\/span><span style=\"color: #339933;\">,<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">banderas<\/span><span style=\"color: #339933;\">,<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">cas<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #009900;\">}<\/span><\/div>\n<\/div>\n<p class=\"p1\">(En aras de la claridad, se han omitido la const-correcci\u00f3n y la fundici\u00f3n).<\/p>\n<p class=\"p1\">El usuario de la librer\u00eda C libcouchbase debe instalar callbacks para ser invocados para operaciones espec\u00edficas. Estos callbacks reciben la informaci\u00f3n de respuesta normal (es decir, respuesta, estado de error, etc.) as\u00ed como un puntero opaco por operaci\u00f3n (el '<em>galleta<\/em>') que pasa el usuario (en este caso la extensi\u00f3n C). Este puntero est\u00e1 controlado y gestionado por el usuario, y generalmente contiene datos espec\u00edficos del contexto\/aplicaci\u00f3n que el usuario puede utilizar para asociar una respuesta a una solicitud determinada.<\/p>\n<h2 class=\"p1\">Interacci\u00f3n con libcouchbase y CPython<\/h2>\n<p class=\"p1\">Desde la perspectiva de la API p\u00fablica expuesta por la extensi\u00f3n, cada operaci\u00f3n da lugar a un '<em>Resultado<\/em>(o una subclase adecuada de la misma) que se devuelve al usuario. La '<em>Resultado<\/em>contiene informaci\u00f3n sobre el resultado de la operaci\u00f3n y los metadatos asociados. Al interactuar con la biblioteca C, el objeto Result es una clase de extensi\u00f3n que hereda de <em>PyObject<\/em> y se asigna antes de cada petici\u00f3n a la biblioteca C. A continuaci\u00f3n, se pasa como '<em>galleta<\/em>a la operaci\u00f3n programada. Una vez que la biblioteca C invoca la llamada de retorno instalada, la llamada de retorno rellena el campo <em>Resultado<\/em> con la informaci\u00f3n pertinente. Por \u00faltimo, cuando el bucle de eventos sale del <em>Resultado<\/em> al usuario.<\/p>\n<p class=\"p1\"><span style=\"font-family: inherit; font-size: 1em; line-height: 1.4375em;\">Una aplicaci\u00f3n simplista ser\u00eda la siguiente:<\/span><\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">est\u00e1tico<\/span> PyObject <span style=\"color: #339933;\">*<\/span><br \/>Conexi\u00f3n_get<span style=\"color: #009900;\">(<\/span>pycbc_Conexi\u00f3n <span style=\"color: #339933;\">*<\/span>auto<span style=\"color: #339933;\">,<\/span> PyObject <span style=\"color: #339933;\">*<\/span>args<span style=\"color: #009900;\">)<\/span><br \/><span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #993333;\">const<\/span> <span style=\"color: #993333;\">char<\/span> <span style=\"color: #339933;\">*<\/span>clave<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #b1b100;\">si<\/span> <span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">!<\/span>PyArg_ParseTuple<span style=\"color: #009900;\">(<\/span><span style=\"color: #ff0000;\">\"s\"<\/span><span style=\"color: #339933;\">,<\/span> <span style=\"color: #339933;\">&#038;<\/span>clave<span style=\"color: #339933;\">,<\/span> args<span style=\"color: #009900;\">)<\/span> <span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #b1b100;\">devolver<\/span> NULL<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #009900;\">}<\/span><\/p>\n<p>\u00a0 \u00a0 lcb_get_cmd_t comando<span style=\"color: #339933;\">,<\/span> <span style=\"color: #339933;\">*<\/span>comandop <span style=\"color: #339933;\">=<\/span> <span style=\"color: #339933;\">&#038;<\/span>comando<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 comando<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">clave<\/span> <span style=\"color: #339933;\">=<\/span> clave<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 comando<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">nkey<\/span> <span style=\"color: #339933;\">=<\/span> <a href=\"https:\/\/www.opengroup.org\/onlinepubs\/009695399\/functions\/strlen.html\"><span style=\"color: #000066;\">strlen<\/span><\/a><span style=\"color: #009900;\">(<\/span>clave<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 pycbc_ValorResultado <span style=\"color: #339933;\">*<\/span>vresult <span style=\"color: #339933;\">=<\/span> PyObject_CallObject<span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">&#038;<\/span>pycbc_ValueResultType<span style=\"color: #339933;\">,<\/span> NULL<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 lcb_get<span style=\"color: #009900;\">(<\/span>auto<span style=\"color: #339933;\">-><\/span>instancia<span style=\"color: #339933;\">,<\/span> vresult<span style=\"color: #339933;\">,<\/span> <span style=\"color: #339933;\">&#038;<\/span>comandop<span style=\"color: #339933;\">,<\/span> <span style=\"color: #0000dd;\">1<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 lcb_wait<span style=\"color: #009900;\">(<\/span>auto<span style=\"color: #339933;\">-><\/span>instancia<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #b1b100;\">devolver<\/span> vresult<span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #009900;\">}<\/span><\/div>\n<\/div>\n<p class=\"p1\">\u00a0<\/p>\n<p class=\"p1\"><span style=\"font-family: inherit; font-size: 1em; line-height: 1.4375em;\">lcb_wait() se bloquear\u00e1 hasta que llegue el resultado y se invoque su callback. La llamada de retorno ser\u00eda algo como esto:<\/span><\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">void<\/span> get_callback<span style=\"color: #009900;\">(<\/span>lcb_t instancia<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">const<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span>galleta<span style=\"color: #339933;\">,<\/span> lcb_error_t err<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">const<\/span> lcb_get_resp_t <span style=\"color: #339933;\">*<\/span>resp<span style=\"color: #009900;\">)<\/span><br \/><span style=\"color: #009900;\">{<\/span><\/p>\n<p>\u00a0 \u00a0 pycbc_ValorResultado <span style=\"color: #339933;\">*<\/span>resultado <span style=\"color: #339933;\">=<\/span> <span style=\"color: #009900;\">(<\/span>pycbc_ValorResultado <span style=\"color: #339933;\">*<\/span><span style=\"color: #009900;\">)<\/span>galleta<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 resultado<span style=\"color: #339933;\">-><\/span>valor <span style=\"color: #339933;\">=<\/span> PyString_FromStringAndSize<span style=\"color: #009900;\">(<\/span>resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">bytes<\/span><span style=\"color: #339933;\">,<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">nbytes<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 resultado<span style=\"color: #339933;\">-><\/span>rc <span style=\"color: #339933;\">=<\/span> err<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 resultado<span style=\"color: #339933;\">-><\/span>banderas <span style=\"color: #339933;\">=<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">banderas<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 resultado<span style=\"color: #339933;\">-><\/span>cas <span style=\"color: #339933;\">=<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">cas<\/span><span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #009900;\">}<\/span><\/div>\n<\/div>\n<p class=\"p1\">[Tenga en cuenta que debido a que el SDK soporta cosas como transcodificadores y diferentes formatos de valores, el c\u00f3digo real de conversi\u00f3n hacia y desde claves y valores es significativamente m\u00e1s complejo que esto, pero no es relevante para la arquitectura real que se est\u00e1 discutiendo aqu\u00ed].<\/p>\n<p class=\"p1\">Para ser m\u00e1s espec\u00edficos, el <em>Resultado<\/em> s\u00f3lo se asigna desde dentro de la llamada de retorno, y lo que realmente se asigna antes de la secuencia de espera es un objeto <em>MultiResultado<\/em> que es un objeto <em>dic<\/em> subclase. Este <em>MultiResultado<\/em> extiende dict a\u00f1adiendo algunos par\u00e1metros internos espec\u00edficos; para cada llamada de retorno invocada durante la secuencia de espera, se crea un nuevo objeto <em>Resultado<\/em> y se inserta en este <em>MultiResultado<\/em> siendo la clave la del elemento de datos al que se accede. Por lo tanto, el c\u00f3digo en realidad se parece a esto:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #808080; font-style: italic;\">\/* &#8230;. *\/<\/span><br \/>pycbc_MultiResultado <span style=\"color: #339933;\">*<\/span>mres <span style=\"color: #339933;\">=<\/span> PyObject_CallObject<span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">&#038;<\/span>pycbc_MultiResultType<span style=\"color: #339933;\">,<\/span> NULL<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>lcb_get<span style=\"color: #009900;\">(<\/span>auto<span style=\"color: #339933;\">-><\/span>instancia<span style=\"color: #339933;\">,<\/span> mres<span style=\"color: #339933;\">,<\/span> <span style=\"color: #339933;\">&#038;<\/span>comandop<span style=\"color: #339933;\">,<\/span> <span style=\"color: #0000dd;\">1<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #808080; font-style: italic;\">\/* &#8230; *\/<\/span><\/div>\n<\/div>\n<p class=\"p1\">y en la devoluci\u00f3n de llamada<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #808080; font-style: italic;\">\/* &#8230; *\/<\/span><br \/>pycbc_MultiResultado <span style=\"color: #339933;\">*<\/span>mres <span style=\"color: #339933;\">=<\/span> <span style=\"color: #009900;\">(<\/span>pycbc_MultiResultado <span style=\"color: #339933;\">*<\/span><span style=\"color: #009900;\">)<\/span>galleta<span style=\"color: #339933;\">;<\/span><br \/>pycbc_ValorResultado <span style=\"color: #339933;\">*<\/span>vres <span style=\"color: #339933;\">=<\/span> PyObject_CallObject<span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">&#038;<\/span>pycbc_ValueResultType<span style=\"color: #339933;\">,<\/span> NULL<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><\/p>\n<p><span style=\"color: #808080; font-style: italic;\">\/* .. asignar miembros de vres *\/<\/span><\/p>\n<p>PyObject <span style=\"color: #339933;\">*<\/span>keyobj <span style=\"color: #339933;\">=<\/span> PyString_FromStringAndSize<span style=\"color: #009900;\">(<\/span>resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">clave<\/span><span style=\"color: #339933;\">,<\/span> resp<span style=\"color: #339933;\">-><\/span>v.<span style=\"color: #202020;\">v0<\/span>.<span style=\"color: #202020;\">nkey<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>vres<span style=\"color: #339933;\">-><\/span>clave <span style=\"color: #339933;\">=<\/span> keyobj<span style=\"color: #339933;\">;<\/span><br \/>PyDict_SetItem<span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">&#038;<\/span>mres<span style=\"color: #339933;\">-><\/span>dic<span style=\"color: #339933;\">,<\/span> clave<span style=\"color: #339933;\">,<\/span> vres<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>Py_DECREF<span style=\"color: #009900;\">(<\/span>vres<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #808080; font-style: italic;\">\/* &#8230; *\/<\/span><\/div>\n<\/div>\n<p class=\"p1\">Este dise\u00f1o interno, aunque complejo, permite una reutilizaci\u00f3n muy eficiente del c\u00f3digo (y por tanto de las pruebas y la depuraci\u00f3n) y funciona bastante bien. Adem\u00e1s, esta abstracci\u00f3n se implementa \u00edntegramente en C puro, por lo que la sobrecarga es m\u00ednima.<\/p>\n<h2 class=\"p1\">Implementaci\u00f3n de E\/S as\u00edncronas<\/h2>\n<h2>Integraci\u00f3n del bucle de eventos<\/h2>\n<p class=\"p1\"><span style=\"font-family: inherit; font-size: 1em; line-height: 1.4375em;\">La biblioteca C antes mencionada ofrece una interfaz extensible de complementos de E\/S. Anteriormente mencion\u00e9 que la librer\u00eda C usa su propio bucle de eventos interno, pero eso no es del todo cierto; m\u00e1s bien lo que hace la librer\u00eda es exponer una API de complemento de E\/S que permite implementar sus propias funciones para programar primitivas as\u00edncronas comunes como vigilantes de socket y eventos de tiempo de espera. Debido a que la biblioteca en s\u00ed es totalmente as\u00edncrona, permite la integraci\u00f3n con el flujo de programas no bloqueantes.<\/span><\/p>\n<p class=\"p1\">Esta API puede consultarse aqu\u00ed: https:\/\/github.com\/couchbase\/libcouchbase\/blob\/62300167f6f7d0f84ee6ac2162591805dfbf163d\/include\/libcouchbase\/types.h#L196-221<\/p>\n<p class=\"p1\">Por lo tanto, con el fin de integrarse con el bucle de eventos del reactor de Twisted, tuve que escribir un 'plugin IO' que implementar\u00eda las primitivas as\u00edncronas comunes utilizando los m\u00e9todos del reactor de Twisted; el resultado se puede ver aqu\u00ed, y por s\u00ed mismo es, creo, bastante sencillo:\u00a0<a href=\"https:\/\/github.com\/couchbaselabs\/couchbase-twisted-client\/blob\/twisted\/txcouchbase\/iops.py\" style=\"font-family: inherit; font-size: 1em; line-height: 1.4375em;\">https:\/\/github.com\/couchbaselabs\/couchbase-twisted-client\/blob\/twisted\/txcouchbase\/iops.py<\/a><\/p>\n<p class=\"p1\">[Tenga en cuenta que el<em>is_sync<\/em>est\u00e1 ah\u00ed para probar la funcionalidad b\u00e1sica en un patr\u00f3n s\u00edncrono utilizando el reactor de Twisted como backend del bucle de eventos. Este no es el valor por defecto].<\/p>\n<p class=\"p1\">Con el fin de exponer esta interfaz IO Plugin a Python, hice clases envoltorio de Python para estas primitivas; as\u00ed que tenemos <em>IOEvent<\/em>, <em>TimerEvent<\/em>etc. La idea b\u00e1sica es que estos objetos contienen punteros internos a los datos de llamada de retorno de C. Adem\u00e1s, existe el objeto '<em>IOPS<\/em>que gestiona uno o varios de estos objetos. La idea b\u00e1sica es que la biblioteca C llama (a trav\u00e9s de la extensi\u00f3n) a la clase '<em>IOPS<\/em>pas\u00e1ndole uno de los objetos Evento; solicitando alg\u00fan tipo de modificaci\u00f3n de la programaci\u00f3n (es decir, vigilar, des vigilar, destruir, etc.). La p\u00e1gina <em>IOPS<\/em> llama a la implementaci\u00f3n del bucle de eventos (en este caso, el reactor) para programar el evento deseado, pas\u00e1ndole el objeto Event correspondiente. Cuando el evento est\u00e1 listo, uno de los eventos '<em>listo_<\/em>*', que llama a C. Comprensiblemente, todo esto causa un cierto impacto en el rendimiento - pero con el beneficio de que el c\u00f3digo es ahora capaz de interactuar de forma as\u00edncrona en cualquier bucle de eventos de Python.<\/p>\n<p class=\"p1\">Poni\u00e9ndolo todo junto, parece algo as\u00ed:<\/p>\n<p class=\"p1\">Cuando libcouchbase crea un socket por primera vez, lo asocia con alg\u00fan tipo de objeto IO Event, que se expone como un puntero:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span>evento <span style=\"color: #339933;\">=<\/span> instancia<span style=\"color: #339933;\">-><\/span>iops.<span style=\"color: #202020;\">crear_evento<\/span><span style=\"color: #009900;\">(<\/span>instancia<span style=\"color: #339933;\">-><\/span>iops<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><\/div>\n<\/div>\n<p class=\"p1\">\u00a0<\/p>\n<p class=\"p1\">Esto llama a nuestro c\u00f3digo Python que se parece a esto:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">est\u00e1tico<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span><br \/>crear_evento<span style=\"color: #009900;\">(<\/span>lcb_io_opt_t <span style=\"color: #339933;\">*<\/span>iobase<span style=\"color: #009900;\">)<\/span><\/p>\n<p><span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 pycbc_iops_t <span style=\"color: #339933;\">=<\/span> <span style=\"color: #009900;\">(<\/span>pycbc_iops_t <span style=\"color: #339933;\">*<\/span><span style=\"color: #009900;\">)<\/span>iobase<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 PyObject <span style=\"color: #339933;\">*<\/span>f\u00e1brica_de_eventos <span style=\"color: #339933;\">=<\/span> PyObject_GetAttrString<span style=\"color: #009900;\">(<\/span>io<span style=\"color: #339933;\">-><\/span>py_impl<span style=\"color: #339933;\">,<\/span> <span style=\"color: #ff0000;\">\"crear_evento\"<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #b1b100;\">devolver<\/span> PyObject_CallObject<span style=\"color: #009900;\">(<\/span>f\u00e1brica_de_eventos<span style=\"color: #339933;\">,<\/span> NULL<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #009900;\">}<\/span><\/div>\n<\/div>\n<p class=\"p1\">Esta 'event_factory' debe devolver una subclase de IOEvent, con algunos campos a\u00f1adidos; puede tener un aspecto similar a<\/p>\n<p class=\"p1\">as\u00ed:<\/p>\n<div class=\"geshifilter\">\n<div class=\"python geshifilter-python\" style=\"font-family:monospace;\"><span style=\"color: #ff7700;font-weight:bold;\">de<\/span> couchbase._libcouchbase <span style=\"color: #ff7700;font-weight:bold;\">importar<\/span> IOEvent<\/p>\n<p><span style=\"color: #ff7700;font-weight:bold;\">clase<\/span> MyIOEvent<span style=\"color: black;\">(<\/span>IOEvent<span style=\"color: black;\">)<\/span>:<br \/>\u00a0 \u00a0 <span style=\"color: #ff7700;font-weight:bold;\">def<\/span> doRead<span style=\"color: black;\">(<\/span><span style=\"color: #008000;\">auto<\/span><span style=\"color: black;\">)<\/span>:<br \/>\u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #008000;\">auto<\/span>.<span style=\"color: black;\">ready_r<\/span><span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span><\/p>\n<p>\u00a0 \u00a0 <span style=\"color: #ff7700;font-weight:bold;\">def<\/span> doWrite<span style=\"color: black;\">(<\/span><span style=\"color: #008000;\">auto<\/span><span style=\"color: black;\">)<\/span>:<br \/>\u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #008000;\">auto<\/span>.<span style=\"color: black;\">listo_w<\/span><span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span><\/p>\n<p><span style=\"color: #ff7700;font-weight:bold;\">clase<\/span> IOPS<span style=\"color: black;\">(<\/span><span style=\"color: #008000;\">objeto<\/span><span style=\"color: black;\">)<\/span>:<br \/>\u00a0 \u00a0 <span style=\"color: #ff7700;font-weight:bold;\">def<\/span> crear_evento<span style=\"color: black;\">(<\/span><span style=\"color: #008000;\">auto<\/span><span style=\"color: black;\">)<\/span>:<br \/>\u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #ff7700;font-weight:bold;\">devolver<\/span> MyIOEvent<span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span><\/div>\n<\/div>\n<p>Ahora, cuando la biblioteca quiere recibir notificaciones cuando un determinado socket est\u00e1 disponible, hace algo como esto:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\">instancia<span style=\"color: #339933;\">-><\/span>iops<span style=\"color: #339933;\">-><\/span>actualizar_evento<span style=\"color: #009900;\">(<\/span>instancia<span style=\"color: #339933;\">-><\/span>iops<span style=\"color: #339933;\">,<\/span> sockfd<span style=\"color: #339933;\">,<\/span> LCB_READ_EVENT<span style=\"color: #339933;\">,<\/span> alg\u00fan_callback<span style=\"color: #339933;\">,<\/span> alg\u00fan_puntero<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><\/div>\n<\/div>\n<p>Que luego llama a esto:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">est\u00e1tico<\/span> <span style=\"color: #993333;\">void<\/span><br \/>actualizar_evento<span style=\"color: #009900;\">(<\/span>lcb_io_opt_t iobase<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span>evento<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">int<\/span> sockfd<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">corto<\/span> banderas<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">*<\/span>devoluci\u00f3n de llamada<span style=\"color: #009900;\">)<\/span><span style=\"color: #009900;\">(<\/span><span style=\"color: #993333;\">int<\/span><span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">corto<\/span><span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span>datos<span style=\"color: #009900;\">)<\/span><br \/><span style=\"color: #009900;\">{<\/span><\/p>\n<p>\u00a0 \u00a0 pycbc_io_opt_t <span style=\"color: #339933;\">*<\/span>io <span style=\"color: #339933;\">=<\/span> <span style=\"color: #009900;\">(<\/span>pycbc_io_opt_t <span style=\"color: #339933;\">*<\/span><span style=\"color: #009900;\">)<\/span>iobase<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 pycbc_IOEvent <span style=\"color: #339933;\">*<\/span>ev <span style=\"color: #339933;\">=<\/span> <span style=\"color: #009900;\">(<\/span>pycbc_IOEvent <span style=\"color: #339933;\">*<\/span><span style=\"color: #009900;\">)<\/span>evento<span style=\"color: #339933;\">;<\/span><\/p>\n<p>\u00a0 \u00a0 evento<span style=\"color: #339933;\">-><\/span>fd <span style=\"color: #339933;\">=<\/span> sockfd<span style=\"color: #339933;\">;<\/span> <span style=\"color: #808080; font-style: italic;\">\/* almacenamiento para '.fileno()' *\/<\/span><br \/>\u00a0 \u00a0 evento<span style=\"color: #339933;\">-><\/span>callback_info.<span style=\"color: #202020;\">devoluci\u00f3n de llamada<\/span> <span style=\"color: #339933;\">=<\/span> devoluci\u00f3n de llamada<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 evento<span style=\"color: #339933;\">-><\/span>callback_info.<span style=\"color: #202020;\">datos<\/span> <span style=\"color: #339933;\">=<\/span> datos<span style=\"color: #339933;\">;<\/span><\/p>\n<p>\u00a0 \u00a0 PyObject <span style=\"color: #339933;\">*<\/span>args <span style=\"color: #339933;\">=<\/span> Py_BuildValue<span style=\"color: #009900;\">(<\/span><span style=\"color: #ff0000;\">\"(O,I)\"<\/span><span style=\"color: #339933;\">,<\/span> ev<span style=\"color: #339933;\">,<\/span> banderas<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 PyObject <span style=\"color: #339933;\">*<\/span>metanfetamina <span style=\"color: #339933;\">=<\/span> PyObject_GetAttrString<span style=\"color: #009900;\">(<\/span>io<span style=\"color: #339933;\">-><\/span>py_impl<span style=\"color: #339933;\">,<\/span> <span style=\"color: #ff0000;\">\"actualizar_evento\"<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 PyObject_CallObject<span style=\"color: #009900;\">(<\/span>metanfetamina<span style=\"color: #339933;\">,<\/span> args<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #009900;\">}<\/span><\/div>\n<\/div>\n<p>Que en Python podr\u00eda parecerse a:<\/p>\n<div class=\"geshifilter\">\n<div class=\"python geshifilter-python\" style=\"font-family:monospace;\"><span style=\"color: #ff7700;font-weight:bold;\">def<\/span> actualizar_evento<span style=\"color: black;\">(<\/span><span style=\"color: #008000;\">auto<\/span><span style=\"color: #66cc66;\">,<\/span> evento<span style=\"color: #66cc66;\">,<\/span> banderas<span style=\"color: black;\">)<\/span>:<br \/>\u00a0 \u00a0 <span style=\"color: #ff7700;font-weight:bold;\">si<\/span> banderas &amp; READ_EVENT:<br \/>\u00a0 \u00a0 \u00a0 \u00a0<span style=\"color: #008000;\">auto<\/span>.<span style=\"color: black;\">reactor<\/span>.<span style=\"color: black;\">addReader<\/span><span style=\"color: black;\">(<\/span>evento<span style=\"color: black;\">)<\/span><\/div>\n<\/div>\n<p>El par\u00e1metro 'event' es un objeto devuelto por nuestro m\u00e9todo anterior 'create_event', que devuelve una instancia de MyIOEvent, que contiene la implementaci\u00f3n necesaria de doRead.<\/p>\n<p>En alg\u00fan momento en el futuro, el reactor detecta que el fileno() subyacente est\u00e1 disponible para lectura, y llama al m\u00e9todo 'doRead()' - mostrado arriba. En nuestra implementaci\u00f3n, doRead llama a 'ready_r()', que es un m\u00e9todo implementado en C:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">est\u00e1tico<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span><br \/>Evento_listo_r<span style=\"color: #009900;\">(<\/span>pycbc_IOEvent <span style=\"color: #339933;\">*<\/span>evento<span style=\"color: #009900;\">)<\/span><br \/><span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 evento<span style=\"color: #339933;\">-><\/span>callback_info.<span style=\"color: #202020;\">devoluci\u00f3n de llamada<\/span><span style=\"color: #009900;\">(<\/span>evento<span style=\"color: #339933;\">-><\/span>fd<span style=\"color: #339933;\">,<\/span> READ_EVENT<span style=\"color: #339933;\">,<\/span> evento<span style=\"color: #339933;\">-><\/span>callback_info.<span style=\"color: #202020;\">datos<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #009900;\">}<\/span><\/div>\n<\/div>\n<h2 class=\"p1\">API de conexi\u00f3n futura\/diferida<\/h2>\n<p class=\"p1\">Para que la API de acceso a datos sea as\u00edncrona, he a\u00f1adido algunos par\u00e1metros privados en el objeto Connection que lo marcan como 'as\u00edncrono' - b\u00e1sicamente establece un campo dentro del objeto Connection y asigna un campo '<em>IOPS<\/em>'. Una vez programada cada operaci\u00f3n, la extensi\u00f3n comprobar\u00e1 si el objeto Connection '<em>F_ASYNC<\/em>est\u00e1 activada. Si lo est\u00e1, no llamar\u00e1 a <em>lcb_wait<\/em>(), pero devuelve un <em>AsyncResult<\/em> (que es una subclase de <em>MultiResultado<\/em>), en lugar de esperar el resultado. Este '<em>AsyncResult<\/em>contiene un objeto '<em>errback<\/em>y<em>devoluci\u00f3n de llamada<\/em>que se invocan cuando el resultado est\u00e1 listo.<\/p>\n<p class=\"p1\">Del mismo modo, en el c\u00f3digo de devoluci\u00f3n de llamada, si la conexi\u00f3n '<em>F_ASYNC<\/em>cada vez que se reciba un resultado, se invocar\u00e1 a la funci\u00f3n AsyncResult.callback o AsyncResult.errback correspondiente (en funci\u00f3n del \u00e9xito o el fracaso).<\/p>\n<p class=\"p1\">Lo bueno de la estructura interna es que se han necesitado muy pocas modificaciones para permitir el funcionamiento as\u00edncrono; y, por tanto, toda la estabilidad de la API de sincronizaci\u00f3n se a\u00f1ade a la nueva interfaz as\u00edncrona.<\/p>\n<p class=\"p1\">Nuestra sencilla implementaci\u00f3n de 'get' tiene ahora este aspecto en C:<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">est\u00e1tico<\/span> PyObject <span style=\"color: #339933;\">*<\/span><\/p>\n<p>consiga<span style=\"color: #009900;\">(<\/span>pycbc_Conexi\u00f3n <span style=\"color: #339933;\">*<\/span>auto<span style=\"color: #339933;\">,<\/span> PyObject <span style=\"color: #339933;\">*<\/span>args<span style=\"color: #009900;\">)<\/span><\/p>\n<p><span style=\"color: #009900;\">{<\/span><\/p>\n<p>\u00a0 <span style=\"color: #808080; font-style: italic;\">\/* Valida los argumentos normalmente *\/<\/span><br \/>\u00a0 &#8230;<\/p>\n<p>\u00a0 <span style=\"color: #202020;\">pycbc_MultiResultado<\/span> <span style=\"color: #339933;\">*<\/span>mres<span style=\"color: #339933;\">;<\/span><\/p>\n<p>\u00a0 <span style=\"color: #b1b100;\">si<\/span> <span style=\"color: #009900;\">(<\/span>auto<span style=\"color: #339933;\">-><\/span>banderas <span style=\"color: #339933;\">&#038;<\/span> F_ASYNC<span style=\"color: #009900;\">)<\/span> <span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 mres <span style=\"color: #339933;\">=<\/span> PyObject_CallObject<span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">&#038;<\/span>pycbc_AsyncResultType<span style=\"color: #339933;\">,<\/span> NULL<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 <span style=\"color: #009900;\">}<\/span> <span style=\"color: #b1b100;\">si no<\/span> <span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 mres <span style=\"color: #339933;\">=<\/span> PyObject_CallObject<span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">&#038;<\/span>pycbc_MultiResultType<span style=\"color: #339933;\">,<\/span> NULL<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 <span style=\"color: #009900;\">}<\/span><\/p>\n<p>\u00a0 <span style=\"color: #808080; font-style: italic;\">\/** Validar argumentos, etc. *\/<\/span><br \/>\u00a0 &#8230;<\/p>\n<p>\u00a0 <span style=\"color: #202020;\">err<\/span> <span style=\"color: #339933;\">=<\/span> lcb_get<span style=\"color: #009900;\">(<\/span>auto<span style=\"color: #339933;\">-><\/span>instancia<span style=\"color: #339933;\">,<\/span> mres<span style=\"color: #339933;\">,<\/span> <span style=\"color: #339933;\">&#038;<\/span>comandop<span style=\"color: #339933;\">,<\/span> <span style=\"color: #0000dd;\">1<\/span><span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><\/p>\n<p>\u00a0 <span style=\"color: #b1b100;\">si<\/span> <span style=\"color: #009900;\">(<\/span>auto<span style=\"color: #339933;\">-><\/span>banderas <span style=\"color: #339933;\">&#038;<\/span> F_ASYNC<span style=\"color: #009900;\">)<\/span> <span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 \u00a0lcb_wait<span style=\"color: #009900;\">(<\/span>auto<span style=\"color: #339933;\">-><\/span>instancia<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 <span style=\"color: #009900;\">}<\/span><\/p>\n<p>\u00a0 <span style=\"color: #b1b100;\">devolver<\/span> mres<span style=\"color: #339933;\">;<\/span><br \/><span style=\"color: #009900;\">}<\/span><\/div>\n<\/div>\n<p class=\"p1\">Y lo mismo en la devoluci\u00f3n de llamada;<\/p>\n<div class=\"geshifilter\">\n<div class=\"c geshifilter-c\" style=\"font-family:monospace;\"><span style=\"color: #993333;\">est\u00e1tico<\/span> <span style=\"color: #993333;\">void<\/span><br \/>get_callback<span style=\"color: #009900;\">(<\/span>lcb_t instancia<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">const<\/span> <span style=\"color: #993333;\">void<\/span> <span style=\"color: #339933;\">*<\/span>galleta<span style=\"color: #339933;\">,<\/span> lcb_error_t err<span style=\"color: #339933;\">,<\/span> <span style=\"color: #993333;\">const<\/span> lcb_get_resp_t <span style=\"color: #339933;\">*<\/span>resp<span style=\"color: #009900;\">)<\/span><\/p>\n<p><span style=\"color: #009900;\">{<\/span><br \/>\u00a0 pycbc_MultiResultado <span style=\"color: #339933;\">*<\/span>mres <span style=\"color: #339933;\">=<\/span> galleta<span style=\"color: #339933;\">;<\/span><br \/>\u00a0 pycbc_ValorResultado <span style=\"color: #339933;\">*<\/span>vres <span style=\"color: #339933;\">=<\/span> PyObject_CallObject<span style=\"color: #009900;\">(<\/span><span style=\"color: #339933;\">&#038;<\/span>pycbc_ValueResultType<span style=\"color: #339933;\">,<\/span> NULL<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><\/p>\n<p>\u00a0 <span style=\"color: #808080; font-style: italic;\">\/** Configurar el valor resultante, e insertar en el diccionario ... *\/<\/span><br \/>\u00a0 &#8230;<\/p>\n<p>\u00a0 <span style=\"color: #b1b100;\">si<\/span> <span style=\"color: #009900;\">(<\/span>mres<span style=\"color: #339933;\">-><\/span>padre<span style=\"color: #339933;\">-><\/span>banderas <span style=\"color: #339933;\">&#038;<\/span> F_ASYNC<span style=\"color: #009900;\">)<\/span> <span style=\"color: #009900;\">{<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #808080; font-style: italic;\">\/* es un AsyncResult, que es una subclase *\/<\/span><br \/>\u00a0 \u00a0 PyObject_CallObject<span style=\"color: #009900;\">(<\/span> <span style=\"color: #009900;\">(<\/span><span style=\"color: #009900;\">(<\/span>pycbc_AsyncResultado <span style=\"color: #339933;\">*<\/span><span style=\"color: #009900;\">)<\/span>mres<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">-><\/span>devoluci\u00f3n de llamada<span style=\"color: #339933;\">,<\/span> NULL<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span><br \/>\u00a0 \u00a0 Py_DECREF<span style=\"color: #009900;\">(<\/span>mres<span style=\"color: #009900;\">)<\/span><span style=\"color: #339933;\">;<\/span> <span style=\"color: #808080; font-style: italic;\">\/* no lo devolvemos *\/<\/span><br \/>\u00a0 <span style=\"color: #009900;\">}<\/span><br \/><span style=\"color: #009900;\">}<\/span><\/div>\n<\/div>\n<p class=\"p1\">Un nuevo '<em>txcouchbase<\/em>que contiene su propio paquete <em>Conexi\u00f3n<\/em> clase. Este <em>Conexi\u00f3n<\/em> durante la construcci\u00f3n establece estas banderas internas. Adem\u00e1s, para cada operaci\u00f3n, la clase <em>AsyncResult<\/em> se envuelve dentro de un callback con el equivalente a la siguiente construcci\u00f3n:<\/p>\n<div class=\"geshifilter\">\n<div class=\"python geshifilter-python\" style=\"font-family:monospace;\"><span style=\"color: #ff7700;font-weight:bold;\">def<\/span> consiga<span style=\"color: black;\">(<\/span><span style=\"color: #008000;\">auto<\/span><span style=\"color: #66cc66;\">,<\/span> *args<span style=\"color: #66cc66;\">,<\/span> **kwargs<span style=\"color: black;\">)<\/span>:<br \/>\u00a0 \u00a0 async_res <span style=\"color: #66cc66;\">=<\/span> <span style=\"color: #008000;\">super<\/span><span style=\"color: black;\">(<\/span>Conexi\u00f3n<span style=\"color: #66cc66;\">,<\/span> <span style=\"color: #008000;\">auto<\/span><span style=\"color: black;\">)<\/span>.<span style=\"color: black;\">consiga<\/span><span style=\"color: black;\">(<\/span>*args<span style=\"color: #66cc66;\">,<\/span> **kwargs<span style=\"color: black;\">)<\/span><br \/>\u00a0 \u00a0 d <span style=\"color: #66cc66;\">=<\/span> Aplazado<span style=\"color: black;\">(<\/span><span style=\"color: black;\">)<\/span><br \/>\u00a0 \u00a0 async_res.<span style=\"color: black;\">devoluci\u00f3n de llamada<\/span> <span style=\"color: #66cc66;\">=<\/span> d.<span style=\"color: black;\">devoluci\u00f3n de llamada<\/span><br \/>\u00a0 \u00a0 async_res.<span style=\"color: black;\">errback<\/span> <span style=\"color: #66cc66;\">=<\/span> d.<span style=\"color: black;\">errback<\/span><br \/>\u00a0 \u00a0 <span style=\"color: #ff7700;font-weight:bold;\">devolver<\/span> d<\/div>\n<\/div>\n<p class=\"p1\">Una vez que el resultado est\u00e1 listo, se invoca la llamada de retorno con un comando <em>MultiResultado<\/em> o <em>Resultado<\/em> (dependiendo de si se ha realizado una variante \u00fanica o m\u00faltiple de la operaci\u00f3n en la API).<\/p>","protected":false},"excerpt":{"rendered":"<p>I&#39;m working on a Twisted interface to the Couchbase client (https:\/\/github.com\/couchbase\/couchbase-python-client). The link there points to the synchronous interface. The experimental twisted branch is at https:\/\/github.com\/couchbaselabs\/couchbase-twisted-client To explain how the Twisted client works, I&#39;ll explain a bit about how the [&hellip;]<\/p>","protected":false},"author":38,"featured_media":13873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[],"ppma_author":[8997],"class_list":["post-1767","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.2 (Yoast SEO v26.2) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Python SDK and Twisted - The Couchbase Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/es\/python-sdk-and-twisted\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Python SDK and Twisted\" \/>\n<meta property=\"og:description\" content=\"I&#039;m working on a Twisted interface to the Couchbase client (https:\/\/github.com\/couchbase\/couchbase-python-client). The link there points to the synchronous interface. The experimental twisted branch is at https:\/\/github.com\/couchbaselabs\/couchbase-twisted-client To explain how the Twisted client works, I&#039;ll explain a bit about how the [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/es\/python-sdk-and-twisted\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2014-12-16T18:41:50+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-09-12T09:16:54+00:00\" \/>\n<meta name=\"author\" content=\"Mark Nunberg, Software Engineer, Couchbase\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Mark Nunberg, Software Engineer, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/\"},\"author\":{\"name\":\"Mark Nunberg, Software Engineer, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/76a75284da32b6f257c8e5e156e6e016\"},\"headline\":\"Python SDK and Twisted\",\"datePublished\":\"2014-12-16T18:41:50+00:00\",\"dateModified\":\"2024-09-12T09:16:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/\"},\"wordCount\":2158,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"articleSection\":[\"Uncategorized\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/\",\"name\":\"Python SDK and Twisted - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2014-12-16T18:41:50+00:00\",\"dateModified\":\"2024-09-12T09:16:54+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Python SDK and Twisted\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/76a75284da32b6f257c8e5e156e6e016\",\"name\":\"Mark Nunberg, Software Engineer, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/895cad0986a0ab674fda857b6ba71ce0\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d5a465565eb8a3990192957806a9bc2989ba9f52a5f953d988b5e8afff3b6dc7?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d5a465565eb8a3990192957806a9bc2989ba9f52a5f953d988b5e8afff3b6dc7?s=96&d=mm&r=g\",\"caption\":\"Mark Nunberg, Software Engineer, Couchbase\"},\"description\":\"Mark Nunberg is a software engineer working at Couchbase. He maintains the C client library (libcouchbase) as well as the Python client. He also developed the Perl client (for use at his previous company) - which initially led him to working at Couchbase. Prior to joining Couchbase, he worked on distributed and high performance routing systems at an eCommerce analytics firm. Mark studied Linguistics at the Hebrew University of Jerusalem.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/es\/author\/mark-nunberg\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Python SDK and Twisted - The Couchbase Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/es\/python-sdk-and-twisted\/","og_locale":"es_MX","og_type":"article","og_title":"Python SDK and Twisted","og_description":"I&#39;m working on a Twisted interface to the Couchbase client (https:\/\/github.com\/couchbase\/couchbase-python-client). The link there points to the synchronous interface. The experimental twisted branch is at https:\/\/github.com\/couchbaselabs\/couchbase-twisted-client To explain how the Twisted client works, I&#39;ll explain a bit about how the [&hellip;]","og_url":"https:\/\/www.couchbase.com\/blog\/es\/python-sdk-and-twisted\/","og_site_name":"The Couchbase Blog","article_published_time":"2014-12-16T18:41:50+00:00","article_modified_time":"2024-09-12T09:16:54+00:00","author":"Mark Nunberg, Software Engineer, Couchbase","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Mark Nunberg, Software Engineer, Couchbase","Est. reading time":"10 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/"},"author":{"name":"Mark Nunberg, Software Engineer, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/76a75284da32b6f257c8e5e156e6e016"},"headline":"Python SDK and Twisted","datePublished":"2014-12-16T18:41:50+00:00","dateModified":"2024-09-12T09:16:54+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/"},"wordCount":2158,"commentCount":2,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","articleSection":["Uncategorized"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/","url":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/","name":"Python SDK and Twisted - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2014-12-16T18:41:50+00:00","dateModified":"2024-09-12T09:16:54+00:00","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/python-sdk-and-twisted\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Python SDK and Twisted"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"El blog de Couchbase","description":"Couchbase, la base de datos NoSQL","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"El blog de Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/76a75284da32b6f257c8e5e156e6e016","name":"Mark Nunberg, Ingeniero de Software, Couchbase","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/895cad0986a0ab674fda857b6ba71ce0","url":"https:\/\/secure.gravatar.com\/avatar\/d5a465565eb8a3990192957806a9bc2989ba9f52a5f953d988b5e8afff3b6dc7?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d5a465565eb8a3990192957806a9bc2989ba9f52a5f953d988b5e8afff3b6dc7?s=96&d=mm&r=g","caption":"Mark Nunberg, Software Engineer, Couchbase"},"description":"Mark Nunberg es un ingeniero de software que trabaja en Couchbase. Mantiene la biblioteca del cliente C (libcouchbase), as\u00ed como el cliente Python. Tambi\u00e9n desarroll\u00f3 el cliente Perl (para su uso en su anterior empresa) - que inicialmente le llev\u00f3 a trabajar en Couchbase. Antes de unirse a Couchbase, trabaj\u00f3 en sistemas de enrutamiento distribuidos y de alto rendimiento en una empresa de an\u00e1lisis de comercio electr\u00f3nico. Mark estudi\u00f3 Ling\u00fc\u00edstica en la Universidad Hebrea de Jerusal\u00e9n.","url":"https:\/\/www.couchbase.com\/blog\/es\/author\/mark-nunberg\/"}]}},"authors":[{"term_id":8997,"user_id":38,"is_guest":0,"slug":"mark-nunberg","display_name":"Mark Nunberg, Software Engineer, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/d5a465565eb8a3990192957806a9bc2989ba9f52a5f953d988b5e8afff3b6dc7?s=96&d=mm&r=g","author_category":"","last_name":"Nunberg","first_name":"Mark","job_title":"","user_url":"","description":"Mark Nunberg es un ingeniero de software que trabaja en Couchbase. Mantiene la biblioteca del cliente C (libcouchbase), as\u00ed como el cliente Python. Tambi\u00e9n desarroll\u00f3 el cliente Perl (para su uso en su anterior empresa) - que inicialmente le llev\u00f3 a trabajar en Couchbase. Antes de unirse a Couchbase, trabaj\u00f3 en sistemas de enrutamiento distribuidos y de alto rendimiento en una empresa de an\u00e1lisis de comercio electr\u00f3nico. Mark estudi\u00f3 Ling\u00fc\u00edstica en la Universidad Hebrea de Jerusal\u00e9n."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/1767","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/users\/38"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/comments?post=1767"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/1767\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media?parent=1767"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/categories?post=1767"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/tags?post=1767"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/ppma_author?post=1767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}