{"id":1844,"date":"2014-12-16T17:28:59","date_gmt":"2014-12-16T17:28:58","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=1844"},"modified":"2014-12-16T17:28:59","modified_gmt":"2014-12-16T17:28:58","slug":"writing-your-own-storage-engine-memcached-part-3","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/pt\/writing-your-own-storage-engine-memcached-part-3\/","title":{"rendered":"Criando seu pr\u00f3prio mecanismo de armazenamento para o Memcached, parte 3"},"content":{"rendered":"<p>No momento, temos um mecanismo capaz de executar get e set load, mas ele est\u00e1 fazendo IO sincronizada do sistema de arquivos. N\u00e3o podemos atender nosso cliente mais r\u00e1pido do que podemos ler o item do disco, mas podemos atender <b>outros<\/b> enquanto estivermos lendo o item do disco.<\/p>\n<p>Nesta entrada, vamos corrigir nosso <code><font color=\"#999999\">get<\/font><\/code> e <code><font color=\"#999999\">store<\/font><\/code> para que eles n\u00e3o bloqueiem a API do mecanismo. Como eu disse anteriormente, a inten\u00e7\u00e3o deste tutorial \u00e9 focar no m\u00e9todo <em>API do mecanismo<\/em>. Isso significa que n\u00e3o tentarei criar um design eficaz, pois isso poderia desviar o foco do que estou tentando explicar. Se as pessoas estiverem interessadas em saber como podemos otimizar isso, eu poderia adicionar uma segunda parte do tutorial no futuro... \u00c9 s\u00f3 me avisar.<\/p>\n<p>Para implementar opera\u00e7\u00f5es ass\u00edncronas em nosso mecanismo, precisamos usar a API que o servidor disponibiliza para n\u00f3s em <code><font color=\"#999999\">create_instance<\/font><\/code>. Vamos estender a estrutura do nosso mecanismo para manter o controle da API do servidor:<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">struct fs_engine {<br \/>\u00a0 \u00a0Motor ENGINE_HANDLE_V1;<br \/>\u00a0 \u00a0<b>SERVER_HANDLE_V1 sapi;<\/b><br \/>};<\/p>\n<p>&#8230;<\/p>\n<p>MEMCACHED_PUBLIC_API<br \/>ENGINE_ERROR_CODE create_instance(uint64_t interface,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 GET_SERVER_API get_server_api,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ENGINE_HANDLE **handle)<br \/>{<\/p>\n<p>&#8230;<\/p>\n<p>\u00a0 \u00a0h-&gt;engine.item_set_cas = fs_item_set_cas;<br \/>\u00a0 \u00a0h-&gt;engine.get_item_info = fs_get_item_info;<br \/>\u00a0 \u00a0<b>h-&gt;sapi = *get_server_api();<\/b><\/p>\n<p>&#8230;<\/p>\n<p>\u00a0 \u00a0 \u00a0 <\/p><\/div>\n<\/div>\n<p>Para implementar uma fun\u00e7\u00e3o ass\u00edncrona na interface do mecanismo, o mecanismo precisa enviar a solicita\u00e7\u00e3o para outro thread antes de retornar <code><font color=\"#999999\">ENGINE_EWOULDBLOCK<\/font><\/code> da fun\u00e7\u00e3o do mecanismo. Quando o backend termina de processar o resultado, ele notifica o n\u00facleo do memcached usando a fun\u00e7\u00e3o <code><font color=\"#999999\">notify_io_complete<\/font><\/code> na interface do servidor. Se ocorrer um erro durante o processamento da solicita\u00e7\u00e3o, o n\u00facleo do memcached retornar\u00e1 a mensagem de erro ao cliente. Se seu mecanismo chamou <code><font color=\"#999999\">notify_io_complete<\/font><\/code> com <code><font color=\"#999999\">ENGINE_SUCCESS<\/font><\/code>o n\u00facleo do memcached chamar\u00e1 a fun\u00e7\u00e3o de interface do mecanismo mais uma vez com o mesmo argumento que o argumento<br \/>primeira vez.<\/p>\n<p>Se voc\u00ea observar a API do servidor, ver\u00e1 que ela tem uma interface para armazenar um ponteiro espec\u00edfico do mecanismo. Isso facilitar\u00e1 nossa vida quando quisermos implementar o io ass\u00edncrono. Portanto, vamos em frente e atualizar nosso <code><font color=\"#999999\">fs_get<\/font><\/code> m\u00e9todo:<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">static ENGINE_ERROR_CODE fs_get(ENGINE_HANDLE* handle,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const void* cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 item** item,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const void* key,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const int nkey,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 uint16_t vbucket)<br \/>{<br \/>\u00a0 \u00a0struct fs_engine *engine = (struct fs_engine *)engine;<br \/>\u00a0 \u00a0void *res = engine-&gt;sapi.cookie-&gt;get_engine_specific(cookie);<br \/>\u00a0 \u00a0Se (res != NULL) {<br \/>\u00a0 \u00a0 \u00a0 *item = res;<br \/>\u00a0 \u00a0 \u00a0 engine-&gt;sapi.cookie-&gt;store_engine_specific(cookie, NULL);<br \/>\u00a0 \u00a0 \u00a0 retornar ENGINE_SUCCESS;<\/p>\n<p>\u00a0 \u00a0}<\/p>\n<p>&#8230;<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>A pr\u00f3xima coisa que precisamos fazer \u00e9 criar uma fun\u00e7\u00e3o que seja executada de forma ass\u00edncrona e armazene o resultado na configura\u00e7\u00e3o espec\u00edfica do mecanismo para o cookie. Como usaremos tarefas ass\u00edncronas para todos os m\u00e9todos do mecanismo, vamos criar uma fun\u00e7\u00e3o para executar tarefas em outro thread:<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">static ENGINE_ERROR_CODE execute(struct task *task)<br \/>{<br \/>\u00a0 \u00a0pthread_attr_t attr;<br \/>\u00a0 \u00a0pthread_t tid;<\/p>\n<p>\u00a0 \u00a0Se (pthread_attr_init(&amp;attr) != 0 ||<br \/>\u00a0 \u00a0 \u00a0 \u00a0pthread_attr_setdetachstate(&amp;attr, PTHREAD_CREATE_DETACHED) != 0 ||<br \/>\u00a0 \u00a0 \u00a0 \u00a0pthread_create(&amp;tid, &amp;attr, task-&gt;callback, task) != 0) {<br \/>\u00a0 \u00a0 \u00a0 retornar ENGINE_FAILED;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0retornar ENGINE_EWOULDBLOCK;<br \/>}<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>Como voc\u00ea pode ver no c\u00f3digo, vou criar um novo thread para executar cada opera\u00e7\u00e3o. Isso n\u00e3o \u00e9 muito eficiente, pois a cria\u00e7\u00e3o de um novo thread gera uma sobrecarga substancial. Em seu projeto, voc\u00ea provavelmente desejaria um pool de threads para executar suas tarefas.<\/p>\n<p>O thread rec\u00e9m-criado executaria o retorno de chamada especializado com um ponteiro para a tarefa como seu \u00fanico argumento. Ent\u00e3o, qual \u00e9 a apar\u00eancia dessa estrutura de tarefa?<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">struct task {<br \/>\u00a0 \u00a0struct fs_engine *engine; \/* Ponteiro para o mecanismo *\/<br \/>\u00a0 \u00a0const void *cookie; \/* O cookie que est\u00e1 solicitando a opera\u00e7\u00e3o *\/<br \/>\u00a0 \u00a0void *(*callback)(void *arg);<br \/>\u00a0 \u00a0uni\u00e3o {<br \/>\u00a0 \u00a0 \u00a0 struct {<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0char key[PATH_MAX];<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0size_t nkey;<br \/>\u00a0 \u00a0 \u00a0 } get; \/* Dados usados pela opera\u00e7\u00e3o get *\/<br \/>\u00a0 \u00a0 \u00a0 struct {<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0item *item;<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0Opera\u00e7\u00e3o ENGINE_STORE_OPERATION;<br \/>\u00a0 \u00a0 \u00a0 } store; \/* Dados usados pela opera\u00e7\u00e3o de armazenamento *\/<br \/>\u00a0 \u00a0} dados;<br \/>};<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>Ent\u00e3o, vamos terminar <code><font color=\"#999999\">fs_get<\/font><\/code>:<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">static ENGINE_ERROR_CODE fs_get(ENGINE_HANDLE* handle,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const void* cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 item** item,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const void* key,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const int nkey,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 uint16_t vbucket)<br \/>{<br \/>\u00a0 \u00a0struct fs_engine *engine = (struct fs_engine *)handle;<br \/>\u00a0 \u00a0\/* Verificar se esta \u00e9 a chamada de retorno de um ewouldblock anterior *\/<br \/>\u00a0 \u00a0void *res = engine-&gt;sapi.cookie-&gt;get_engine_specific(cookie);<br \/>\u00a0 \u00a0Se (res != NULL) {<br \/>\u00a0 \u00a0 \u00a0 *item = res;<br \/>\u00a0 \u00a0 \u00a0 engine-&gt;sapi.cookie-&gt;store_engine_specific(cookie, NULL);<br \/>\u00a0 \u00a0 \u00a0 retornar ENGINE_SUCCESS;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0\/* N\u00e3o oferecemos suporte a chaves maiores que PATH_MAX *\/<br \/>\u00a0 \u00a0se (nkey &gt;= PATH_MAX) {<br \/>\u00a0 \u00a0 \u00a0 retornar ENGINE_FAILED;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0\/* Configurar a estrutura de retorno de chamada *\/<br \/>\u00a0 \u00a0struct task *task = calloc(1, sizeof(*task));<br \/>\u00a0 \u00a0Se (tarefa == NULL) {<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_ENOMEM;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0task-&gt;engine = (struct fs_engine *)handle;<br \/>\u00a0 \u00a0task-&gt;cookie = cookie;<br \/>\u00a0 \u00a0task-&gt;callback = fs_get_callback;<br \/>\u00a0 \u00a0memcpy(task-&gt;data.get.key, key, nkey);<br \/>\u00a0 \u00a0task-&gt;data.get.nkey = nkey;<\/p>\n<p>\u00a0 \u00a0ENGINE_ERROR_CODE ret = execute(task);<br \/>\u00a0 \u00a0Se (ret != ENGINE_EWOULDBLOCK) {<br \/>\u00a0 \u00a0 \u00a0 free(task);<br \/>\u00a0 \u00a0}<br \/>\u00a0 \u00a0return ret;<br \/>}<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>Se voc\u00ea observar o c\u00f3digo acima, ver\u00e1 que especificamos <code><font color=\"#999999\">gs_get_callback<\/font><\/code> como a fun\u00e7\u00e3o a ser executada. Ent\u00e3o, vamos em frente e implementar o retorno de chamada:<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">void est\u00e1tico *fs_get_callback(void *arg)<br \/>{<br \/>\u00a0 \u00a0struct task *task = arg;<br \/>\u00a0 \u00a0char *fname = task-&gt;data.get.key;<br \/>\u00a0 \u00a0task-&gt;data.get.key[task-&gt;data.get.nkey] = '<\/p>\n<p>\u00a0 \u00a0struct stat st;<br \/>\u00a0 \u00a0se (stat(fname, &amp;st) == -1) {<br \/>\u00a0 \u00a0 \u00a0 task-&gt;engine-&gt;sapi.cookie-&gt;notify_io_complete(task-&gt;cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ENGINE_KEY_ENOENT);<br \/>\u00a0 \u00a0 \u00a0 free(task);<br \/>\u00a0 \u00a0 \u00a0 retornar NULL;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0struct fs_item* it = NULL;<br \/>\u00a0 \u00a0ENGINE_ERROR_CODE ret = fs_allocate((ENGINE_HANDLE*)task-&gt;engine,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0task-&gt;cookie, (void**)&amp;it,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0task-&gt;data.get.key,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0tarefa-&gt;dados.get.nkey,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0st.st_size, 0, 0);<br \/>\u00a0 \u00a0Se (ret != ENGINE_SUCCESS) {<br \/>\u00a0 \u00a0 \u00a0 task-&gt;engine-&gt;sapi.cookie-&gt;notify_io_complete(task-&gt;cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ENGINE_ENOMEM);<br \/>\u00a0 \u00a0 \u00a0 free(task);<br \/>\u00a0 \u00a0 \u00a0 retornar NULL;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0FILE *fp = fopen(fname, \"r\");<br \/>\u00a0 \u00a0Se (fp == NULL) {<br \/>\u00a0 \u00a0 \u00a0 fs_release((ENGINE_HANDLE*)task-&gt;engine, task-&gt;cookie, it);<br \/>\u00a0 \u00a0 \u00a0 task-&gt;engine-&gt;sapi.cookie-&gt;notify_io_complete(task-&gt;cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ENGINE_FAILED);<br \/>\u00a0 \u00a0 \u00a0 free(task);<br \/>\u00a0 \u00a0 \u00a0 retornar NULL;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0size_t nr = fread(it-&gt;data, 1, it-&gt;ndata, fp);<br \/>\u00a0 \u00a0fclose(fp);<br \/>\u00a0 \u00a0se (nr != it-&gt;ndata) {<br \/>\u00a0 \u00a0 \u00a0 fs_release((ENGINE_HANDLE*)task-&gt;engine, task-&gt;cookie, it);<br \/>\u00a0 \u00a0 \u00a0 task-&gt;engine-&gt;sapi.cookie-&gt;notify_io_complete(task-&gt;cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ENGINE_FAILED);<br \/>\u00a0 \u00a0 \u00a0 free(task);<br \/>\u00a0 \u00a0 \u00a0 retornar NULL;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0task-&gt;engine-&gt;sapi.cookie-&gt;store_engine_specific(task-&gt;cookie, it);<br \/>\u00a0 \u00a0task-&gt;engine-&gt;sapi.cookie-&gt;notify_io_complete(task-&gt;cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0ENGINE_SUCCESS);<br \/>\u00a0 \u00a0retornar NULL;<br \/>}<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>Como voc\u00ea pode ver, \u00e9 muito f\u00e1cil adicionar suporte ass\u00edncrono \u00e0s fun\u00e7\u00f5es do mecanismo. Vamos seguir em frente e fazer o mesmo para <code><font color=\"#999999\">fs_store<\/font><\/code>:<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">static ENGINE_ERROR_CODE fs_store(ENGINE_HANDLE* handle,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const void *cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 item* item,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 uint64_t *cas,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 Opera\u00e7\u00e3o ENGINE_STORE_OPERATION,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 uint16_t vbucket)<br \/>{<br \/>\u00a0 \u00a0struct fs_engine *engine = (struct fs_engine *)handle;<br \/>\u00a0 \u00a0\/* Verificar se esta \u00e9 a chamada de retorno de um ewouldblock anterior *\/<br \/>\u00a0 \u00a0void *res = engine-&gt;sapi.cookie-&gt;get_engine_specific(cookie);<br \/>\u00a0 \u00a0Se (res != NULL) {<br \/>\u00a0 \u00a0 \u00a0 *cas = 0;<br \/>\u00a0 \u00a0 \u00a0 engine-&gt;sapi.cookie-&gt;store_engine_specific(cookie, NULL);<br \/>\u00a0 \u00a0 \u00a0 retornar ENGINE_SUCCESS;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0\/* Configurar a estrutura de retorno de chamada *\/<br \/>\u00a0 \u00a0struct task *task = calloc(1, sizeof(*task));<br \/>\u00a0 \u00a0Se (tarefa == NULL) {<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_ENOMEM;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0task-&gt;engine = (struct fs_engine *)handle;<br \/>\u00a0 \u00a0task-&gt;cookie = cookie;<br \/>\u00a0 \u00a0task-&gt;callback = fs_store_callback;<br \/>\u00a0 \u00a0task-&gt;data.store.item = item;<br \/>\u00a0 \u00a0task-&gt;data.store.operation = operation;<\/p>\n<p>\u00a0 \u00a0ENGINE_ERROR_CODE ret = execute(task);<br \/>\u00a0 \u00a0Se (ret != ENGINE_EWOULDBLOCK) {<br \/>\u00a0 \u00a0 \u00a0 free(task);<br \/>\u00a0 \u00a0}<br \/>\u00a0 \u00a0return ret;<br \/>}<\/div>\n<\/div>\n<p>E <code><font color=\"#999999\">fs_store_callback<\/font><\/code> se parece com o seguinte:<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">void est\u00e1tico *fs_store_callback(void *arg)<br \/>{<br \/>\u00a0 \u00a0struct task *task = arg;<br \/>\u00a0 \u00a0struct fs_item* it = task-&gt;data.store.item;<br \/>\u00a0 \u00a0char fname[it-&gt;nkey + 1];<br \/>\u00a0 \u00a0memcpy(fname, it-&gt;key, it-&gt;nkey);<br \/>\u00a0 \u00a0fname[it-&gt;nkey] = '<\/p>\n<p>\u00a0 \u00a0FILE *fp = fopen(fname, \"w\");<br \/>\u00a0 \u00a0Se (fp == NULL) {<br \/>\u00a0 \u00a0 \u00a0 task-&gt;engine-&gt;sapi.cookie-&gt;notify_io_complete(task-&gt;cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ENGINE_NOT_STORED);<br \/>\u00a0 \u00a0 \u00a0 free(task);<br \/>\u00a0 \u00a0 \u00a0 retornar NULL;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0size_t nw = fwrite(it-&gt;data, 1, it-&gt;ndata, fp);<br \/>\u00a0 \u00a0fclose(fp);<br \/>\u00a0 \u00a0se (nw != it-&gt;ndata) {<br \/>\u00a0 \u00a0 \u00a0 remove(fname);<br \/>\u00a0 \u00a0 \u00a0 task-&gt;engine-&gt;sapi.cookie-&gt;notify_io_complete(task-&gt;cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ENGINE_NOT_STORED);<br \/>\u00a0 \u00a0 \u00a0 free(task);<br \/>\u00a0 \u00a0 \u00a0 retornar NULL;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0task-&gt;engine-&gt;sapi.cookie-&gt;store_engine_specific(task-&gt;cookie, it);<br \/>\u00a0 \u00a0task-&gt;engine-&gt;sapi.cookie-&gt;notify_io_complete(task-&gt;cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0ENGINE_SUCCESS);<br \/>\u00a0 \u00a0retornar NULL;<br \/>}<\/div>\n<\/div>\n<p>Se voc\u00ea observar atentamente o c\u00f3digo acima, ver\u00e1 que ainda n\u00e3o diferenciamos entre <code><font color=\"#999999\">add<\/font><\/code>\/<code><font color=\"#999999\">set<\/font><\/code>\/<code><font color=\"#999999\">substituir<\/font><\/code>mas vamos corrigir isso na pr\u00f3xima sess\u00e3o.<\/p>","protected":false},"excerpt":{"rendered":"<p>Right now we&#39;ve got an engine capable of running get and set load, but it is doing synchrounus filesystem IO. We can&#39;t serve our client faster than we can read the item from disk, but we might serve other connections [&hellip;]<\/p>","protected":false},"author":14,"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":[8981],"class_list":["post-1844","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.5 (Yoast SEO v26.5) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Writing your own storage engine for Memcached, part 3 - 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\/pt\/writing-your-own-storage-engine-memcached-part-3\/\" \/>\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Writing your own storage engine for Memcached, part 3\" \/>\n<meta property=\"og:description\" content=\"Right now we&#039;ve got an engine capable of running get and set load, but it is doing synchrounus filesystem IO. We can&#039;t serve our client faster than we can read the item from disk, but we might serve other connections [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/pt\/writing-your-own-storage-engine-memcached-part-3\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2014-12-16T17:28:58+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/11\/couchbase-nosql-dbaas.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1800\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Trond Norbye, Senior Developer, 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=\"Trond Norbye, Senior Developer, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/\"},\"author\":{\"name\":\"Trond Norbye, Senior Developer, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/ef0f9ff42d878c2fc0ab3a685e96f36d\"},\"headline\":\"Writing your own storage engine for Memcached, part 3\",\"datePublished\":\"2014-12-16T17:28:58+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/\"},\"wordCount\":1305,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"articleSection\":[\"Uncategorized\"],\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/\",\"name\":\"Writing your own storage engine for Memcached, part 3 - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2014-12-16T17:28:58+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#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\/writing-your-own-storage-engine-memcached-part-3\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Writing your own storage engine for Memcached, part 3\"}]},{\"@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\":\"pt-BR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@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\/ef0f9ff42d878c2fc0ab3a685e96f36d\",\"name\":\"Trond Norbye, Senior Developer, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/03d13b4ab5eaa14c91cab7658f04df07\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bf3c00ca228efa25f7bfc168f566d6389279b44d4bbba4683c260a8bf33da03d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bf3c00ca228efa25f7bfc168f566d6389279b44d4bbba4683c260a8bf33da03d?s=96&d=mm&r=g\",\"caption\":\"Trond Norbye, Senior Developer, Couchbase\"},\"description\":\"Trond Norbye is a Software Architect at Couchbase. Core contributor to Couchbase &amp; Memcached projects. Created the C\/C++ &amp; node.js Couchbase client libraries.\",\"url\":\"https:\/\/www.couchbase.com\/blog\/pt\/author\/trond-norbye\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Writing your own storage engine for Memcached, part 3 - 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\/pt\/writing-your-own-storage-engine-memcached-part-3\/","og_locale":"pt_BR","og_type":"article","og_title":"Writing your own storage engine for Memcached, part 3","og_description":"Right now we&#39;ve got an engine capable of running get and set load, but it is doing synchrounus filesystem IO. We can&#39;t serve our client faster than we can read the item from disk, but we might serve other connections [&hellip;]","og_url":"https:\/\/www.couchbase.com\/blog\/pt\/writing-your-own-storage-engine-memcached-part-3\/","og_site_name":"The Couchbase Blog","article_published_time":"2014-12-16T17:28:58+00:00","og_image":[{"width":1800,"height":630,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2022\/11\/couchbase-nosql-dbaas.png","type":"image\/png"}],"author":"Trond Norbye, Senior Developer, Couchbase","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Trond Norbye, Senior Developer, Couchbase","Est. reading time":"7 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/"},"author":{"name":"Trond Norbye, Senior Developer, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/ef0f9ff42d878c2fc0ab3a685e96f36d"},"headline":"Writing your own storage engine for Memcached, part 3","datePublished":"2014-12-16T17:28:58+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/"},"wordCount":1305,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","articleSection":["Uncategorized"],"inLanguage":"pt-BR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/","url":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/","name":"Writing your own storage engine for Memcached, part 3 - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2014-12-16T17:28:58+00:00","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/"]}]},{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-3\/#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\/writing-your-own-storage-engine-memcached-part-3\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Writing your own storage engine for Memcached, part 3"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"Blog do Couchbase","description":"Couchbase, o banco de dados 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":"pt-BR"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"Blog do Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"pt-BR","@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\/ef0f9ff42d878c2fc0ab3a685e96f36d","name":"Trond Norbye, desenvolvedor s\u00eanior, Couchbase","image":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/03d13b4ab5eaa14c91cab7658f04df07","url":"https:\/\/secure.gravatar.com\/avatar\/bf3c00ca228efa25f7bfc168f566d6389279b44d4bbba4683c260a8bf33da03d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bf3c00ca228efa25f7bfc168f566d6389279b44d4bbba4683c260a8bf33da03d?s=96&d=mm&r=g","caption":"Trond Norbye, Senior Developer, Couchbase"},"description":"Trond Norbye \u00e9 arquiteto de software na Couchbase. Principal colaborador dos projetos Couchbase e Memcached. Criou as bibliotecas de clientes C\/C++ e node.js do Couchbase.","url":"https:\/\/www.couchbase.com\/blog\/pt\/author\/trond-norbye\/"}]}},"authors":[{"term_id":8981,"user_id":14,"is_guest":0,"slug":"trond-norbye","display_name":"Trond Norbye, Senior Developer, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/bf3c00ca228efa25f7bfc168f566d6389279b44d4bbba4683c260a8bf33da03d?s=96&d=mm&r=g","author_category":"","last_name":"Norbye","first_name":"Trond","job_title":"","user_url":"","description":"Trond Norbye \u00e9 arquiteto de software na Couchbase. Principal colaborador dos projetos Couchbase e Memcached. Criou as bibliotecas de clientes C\/C++ e node.js do Couchbase."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/1844","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/users\/14"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=1844"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/1844\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=1844"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=1844"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=1844"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/ppma_author?post=1844"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}