{"id":1843,"date":"2014-12-16T17:36:08","date_gmt":"2014-12-16T17:36:08","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=1843"},"modified":"2014-12-16T17:36:08","modified_gmt":"2014-12-16T17:36:08","slug":"writing-your-own-storage-engine-memcached-part-2","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/","title":{"rendered":"Writing your own storage engine for Memcached, part 2"},"content":{"rendered":"<p>In the previous blog post I described the engine initialization and\u00a0destruction. This blog post will cover the memory allocation model\u00a0in the engine interface.<\/p>\n<p>The memcached core is responsible for allocating all of the\u00a0memory it needs for its connections (send \/ receive buffers\u00a0etc), and the engine is responsible for allocating (and\u00a0freeing) all of the memory it needs to keep track of the\u00a0items. The engines shouldn&#39;t have to care about the memory the\u00a0core allocates (and use), but the core will access the memory\u00a0managed by the engine.<\/p>\n<p>When the memcached core is about to store a new item it needs\u00a0to get a (as of today continous) buffer to store the data for the\u00a0item. The core will try to allocate this buffer by calling the\u00a0<code><font color=\"#999999\">allocate<\/font><\/code> function in the API. So let&#39;s start\u00a0extending our example code by adding out own implementation\u00a0of the <code><font color=\"#999999\">allocate<\/font><\/code> function. The first thing we need\u00a0to do is to add it to our engine descriptor we return from\u00a0<code><font color=\"#999999\">create_instance<\/font><\/code>. We&#39;re going to add a number of\u00a0functions in todays entry, so let&#39;s just map all of them\u00a0while we&#39;re at it:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">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 \/>{<br \/>\u00a0 \u00a0[ &#8230; cut &#8230; ]<br \/>\u00a0 \/*<br \/>\u00a0 \u00a0* Map the API entry points to our functions that implement them.<br \/>\u00a0 \u00a0*\/<br \/>\u00a0 \u00a0h->engine.initialize = fs_initialize;<br \/>\u00a0 \u00a0h->engine.destroy = fs_destroy;<br \/>\u00a0 \u00a0<b>h->engine.get_info = fs_get_info;<br \/>\u00a0 \u00a0h->engine.allocate = fs_allocate;<br \/>\u00a0 \u00a0h->engine.remove = fs_item_delete;<br \/>\u00a0 \u00a0h->engine.release = fs_item_release;<br \/>\u00a0 \u00a0h->engine.get = fs_get;<br \/>\u00a0 \u00a0h->engine.get_stats = fs_get_stats;<br \/>\u00a0 \u00a0h->engine.reset_stats = fs_reset_stats;<br \/>\u00a0 \u00a0h->engine.store = fs_store;<br \/>\u00a0 \u00a0h->engine.flush = fs_flush;<br \/>\u00a0 \u00a0h->engine.unknown_command = fs_unknown_command;<br \/>\u00a0 \u00a0h->engine.item_set_cas = fs_item_set_cas;<br \/>\u00a0 \u00a0h->engine.get_item_info = fs_get_item_info;<\/b><br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>The next thing we need to do is to create a data structure to\u00a0keep the information we need. The purpose of this tutorial\u00a0isn&#39;t to create a memory efficient implementation, but to\u00a0exercise\u00a0the API. So let&#39;s just create the following struct:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">struct fs_item {<br \/>\u00a0 \u00a0void *key;<br \/>\u00a0 \u00a0size_t nkey;<br \/>\u00a0 \u00a0void *data;<br \/>\u00a0 \u00a0size_t ndata;<br \/>\u00a0 \u00a0int flags;<br \/>\u00a0 \u00a0rel_time_t exptime;<br \/>};<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>Our implementation of <code><font color=\"#999999\">allocate<\/font><\/code> would then look\u00a0like:<\/p>\n<p>\u00a0<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">static ENGINE_ERROR_CODE fs_allocate(ENGINE_HANDLE* handle,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0const void* cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0item **item,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0const void* key,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0const size_t nkey,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0const size_t nbytes,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0const int flags,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0const rel_time_t exptime)<br \/>{<br \/>\u00a0 \u00a0struct fs_item *it = malloc(sizeof(struct fs_item));<br \/>\u00a0 \u00a0if (it == NULL) {<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_ENOMEM;<br \/>\u00a0 \u00a0}<br \/>\u00a0 \u00a0it->flags = flags;<br \/>\u00a0 \u00a0it->exptime = exptime;<br \/>\u00a0 \u00a0it->nkey = nkey;<br \/>\u00a0 \u00a0it->ndata = nbytes;<br \/>\u00a0 \u00a0it->key = malloc(nkey);<br \/>\u00a0 \u00a0it->data = malloc(nbytes);<br \/>\u00a0 \u00a0if (it->key == NULL || it->data == NULL) {<br \/>\u00a0 \u00a0 \u00a0 free(it->key);<br \/>\u00a0 \u00a0 \u00a0 free(it->data);<br \/>\u00a0 \u00a0 \u00a0 free(it);<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_ENOMEM;<br \/>\u00a0 \u00a0}<br \/>\u00a0 \u00a0memcpy(it->key, key, nkey);<br \/>\u00a0 \u00a0*item = it;<br \/>\u00a0 \u00a0return ENGINE_SUCCESS;<br \/>}<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>If you look in the implementation above you&#39;ll see that we didn&#39;t\u00a0return the pointer to the actual memory for the data storage to\u00a0the memcached core. To get that address memcached will call\u00a0<code><font color=\"#999999\">get_item_info<\/font><\/code> in the API. So let&#39;s implememnt that:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">static bool fs_get_item_info(ENGINE_HANDLE *handle, const void *cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0const item* item, item_info *item_info)<br \/>{<br \/>\u00a0 \u00a0struct fs_item* it = (struct fs_item*)item;<br \/>\u00a0 \u00a0if (item_info->nvalue < 1) {<br \/>\u00a0 \u00a0 \u00a0 return false;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0item_info->cas = 0; \/* Not supported *\/<br \/>\u00a0 \u00a0item_info->clsid = 0; \/* Not supported *\/<br \/>\u00a0 \u00a0item_info->exptime = it->exptime;<br \/>\u00a0 \u00a0item_info->flags = it->flags;<br \/>\u00a0 \u00a0item_info->key = it->key;<br \/>\u00a0 \u00a0item_info->nkey = it->nkey;<br \/>\u00a0 \u00a0item_info->nbytes = it->ndata; \/* Total length of the items data *\/<br \/>\u00a0 \u00a0item_info->nvalue = 1; \/* Number of fragments used *\/<br \/>\u00a0 \u00a0item_info->value[0].iov_base = it->data; \/* pointer to fragment 1 *\/<br \/>\u00a0 \u00a0item_info->value[0].iov_len = it->ndata; \/* Length of fragment 1 *\/<\/p>\n<p>\u00a0 \u00a0return true;<br \/>}<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>The <code><font color=\"#999999\">get_item_info<\/font><\/code> function is important and\u00a0deserve more information. If you look in the engine API the\u00a0&#8220;item&#8221; is defined as a void pointer, and we defined our own\u00a0item-structure to keep track of the information we need on a\u00a0per item basis. The memcached core will however need to know<br \/>where to read \/ write the memory for the key and the data\u00a0going to \/ coming from a clinet. To do so in will invoke\u00a0<code><font color=\"#999999\">get_item_info<\/font><\/code>. If you look closely at our\u00a0implementation of <code><font color=\"#999999\">fs_get_item_info<\/font><\/code> you will see\u00a0that the first thing I&#39;m doing is to check that\u00a0<code><font color=\"#999999\">item_info->nvalue<\/font><\/code> contains at least 1<br \/>element. Right now it will <b>always<\/b> be one, but the\u00a0intention is that we&#39;re going to support scattered IO.<\/p>\n<p>When the core is done moving the data it received over the wire\u00a0into the item, it will try to store the item in our engine by\u00a0calling <code><font color=\"#999999\">store<\/font><\/code>. So let&#39;s go ahead and create a simple\u00a0implementation (we&#39;ll extend it later on in the tutorial):<\/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 ENGINE_STORE_OPERATION 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_item* it = item;<br \/>\u00a0 \u00a0char fname[it->nkey + 1];<br \/>\u00a0 \u00a0memcpy(fname, it->key, it->nkey);<br \/>\u00a0 \u00a0fname[it->nkey] = &#8216;\u0000&#8217;;<br \/>\u00a0 \u00a0FILE *fp = fopen(fname, &#8220;w&#8221;);<br \/>\u00a0 \u00a0if (fp == NULL) {<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_NOT_STORED;<br \/>\u00a0 \u00a0}<br \/>\u00a0 \u00a0size_t nw = fwrite(it->data, 1, it->ndata, fp);<br \/>\u00a0 \u00a0fclose(fp);<br \/>\u00a0 \u00a0if (nw != it->ndata) {<br \/>\u00a0 \u00a0 \u00a0 remove(fname);<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_NOT_STORED;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0*cas = 0;<br \/>\u00a0 \u00a0return ENGINE_SUCCESS;<br \/>}<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>If you look at the implementation above you will see that it\u00a0doesn&#39;t implement the correct semantics\u00a0for <em>add<\/em>\/<em>replace<\/em>\/<em>set<\/em> etc, and it will\u00a0block memcached while we&#39;re doing file IO. Don&#39;t care about that\u00a0right now, because we&#39;ll get back to that.<\/p>\n<p>When the core is done using the item it allocated, it will release\u00a0the item by calling the <code><font color=\"#999999\">release<\/font><\/code> function in the API.\u00a0The engine may reuse the items storage for something else at this\u00a0time. So let&#39;s hook up our <code><font color=\"#999999\">release<\/font><\/code> implementation:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">static void fs_item_release(ENGINE_HANDLE* handle,<br \/>\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 item* item)<br \/>{<br \/>\u00a0 \u00a0struct fs_item *it = item;<br \/>\u00a0 \u00a0free(it->key);<br \/>\u00a0 \u00a0free(it->data);<br \/>\u00a0 \u00a0free(it);<br \/>}<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>Now we&#39;ve created all of the code to successfully store items\u00a0in our engine, but we can&#39;t read any of them back. So let&#39;s\u00a0implement <font color=\"#999999\"><code>get<\/code><\/font><\/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 \/>{<\/p>\n<p>\u00a0 \u00a0char fname[nkey + 1];<br \/>\u00a0 \u00a0memcpy(fname, key, nkey);<br \/>\u00a0 \u00a0fname[nkey] = &#8216;\u0000&#8217;;<\/p>\n<p>\u00a0 \u00a0struct stat st;<br \/>\u00a0 \u00a0if (stat(fname, &#038;st) == -1) {<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_NOT_STORED;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0struct fs_item* it = NULL;<br \/>\u00a0 \u00a0ENGINE_ERROR_CODE ret = fs_allocate(handle, cookie, (void**)&#038;it, key, 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 \u00a0if (ret != ENGINE_SUCCESS) {<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_ENOMEM;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0FILE *fp = fopen(fname, &#8220;r&#8221;);<br \/>\u00a0 \u00a0if (fp == NULL) {<br \/>\u00a0 \u00a0 \u00a0 fs_release(handle, cookie, it);<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_FAILED;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0size_t nr = fread(it->data, 1, it->ndata, fp);<br \/>\u00a0 \u00a0fclose(fp);<br \/>\u00a0 \u00a0if (nr != it->ndata) {<br \/>\u00a0 \u00a0 \u00a0 fs_release(handle, cookie, it);<br \/>\u00a0 \u00a0 \u00a0 return ENGINE_FAILED;<br \/>\u00a0 \u00a0}<\/p>\n<p>\u00a0 \u00a0*item = it;<br \/>\u00a0 \u00a0return ENGINE_SUCCESS;<br \/>}<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>Let&#39;s add a dummy implementation for the rest of the API and\u00a0try to load and test the engine:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">static const engine_info* fs_get_info(ENGINE_HANDLE* handle)<br \/>{<br \/>\u00a0 \u00a0static engine_info info = {<br \/>\u00a0 \u00a0 \u00a0 .description = &#8220;Filesystem engine v0.1&#8221;,<br \/>\u00a0 \u00a0 \u00a0 .num_features = 0<br \/>\u00a0 \u00a0};<\/p>\n<p>\u00a0 \u00a0return &info;<br \/>}<\/p>\n<p>static ENGINE_ERROR_CODE fs_item_delete(ENGINE_HANDLE* handle,<br \/>\u00a0 \u00a0 \u00a0 \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 \u00a0 \u00a0 \u00a0 const void* key,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const size_t nkey,<br \/>\u00a0 \u00a0 \u00a0 \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 \u00a0 \u00a0 \u00a0 uint16_t vbucket)<br \/>{<br \/>\u00a0 \u00a0return ENGINE_KEY_ENOENT;<br \/>}<\/p>\n<p>static ENGINE_ERROR_CODE fs_get_stats(ENGINE_HANDLE* handle,<br \/>\u00a0 \u00a0 \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 \u00a0 \u00a0 const char* stat_key,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 int nkey,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ADD_STAT add_stat)<br \/>{<br \/>\u00a0 \u00a0return ENGINE_SUCCESS;<br \/>}<\/p>\n<p>static ENGINE_ERROR_CODE fs_flush(ENGINE_HANDLE* handle,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 const void* cookie, time_t when)<br \/>{<\/p>\n<p>\u00a0 \u00a0return ENGINE_SUCCESS;<br \/>}<\/p>\n<p>static void fs_reset_stats(ENGINE_HANDLE* handle, const void *cookie)<br \/>{<\/p>\n<p>}<\/p>\n<p>static ENGINE_ERROR_CODE fs_unknown_command(ENGINE_HANDLE* handle,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \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 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 protocol_binary_request_header *request,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ADD_RESPONSE response)<br \/>{<br \/>\u00a0 \u00a0return ENGINE_ENOTSUP;<br \/>}<\/p>\n<p>static void fs_item_set_cas(ENGINE_HANDLE *handle, const void *cookie,<br \/>\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 item* item, uint64_t val)<br \/>{<br \/>}<\/p>\n<p>\u00a0 \u00a0 \u00a0 <\/p><\/div>\n<\/div>\n<p>So let&#39;s go ahead and try our engine:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">trond@opensolaris> \/opt\/memcached\/bin\/memcached -E .libs\/fs_engine.so<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>From another terminal I&#39;m typing in:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">trond@opensolaris> telnet localhost 11211<br \/>Trying ::1&#8230;<br \/>Connected to opensolaris.<br \/>Escape character is &#8216;^]&#8217;.<br \/>add test 0 0 4<br \/>test<br \/>STORED<br \/>get test<br \/>VALUE test 0 4<br \/>test<br \/>END<br \/>quit<br \/>Connection to storm closed by foreign host.<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>Terminate memcached by pressing <code><font color=\"#999999\">ctrl-c<\/font><\/code>, and look in the current directory:<\/p>\n<div class=\"geshifilter\">\n<div class=\"text geshifilter-text\" style=\"font-family:monospace;\">trond@opensolaris> ls -l test<br \/>-rw-r&#8211;r&#8211; \u00a0 1 trond \u00a0 \u00a0users \u00a0 \u00a0 \u00a0 \u00a0 \u00a06 Oct \u00a08 12:56 test<br \/>trond@opensolaris> cat test<br \/>test<br \/>\u00a0 \u00a0 \u00a0 <\/div>\n<\/div>\n<p>That&#39;s all for this time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous blog post I described the engine initialization and\u00a0destruction. This blog post will cover the memory allocation model\u00a0in the engine interface. The memcached core is responsible for allocating all of the\u00a0memory it needs for its connections (send \/ [&hellip;]<\/p>\n","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-1843","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 v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Writing your own storage engine for Memcached, part 2 - 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\/writing-your-own-storage-engine-memcached-part-2\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Writing your own storage engine for Memcached, part 2\" \/>\n<meta property=\"og:description\" content=\"In the previous blog post I described the engine initialization and\u00a0destruction. This blog post will cover the memory allocation model\u00a0in the engine interface. The memcached core is responsible for allocating all of the\u00a0memory it needs for its connections (send \/ [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2014-12-16T17:36:08+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 minutes\" \/>\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-2\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/\"},\"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 2\",\"datePublished\":\"2014-12-16T17:36:08+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/\"},\"wordCount\":1384,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/\",\"name\":\"Writing your own storage engine for Memcached, part 2 - 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-2\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2014-12-16T17:36:08+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#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-2\/#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 2\"}]},{\"@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\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@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\":\"en-US\",\"@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\/author\/trond-norbye\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Writing your own storage engine for Memcached, part 2 - 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\/writing-your-own-storage-engine-memcached-part-2\/","og_locale":"en_US","og_type":"article","og_title":"Writing your own storage engine for Memcached, part 2","og_description":"In the previous blog post I described the engine initialization and\u00a0destruction. This blog post will cover the memory allocation model\u00a0in the engine interface. The memcached core is responsible for allocating all of the\u00a0memory it needs for its connections (send \/ [&hellip;]","og_url":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/","og_site_name":"The Couchbase Blog","article_published_time":"2014-12-16T17:36:08+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 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/"},"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 2","datePublished":"2014-12-16T17:36:08+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/"},"wordCount":1384,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/","url":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/","name":"Writing your own storage engine for Memcached, part 2 - 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-2\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2014-12-16T17:36:08+00:00","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/writing-your-own-storage-engine-memcached-part-2\/#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-2\/#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 2"}]},{"@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":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@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":"en-US","@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\/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 is a Software Architect at Couchbase. Core contributor to Couchbase &amp; Memcached projects. Created the C\/C++ &amp; node.js Couchbase client libraries."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/1843","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/14"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=1843"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/1843\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=1843"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=1843"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=1843"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=1843"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}