<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7638694470910246382</id><updated>2012-01-28T06:53:08.889+01:00</updated><category term='memcached'/><category term='storage engine'/><category term='c++'/><category term='libcouchbase'/><category term='sqlite'/><category term='c'/><title type='text'>Trond Norbye's Weblog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>26</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-6372520765829980749</id><published>2012-01-27T23:29:00.003+01:00</published><updated>2012-01-27T23:29:33.731+01:00</updated><title type='text'>So whats the story about libcouchbase and Windows?</title><content type='html'>A couple of days ago I showed you an example program using libcouchbase to create a small application to put data into a Couchbase cluster, but the code wouldn't compile on Windows. That does by no means imply that libcouchbase doesn't work on Windows, its more that I was in a hurry writing the blog post so I didn't have the time fixing everything up in time for the blog post.&lt;br /&gt;
&lt;br /&gt;
In this blog post I'll show you how easy it is to get everything up'n'running using Windows 7 and Microsoft Visual Studio 2010. In addition to that you need to &lt;a href="http://code.google.com/p/msysgit/downloads/list"&gt;download&lt;/a&gt; and install git to be able to check out the source code (select the option that you want to put git in the path (not the full msys suite, but just git)).&lt;br /&gt;
&lt;br /&gt;
I have to admit that I am far from a "hardcore Windows developer", so there is a lot of things I don't know about the platform. For instance I don't know where I should install third party header files and libraries, so I just decided that I'm going to install all of them into &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;C:\local&lt;/span&gt; (with an install, lib and bin directory). I'd be happy if someone could tell me how I'm supposed to do this ;-)&lt;br /&gt;
&lt;br /&gt;
So let's open up the Visual Studio Command Prompt and start building everything:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Setting environment for using Microsoft Visual Studio 2010 x86 tools.
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC&amp;gt; &lt;b&gt;cd %HOMEPATH%&lt;/b&gt;
C:\Users\Trond&amp;gt; &lt;b&gt;mkdir build&lt;/b&gt;
C:\Users\Trond&amp;gt; &lt;b&gt;cd build&lt;/b&gt;
&lt;/pre&gt;
&lt;br /&gt;
Since we're going to build dll's you need to set C:\local\bin into your path so that the runtime linker finds the dll's:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;C:\Users\Trond\build&amp;gt; &lt;b&gt;set PATH=c:\local\bin;%PATH%&lt;/b&gt;
&lt;/pre&gt;
&lt;br /&gt;
We need to install two dependencies before we can compile libcouchbase itself. Let's check out all of the source code we're going to use:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;C:\Users\Trond\build&amp;gt; &lt;b&gt;git clone git://github.com/membase/libisasl.git&lt;/b&gt;
C:\Users\Trond\build&amp;gt; &lt;b&gt;git clone git://github.com/membase/libvbucket.git&lt;/b&gt;
C:\Users\Trond\build&amp;gt; &lt;b&gt;git clone git://github.com/couchbase/libcouchbase.git&lt;/b&gt;
C:\Users\Trond\build&amp;gt; &lt;b&gt;git clone git://github.com/membase/memcached.git&lt;/b&gt;
C:\Users\Trond\build&amp;gt; &lt;b&gt;git clone git://github.com/trondn/vacuum.git&lt;/b&gt;
&lt;/pre&gt;
&lt;br /&gt;
&lt;div&gt;
The first dependency we're going to build is the SASL library. This is the library libcouchbase use for authenticating to the Couchbase servers. To build and install the library, simply execute:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;C:\Users\Trond\build&amp;gt; &lt;b&gt;cd libisasl&lt;/b&gt;
C:\Users\Trond Norbye\build\libisasl&amp;gt; &lt;b&gt;nmake -f NMakefile install&lt;/b&gt;
&lt;/pre&gt;
&lt;br /&gt;
&lt;div&gt;
That will install libisasl with its header files and libraries into &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;c:\local&lt;/span&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The next library we need to build is libvbucket; the library libcouchbase use to figure out where a a vbucket is located (if you don't know what a vbucket is, you don't really need to know). It is just as easy as libvbucket to build:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;C:\Users\Trond\build\libisasl&amp;gt; &lt;b&gt;cd ..\libvbucket&lt;/b&gt;
C:\Users\Trond\build\libvbucket&amp;gt; &lt;b&gt;nmake -f NMakefile install&lt;/b&gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;
The next thing we need to do is to install some headerfiles libcouchbase needs during build time. These header files contains the protocol definitions libcouchbase needs (but it is not needed by the application). So let's go ahead and install them (to make it easier for us to build libcouchbase)&lt;/div&gt;
&lt;br /&gt;
&lt;pre&gt;C:\Users\Trond\build\libvbucket&amp;gt; &lt;b&gt;cd ..\memcached&lt;/b&gt;
C:\Users\Trond\build\memcached&amp;gt; &lt;b&gt;git checkout -b branch-20 origin/branch-20&lt;/b&gt;
C:\Users\Trond\build\memcached&amp;gt; &lt;b&gt;mkdir c:\local\include\memcached&lt;/b&gt;
C:\Users\Trond\build\memcached&amp;gt; &lt;b&gt;copy include\memcached c:\local\include\memcached&lt;/b&gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;
So let's go ahead and build libcouchbase!&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;C:\Users\Trond\build\memcached&amp;gt; &lt;b&gt;cd ..\libcouchbase&lt;/b&gt;
C:\Users\Trond\build\libcouchbase&amp;gt; &lt;b&gt;nmake -f NMakefile install&lt;/b&gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;
I guess that most Windows developers don't use nmake during their development, but use the full IDE instead. That's why I've created a project you may open in the vacuum project. So feel free to open that project now, and it should build without any problems.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Now we're going to need a Couchbase server we can connect to. If you don't have any running, you should &lt;a href="http://www.couchbase.com/download"&gt;download and install&lt;/a&gt; one now.&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Let's go ahead and create the spool directory and start the vacuum server...&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;C:\Users\Trond\build\vacuum\Debug&amp;gt; &lt;b&gt;mkdir c:\vacuum&lt;/b&gt;
C:\Users\Trond\build\vacuum\Debug&amp;gt; &lt;b&gt;vacuum -h 127.0.0.1:8091&lt;/b&gt;
&lt;/pre&gt;
&lt;br /&gt;
&lt;div&gt;
And you can start copy JSON files into &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;C:\vacuum&lt;/span&gt; and see them being added to the Couchbase cluster!&lt;/div&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-6372520765829980749?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/6372520765829980749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2012/01/so-whats-story-about-libcouchbase-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/6372520765829980749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/6372520765829980749'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2012/01/so-whats-story-about-libcouchbase-and.html' title='So whats the story about libcouchbase and Windows?'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-4267828052348509921</id><published>2012-01-24T00:17:00.000+01:00</published><updated>2012-01-24T18:34:10.713+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='memcached'/><category scheme='http://www.blogger.com/atom/ns#' term='libcouchbase'/><category scheme='http://www.blogger.com/atom/ns#' term='c'/><title type='text'>So how do I use this "libcouchbase"?</title><content type='html'>&lt;br /&gt;
Some of you may have noticed that we released &lt;a href="http://www.couchbase.com/download"&gt;Couchbase 1.8 earlier&amp;nbsp;today&lt;/a&gt;, and a new set of smart clients for various languages. For me&amp;nbsp;personally this is a milestone, because &lt;a href="http://www.couchbase.com/develop/c/current"&gt;libcouchbase&lt;/a&gt; is now a&lt;br /&gt;
supported client for the C language.&lt;br /&gt;
&lt;br /&gt;
So why do I care about that? Well, libcouchbase started out of my needs&amp;nbsp;to easily test various components of the server. Since I did most of&amp;nbsp;my development on the components on the server implemented in C, it&amp;nbsp;made sense for me to use C for my testing.&lt;br /&gt;
&lt;br /&gt;
I've received some questions on how libcouchbase work in a&amp;nbsp;multithreaded context, so I should probably start off by clarifying&amp;nbsp;that: libcouchbase doesn't use &lt;b&gt;any&lt;/b&gt; form of locking to protect&amp;nbsp;it's internal data structures, but it doesn't mean you can't use&amp;nbsp;libcouchbase in a multithreaded program. All it means is that you as a&amp;nbsp;client user must either use locking to protect yourself from accessing&amp;nbsp;the libcouchbase instance from multiple threads at the same time, or&amp;nbsp;just let each thread operate on it's own instance of libcouchbase. One easy way to solve this is to have a "pool" of libcouchbase instances each thread pop and push its instance to whenever they need to access a Couchbase server. Access to this pool should be protected with a lock (but I guess you figured that out ;-)&lt;br /&gt;
&lt;br /&gt;
In this blog post I'll create a demo program you may use to upload&amp;nbsp;JSON documents into a Couchbase server. You'll find the complete&amp;nbsp;source available at &lt;a href="https://github.com/trondn/vacuum"&gt;https://github.com/trondn/vacuum&lt;/a&gt; if you would like&lt;br /&gt;
to try the example.&lt;br /&gt;
&lt;br /&gt;
The idea of this program is that it will "monitor" a directory and&amp;nbsp;upload all files appearing there into a Couchbase cluster. I'm pretty&amp;nbsp;sure most of you start thinking: "how do we do that in a portable&amp;nbsp;way?". That's not an easy task to do, so I'm not even going to try to&amp;nbsp;do that. I'll try to write it in a &lt;u&gt;semi-portable&lt;/u&gt;&amp;nbsp;way so that it&amp;nbsp;shouldn't be that hard to implement on other platforms. That means&amp;nbsp;that I'm using the following limitations:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;I'm using &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;opendir&lt;/span&gt; and &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;readdir&lt;/span&gt; to traverse the directory. This can&amp;nbsp;easily be reimplemented with &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;FindFirst&lt;/span&gt; and &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;FindNext&lt;/span&gt; on Microsoft Windows.&lt;/li&gt;
&lt;li&gt;Monitor of the directory means that I'm going to scan the directory,&amp;nbsp;then sleep a given number of seconds before running another scan. I&amp;nbsp;know some platforms supports subscribing of changes to the&amp;nbsp;filesystem, but I'm not going to spend time on that (at least not right now ;-)).&lt;/li&gt;
&lt;li&gt;To avoid file locking or accessing the file while others are writing&amp;nbsp;the file, the clients should write the file into the directory with&amp;nbsp;a leading "dot" in the filename, and then rename the file when they&amp;nbsp;are done. The program ignores all files starting with a dot.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
So let's jump to the code. The first piece of code that might be&amp;nbsp;interesting to look at would be where we create the libcouchbase&amp;nbsp;instance in main():&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;    instance = libcouchbase_create(host, user, passwd, bucket, NULL);
    if (instance == NULL) {
        fprintf(stderr, "Failed to create couchbase instance\n");
        exit(EXIT_FAILURE);
    }
&lt;/pre&gt;
&lt;br /&gt;
The above code snippet creates the libcouchbase instance. There is no&amp;nbsp;way you can use a static structure for this, because doing so will&amp;nbsp;make it incredible hard to maintain binary compatibility. I like to be&amp;nbsp;able to fix bugs within the library and release new versions you may&amp;nbsp;use without having to recompile your program, and by hiding the&amp;nbsp;internal datastructures from the clients makes it easier to ensure&amp;nbsp;that the client don't depend on their size. The first parameter to&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_create&lt;/span&gt; is the name (and port) of the REST port for the&amp;nbsp;couchbase server (default: localhost:8091). The second and third&amp;nbsp;parameter is the credentials you'd like to use to connect to the REST&amp;nbsp;port to get the pool information (default is to not authenticate). The&amp;nbsp;forth parameter is the bucket you'd like to connect to, and if you&amp;nbsp;don't specify a bucket you'll end up in the "default bucket". The&amp;nbsp;fifth argument is a special object you may want to use if you are&amp;nbsp;going to use "advanced" features in libcouchbase. Most users will&amp;nbsp;probably just use the defaults and pass NULL here.&lt;br /&gt;
&lt;br /&gt;
The next thing we need to do is to set up some callback handlers to be&amp;nbsp;able to figure out what happens. In the example we're only going to&amp;nbsp;use one operation (to load data into the cache) so we'll need to set&amp;nbsp;up a handler to catch the result of storage operations. Unfortunately&amp;nbsp;we may also encounter problems, so we need to set up an error&amp;nbsp;handler (we'll get back to work in a bit).&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;    libcouchbase_set_storage_callback(instance, storage_callback);
    libcouchbase_set_error_callback(instance, error_callback);
&lt;/pre&gt;
&lt;br /&gt;
Now that we've created and initialized the instance, we need to try to connect to the Couchbase cluster:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;    libcouchbase_error_t ret = libcouchbase_connect(instance);
    if (ret != LIBCOUCHBASE_SUCCESS) {
        fprintf(stderr, "Failed to connect: %s\n",
                libcouchbase_strerror(instance, ret));
        exit(EXIT_FAILURE);
    }
&lt;/pre&gt;
&lt;br /&gt;
Due to the fact that libcouchbase is fully asynchronous, all that&amp;nbsp;happened above was that we initiated the connect. That means that we&amp;nbsp;need to &lt;b&gt;wait&lt;/b&gt; for the server to be connected to the Couchbase&amp;nbsp;cluster and connect to the correct bucket. If our program should do&amp;nbsp;other stuff now would be the time to do so, but since we don't have&amp;nbsp;any other initialization to do we can just wait for it to complete:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;    libcouchbase_wait(instance);
&lt;/pre&gt;
&lt;br /&gt;
One of the "cool" features we've got in libcouchbase is that it&amp;nbsp;provides an internal statistics interface, so we may tell it to&amp;nbsp;collect timing information of the operations with the following&amp;nbsp;snippet:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;   if ((ret = libcouchbase_enable_timings(instance) != LIBCOUCHBASE_SUCCESS)) {
      fprintf(stderr, "Failed to enable timings: %s\n",
              libcouchbase_strerror(instance, ret));
   }

&lt;/pre&gt;
Our program is now fully initialized, and we can enter the main loop&amp;nbsp;that looks like pretty much like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;   while (forever)
   {
      process_files();
      sleep(nsec);
   }
&lt;/pre&gt;
&lt;br /&gt;
So how does our &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;process_files()&lt;/span&gt; look like? I'm not going to make the&amp;nbsp;example too big by pasting all of it, but the first piece in there&amp;nbsp;looks like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;   if (de-&amp;gt;d_name[0] == '.') {
       if (strcmp(de-&amp;gt;d_name, ".dump_stats") == 0) {
           fprintf(stdout, "Dumping stats:\n");
           libcouchbase_get_timings(instance, stdout, timings_callback);
           fprintf(stdout, "----\n");
           remove(de-&amp;gt;d_name);&amp;lt;
       }
       continue;
   }
&lt;/pre&gt;
&lt;br /&gt;
As you see from the above code snippet we'll ignore all files that&amp;nbsp;starts with a '&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;.&lt;/span&gt;' except for the file named "&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;.dump_stats&lt;/span&gt;". Whenever we&amp;nbsp;see that file we dump the internal stats timings by using the&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;timings_callback&lt;/span&gt; (I'll get back to that later).&lt;br /&gt;
&lt;br /&gt;
The next thing we do is to try to read the file into memory and decode&amp;nbsp;it's JSON before we try to get the "&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;_id&lt;/span&gt;" field to use as a key. If all&amp;nbsp;of that succeeds, we try to store the data in Coucbase with:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;      int error = 0;
      ret = libcouchbase_store(instance, &amp;amp;error, LIBCOUCHBASE_SET,
                               id-&amp;gt;valuestring, strlen(id-&amp;gt;valuestring),
                               ptr, size, 0, 0, 0);
      if (ret == LIBCOUCHBASE_SUCCESS) {
         libcouchbase_wait(instance);
      } else {
         error = 1;
      }
&lt;/pre&gt;
&lt;br /&gt;
The &amp;amp;error piece here is quite interesting. It is a "cookie" passed to&amp;nbsp;the callback, so that I may know if I encountered a problem or&amp;nbsp;not. You'll see how I'm using it when I discuss the &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;storage_callback&lt;/span&gt;&amp;nbsp;below.&lt;br /&gt;
&lt;br /&gt;
This is basically all of the important logic in the example. I&amp;nbsp;promised that I would get back to the different callbacks, so let's&amp;nbsp;start by looking at the error callback:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;   static void error_callback(libcouchbase_t instance,
                              libcouchbase_error_t error,
                              const char *errinfo)
   {
       /* Ignore timeouts... */
       if (error != LIBCOUCHBASE_ETIMEDOUT) {
           fprintf(stderr, "\rFATAL ERROR: %s\n",
                   libcouchbase_strerror(instance, error));
           if (errinfo &amp;amp;&amp;amp; strlen(errinfo) != 0) {
               fprintf(stderr, "\t\"%s\"\n", errinfo);
           }
           exit(EXIT_FAILURE);
       }
   }
&lt;/pre&gt;
&lt;br /&gt;
As you see from the above snippet libcouchbase will call the&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;error_callback&lt;/span&gt; whenever a timeout occurs, but we just want to retry&amp;nbsp;the operation. If we encounter a real error we print out an error&amp;nbsp;message and terminate the program.&lt;br /&gt;
&lt;br /&gt;
The next callback we use is the &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;storage_callback&lt;/span&gt;. It is called when&amp;nbsp;the store operation completed, so it is the right place for us to&amp;nbsp;figure out if an error occured while storing the data. Our callback&amp;nbsp;looks like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;   static void storage_callback(libcouchbase_t instance,
                                const void *cookie,
                                libcouchbase_storage_t operation,
                                libcouchbase_error_t err,
                                const void *key, size_t nkey,
                                uint64_t cas)
   {
      int *error = (void*)cookie;
       if (err == LIBCOUCHBASE_SUCCESS) {
           *error = 0;
       } else {
           *error = 1;
           fprintf(stderr, "Failed to store \"");
           fwrite(key, 1, nkey, stderr);
           fprintf(stderr, "\": %s\n",
                   libcouchbase_strerror(instance, err));
           fflush(stderr);
       }
   }
&lt;/pre&gt;
&lt;br /&gt;
As you see we're storing the result of the operation in the integer&amp;nbsp;passed as the cookie. The observant reader may see that we might as&amp;nbsp;well could unlink the file and remove the memory from within the&amp;nbsp;callback (if we provided that information as the cookie instead ;))&lt;br /&gt;
&lt;br /&gt;
The last callback to cover is the timings callback we're using to dump&amp;nbsp;out the timing statistics.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;   static void timings_callback(libcouchbase_t instance, const void *cookie,
                                libcouchbase_timeunit_t timeunit,
                                uint32_t min, uint32_t max,
                                uint32_t total, uint32_t maxtotal)
   {
      char buffer[1024];
      int offset = sprintf(buffer, "[%3u - %3u]", min, max);
      switch (timeunit) {
      case LIBCOUCHBASE_TIMEUNIT_NSEC:
         offset += sprintf(buffer + offset, "ns");
         break;
      case LIBCOUCHBASE_TIMEUNIT_USEC:
         offset += sprintf(buffer + offset, "us");
         break;
      case LIBCOUCHBASE_TIMEUNIT_MSEC:
         offset += sprintf(buffer + offset, "ms");
         break;
      case LIBCOUCHBASE_TIMEUNIT_SEC:
         offset += sprintf(buffer + offset, "s");
         break;
      default:
         ;
      }

      int num = (float)40.0 * (float)total / (float)maxtotal;
      offset += sprintf(buffer + offset, " |");
      for (int ii = 0; ii &amp;lt; num; ++ii) {
         offset += sprintf(buffer + offset, "#");
      }

      offset += sprintf(buffer + offset, " - %u\n", total);
      fputs(buffer, (FILE*)cookie);
   }
&lt;/pre&gt;
&lt;br /&gt;
When you request the timings from libcouchbase it reports all of the&amp;nbsp;timing metrics collected by calling the timings callback. As you can&amp;nbsp;see from the API you'll get the minimum, maximum value for the range,&amp;nbsp;and the number of operations performed within that range. These&amp;nbsp;metrics are not to be considered as exact numbers, because they depend&amp;nbsp;on when what you do in your client code from the time you call the&amp;nbsp;operation until you call libcouchbase_wait for the operation to&amp;nbsp;complete.&lt;br /&gt;
&lt;br /&gt;
So let's run the go ahead and run the program. I've prepopulated&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/var/spool/vacuum&lt;/span&gt; with a number of JSON files, to have the program do&amp;nbsp;something.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@illumos ~&amp;gt; ./vacuum
sleeping 3 secs before retry..
&lt;/pre&gt;
&lt;br /&gt;
From another withdow I execute the command:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@illumos ~&amp;gt; touch /var/spool/vacuum/.dump_stats
&lt;/pre&gt;
&lt;br /&gt;
And when the timer expires in first window, it prints out:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Dumping stats:
[ 60 - &amp;nbsp;69]us |######################################## - 18
[ 70 - &amp;nbsp;79]us |## - 1
[240 - 249]us |## - 1
----
sleeping 3 secs before retry..
&lt;/pre&gt;
&lt;br /&gt;
Hopefully this blog revealed how easy it is to use libcouchbase to&amp;nbsp;communicate with a Couchbase cluster. We've got various clients for&amp;nbsp;other programming languages like PHP and Ruby built on top of&amp;nbsp;libcouchbase, so I can promise you that you'll see more functionallity&amp;nbsp;added!&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-4267828052348509921?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/4267828052348509921/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2012/01/so-how-do-i-use-this-libcouchbase.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/4267828052348509921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/4267828052348509921'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2012/01/so-how-do-i-use-this-libcouchbase.html' title='So how do I use this &quot;libcouchbase&quot;?'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total><georss:featurename>7224 Melhus, Norway</georss:featurename><georss:point>63.2855529 10.2780645</georss:point><georss:box>63.2784149 10.2583235 63.2926909 10.297805499999999</georss:box></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-7092164707338548719</id><published>2012-01-12T13:11:00.000+01:00</published><updated>2012-01-12T13:11:44.930+01:00</updated><title type='text'>Couchbase Server meets SmartOS!</title><content type='html'>I've loved Solaris since we first met back in 95. Since then I've been using Solaris as my primary os (including desktop). For a period of time I even ran Trusted Solaris 2.5 on my SS5 ;-) The kernel and all of the fantastic tools available makes it a superior platform for software development.&lt;br /&gt;
&lt;br /&gt;
Ever since I started working for NorthScale (now Couchbase) I've done my very best to ensure that our stuff works on Solaris (if not I'd have a hard time doing any development ;), so getting it to work on SmartOS shouldn't be a big problem.&lt;br /&gt;
&lt;br /&gt;
So far I've only been using the Sun Studio tools (I can't help it but I still find dbx superior to gdb...), but a few days ago I added a slave running SmartOS to my &lt;a href="http://www.norbye.org/jenkins/job/Couchbase-Server-2.0/"&gt;Jenkins&lt;/a&gt; cluster to see how much work it would be to get everything built with the tools and libraries I could install with pkgin. We do compile our software on different platforms and with different compilers, so I didn't expect too much trouble.&lt;br /&gt;
&lt;br /&gt;
The "biggest" problem I had was to get libtool to stop trying to link 32bit object files into my 64 bit binaries. The workaround so far is to use:&lt;br /&gt;
&lt;pre style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CXX="g++ -m64 -L/opt/local/lib/amd64"
CC="gcc -m64 -L/opt/local/lib/amd64"
&lt;/pre&gt;
&lt;br /&gt;
This was a small goal for me, but it will really ease my testing of a full cluster :-)&lt;br /&gt;
&lt;br /&gt;
Now that I've got everything built on SmartOS, OpenIndiana, Soalris 10 I guess I should create packages for pkgin, IPS and SVr4 :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-7092164707338548719?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/7092164707338548719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2012/01/couchbase-server-meets-smartos.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/7092164707338548719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/7092164707338548719'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2012/01/couchbase-server-meets-smartos.html' title='Couchbase Server meets SmartOS!'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-1627688288956498711</id><published>2012-01-09T11:58:00.000+01:00</published><updated>2012-01-09T12:00:33.177+01:00</updated><title type='text'>Thank you Joyent, I love you!</title><content type='html'>&lt;br /&gt;
&lt;div class="p1"&gt;
I’ve always been a strong believer of that people should be able to choose the platform they feel suits their needs the most. If people want to make stupid decisions and not choose my beloved Solaris, I’m not going to stop them. This means that I need to ensure that the software I’m working on not only compiles, but also works on multiple platforms. In “the old days” I used to install a new os on my desktop box every time I upgraded my desktop (which always ran the latest bits of Solaris), but lately I’ve been using virtual machines to make it easier for myself ;)&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;You may wonder why I don’t just set up virtual machines in the cloud? The answer is pretty simple. I’m working from home, and I’ve had my share of problems with my ISP. I don’t want to end up in a situation where I can’t do my work just because my ISP fails to keep me connected with the rest of the world...&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;I guess it must be roughly a year ago since I decided to replace all of my VirtualBox instances running on a handful of old machines with KVM on top of Debian on my server.&amp;nbsp;&lt;/span&gt;With the server running there I moved more and more stuff off my desktop box, and I ended up in a situation I really didn’t like. I had the services I “needed” running on top of a server without mirrored disks. To save money I had ordered that dell server with just a single disk. I’ve had enough disks dying on me over the years, so to me this feels like hiking in the middle of the freeway at midnight.. To get out of this situation I created an iSCSI share on my OpenIndiana box on top of a ZFS mirror that I connected to the Debian box. Now I could sleep a little bit better at night...&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;I had noticed the releases of SmartOS, and it looked really cool! Unfortunately for me I didn’t have a machine I could try it on, but luckily for me Debian gave me a helping hand in December to speed up the process! After upgrading packages with aptitude my box would no longer boot! I don’t have a keyboard/monitor attached to the box, so I had to bring the server into my office to “debug” the issue. I didn’t figure out why it wasn’t booting with the new kernel, but I was able to boot it with the old kernel and get the stuff I &lt;b&gt;needed&lt;/b&gt; off the box. I &lt;i&gt;could&lt;/i&gt; have spent more time trying to figure out why it was failing, but instead I took this as a golden opportunity to try out SmartOS. Doing so brought nothing but joy into my life!!!!!&lt;/span&gt;&lt;br /&gt;
&lt;span class="s1"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;After booting off the USB stick I had to answer a couple of questions to configure the system for the first time, and I was ready to create my first machine. I followed the instructions in "&lt;span class="s2"&gt;&lt;a href="http://wiki.smartos.org/display/DOC/How+to+create+a+Virtual+Machine+in+SmartOS"&gt;How to create a Virtual Machine in SmartOS&lt;/a&gt;"&lt;/span&gt; and I had my first VM up’n running in less than a minute! So simple, and yet so powerful!&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;Roughly a week later my box looks like:&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[root@00-26-b9-85-bd-92 ~]# &lt;b&gt;vmadm list&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;UUID&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; TYPE&amp;nbsp; RAM&amp;nbsp; &amp;nbsp; &amp;nbsp; STATE &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ALIAS&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;80658f56-0eb3-405f-a6eb-690461c2d9ce&amp;nbsp; OS&amp;nbsp; &amp;nbsp; 256&amp;nbsp; &amp;nbsp; &amp;nbsp; running &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;9c441211-bb77-446f-abec-2291039aeca2&amp;nbsp; OS&amp;nbsp; &amp;nbsp; 256&amp;nbsp; &amp;nbsp; &amp;nbsp; running &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; smartos64&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;b0e34876-cd7a-4922-ad6e-921452d34359&amp;nbsp; OS&amp;nbsp; &amp;nbsp; 512&amp;nbsp; &amp;nbsp; &amp;nbsp; running &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; jenkins&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;13f4223b-a2c1-400a-b682-79372c3ba846&amp;nbsp; KVM &amp;nbsp; 1024 &amp;nbsp; &amp;nbsp; running &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; solaris11&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;7bad78f1-d202-4dfe-97f6-e421e8da8d58&amp;nbsp; KVM &amp;nbsp; 1024 &amp;nbsp; &amp;nbsp; running &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ubuntu64 &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;8e042001-bc79-48eb-a0bc-704ca64f20e0&amp;nbsp; KVM &amp;nbsp; 1024 &amp;nbsp; &amp;nbsp; running &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; debian &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;e1710b31-0270-43a3-89dc-71398ba3630a&amp;nbsp; KVM &amp;nbsp; 1024 &amp;nbsp; &amp;nbsp; running &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; windows&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;f165261a-6a27-4ee3-a6d3-f3814cf69bd6&amp;nbsp; KVM &amp;nbsp; 1024 &amp;nbsp; &amp;nbsp; running &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ubuntu32 &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;br /&gt;
&lt;span class="s1"&gt;&lt;/span&gt;My "jenkins" vm is just running the Jenkins CI web application for&amp;nbsp;&lt;span class="s2"&gt;&lt;a href="http://www.norbye.org/jenkins/"&gt;http://www.norbye.org/jenkins/&lt;/a&gt;, and it connects via ssh into the other VMs (and a couple of other machines) to build software there. This is part of my automatic build process. I've got it on my ever growing todo list to set up NIS/LDAP, but in the mean time I'm just sync'ing the user definitions around so that I can log into all of the machines.&lt;/span&gt;&lt;br /&gt;
&lt;span class="s2"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;But wait, I said I didn’t like to keep stuff on filesystems that isn’t mirrored, and choosing another OS doesn't change this? Right now I don’t care if the disk dies, because I can easily recreate all of the vm’s from scratch (i’ve got the descriptions I used to create the vm’s stored somewhere else). All “users” on the vm’s mount their home directory from my NFS server, so none of those files would get lost. I guess I could use zfs send/receive to back up the vm itself while I’m waiting for another disk for the box.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="s1"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;So is there anything I miss from the current release, or is it perfect? There is one thing I really miss, and that is the ability to use the alias instead of the uuid when I’m using &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;vmadm&lt;/span&gt;. I know that the alias doesn’t have to be unique, but if the alias &lt;b&gt;is&lt;/b&gt; unique it would be a lot easier to use (instead of having to do a &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;vmadm list&lt;/span&gt; first).&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="s1"&gt;The second thing I’d love to see would be something like:&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="s1"&gt;# &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;vmadm bootinstall name iso-file&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="s1"&gt;it would expand into something like:&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;cp iso-file uuid/root/cdrom.iso&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;vmadm boot uuid order=cd,once=d cdrom=/cdrom.iso,ide&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;vmadm info uuid vnc&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;br /&gt;
&lt;span class="s1"&gt;But hey, if this is the biggest problems I’ve got with SmartOS I must be pretty happy with it. After all how often do you really create and install new VMs? The fact that I may use the tools I know and love&amp;nbsp;&lt;/span&gt;(dtrace, zfs, smf etc) is just awesome!&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class="p2"&gt;
&lt;span class="s1"&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class="p1"&gt;
&lt;span class="s1"&gt;Thank you Joyent for bringing this to my fingertips!&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-1627688288956498711?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/1627688288956498711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2012/01/thank-you-joyent-i-love-you.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1627688288956498711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1627688288956498711'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2012/01/thank-you-joyent-i-love-you.html' title='Thank you Joyent, I love you!'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-4801655430241180160</id><published>2011-10-05T00:00:00.001+02:00</published><updated>2011-10-18T17:07:48.877+02:00</updated><title type='text'>libcouchbase - Explore the full features of your Couchbase server from C</title><content type='html'>I started the implementation of libcouchbase almost year ago out of my personal need to test the inner workings of &lt;a href="http://www.couchbase.com/"&gt;Couchbase&lt;/a&gt;. Since I work in
"core" of Couchase I needed an easy way to test out the changes I made
there. At that time there were only clients for Java, Python and C#
with support for REST interface to Couchbase. I'm no fan of Python,
and my Solaris machine doesn't support C# so Java was my only
option. I do like Java a lot, so that was my language of choice
initially when I had to test something. Unfortunately I do find it a
bit of extra hassle having to deal with multiple languages (and that I
can't link the test code into the core to test it "from the inside"
;-)) Given that I set off trying to come up with an alternative.&lt;br /&gt;
&lt;br /&gt;
Over the past few years I have put some effort into &lt;a href="https://launchpad.net/libmemcached"&gt;libmemcached&lt;/a&gt;, so
the first thing I did was look at what it would take to add support
for a "smart client behavior" into libmemcached. If that would be
possible I could have a full featured library people maintained.
Unfortunately it turned out that it wasn't straight forward to add
what I wanted due to the way the library works. libmemcached would
initialize it's list of server structures at initialization time, and
map the key to the server it would belong to. For Couchbase I would
have to be able to replace the list of servers at any time. In
addition to that Couchbase uses a two-level mapping of the key. First
we map the key to a &lt;a href="http://dustin.github.com/2010/06/29/memcached-vbuckets.html"&gt;&lt;i&gt;vbucket&lt;/i&gt;&lt;/a&gt;, then we try to look up the server
where the bucket reside. At the time I felt that I would have to
change too much of the inner workings of libmemcached to get it to
work for Couchbase (and given the fact that libmemcached isn't a
vendor-specific library I felt it was the wrong thing to do). Since I didn't want to sit on a "ticking bomb" when it comes to trying
to merge my private "patch" to libmemcached with the upstream changes
I decided to just write something that would fulfill my needs.&amp;nbsp;I think it's worth mentioning that libcouchbase isn't meant to be a replacement for libmemcached, but an alternative for those of you who want / needs to explore the full features of Couchbase.&lt;br /&gt;
&lt;br /&gt;
The "quick'n'dirty" solution would have been to just hardcode the
stuff I needed into various test programs and use copy'n'paste until I
met the grim reaper, but that wouldn't have been particulary
innovative (or fun). Instead I sat down and defined some criterias for
the library:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;It must all asynchronously &lt;blockquote&gt;
I believe that all libraries should provide an asynchronously
interface, and if the library provides a synchronously interface it
should be built on top of the asynchronously interface and wait for
the completion.&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;No internal locking
&lt;blockquote&gt;
Using locks internally may lead to lock contention. If I can avoid
locking inside the library it will potentially scale better. It will be left entierly up to the client of the library to
protect the variables from access by multiple threads.&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;It has to be cross platform!&lt;blockquote&gt;
I do all of my development on Solaris, but I do respect that others
may have different needs than myself. Some people prefer Mac OS, some
prefer Windows and I've even heard of people using Linux. Why
shouldn't all of them be able to use libcouchbase?&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;It shall not depend of a "shit load" of other modules&lt;blockquote&gt;
It is not that I suffer from the &lt;a href="http://en.wikipedia.org/wiki/Not_Invented_Here"&gt;not-invented-here-syndrome&lt;/a&gt;, but I
do find it a PITA to have to compile tons of other libraries (which
may have additional dependencies etc) just to get the library
working (and keeping all of them in sync with each other as new releases comes). All library use should be justified.&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;Binary compatibility&lt;blockquote&gt;
The binary interface should be stable and not change all the
time. Coming up with a good API isn't easy, so during development the
api is expected to evolve. If a client only use a committed interface
he shouldn't have to do _anything_ except replacing the shared object
when the new version is released.&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;No GPL&lt;blockquote&gt;
I just can't stand the GPL license.&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Although I started working on libcouchbase a year ago, it doesn't &amp;nbsp;mean that I have worked full time on it. My primary job is working on the core, so I've only extended the library when I've needed the functionality for my own testing.&amp;nbsp;More recently I'm happy to say I've received some contributions from some people who've picked it up and used it and from some other Couchbase developers (thanks Sergey, Paul, Sebastian, Jan, and Bill!). &amp;nbsp;No doubt, there's something we can do better, so please drop an email to the Couchbase development mailing list (&lt;a href="mailto:couchbase@googlegroups.com"&gt;couchbase@googlegroups.com&lt;/a&gt;) if there's something you think needs some fixin'. I still think that&amp;nbsp;the library has some rough edges that needs to be sorted out before you can start using it in production ;-)&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;








Building the software&lt;/h2&gt;
If you're trying to build libcoucbase on Solaris / *BSD / MacOS or Linux you can just use the "&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;configure &amp;amp;&amp;amp; make&lt;/span&gt;" way you're so used to. For the Windows users I've written an NMakefile you may use to compile and install the various bits. Since I'm doing almost all of my development I might have done stuff "wrong", but I'll be happy if you drop me an email telling me what I need to change.
&lt;br /&gt;
&lt;br /&gt;
I've been using Windows 7 with Microsoft Developer Studio 2010 to build and test the library. Execute the following commands to build and install the bits:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;nmake -f NMakefile install&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
By default it will use c:\local as the root directory (to make it easy&amp;nbsp;for you to create an installer or move it wherever you want). You can&amp;nbsp;always override this by specifying INSTALL like:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;nmake -f NMakefile INSTALL=c:\couchbase install&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
















Prerequisites&lt;/h2&gt;
We do have some prerequisites for libcouchbase. You might find binary packages available for your platform, but it shouldn't be very hard to build from source.&lt;br /&gt;
&lt;br /&gt;
1) The header files from the engine branch of memcached.&lt;br /&gt;
&lt;br /&gt;
Simply copy the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;memcached&lt;/span&gt; directory from&amp;nbsp;&lt;a href="https://github.com/memcached/memcached/tree/engine-pu/include"&gt;https://github.com/memcached/memcached/tree/engine-pu/include&lt;/a&gt; to &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;c:\local\include&lt;/span&gt; (or whatever you choose as your directory)&lt;br /&gt;
&lt;br /&gt;
2) A sasl implementation.&lt;br /&gt;
&lt;br /&gt;
libcouchbase needs to run SASL authentications to the different buckets. If you don't want to install a full featured SASL library you could always install "libisasl" from: &lt;a href="https://github.com/membase/libisasl"&gt;https://github.com/membase/libisasl&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
3) libvbucket&lt;br /&gt;
&lt;br /&gt;
The mapping between a key and the vbucket (and to locate which server the vbucket is located on) is provided by this library. &lt;a href="https://github.com/membase/libvbucket"&gt;https://github.com/membase/libvbucket&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
4) libevent (optional)&lt;br /&gt;
&lt;br /&gt;
libcouchbase allows plugins to different event notification frameworks. The default framework for UNIX-like systems is libevent, so unless you're going to create your own plugin you might want to install this. Please note that the default for windows is something else so you don't need this at all for windows.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;













So how does the library work?&lt;/h2&gt;
&lt;br /&gt;
The primary idea with the library is that everything should be event 
driven, and that a callback should be triggered when something 
happens. That means that you must set up callbacks to handle 
everything you want. There is no simple:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;
std::string myvalue = libcoucbase-&amp;gt;get("hello");&lt;/div&gt;
&lt;br /&gt;
but you can easily implement that if you want.&lt;br /&gt;
&lt;br /&gt;
Given the fact that there is no locking within libcouchbase you may think that it's not suited for use in a multithreaded process, but thats not true. As long as you don't use the same handle to libcouchbase from multiple threads you can use as many threads as you want (if you want to use the same libcouchbase instance from multiple threads you need to provide locking)&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;














Enough talk, show me the code!&lt;/h2&gt;
All you need to do in your program to start using libcouchbase is to include &lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase/couchbase.h&lt;/span&gt;. and link with libcouchbase. The first thing you would need to do is to create an instance to libcouchbase:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;#include &lt;/span&gt;&lt;libcouchbase couchbase.h=""&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;...&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;const char *host = NULL; /* Use localhost:8091 */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;const char *username = NULL; /* No user specified */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;const char *password = NULL; /* No password specified */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;const char *bucket = NULL; /* use default bucket */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;struct libcouchbase_io_opt_st *io = NULL; /* Use default io options */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_t handle = libcouchbase_create(host, username, password,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bucket, io);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;if (handle == NULL) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; /* Failed to create the handle */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The code fragment above does nothing more than allocate the handle to libcouchbase, and it did not try to connect it to the server to receive the list of servers etc. The username/password combination here will be used to authenticate to the REST server listening on the host port. With the handle in place we should set up the first callback: the error handler. Let's create a simple error callback that prints out the error and terminates the application:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;static void error_callback(libcouchbase_t instance,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_error_t error,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const char *errinfo)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, "%s", libcouchbase_strerror(instance, error));&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (errinfo) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, ": %s", errinfo);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, "\n");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; exit(EXIT_FAILURE);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br /&gt;The callback is installed with:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_set_error_callback(handle, error_callback);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now that we've got our error callback installed, we can start connecting to the server and receive the list of servers. Since everything is asyncronous we need to wait for the connect to complete (I'm not going to show you how to use the library in a shared event loop in this example).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_connect(handle);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;// Wait for the connect to compelete&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_wait(handle);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;At this time we've got a "working" instance to libcouchbase we may use. So let's go ahead and store some items in the cache. If we don't care about the response message from the server we don't need to set up a callback, but to make the example more complete lets create a callback that terminates the program if we fail to store one of the items:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;static void storage_callback(libcouchbase_t instance,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const void *cookie,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_storage_t operation,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_error_t error,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const void *key, size_t nkey,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint64_t cas)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (error != LIBCOUCHBASE_SUCCESS) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, "Failed to store \"");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fwrite(key, nkey, 1, stderr);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, "\"\n");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; exit(EXIT_FAILURE);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br /&gt;I don't have a good example of what we want to store, so let's just loop and store some numbers:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_set_storage_callback(instance, storage_callback);&lt;/span&gt;&lt;/libcouchbase&gt;&lt;br /&gt;
&lt;libcouchbase couchbase.h=""&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;for (int ii = 0; ii &amp;lt; 10; ++ii) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char key[80];&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; size_t nkey = sprintf(key, "%d", ii);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_store(handle, NULL, LIBCOUCHBASE_SET,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key, nkey, &amp;amp;ii, sizeof(ii), 0, 0, 0);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/* Wait for all of them to complete */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_wait(handle);&lt;/span&gt;&lt;/libcouchbase&gt;&lt;br /&gt;
&lt;h2&gt;



Timings&lt;/h2&gt;
One of the things I find cool about libcouchbase is the ability to get some timings statistics about the current traffic. Everyone familiar to DTrace loves the ability to dump a histogram representing whatever you decided to measure. A lot of the times when you're running your stuff in production you might want to look at the response times you've got from your Couchbase cluster. In order to help you do that I've added some relatively lightweight timings you may use. Due to the asyncronous nature of libcouchbase (and that you're responsible to drive the event loop) you may impose a large effect on the timings so that they no longer represents the truth.. Anyway, let's add an example that utilize them to crate a histogram of the store section above (but instead of running all of them in a single batch, use a&lt;br /&gt;
synchronous set.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_enable_timings(handle);&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;for (int ii = 0; ii &amp;lt; 10; ++ii) {&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char key[80];&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; size_t nkey = sprintf(key, "%d", ii);&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_store(handle, NULL, LIBCOUCHBASE_SET,&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key, nkey, &amp;amp;ii, sizeof(ii), 0, 0, 0);&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_wait(handle);&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/* Get the current timings */&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_get_timings(handle, stdout, timings_callback);&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/* Stop collecting timing information */&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;libcouchbase_disable_timings(handle);&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
So how does this "timings_callback" look like? That's completely up to you, but we could create a simple histogram with the following code:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;static void timings_callback(libcouchbase_t instance, const void *cookie,&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_timeunit_t timeunit,&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint32_t min, uint32_t max,&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint32_t total, uint32_t maxtotal)&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char buffer[1024];&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int offset = sprintf(buffer, "[%3u - %3u]", min, max);&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; switch (timeunit) {&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; case LIBCOUCHBASE_TIMEUNIT_NSEC:&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "ns");&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; case LIBCOUCHBASE_TIMEUNIT_USEC:&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "us");&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; case LIBCOUCHBASE_TIMEUNIT_MSEC:&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "ms");&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; case LIBCOUCHBASE_TIMEUNIT_SEC:&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "s");&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; default:&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int num = (float)40.0 * (float)total / (float)maxtotal;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, " |");&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (int ii = 0; ii &amp;lt; num; ++ii) {&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "#");&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, " - %u\n", total);&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fputs(buffer, (FILE*)cookie);&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
This would generate something like:&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[140 - 149]us |# - 2&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[150 - 159]us |## - 3&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[160 - 169]us |######################################## - 47&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[170 - 179]us |###################### - 26&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[180 - 189]us |########### - 14&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[190 - 199]us |# - 2&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[210 - 219]us | - 1&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[220 - 229]us | - 1&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[230 - 239]us | - 1&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[250 - 259]us | - 1&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[280 - 289]us | - 1&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;[400 - 409]us | - 1&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;



Source code&lt;/h2&gt;
I guess you want to try the program yourself :)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;libcouchbase couchbase.h=""&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;#include &lt;stdio.h&gt;&lt;libcouchbase couchbase.h=""&gt;&lt;libcouchbase couchbase.h=""&gt;&lt;/libcouchbase&gt;&lt;/libcouchbase&gt;&lt;/stdio.h&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;#include &lt;stdlib.h&gt;&lt;stdio.h&gt;&lt;stdio.h&gt;&lt;/stdio.h&gt;&lt;/stdio.h&gt;&lt;/stdlib.h&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;#include &lt;libcouchbase couchbase.h=""&gt;&lt;stdlib.h&gt;&lt;stdlib.h&gt;&lt;/stdlib.h&gt;&lt;/stdlib.h&gt;&lt;/libcouchbase&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;static void error_callback(libcouchbase_t instance,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_error_t error,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const char *errinfo)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; fprintf(stderr, "%s", libcouchbase_strerror(instance, error));&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; if (errinfo) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, ": %s", errinfo);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; fprintf(stderr, "\n");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; exit(EXIT_FAILURE);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;static void storage_callback(libcouchbase_t instance,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const void *cookie,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_storage_t operation,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_error_t error,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const void *key, size_t nkey,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint64_t cas)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; if (error != LIBCOUCHBASE_SUCCESS) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, "Failed to store \"");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fwrite(key, nkey, 1, stderr);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, "\"\n");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; exit(EXIT_FAILURE);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;static void timings_callback(libcouchbase_t instance, const void *cookie,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_timeunit_t timeunit,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint32_t min, uint32_t max,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint32_t total, uint32_t maxtotal)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; char buffer[1024];&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; int offset = sprintf(buffer, "[%3u - %3u]", min, max);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; switch (timeunit) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; case LIBCOUCHBASE_TIMEUNIT_NSEC:&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "ns");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; case LIBCOUCHBASE_TIMEUNIT_USEC:&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "us");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; case LIBCOUCHBASE_TIMEUNIT_MSEC:&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "ms");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; case LIBCOUCHBASE_TIMEUNIT_SEC:&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "s");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; default:&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; int num = (float)40.0 * (float)total / (float)maxtotal;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, " |");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; for (int ii = 0; ii &amp;lt; num; ++ii) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, "#");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; offset += sprintf(buffer + offset, " - %u\n", total);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; fputs(buffer, (FILE*)cookie);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;int main(int argc, char **argv)&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; const char *host = NULL; /* Use localhost:8091 */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; const char *username = NULL; /* No user specified */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; const char *password = NULL; /* No password specified */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; const char *bucket = NULL; /* use default bucket */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; struct libcouchbase_io_opt_st *io = NULL; /* Use default io options */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_t handle = libcouchbase_create(host,&amp;nbsp;&lt;/span&gt;&lt;/libcouchbase&gt;&lt;br /&gt;
&lt;libcouchbase couchbase.h=""&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; username,&amp;nbsp;&lt;/span&gt;&lt;/libcouchbase&gt;&lt;br /&gt;
&lt;libcouchbase couchbase.h=""&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; password,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bucket, io);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; if (handle == NULL) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Failed to create the handle */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fprintf(stderr, "Failed to create instance\n");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; exit(EXIT_FAILURE);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_set_error_callback(handle, error_callback);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_connect(handle);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; // Wait for the connect to compelete&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_wait(handle);&lt;/span&gt;&lt;/libcouchbase&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_set_storage_callback(instance, storage_callback);&lt;/span&gt;&lt;libcouchbase couchbase.h=""&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/libcouchbase&gt;&lt;br /&gt;
&lt;libcouchbase couchbase.h=""&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_enable_timings(handle);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; for (int ii = 0; ii &amp;lt; 100; ++ii) {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; char key[80];&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; size_t nkey = sprintf(key, "%d", ii);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_store(handle, NULL, LIBCOUCHBASE_SET,&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; key, nkey, &amp;amp;ii, sizeof(ii), 0, 0, 0);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; libcouchbase_wait(handle);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; /* Get the current timings */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_get_timings(handle, stdout, timings_callback);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; /* Stop collecting timing information */&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_disable_timings(handle);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; libcouchbase_destroy(handle);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; return 0;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&amp;nbsp;&lt;/libcouchbase&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-4801655430241180160?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/4801655430241180160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2011/10/libcouchbase-explore-full-features-of.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/4801655430241180160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/4801655430241180160'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2011/10/libcouchbase-explore-full-features-of.html' title='libcouchbase - Explore the full features of your Couchbase server from C'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>2</thr:total><georss:featurename>Gyllevegen 1, 7224, Norway</georss:featurename><georss:point>63.2936335818312 10.282087326049805</georss:point><georss:box>63.2900655818312 10.272216826049805 63.2972015818312 10.291957826049805</georss:box></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-8408918798376012201</id><published>2011-01-14T17:59:00.000+01:00</published><updated>2011-01-14T17:59:44.804+01:00</updated><title type='text'>SASL client library...</title><content type='html'>There is a lot of software out there that allows you to plug in SASL authentication if you got a SASL client library on your system. Some operating systems allows an easy download of a SASL library for their platform, but I have not seen any for Windows yet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Membase supports SASL authentication, so when I started to implement libmembase I decided that I wanted to treat libsasl as a required dependency. One of my goals with libmembase is that it should be easy to compile as a dll for Windows (I'm not there yet), so I needed a libsasl dll for Windows.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Earlier today I fixed and pushed a (client side) SASL library to &lt;a ref-"https://github.com/membase/libisasl/"&gt;https://github.com/membase/libisasl/&lt;/a&gt;. If you're running on a Unix-like system you should build the library by using autotools, but I've added a Makefile you may use to build the dll on Windows with the following command:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;nmake -f NMakefile
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
There is no install-target in the Makefile, so you need to copy the header-files from "&lt;code&gt;include&lt;/code&gt;", &lt;code&gt;libsasl.dll&lt;/code&gt; and &lt;code&gt;libsasl.lib&lt;/code&gt; to the desired location on your machine when you're done.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-8408918798376012201?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/8408918798376012201/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2011/01/sasl-client-library.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/8408918798376012201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/8408918798376012201'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2011/01/sasl-client-library.html' title='SASL client library...'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-8087729673835366070</id><published>2011-01-11T16:31:00.000+01:00</published><updated>2011-01-11T16:31:37.118+01:00</updated><title type='text'>Developing with Membase</title><content type='html'>As a developer I need to be able to start my processes in a certain way. One way to do that may be to modify the startup code we've got in our management system, but I found it way more flexible and easy to just replace our binaries with wrapper scripts that starts up our binaries.&lt;br /&gt;
&lt;br /&gt;
Please note that this is something I do when I try to track down a certain bug, and not something I recommend in your production environment.&lt;br /&gt;
&lt;br /&gt;
I've created my own little script that installs the wrapper script:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;#! /bin/ksh

cat &amp;gt; /opt/membase/bin/launcher.sh &amp;lt;&lt;eof&gt;
#! /bin/ksh
logfile=/tmp/membase.log
binary=\`basename \$0\`
echo pid \$\$ : \$0 \$* &amp;gt;&amp;gt; \${logfile}
exec \${0}.bin "\$@" 2&amp;gt;&amp;amp;1 | awk "{printf(\"%d: %s\n\", $$, \\\$0); }" &amp;gt;&amp;gt; \${logfile}
EOF

chmod a+x /opt/membase/bin/launcher.sh

for f in memcached vbucketmigrator moxi
do
&amp;nbsp;&amp;nbsp; mv /opt/membase/bin/${f}/${f} /opt/membase/bin/${f}/${f}.bin
&amp;nbsp;&amp;nbsp; ln -s ../launcher.sh /opt/membase/bin/${f}/${f}
done
&lt;/eof&gt;&lt;/pre&gt;&lt;br /&gt;
As an extra bonus this redirects all of the output from the processes to &lt;code&gt;/tmp/membase.log&lt;/code&gt;, so that I can just check there for the error text instead of running browse_logs and start decoding the output there.&lt;br /&gt;
&lt;br /&gt;
The above script use the &lt;b&gt;same&lt;/b&gt; wrapper script for all processes, but sometimes I want to add extra options to one of the processes (like enabling verbosity for &lt;code&gt;vbucketmigrator&lt;/code&gt;). All I need to do is just to replace the link with a copy of the file:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;root@ubuntu:/opt/membase/bin# rm vbucketmigrator/vbucketmigrator
root@ubuntu:/opt/membase/bin# cp -p launcher.sh vbucketmigrator/vbucketmigrator
&lt;/pre&gt;&lt;br /&gt;
and edit the file. Since I'm going to add extra command line options, I'm most likely expecting more output so normally I store the output in its own file as well:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;#! /bin/ksh
&lt;b&gt;logfile=/tmp/vbucketmigrator.$$&lt;/b&gt;&lt;/pre&gt;&lt;pre&gt;binary=`basename $0`
echo pid $$ : $0 $* &amp;gt;&amp;gt; ${logfile}
exec ${0}.bin "$@" &lt;b&gt;-vv&lt;/b&gt; 2&amp;gt;&amp;amp;1 &amp;gt;&amp;gt; ${logfile}
&lt;/pre&gt;&lt;br /&gt;
The next time &lt;code&gt;vbucketmigrator&lt;/code&gt; starts it will dump the message traffic to &lt;code&gt;/tmp/vbucketmigrator.&lt;i&gt;pid&lt;/i&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-8087729673835366070?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/8087729673835366070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2011/01/developing-with-membase.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/8087729673835366070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/8087729673835366070'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2011/01/developing-with-membase.html' title='Developing with Membase'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-7213291065353629935</id><published>2011-01-10T22:07:00.001+01:00</published><updated>2011-01-11T09:06:25.459+01:00</updated><title type='text'>Dumping stats from a memcached server...</title><content type='html'>I normally dump the stats from my memcached servers with the following command:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;echo stats | nc localhost 11211&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
But if you start memcached with the ascii protocol disabled it becomes hard to dump the stats. I just created a small tool named &lt;code&gt;mcstats&lt;/code&gt; that dumps the stats in the same format as you would get from the above command.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;./mcstat&lt;/b&gt;
STAT evictions 0
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT reclaimed 0
STAT engine_maxbytes 67108864
STAT pid 15436
STAT uptime 682
STAT time 1294693339
STAT version 1.3.3_499_g580ae55
STAT libevent 1.4.13-stable
STAT pointer_size 32
STAT rusage_user 0.012261
STAT rusage_system 0.014169
STAT daemon_connections 10
STAT curr_connections 11
STAT total_connections 16
STAT connection_structures 14
STAT cmd_get 0
STAT cmd_set 0
STAT cmd_flush 0
STAT auth_cmds 0
STAT auth_errors 0
STAT get_hits 0
STAT get_misses 0
STAT delete_misses 0
STAT delete_hits 0
STAT incr_misses 0
STAT incr_hits 0
STAT decr_misses 0
STAT decr_hits 0
STAT cas_misses 0
STAT cas_hits 0
STAT cas_badval 0
STAT bytes_read 106
STAT bytes_written 5656
STAT limit_maxbytes 67108864
STAT rejected_conns 0
STAT threads 4
STAT conn_yields 0
&lt;/pre&gt;&lt;br /&gt;
By default &lt;code&gt;mcstat&lt;/code&gt; will connect to "&lt;code&gt;localhost:11211&lt;/code&gt;", but you may tell it to go somewhere else by using &lt;code&gt;-h host&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
You'll find the tool in &lt;a href="https://github.com/trondn/memcached/tree/engine"&gt;my git branch of the memcached source repository&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Happy new year btw.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-7213291065353629935?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/7213291065353629935/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2011/01/dumping-stats-from-memcached-server.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/7213291065353629935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/7213291065353629935'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2011/01/dumping-stats-from-memcached-server.html' title='Dumping stats from a memcached server...'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-1868650968497303831</id><published>2010-12-21T14:54:00.000+01:00</published><updated>2010-12-21T14:54:38.366+01:00</updated><title type='text'>Running Membase on OpenSolaris</title><content type='html'>I've been wanting to run &lt;a href="http://www.membase.com"&gt;membase&lt;/a&gt; on my &lt;a href="http://hub.opensolaris.org/bin/view/Main/"&gt;OpenSolaris&lt;/a&gt; server for a long time, but I haven't had the time to look into all of the details on getting it there. All of the command line tools compile and works perfectly well on &lt;a href="http://hub.opensolaris.org/bin/view/Main/"&gt;OpenSolaris&lt;/a&gt; already, but I haven't been able to get the web ui and cluster management up and running. One of the bugs preventing an easy install of &lt;a href="http://www.membase.com"&gt;membase&lt;/a&gt; on &lt;a href="http://hub.opensolaris.org/bin/view/Main/"&gt;OpenSolaris&lt;/a&gt; (and other unsupported platforms) is that the makefile for the admin ui don't have an install target (This is tracked in &lt;a href="http://jira.membase.org/browse/MB-2700"&gt;MB-2700&lt;/a&gt; ).&lt;br /&gt;
&lt;br /&gt;
Earlier today I cloned the layout of the web application on disk from the &lt;a href="http://www.ubuntu.com"&gt;Ubuntu&lt;/a&gt; installation to my &lt;a href="http://hub.opensolaris.org/bin/view/Main/"&gt;OpenSolaris&lt;/a&gt; box, and with some work I was able to get the system up and running on my &lt;a href="http://hub.opensolaris.org/bin/view/Main/"&gt;OpenSolaris&lt;/a&gt; box. Just check it out:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@storm&amp;gt; &lt;b&gt;svcs membase&lt;/b&gt;
STATE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; STIME&amp;nbsp;&amp;nbsp;&amp;nbsp; FMRI
online&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13:37:26 svc:/application/database/membase:membase
&lt;/pre&gt;&lt;br /&gt;
So let's assume you've got all of the dependencies you need to build &lt;a href="http://www.membase.com"&gt;membase&lt;/a&gt; installed on your &lt;a href="http://hub.opensolaris.org/bin/view/Main/"&gt;OpenSolaris&lt;/a&gt; (I'll come up with this list later on and create an IPS package for you to install from my IPS server).&lt;br /&gt;
&lt;br /&gt;
Due to &lt;a href="http://jira.membase.org/browse/MB-3227"&gt;MB-3227&lt;/a&gt; we can't use the build method outlined in "&lt;a href="http://wiki.membase.org/display/membase/Building+on+OSX+from+source"&gt;Building on OSX from source&lt;/a&gt;" to build membase, but I've updated my build environment I blogged about &lt;a href="http://trondn.blogspot.com/2010/10/building-membase-from-sources.html"&gt;a while ago&lt;/a&gt; so that it will build and install everything for you! So let's go ahead and build the stuff!&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@storm&amp;gt; &lt;b&gt;git clone git://github.com/trondn/tools.git&lt;/b&gt;
Initialized empty Git repository in /tmp/tools/.git/
remote: Counting objects: 168, done.
remote: Compressing objects: 100% (163/163), done.
remote: Total 168 (delta 89), reused 0 (delta 0)
Receiving objects: 100% (168/168), 157.56 KiB | 231 KiB/s, done.
Resolving deltas: 100% (89/89), done.
trond@storm&amp;gt; &lt;b&gt;cd tools/membase/smf/membase&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;./setup -u -s -z scratch&lt;/b&gt;               (scratch is the name of my zfs pool)
trond@storm&amp;gt; &lt;b&gt;cd ../..&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;chown -R trond:staff /opt/membase&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;./setup.sh -d /opt/membase/dev membase&lt;/b&gt;
Download commit hook - Ok.
Checking out libmemcached (Bazaar) - Ok.
Checking out bucket_engine (git) - Ok.
Checking out ep-engine (git) - Ok.
Checking out libconflate (git) - Ok.
Checking out libvbucket (git) - Ok.
Checking out memcached (git) - Ok.
Checking out moxi (git) - Ok.
Checking out vbucketmigrator (git) - Ok.
Checking out membase-cli (git) - Ok.
Checking out ns_server (git) - Ok.
Checking out memcachetest (git) - Ok.
Checking out mc-hammer (git) - Ok.
Checking out libmembase (git) - Ok.
Configure build for SunOS
trond@storm&amp;gt; &lt;b&gt;cd membase&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;gmake install&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
Due to &lt;a href="http://jira.membase.org/browse/MB-2926"&gt;MB-2926&lt;/a&gt; we need to change the file layout to move the files so that management process in membase can locate the tools. In theory we shouldn't need to create links for the libraries as well (because the runtime path of the binary should pick up the correct one), but unfortunately ns_server use the absolute path for the library.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@storm&amp;gt; &lt;b&gt;cd /opt/membase/bin/dev&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;rm memcached vbucketmigrator moxi&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;mkdir memcached vbucketmigrator moxi bucket_engine ep_engine&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;cd memcached&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;ln -s ../amd64/memcached&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;ln -s ../../lib/amd64/default_engine.so&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;ln -s ../../lib/amd64/stdin_term_handler.so&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;cd ../vbucketmigrator&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;ln -s ../amd64/vbucketmigrator&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;cd ../moxi&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;ln -s ../amd64/moxi&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;cd ../bucket_engine&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;ln -s ../../lib/amd64/bucket_engine.so&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;cd ../ep_engine&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;ln -s ../../lib/amd64/ep.so&lt;/b&gt;
trond@storm&amp;gt; &lt;b&gt;chown -R membase:membase /opt/membase&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
So let's go ahead and start the service by running:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@storm&amp;gt; &lt;b&gt;svcadm enable membase&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
And navigate your browser to http://localhost:8091/ and start configuring your server. I was able to let my &lt;a href="http://hub.opensolaris.org/bin/view/Main/"&gt;OpenSolaris&lt;/a&gt; server join my &lt;a href="http://www.ubuntu.com"&gt;Ubuntu&lt;/a&gt; cluster. &lt;br /&gt;
&lt;br /&gt;
Happy holidays!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-1868650968497303831?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/1868650968497303831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/12/running-membase-on-opensolaris.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1868650968497303831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1868650968497303831'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/12/running-membase-on-opensolaris.html' title='Running Membase on OpenSolaris'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-8568138031784775892</id><published>2010-12-17T16:50:00.000+01:00</published><updated>2010-12-17T16:50:45.248+01:00</updated><title type='text'>Building libmembase</title><content type='html'>I guess I was a bit too excited yesterday when I announced libmembase, because I completely forgot to mention where to get and build it.&lt;br /&gt;
&lt;br /&gt;
You will find the code at: &lt;a href="https://github.com/trondn/libmembase"&gt;https://github.com/trondn/libmembase&lt;/a&gt;, and building it should be pretty easy once you've installed all of the required dependencies. I've tried to keep the list of dependencies as short as possible (so you might think I'm suffering from the "not invented here syndrome" since I implemented the REST support myself instead of using cURL, but the main reason for that was to keep the list short ;-)):&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;git&lt;/li&gt;
&lt;li&gt;Auto tools (automake, autoheader, autoconf, libtoolize)&lt;/li&gt;
&lt;li&gt;A C99 compiler&lt;/li&gt;
&lt;li&gt;libvbucket&lt;/li&gt;
&lt;li&gt;libevent&lt;/li&gt;
&lt;li&gt;SASL library&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
I have integrated building of libmembase in the build system I'm using to build membase making it easy for you to get the code and run configure with the correct set of options (see &lt;a href="http://trondn.blogspot.com/2010/10/building-membase-from-sources.html"&gt;this blog entry for a description&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
If you want to build it yourself all you need is:  &lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;$ git clone git://github.com/trondn/libmembase.git
$ cd libmembase
$ ./config/autorun.sh
$ ./configure --prefix=/opt/membase
$ make all install
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-8568138031784775892?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/8568138031784775892/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/12/building-libmembase.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/8568138031784775892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/8568138031784775892'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/12/building-libmembase.html' title='Building libmembase'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-667371083614503806</id><published>2010-12-16T22:12:00.000+01:00</published><updated>2010-12-16T22:12:19.331+01:00</updated><title type='text'>libmembase - a C interface to Membase</title><content type='html'>Membase is "on the wire" compatible with any memcached server if you connect to the &lt;a href="http://www.iana.org/assignments/port-numbers"&gt;standard memcached port&lt;/a&gt; (registered by myself back in 2009), so that you should should be able to access membase with any "memcapable" client. Backing this port is our membase proxy named moxi, and behind the scene it will do SASL authentication and proxy your requests to the correct membase server containing the item you want. One of the things that differs Membase from Memcached is that we store each item in a given &lt;a href="http://wiki.membase.org/display/membase/vBuckets"&gt;vbucket&lt;/a&gt; that is mapped to a server. When you grow or shrink the cluster, membase will move the vbuckets to new servers.&lt;br /&gt;
&lt;br /&gt;
There is no such thing as a free lunch, so accessing membase through moxi "costs" more than talking directly to the individual nodes yourself. We like to refer to such clients as "smart clients". As a developer on Memcached I need to test various stuff, so I went ahead and hacked together a quick prototype of such a library to ease my testing. Initially I wanted to extend libmemcached with this functionality, but that seemed to be a big (and risky) change I didn't have the guts to do at the time.&lt;br /&gt;
&lt;br /&gt;
The current state of the library is far from production quality, and with a minimal list of supported features. So why announce it now? Well I don't think I'll find the time to implement everything myself, so I'm hoping that people will join me in adding features to the library when they need something that isn't there...&lt;br /&gt;
&lt;br /&gt;
I've designed the library to be 100% callback based and integrated with libevent, making it easy for you to plug it into your application.&lt;br /&gt;
&lt;br /&gt;
So let's say you want to create a TAP stream and listen to all of the modifications that happens in your cluster. All you need to do would be:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;struct event_base *evbase = event_init();

   libmembase_t instance = libmembase_create(host, username, passwd, bucket, evbase);
   libmembase_connect(instance);

   libmembase_tap_filter_t filter;
   libmembase_callback_t callbacks = {
      .tap_mutation = tap_mutation
   };
   libmembase_set_callbacks(instance, &amp;amp;callbacks);
   libmembase_tap_cluster(instance, filter, true);
&lt;/pre&gt;&lt;br /&gt;
Then you would implement the tap callback function as:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static void tap_mutation(libmembase_t instance, const void *key, size_t nkey, const void *data, size_t nbytes, uint32_t flags, uint32_t exp, const void *es, size_t nes)
{
   // Do whatever you want with the object
}

&lt;/pre&gt;&lt;br /&gt;
And thats all you need to do to tap your entire cluster :-) Let's extend the example to tap &lt;u&gt;multiple&lt;/u&gt; buckets from the same code. &lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;struct event_base *evbase = event_init();

   libmembase_t instance1 = libmembase_create(host, username, passwd, bucket1, evbase);
   libmembase_t instance2 = libmembase_create(host, username, passwd, bucket2, evbase);
   libmembase_connect(instance1);
   libmembase_connect(instance2);

   libmembase_tap_filter_t filter;
   libmembase_callback_t callbacks = {
      .tap_mutation = tap_mutation
   };
   libmembase_set_callbacks(instance1, &amp;amp;callbacks);
   libmembase_set_callbacks(instance2, &amp;amp;callbacks);
   libmembase_tap_cluster(instance1, filter, false);
   libmembase_tap_cluster(instance2, filter, false);

   event_base_loop(evbase, 0);
&lt;/pre&gt;&lt;br /&gt;
The instance handle is passed to the callback function so you should be able to tell which bucket each mutation event belongs to.&lt;br /&gt;
&lt;br /&gt;
As I said all of the functions in the API is callback based, so if you want to retrieve an object you have to register a callback for &lt;code&gt;get&lt;/code&gt; before calling &lt;code&gt;libmembase_mget&lt;/code&gt;. Ex:&lt;br /&gt;
&lt;pre&gt;libmembase_callback_t callbacks = {
        .get = get_callback
    };
    libmembase_set_callbacks(instance, &amp;amp;callbacks);
    libmembase_mget(instance, num_keys, (const void * const *)keys, nkey);

    // If you don't want to run your own event loop, you can call the following method
    // that will run all spooled commands and wait for their replies before breaking out
    // of the event loop
    libmembase_execute(instance);
&lt;/pre&gt;&lt;br /&gt;
The signature for the get callback looks like:&lt;br /&gt;
&lt;pre&gt;void get_callback(libmembase_t instance, libmembase_error_t error, const void *key, size_t nkey, const void *bytes, size_t nbytes, uint32_t flags, uint64_t cas)
{
   // do whatever you want...
}
&lt;/pre&gt;&lt;br /&gt;
So what is missing from the library right now? &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Proper error handling. Right now I'm using asserts and abort() to handle error situations, causing your application to crash... you don't want that in production ;-)&lt;/li&gt;
&lt;li&gt;Timeouts.. Right now it will only time out on TCP timeouts../&lt;/li&gt;
&lt;li&gt;A lot of operations! I'm only supporting get/add/replace/set...&lt;/li&gt;
&lt;li&gt;Fetch replicas..&lt;/li&gt;
&lt;li&gt;Gracefully handle change in the vbucket list&lt;/li&gt;
&lt;li&gt;+++&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Do you feel like hacking on some of them?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-667371083614503806?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/667371083614503806/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/12/libmembase-c-interface-to-membase.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/667371083614503806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/667371083614503806'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/12/libmembase-c-interface-to-membase.html' title='libmembase - a C interface to Membase'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-9211968254916550585</id><published>2010-10-30T11:58:00.000+02:00</published><updated>2010-10-30T11:58:40.882+02:00</updated><title type='text'>Running Moxi on Solaris</title><content type='html'>I have been working on getting membase up'n'running on OpenSolaris as a side project. Most of it is already in place, but there are still some Makefile issues to sort out. I thought that while we're waiting to complete that task, I could show you how to easily run moxi as a service controlled by SMF.&lt;br /&gt;
&lt;br /&gt;
I've created some scripts to make it easier for you to build and install everything, so the first we need to do is to check out (or update your clone) of my tools repository:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;git clone git://github.com/trondn/tools.git&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;cd tools/membase&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
Next up we need to create some new ZFS datasets for our moxi installation. I've created a script that creates the zfs datasets and set up the mountpoints:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;./smf/moxi/setup.sh -u -z rpool&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
The &lt;code&gt;-u&lt;/code&gt; option tells the script to create authorizations, profiles, users and groups we need, and the &lt;code&gt;-z&lt;/code&gt; option tells the script to create the zfs filesystems in the zfs pool named &lt;code&gt;rpool&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Next up we need to compile (and install) the source code. The directory &lt;code&gt;/opt/membase&lt;/code&gt; is not writable for us, so let's change the ownership so I can install files there...:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;pfexec chown trond:staff /opt/membase&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;./setup.sh -d /opt/membase moxi&lt;/b&gt;
Download commit hook - Ok.
Checking out libmemcached (Bazaar) - Ok.
Checking out bucket_engine (git) - Ok.
Checking out ep-engine (git) - Ok.
Checking out libconflate (git) - Ok.
Checking out libvbucket (git) - Ok.
Checking out memcached (git) - Ok.
Checking out moxi (git) - Ok.
Checking out vbucketmigrator (git) - Ok.
Checking out membase-cli (git) - Ok.
Checking out ns_server (git) - Ok.
Checking out memcachetest (git) - Ok.
Configure build for SunOS
trond@opensolaris&amp;gt; &lt;b&gt;cd moxi/SunOS&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;make all install&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
Now we've got everything installed to &lt;code&gt;/opt/membase&lt;/code&gt;, so let's change the ownership to &lt;code&gt;membase:membase&lt;/code&gt; and install the SMF script to manage moxi:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;chown -R membase:membase /opt/membase&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;cd ../../smf/moxi&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;./setup.sh -s&lt;/b&gt;
moxi installed as /lib/svc/method/moxi
moxi.xml installed as /var/svc/manifest/application/moxi.xml
&lt;/pre&gt;&lt;br /&gt;
So let's check out the configuration options we got for our new SMF service:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;svccfg&lt;/b&gt;
svc:&amp;gt; &lt;b&gt;select moxi&lt;/b&gt;
svc:/application/database/moxi&amp;gt; &lt;b&gt;listprop&lt;/b&gt;
manifestfiles                                        framework
manifestfiles/var_svc_manifest_application_moxi_xml  astring  /var/svc/manifest/application/moxi.xml
general                                              framework
general/action_authorization                         astring  solaris.smf.manage.moxi
general/entity_stability                             astring  Unstable
general/single_instance                              boolean  true
general/value_authorization                          astring  solaris.smf.value.moxi
multi-user-server                                    dependency
multi-user-server/entities                           fmri     svc:/milestone/multi-user-server
multi-user-server/grouping                           astring  require_all
multi-user-server/restart_on                         astring  none
multi-user-server/type                               astring  service
moxi                                                 application
moxi/corepattern                                     astring  /var/opt/membase/cores/core.%f.%p
moxi/downstream_max                                  astring  8
moxi/port                                            astring  11211
moxi/threads                                         astring  4
moxi/url                                             astring  http://membase:8091/pools/default/bucketStreaming/default
moxi/version                                         astring  1.6.0
tm_common_name                                       template
tm_common_name/C                                     ustring  Membase
tm_man_moxi                                          template
tm_man_moxi/manpath                                  astring  /opt/membase/share/man
tm_man_moxi/section                                  astring  1
tm_man_moxi/title                                    astring  moxi
&lt;/pre&gt;&lt;br /&gt;
You will most likely want to set the URL parameter to point to the bucket you want to use..&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;svc:/application/database/moxi&amp;gt; &lt;b&gt;setprop moxi/url=http://myserver:8091/pools/default/bucketStreaming/default&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
Let's refresh the configuration and start the service:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;svccfg refresh moxi&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;svcadm enable moxi&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;svcs moxi&lt;/b&gt;
STATE          STIME    FMRI
online          9:45:41 svc:/application/database/moxi:moxi
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-9211968254916550585?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/9211968254916550585/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/10/running-moxi-on-solaris.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/9211968254916550585'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/9211968254916550585'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/10/running-moxi-on-solaris.html' title='Running Moxi on Solaris'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-1573374848294719128</id><published>2010-10-30T08:28:00.000+02:00</published><updated>2010-10-30T08:28:45.208+02:00</updated><title type='text'>Installing Python script from automake, Fixup :)</title><content type='html'>In my previous blog post I added a wrapper script to start the Python script, but it turns out that this script don't work unless you pass &lt;code&gt;--libdir=something&lt;/code&gt; to &lt;code&gt;configure&lt;/code&gt;. I didn't catch that originally because I always specify the library directory due to the fact that I'm building both 32bit and 64bit binaries on my Solaris machine.&lt;br /&gt;
&lt;br /&gt;
The following script should address the problem:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;&lt;code&gt;&lt;code&gt;&lt;code&gt;#! /bin/sh
prefix=@prefix@
exec_prefix=@exec_prefix@
root=@libdir@/python

if test -z "${PYTHONPATH}"; then
&amp;nbsp;&amp;nbsp; PYTHONPATH=$root
else
&amp;nbsp;&amp;nbsp; PYTHONPATH=$root:${PYTHONPATH}
fi
export PYTHONPATH
exec $root/`basename $0`.py "$@"
&lt;/code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-1573374848294719128?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/1573374848294719128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/10/installing-python-script-from-automake.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1573374848294719128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1573374848294719128'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/10/installing-python-script-from-automake.html' title='Installing Python script from automake, Fixup :)'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-3592738042122599719</id><published>2010-10-28T15:35:00.000+02:00</published><updated>2010-10-28T15:35:14.940+02:00</updated><title type='text'>Installing Python scripts from automake...</title><content type='html'>I've been working on making it easier for developers to compile and install membase, and today I learned some more automake magic. I'm one of those developers who don't want to spend a lot of time working on the build system, I want to spend my time working on the code. At the same time I don't want to do unnecessary boring manual work that the build system should do for me.&lt;br /&gt;
&lt;br /&gt;
Parts of membase is implemented in Python, and I've been trying to figure out how to install those pieces. I don't like to mess up the &lt;code&gt;/bin&lt;/code&gt; directory with "library" files, so I needed a way to package the Python bits better. I've been using a wrapper script that sets the &lt;code&gt;PYTHONPATH&lt;/code&gt; variable earlier, but I've never tried to integrate that into an automake generated makefile.&lt;br /&gt;
&lt;br /&gt;
As always I started out asking google for help, but I didn't end up with a good and easy example so I ended up reading through the automake manual. It turns out that it's fairly easy to do exactly what I want, so I decided to share the knowledge in a blog post :-)&lt;br /&gt;
&lt;br /&gt;
We don't want to hardcode the path to our binary anywhere, so the first thing we need to do is to update configure.ac to also generate our wrapper script:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;AC_CONFIG_FILES(Makefile python_wrapper)
&lt;/pre&gt;&lt;br /&gt;
I've got multiple programs implemented with Python, and I don't want to create a ton of wrappers, so my &lt;code&gt;python_wrapper.in&lt;/code&gt; looks like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;#! /bin/sh
if test -z "${PYTHONPATH}"; then
&amp;nbsp;&amp;nbsp; PYTHONPATH=@libdir@/python
else
&amp;nbsp;&amp;nbsp; PYTHONPATH=@libdir@/python:${PYTHONPATH}
fi
export PYTHONPATH
exec @libdir@/python/`basename $0`.py "$@"
&lt;/pre&gt;&lt;br /&gt;
This means that if I install this script as &lt;code&gt;/opt/membase/bin/stats&lt;/code&gt;, it will try to execute &lt;code&gt;/opt/membase/lib/python/stats.py&lt;/code&gt; with the same arguments. So let's go ahead and add a rule to &lt;code&gt;Makefile.am&lt;/code&gt; to generate the scripts with the correct names:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;PYTHON_TOOLS=stats
${PYTHON_TOOLS}: python_wrapper
&amp;nbsp;&amp;nbsp; &amp;nbsp;cp $&amp;lt; $@

BUILT_SOURCES += ${PYTHON_TOOLS}
CLEANFILES+= ${PYTHON_TOOLS}
bin_SCRIPTS+= ${PYTHON_TOOLS}
&lt;/pre&gt;&lt;br /&gt;
&amp;nbsp;Now we've got the wrapper script in place, and we've generated all of the scripts to start our programs. The next thing up would be to create the destination directory for the python bits, and install all of them there. To do so we need to create a variable that ends with "&lt;code&gt;dir&lt;/code&gt;" to contain the name of the directory. Let's name our "&lt;code&gt;pythonlibdir&lt;/code&gt;" and put it in a subdirectory named python of the specified &lt;code&gt;libdir&lt;/code&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;pythonlibdir=$(libdir)/python
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
Finally we need to list all of the files we want to go there:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;pythonlib_DATA= \
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mc_bin_client.py \
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mc_bin_server.py \
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; memcacheConstants.py
&amp;nbsp; 
pythonlib_SCRIPTS= \
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; stats.py \
&lt;/pre&gt;&lt;br /&gt;
The reason I use &lt;code&gt;pythonlib_SCRIPTS&lt;/code&gt; for the last one is because I want the execute bit set on file.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-3592738042122599719?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/3592738042122599719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/10/installing-python-scripts-from-automake.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/3592738042122599719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/3592738042122599719'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/10/installing-python-scripts-from-automake.html' title='Installing Python scripts from automake...'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-4042083965622588737</id><published>2010-10-17T14:18:00.000+02:00</published><updated>2010-10-17T14:18:39.227+02:00</updated><title type='text'>Writing your own storage engine for Memcached, part 3</title><content type='html'>Right now we've got an engine capable of running get and set load, but it is doing synchrounus filesystem IO. We can't serve our client faster than we can read the item from disk, but we might serve &lt;b&gt;other&lt;/b&gt; connections while we're reading the item off disk.&lt;br /&gt;
&lt;br /&gt;
In this entry we're going to fix our &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;store&lt;/code&gt; method so that they don't block the engine API. As I've said earlier, the intention of this tutorial is to focus on the &lt;em&gt;engine API&lt;/em&gt;. That means I'm not going to try to make an effective design, because that could distract the focus from what I'm trying to explain. If people are interested in how we could optimize this, I could add a second part of the tutorial in the future... Just let me know.&lt;br /&gt;
&lt;br /&gt;
In order to implement asynchronous operations in our engine, we need to make use of the API the server makes available to us in &lt;code&gt;create_instance&lt;/code&gt;. Let's extend our engine&amp;nbsp;structure to keep track of the server API:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;struct fs_engine {
   ENGINE_HANDLE_V1 engine;
   &lt;b&gt;SERVER_HANDLE_V1 sapi;&lt;/b&gt;
};

...

MEMCACHED_PUBLIC_API
ENGINE_ERROR_CODE create_instance(uint64_t interface,
                                  GET_SERVER_API get_server_api,
                                  ENGINE_HANDLE **handle)
{

...

   h-&amp;gt;engine.item_set_cas = fs_item_set_cas;
   h-&amp;gt;engine.get_item_info = fs_get_item_info;
   &lt;b&gt;h-&amp;gt;sapi = *get_server_api();&lt;/b&gt;

...

      &lt;/pre&gt;&lt;br /&gt;
To implement an&amp;nbsp;asynchronous&amp;nbsp;function in the engine interface, the engine needs to dispatch the request to another thread before it return &lt;code&gt;ENGINE_EWOULDBLOCK&lt;/code&gt; from the engine function. When the backend is done processing the result, it notifies the memcached core by using the &lt;code&gt;notify_io_complete&lt;/code&gt; function in the server interface. If an error&amp;nbsp;occurred&amp;nbsp;while processing the request, the memcached core will return the error message to the client. If your engine called &lt;code&gt;notify_io_complete&lt;/code&gt; with &lt;code&gt;ENGINE_SUCCESS&lt;/code&gt;, the memcached core will call the engine interface function once more with the same argument as the&lt;br /&gt;
first time.&lt;br /&gt;
&lt;br /&gt;
If you look at the server api, you'll see that the it got an interface for storing an engine-specific pointer. This will make our life easier when we want to implement async io. So let's go ahead and update our &lt;code&gt;fs_get&lt;/code&gt; method:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static ENGINE_ERROR_CODE fs_get(ENGINE_HANDLE* handle,
                                const void* cookie,
                                item** item,
                                const void* key,
                                const int nkey,
                                uint16_t vbucket)
{
   struct fs_engine *engine = (struct fs_engine *)engine;
   void *res = engine-&amp;gt;sapi.cookie-&amp;gt;get_engine_specific(cookie);
   if (res != NULL) {
      *item = res;
      engine-&amp;gt;sapi.cookie-&amp;gt;store_engine_specific(cookie, NULL);
      return ENGINE_SUCCESS;

   }

...
      &lt;/pre&gt;&lt;br /&gt;
The next thing we need to do is to create a function that runs asynchronously and stores the result in the engine_specific setting for the cookie. Since we're going to use async tasks for&amp;nbsp;all of the engine methods, let's go ahead and create a function to run tasks in another thread:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static ENGINE_ERROR_CODE execute(struct task *task)
{
   pthread_attr_t attr;
   pthread_t tid;

   if (pthread_attr_init(&amp;amp;attr) != 0 ||
       pthread_attr_setdetachstate(&amp;amp;attr, PTHREAD_CREATE_DETACHED) != 0 ||
       pthread_create(&amp;amp;tid, &amp;amp;attr, task-&amp;gt;callback, task) != 0) {
      return ENGINE_FAILED;
   }

   return ENGINE_EWOULDBLOCK;
}
      &lt;/pre&gt;&lt;br /&gt;
As you can see from the code I'm going to create a new thread to execute each operation. This isn't very efficient, because creating a new thread got a substansial overhead. In your&amp;nbsp;design you would probably want a pool of threads to run your tasks.&lt;br /&gt;
&lt;br /&gt;
The newly created thread would run the specialized callback with a pointer to the task as it's only argument. So what does this task structure look like?&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;struct task {
   struct fs_engine *engine; /* Pointer to the engine */
   const void *cookie; /* The cookie requesting the operation */
   void *(*callback)(void *arg);
   union {
      struct {
         char key[PATH_MAX];
         size_t nkey;
      } get; /* Data used by the get operation */
      struct {
         item *item;
         ENGINE_STORE_OPERATION operation;
      } store; /* Data used by the store operation */
   } data;
};
      &lt;/pre&gt;&lt;pre&gt;&lt;/pre&gt;So let's finish up &lt;code&gt;fs_get&lt;/code&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static ENGINE_ERROR_CODE fs_get(ENGINE_HANDLE* handle,
                                const void* cookie,
                                item** item,
                                const void* key,
                                const int nkey,
                                uint16_t vbucket)
{
   struct fs_engine *engine = (struct fs_engine *)handle;
   /* Check to see if this is the callback from an earlier ewouldblock */
   void *res = engine-&amp;gt;sapi.cookie-&amp;gt;get_engine_specific(cookie);
   if (res != NULL) {
      *item = res;
      engine-&amp;gt;sapi.cookie-&amp;gt;store_engine_specific(cookie, NULL);
      return ENGINE_SUCCESS;
   }

   /* We don't support keys longer than PATH_MAX */
   if (nkey &amp;gt;= PATH_MAX) {
      return ENGINE_FAILED;
   }

   /* Set up the callback struct */
   struct task *task = calloc(1, sizeof(*task));
   if (task == NULL) {
      return ENGINE_ENOMEM;
   }

   task-&amp;gt;engine = (struct fs_engine *)handle;
   task-&amp;gt;cookie = cookie;
   task-&amp;gt;callback = fs_get_callback;
   memcpy(task-&amp;gt;data.get.key, key, nkey);
   task-&amp;gt;data.get.nkey = nkey;

   ENGINE_ERROR_CODE ret = execute(task);
   if (ret != ENGINE_EWOULDBLOCK) {
      free(task);
   }
   return ret;
}
      &lt;/pre&gt;&lt;br /&gt;
If you look at the code above, you'll see that we specify &lt;code&gt;gs_get_callback&lt;/code&gt; as the function to execute. So let's go ahead and implement the callback:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static void *fs_get_callback(void *arg)
{
   struct task *task = arg;
   char *fname = task-&amp;gt;data.get.key;
   task-&amp;gt;data.get.key[task-&amp;gt;data.get.nkey] = '\0';

   struct stat st;
   if (stat(fname, &amp;amp;st) == -1) {
      task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;notify_io_complete(task-&amp;gt;cookie,
                                                    ENGINE_KEY_ENOENT);
      free(task);
      return NULL;
   }

   struct fs_item* it = NULL;
   ENGINE_ERROR_CODE ret = fs_allocate((ENGINE_HANDLE*)task-&amp;gt;engine,
                                       task-&amp;gt;cookie, (void**)&amp;amp;it,
                                       task-&amp;gt;data.get.key,
                                       task-&amp;gt;data.get.nkey,
                                       st.st_size, 0, 0);
   if (ret != ENGINE_SUCCESS) {
      task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;notify_io_complete(task-&amp;gt;cookie,
                                                    ENGINE_ENOMEM);
      free(task);
      return NULL;
   }

   FILE *fp = fopen(fname, "r");
   if (fp == NULL) {
      fs_release((ENGINE_HANDLE*)task-&amp;gt;engine, task-&amp;gt;cookie, it);
      task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;notify_io_complete(task-&amp;gt;cookie,
                                                    ENGINE_FAILED);
      free(task);
      return NULL;
   }

   size_t nr = fread(it-&amp;gt;data, 1, it-&amp;gt;ndata, fp);
   fclose(fp);
   if (nr != it-&amp;gt;ndata) {
      fs_release((ENGINE_HANDLE*)task-&amp;gt;engine, task-&amp;gt;cookie, it);
      task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;notify_io_complete(task-&amp;gt;cookie,
                                                    ENGINE_FAILED);
      free(task);
      return NULL;
   }

   task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;store_engine_specific(task-&amp;gt;cookie, it);
   task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;notify_io_complete(task-&amp;gt;cookie,
                                                 ENGINE_SUCCESS);
   return NULL;
}
      &lt;/pre&gt;&lt;br /&gt;
As you see it's quite easy to add asynchronous support for the engine functions. Let's go ahead and do the same for &lt;code&gt;fs_store&lt;/code&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static ENGINE_ERROR_CODE fs_store(ENGINE_HANDLE* handle,
                                  const void *cookie,
                                  item* item,
                                  uint64_t *cas,
                                  ENGINE_STORE_OPERATION operation,
                                  uint16_t vbucket)
{
   struct fs_engine *engine = (struct fs_engine *)handle;
   /* Check to see if this is the callback from an earlier ewouldblock */
   void *res = engine-&amp;gt;sapi.cookie-&amp;gt;get_engine_specific(cookie);
   if (res != NULL) {
      *cas = 0;
      engine-&amp;gt;sapi.cookie-&amp;gt;store_engine_specific(cookie, NULL);
      return ENGINE_SUCCESS;
   }


   /* Set up the callback struct */
   struct task *task = calloc(1, sizeof(*task));
   if (task == NULL) {
      return ENGINE_ENOMEM;
   }

   task-&amp;gt;engine = (struct fs_engine *)handle;
   task-&amp;gt;cookie = cookie;
   task-&amp;gt;callback = fs_store_callback;
   task-&amp;gt;data.store.item = item;
   task-&amp;gt;data.store.operation = operation;

   ENGINE_ERROR_CODE ret = execute(task);
   if (ret != ENGINE_EWOULDBLOCK) {
      free(task);
   }
   return ret;
}
&lt;/pre&gt;&lt;br /&gt;
And &lt;code&gt;fs_store_callback&lt;/code&gt; looks like the following:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static void *fs_store_callback(void *arg)
{
   struct task *task = arg;
   struct fs_item* it = task-&amp;gt;data.store.item;
   char fname[it-&amp;gt;nkey + 1];
   memcpy(fname, it-&amp;gt;key, it-&amp;gt;nkey);
   fname[it-&amp;gt;nkey] = '\0';

   FILE *fp = fopen(fname, "w");
   if (fp == NULL) {
      task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;notify_io_complete(task-&amp;gt;cookie,
                                                    ENGINE_NOT_STORED);
      free(task);
      return NULL;
   }

   size_t nw = fwrite(it-&amp;gt;data, 1, it-&amp;gt;ndata, fp);
   fclose(fp);
   if (nw != it-&amp;gt;ndata) {
      remove(fname);
      task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;notify_io_complete(task-&amp;gt;cookie,
                                                    ENGINE_NOT_STORED);
      free(task);
      return NULL;
   }

   task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;store_engine_specific(task-&amp;gt;cookie, it);
   task-&amp;gt;engine-&amp;gt;sapi.cookie-&amp;gt;notify_io_complete(task-&amp;gt;cookie,
                                                 ENGINE_SUCCESS);
   return NULL;
}
&lt;/pre&gt;&lt;br /&gt;
If you look closely at the code above you'll see that we still don't differentiate between &lt;code&gt;add&lt;/code&gt;/&lt;code&gt;set&lt;/code&gt;/&lt;code&gt;replace&lt;/code&gt;, but we'll fix that in the next session.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-4042083965622588737?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/4042083965622588737/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/10/writing-your-own-storage-engine-for_17.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/4042083965622588737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/4042083965622588737'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/10/writing-your-own-storage-engine-for_17.html' title='Writing your own storage engine for Memcached, part 3'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-5401620178203739048</id><published>2010-10-12T14:14:00.000+02:00</published><updated>2010-10-12T14:14:29.007+02:00</updated><title type='text'>Building membase from the sources...</title><content type='html'>I thought I should share some information about my personal development model for membase. &lt;br /&gt;
&lt;br /&gt;
I've set up a "sandbox" where I'm doing all of my development in with the following commands:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;pfexec zfs create -o mountpoint=/source rpool/source&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;pfexec chown trond:staff /source&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;mkdir /source/membase&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;cd /source/membase&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;git clone git://github.com/trondn/tools.git&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;cd tools/membase&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
I like to keep my changes as separated as possible, to reduce the dependencies between them. Whenever I am fixing a bug report I would do something like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;mkdir bug&lt;i&gt;nnn&lt;/i&gt;&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;cd bug&lt;i&gt;nnn&lt;/i&gt;&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;ln -s ../Makefile&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;make&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
That would build the entire Membase stack and put the files in &lt;code&gt;/tmp/membase-build&lt;/code&gt;. I would then change my working directory to the module where I'm going to fix a bug and (hopefully) fix it.&lt;br /&gt;
&lt;br /&gt;
After fixing the bug (and writing a test case!) I would commit the change and push it for review with the following commands:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;git add -p&lt;/b&gt;   (and select the changes to include)
trond@opensolaris&amp;gt; &lt;b&gt;git commit -m "bug&lt;i&gt;nnn&lt;/i&gt;: blah blah blah"&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;git for-review&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
The last command there would push the change to our review system, so that Dustin or someone else can read through the diffs and accept the patch if they like it.&lt;br /&gt;
&lt;br /&gt;
If you look at the workflow above it looks pretty easy, there is however one little thing that is really annoying... That is that Membase is a cross platform project, so I need to ensure that the code compiles and works on all of our platforms. With the method above I would have to log into another system and set everything up and copy my change over to see that it works. For simple changes that only touch one module I could always use buildbot or Hudson to test it on all platforms, but that doesn't work if I do an interface change that affects all of our modules.&lt;br /&gt;
&lt;br /&gt;
I'm kind of lazy so I don't want to do such boring work all of the time, so instead I wrote a script to set up the sources and create makefiles so that I can easily build the same source tree on all of my platforms. &lt;br /&gt;
&lt;br /&gt;
In order for it to work you need to set up sharing on your filesystem:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;pfexec zfs set sharenfs=on rpool/source&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;pfexec zfs set sharesmb=name=source rpool/source&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
To set up a tree for lets say bug 9999 I would run something like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;./setup.sh bug_9999&lt;/b&gt;
Download commit hook - Ok.
Checking out libmemcached (Bazaar) - Ok.
  Generate configure script - Ok.
Checking out bucket_engine (git) - Ok.
Checking out ep-engine (git) - Ok.
  Generate configure script - Ok.
Checking out libconflate (git) - Ok.
  Generate configure script - Ok.
Checking out libvbucket (git) - Ok.
  Generate configure script - Ok.
Checking out memcached (git) - Ok.
  Generate configure script - Ok.
Checking out moxi (git) - Ok.
  Generate configure script - Ok.
Checking out vbucketmigrator (git) - Ok.
  Generate configure script - Ok.
Checking out membase-cli (git) - Ok.
Checking out ns_server (git) - Ok.
Checking out memcachetest (git) - Ok.
  Generate configure script - Ok.
Configure build for SunOS
&lt;/pre&gt;&lt;br /&gt;
This will set up a build environemnt for Solaris that builds membase as a "multi isa" (both 32 and 64 bit) stack in &lt;code&gt;/tmp/membase&lt;/code&gt;. But let's add support for my MacOSX, Ubuntu and Debian box as well. Since all of the code is located on my opensolaris box, I need to use the &lt;code&gt;-s&lt;/code&gt; option to let it know where the source is located:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;./setup.sh -s /net/opensolaris/source/membase/tools/membase -p Ubuntu bug_9999&lt;/b&gt;
Configure build for Ubuntu
trond@opensolaris&amp;gt; &lt;b&gt;./setup.sh -s /net/opensolaris/source/membase/tools/membase -p Darwin bug_9999&lt;/b&gt;
Configure build for Darwin
trond@opensolaris&amp;gt; &lt;b&gt;./setup.sh -s /net/opensolaris/source/membase/tools/membase -p Debian bug_9999&lt;/b&gt;
Configure build for Debian
&lt;/pre&gt;&lt;br /&gt;
So let's look inside the &lt;code&gt;bug_9999&lt;/code&gt; directory:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;ls -l bug_9999&lt;/b&gt;
total 15
drwxr-xr-x  13 trond    staff         14 Oct 12 13:35 Darwin
drwxr-xr-x  13 trond    staff         14 Oct 12 13:35 Debian
drwxr-xr-x  13 trond    staff         14 Oct 12 13:33 src
drwxr-xr-x   4 trond    staff          5 Oct 12 13:33 SunOS
drwxr-xr-x  13 trond    staff         14 Oct 12 13:35 Ubuntu
&lt;/pre&gt;&lt;br /&gt;
All of the sources are located in the src directory, and all of the makefiles for the various platforms will reference that code.&lt;br /&gt;
&lt;br /&gt;
To build on all of my platforms I'm just executing:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;ssh ubuntu "cd /net/opensolaris/source/membase/tools/membase/bug_9999/Ubuntu &amp;amp;&amp;amp; make" &amp;gt; ubuntu.log 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;ssh debian "cd /net/opensolaris/source/membase/tools/membase/bug_9999/Debian &amp;amp;&amp;amp; make" &amp;gt; debian.log 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;ssh darwin "cd /net/opensolaris/source/membase/tools/membase/bug_9999/Darwin &amp;amp;&amp;amp; make" &amp;gt; darwin.log 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
but I've got that in a script of course:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;cat bug_9999/build.sh&lt;/b&gt;
#! /bin/ksh
cd SunOS &amp;amp;&amp;amp; gmake &amp;gt; sunos.log 2&amp;gt;&amp;amp;1 &amp;amp;
ssh ubuntu "cd /net/opensolaris/source/membase/tools/membase/bug_9999/Ubuntu &amp;amp;&amp;amp; make" &amp;gt; ubuntu.log 2&amp;gt;&amp;amp;1 &amp;amp;
ssh debian "cd /net/opensolaris/source/membase/tools/membase/bug_9999/Debian &amp;amp;&amp;amp; make" &amp;gt; debian.log 2&amp;gt;&amp;amp;1 &amp;amp;
ssh darwin "cd /net/opensolaris/source/membase/tools/membase/bug_9999/Darwin &amp;amp;&amp;amp; make" &amp;gt; darwin.log 2&amp;gt;&amp;amp;1 &amp;amp;
xterm -T SunOS -e tail -f sunos.log &amp;amp;
xterm -T Ubuntu -e tail -f ubuntu.log &amp;amp;
xterm -T Debian -e tail -f debian.log &amp;amp;
xterm -T MacOS -e tail -f darwin.log &amp;amp;&lt;/pre&gt;&lt;br /&gt;
Unfortunately you can't start the membase we just installed in &lt;code&gt;/tmp/membase&lt;/code&gt;, but I'm working on it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-5401620178203739048?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/5401620178203739048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/10/building-membase-from-sources.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/5401620178203739048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/5401620178203739048'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/10/building-membase-from-sources.html' title='Building membase from the sources...'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-5542750452037304223</id><published>2010-10-08T22:39:00.000+02:00</published><updated>2010-10-08T22:39:34.544+02:00</updated><title type='text'>Writing your own storage engine for Memcached, part 2</title><content type='html'>In the previous blog post I described the engine initialization and&amp;nbsp;destruction. This blog post will cover the memory allocation model&amp;nbsp;in the engine interface.&lt;br /&gt;
&lt;br /&gt;
The memcached core is responsible for allocating all of the&amp;nbsp;memory it needs for its connections (send / receive buffers&amp;nbsp;etc), and the engine is responsible for allocating (and&amp;nbsp;freeing) all of the memory it needs to keep track of the&amp;nbsp;items. The engines shouldn't have to care about the memory the&amp;nbsp;core allocates (and use), but the core will access the memory&amp;nbsp;managed by the engine.&lt;br /&gt;
&lt;br /&gt;
When the memcached core is about to store a new item it needs&amp;nbsp;to get a (as of today continous) buffer to store the data for the&amp;nbsp;item. The core will try to allocate this buffer by calling the&amp;nbsp;&lt;code&gt;allocate&lt;/code&gt; function in the API. So let's start&amp;nbsp;extending our example code by adding out own implementation&amp;nbsp;of the &lt;code&gt;allocate&lt;/code&gt; function. The first thing we need&amp;nbsp;to do is to add it to our engine descriptor we return from&amp;nbsp;&lt;code&gt;create_instance&lt;/code&gt;. We're going to add a number of&amp;nbsp;functions in todays entry, so let's just map all of them&amp;nbsp;while we're at it:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;MEMCACHED_PUBLIC_API
ENGINE_ERROR_CODE create_instance(uint64_t interface,
                                  GET_SERVER_API get_server_api,
                                  ENGINE_HANDLE **handle)
{
   [ ... cut ... ]
  /*
   * Map the API entry points to our functions that implement them.
   */
   h-&amp;gt;engine.initialize = fs_initialize;
   h-&amp;gt;engine.destroy = fs_destroy;
   &lt;b&gt;h-&amp;gt;engine.get_info = fs_get_info;
   h-&amp;gt;engine.allocate = fs_allocate;
   h-&amp;gt;engine.remove = fs_item_delete;
   h-&amp;gt;engine.release = fs_item_release;
   h-&amp;gt;engine.get = fs_get;
   h-&amp;gt;engine.get_stats = fs_get_stats;
   h-&amp;gt;engine.reset_stats = fs_reset_stats;
   h-&amp;gt;engine.store = fs_store;
   h-&amp;gt;engine.flush = fs_flush;
   h-&amp;gt;engine.unknown_command = fs_unknown_command;
   h-&amp;gt;engine.item_set_cas = fs_item_set_cas;
   h-&amp;gt;engine.get_item_info = fs_get_item_info;&lt;/b&gt;
      &lt;/pre&gt;The next thing we need to do is to create a data structure to&amp;nbsp;keep the information we need. The purpose of this tutorial&amp;nbsp;isn't to create a memory efficient implementation, but to&amp;nbsp;exercise&amp;nbsp;the API. So let's just create the following struct:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;struct fs_item {
   void *key;
   size_t nkey;
   void *data;
   size_t ndata;
   int flags;
   rel_time_t exptime;
};
      &lt;/pre&gt;Our implementation of &lt;code&gt;allocate&lt;/code&gt; would then look&amp;nbsp;like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static ENGINE_ERROR_CODE fs_allocate(ENGINE_HANDLE* handle,
                                     const void* cookie,
                                     item **item,
                                     const void* key,
                                     const size_t nkey,
                                     const size_t nbytes,
                                     const int flags,
                                     const rel_time_t exptime)
{
   struct fs_item *it = malloc(sizeof(struct fs_item));
   if (it == NULL) {
      return ENGINE_ENOMEM;
   }
   it-&amp;gt;flags = flags;
   it-&amp;gt;exptime = exptime;
   it-&amp;gt;nkey = nkey;
   it-&amp;gt;ndata = nbytes;
   it-&amp;gt;key = malloc(nkey);
   it-&amp;gt;data = malloc(nbytes);
   if (it-&amp;gt;key == NULL || it-&amp;gt;data == NULL) {
      free(it-&amp;gt;key);
      free(it-&amp;gt;data);
      free(it);
      return ENGINE_ENOMEM;
   }
   memcpy(it-&amp;gt;key, key, nkey);
   *item = it;
   return ENGINE_SUCCESS;
}
      &lt;/pre&gt;If you look in the implementation above you'll see that we didn't&amp;nbsp;return the pointer to the actual memory for the data storage to&amp;nbsp;the memcached core. To get that address memcached will call&amp;nbsp;&lt;code&gt;get_item_info&lt;/code&gt; in the API. So let's implememnt that:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static bool fs_get_item_info(ENGINE_HANDLE *handle, const void *cookie,
                             const item* item, item_info *item_info)
{
   struct fs_item* it = (struct fs_item*)item;
   if (item_info-&amp;gt;nvalue &amp;lt; 1) {
      return false;
   }

   item_info-&amp;gt;cas = 0; /* Not supported */
   item_info-&amp;gt;clsid = 0; /* Not supported */
   item_info-&amp;gt;exptime = it-&amp;gt;exptime;
   item_info-&amp;gt;flags = it-&amp;gt;flags;
   item_info-&amp;gt;key = it-&amp;gt;key;
   item_info-&amp;gt;nkey = it-&amp;gt;nkey;
   item_info-&amp;gt;nbytes = it-&amp;gt;ndata; /* Total length of the items data */
   item_info-&amp;gt;nvalue = 1; /* Number of fragments used */
   item_info-&amp;gt;value[0].iov_base = it-&amp;gt;data; /* pointer to fragment 1 */
   item_info-&amp;gt;value[0].iov_len = it-&amp;gt;ndata; /* Length of fragment 1 */

   return true;
}
      &lt;/pre&gt;The &lt;code&gt;get_item_info&lt;/code&gt; function is important and&amp;nbsp;deserve more information. If you look in the engine API the&amp;nbsp;"item" is defined as a void pointer, and we defined our own&amp;nbsp;item-structure to keep track of the information we need on a&amp;nbsp;per item basis. The memcached core will however need to know&lt;br /&gt;
where to read / write the memory for the key and the data&amp;nbsp;going to / coming from a clinet. To do so in will invoke&amp;nbsp;&lt;code&gt;get_item_info&lt;/code&gt;. If you look closely at our&amp;nbsp;implementation of &lt;code&gt;fs_get_item_info&lt;/code&gt; you will see&amp;nbsp;that the first thing I'm doing is to check that&amp;nbsp;&lt;code&gt;item_info-&amp;gt;nvalue&lt;/code&gt; contains at least 1&lt;br /&gt;
element. Right now it will &lt;b&gt;always&lt;/b&gt; be one, but the&amp;nbsp;intention is that we're going to support scattered IO.&lt;br /&gt;
&lt;br /&gt;
When the core is done moving the data it received over the wire&amp;nbsp;into the item, it will try to store the item in our engine by&amp;nbsp;calling &lt;code&gt;store&lt;/code&gt;. So let's go ahead and create a simple&amp;nbsp;implementation (we'll extend it later on in the tutorial):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static ENGINE_ERROR_CODE fs_store(ENGINE_HANDLE* handle,
                                  const void *cookie,
                                  item* item,
                                  uint64_t *cas,
                                  ENGINE_STORE_OPERATION operation,
                                  uint16_t vbucket)
{
   struct fs_item* it = item;
   char fname[it-&amp;gt;nkey + 1];
   memcpy(fname, it-&amp;gt;key, it-&amp;gt;nkey);
   fname[it-&amp;gt;nkey] = '\0';
   FILE *fp = fopen(fname, "w");
   if (fp == NULL) {
      return ENGINE_NOT_STORED;
   }
   size_t nw = fwrite(it-&amp;gt;data, 1, it-&amp;gt;ndata, fp);
   fclose(fp);
   if (nw != it-&amp;gt;ndata) {
      remove(fname);
      return ENGINE_NOT_STORED;
   }

   *cas = 0;
   return ENGINE_SUCCESS;
}
      &lt;/pre&gt;If you look at the implementation above you will see that it&amp;nbsp;doesn't implement the correct semantics&amp;nbsp;for &lt;em&gt;add&lt;/em&gt;/&lt;em&gt;replace&lt;/em&gt;/&lt;em&gt;set&lt;/em&gt; etc, and it will&amp;nbsp;block memcached while we're doing file IO. Don't care about that&amp;nbsp;right now, because we'll get back to that.&lt;br /&gt;
&lt;br /&gt;
When the core is done using the item it allocated, it will release&amp;nbsp;the item by calling the &lt;code&gt;release&lt;/code&gt; function in the API.&amp;nbsp;The engine may reuse the items storage for something else at this&amp;nbsp;time. So let's hook up our &lt;code&gt;release&lt;/code&gt; implementation:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static void fs_item_release(ENGINE_HANDLE* handle,
                            const void *cookie,
                            item* item)
{
   struct fs_item *it = item;
   free(it-&amp;gt;key);
   free(it-&amp;gt;data);
   free(it);
}
      &lt;/pre&gt;Now we've created all of the code to successfully store items&amp;nbsp;in our engine, but we can't read any of them back. So let's&amp;nbsp;implement &lt;code&gt;get&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static ENGINE_ERROR_CODE fs_get(ENGINE_HANDLE* handle,
                                const void* cookie,
                                item** item,
                                const void* key,
                                const int nkey,
                                uint16_t vbucket)
{

   char fname[nkey + 1];
   memcpy(fname, key, nkey);
   fname[nkey] = '\0';

   struct stat st;
   if (stat(fname, &amp;amp;st) == -1) {
      return ENGINE_NOT_STORED;
   }

   struct fs_item* it = NULL;
   ENGINE_ERROR_CODE ret = fs_allocate(handle, cookie, (void**)&amp;amp;it, key, nkey,
                                       st.st_size, 0, 0);
   if (ret != ENGINE_SUCCESS) {
      return ENGINE_ENOMEM;
   }

   FILE *fp = fopen(fname, "r");
   if (fp == NULL) {
      fs_release(handle, cookie, it);
      return ENGINE_FAILED;
   }

   size_t nr = fread(it-&amp;gt;data, 1, it-&amp;gt;ndata, fp);
   fclose(fp);
   if (nr != it-&amp;gt;ndata) {
      fs_release(handle, cookie, it);
      return ENGINE_FAILED;
   }

   *item = it;
   return ENGINE_SUCCESS;
}
      &lt;/pre&gt;Let's add a dummy implementation for the rest of the API and&amp;nbsp;try to load and test the engine:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;static const engine_info* fs_get_info(ENGINE_HANDLE* handle)
{
   static engine_info info = {
      .description = "Filesystem engine v0.1",
      .num_features = 0
   };

   return &amp;amp;info;
}

static ENGINE_ERROR_CODE fs_item_delete(ENGINE_HANDLE* handle,
                                        const void* cookie,
                                        const void* key,
                                        const size_t nkey,
                                        uint64_t cas,
                                        uint16_t vbucket)
{
   return ENGINE_KEY_ENOENT;
}

static ENGINE_ERROR_CODE fs_get_stats(ENGINE_HANDLE* handle,
                                      const void* cookie,
                                      const char* stat_key,
                                      int nkey,
                                      ADD_STAT add_stat)
{
   return ENGINE_SUCCESS;
}

static ENGINE_ERROR_CODE fs_flush(ENGINE_HANDLE* handle,
                                  const void* cookie, time_t when)
{

   return ENGINE_SUCCESS;
}

static void fs_reset_stats(ENGINE_HANDLE* handle, const void *cookie)
{

}

static ENGINE_ERROR_CODE fs_unknown_command(ENGINE_HANDLE* handle,
                                            const void* cookie,
                                            protocol_binary_request_header *request,
                                            ADD_RESPONSE response)
{
   return ENGINE_ENOTSUP;
}

static void fs_item_set_cas(ENGINE_HANDLE *handle, const void *cookie,
                            item* item, uint64_t val)
{
}

      &lt;/pre&gt;So let's go ahead and try our engine:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;/opt/memcached/bin/memcached -E .libs/fs_engine.so&lt;/b&gt;
      &lt;/pre&gt;From another terminal I'm typing in:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;telnet localhost 11211&lt;/b&gt;
Trying ::1...
Connected to opensolaris.
Escape character is '^]'.
&lt;b&gt;add test 0 0 4
test&lt;/b&gt;
STORED
&lt;b&gt;get test&lt;/b&gt;
VALUE test 0 4
test
END
&lt;b&gt;quit&lt;/b&gt;
Connection to storm closed by foreign host.
      &lt;/pre&gt;Terminate memcached by pressing &lt;code&gt;ctrl-c&lt;/code&gt;, and look in the current directory:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;ls -l test&lt;/b&gt;
-rw-r--r--   1 trond    users          6 Oct  8 12:56 test
trond@opensolaris&amp;gt; &lt;b&gt;cat test&lt;/b&gt;
test
      &lt;/pre&gt;That's all for this time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-5542750452037304223?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/5542750452037304223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/10/writing-your-own-storage-engine-for_08.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/5542750452037304223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/5542750452037304223'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/10/writing-your-own-storage-engine-for_08.html' title='Writing your own storage engine for Memcached, part 2'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-1359920490865517857</id><published>2010-10-04T09:46:00.000+02:00</published><updated>2010-10-04T09:46:24.074+02:00</updated><title type='text'>Writing your own storage engine for Memcached</title><content type='html'>&lt;p&gt;         I am working full time on &lt;a href="http://membase.org/"&gt;membase&lt;/a&gt;, which utilize the "&lt;a href="http://github.com/trondn/memcached/blob/engine/include/memcached/engine.h#L212"&gt;engine interface&lt;/a&gt;"          we're adding to &lt;a href="http://www.memcached.org/"&gt;Memcached&lt;/a&gt;. Being the one who designed the API and wrote the documentation, I can say that we do need more (and better) documentation without insulting anyone. This blog entry will be the first entry in mini-tutorial on how to write your own storage engine. I will try to cover all aspects of the engine interface while we're building an engine that stores all of the keys on files on the server.      &lt;/p&gt;&lt;p&gt;         This entry will cover the basic steps of setting up your development environment and cover the lifecycle of the engine.      &lt;/p&gt;&lt;h2&gt;Set up the development environment&lt;/h2&gt;&lt;p&gt;         The easiest way to get "up'n'running" is to install my development branch of the engine interface. Just execute the following commands:      &lt;/p&gt;&lt;pre&gt;$ &lt;b&gt;git clone git://github.com/trondn/memcached.git&lt;/b&gt;
$ &lt;b&gt;cd memcached&lt;/b&gt;
$ &lt;b&gt;git -b engine origin/engine&lt;/b&gt;
$ &lt;b&gt;./config/autorun.sh&lt;/b&gt;
$ &lt;b&gt;./configure --prefix=/opt/memcached&lt;/b&gt;
$ &lt;b&gt;make all install&lt;/b&gt;
     &lt;/pre&gt;&lt;p&gt;         Lets verify that the server works by executing the following commands:      &lt;/p&gt;&lt;pre&gt;$ &lt;b&gt;/opt/memcached/bin/memcached -E default_engine.so &amp;amp;&lt;/b&gt;
$ &lt;b&gt;echo version | nc localhost 11211&lt;/b&gt;
VERSION 1.3.3_433_g82fb476     &amp;lg;-- you may get another output string....
$ &lt;b&gt;fg&lt;/b&gt;
$ &lt;b&gt;ctrl-C&lt;/b&gt;
     &lt;/pre&gt;&lt;h2&gt;Creating the filesystem engine&lt;/h2&gt;&lt;p&gt;         You might want to use autoconf to build your engine, but setting up autoconf is way beyond the scope of this tutorial. Let's just use the following &lt;code&gt;Makefile&lt;/code&gt; instead.      &lt;/p&gt;&lt;pre&gt;ROOT=/opt/memcached
INCLUDE=-I${ROOT}/include

#CC = gcc
#CFLAGS=-std=gnu99 -g -DNDEBUG -fno-strict-aliasing -Wall \
# -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \
# -Wredundant-decls \
# ${INCLUDE} -DHAVE_CONFIG_H
#LDFLAGS=-shared

CC=cc
CFLAGS=-I${ROOT}/include -m64 -xldscope=hidden -mt -g \
      -errfmt=error -errwarn -errshort=tags  -KPIC
LDFLAGS=-G -z defs -m64 -mt

all: .libs/fs_engine.so

install: all
 ${CP} .libs/fs_engine.so ${ROOT}/lib

SRC = fs_engine.c
OBJS = ${SRC:%.c=.libs/%.o}

.libs/fs_engine.so: .libs $(OBJS)
 ${LINK.c} -o $@ ${OBJS}

.libs:; -@mkdir $@

.libs/%.o: %.c
 ${COMPILE.c} $&lt; -o $@   clean:  $(RM) .libs/fs_engine.so $(OBJS)       &lt;/pre&gt;
&lt;p&gt;         I am doing most of my development on Solaris using the Sun Studio compilers, but I have added a section with settings for gcc there if you're using gcc. Just comment out lines for &lt;code&gt;CC&lt;/code&gt;,
   &lt;code&gt;CFLAGS&lt;/code&gt; and &lt;code&gt;LDFLAGS&lt;/code&gt; and remove the &lt;code&gt;#&lt;/code&gt; for the gcc alternatives.
     &lt;/p&gt;&lt;p&gt;         In order for memcached to utilize your storage engine it needs to first load your module, and then create an instance the engine. You use the &lt;code&gt;-E&lt;/code&gt; option to memcached to specify the name
         of the module memcached should load. With the module loaded memcached will look for a symbol named &lt;code&gt;create_instance&lt;/code&gt; in the module to create an handle memcached can use to communicate with the engine. This is the first function we need to create, and it should
         have the following signature:
     &lt;/p&gt;&lt;pre&gt;MEMCACHED_PUBLIC_API
ENGINE_ERROR_CODE create_instance(uint64_t interface, GET_SERVER_API get_server_api, ENGINE_HANDLE **handle);
     &lt;/pre&gt;&lt;p&gt;         The purpose of this function is to provide the server a handle to our module, but we should &lt;b&gt;not&lt;/b&gt; perform any kind of initialization of our engine yet. The reason for that is because the memcached server may not support the version of the API we provide. The intention is
         that the server should notify the engine with the "highest" interface version it supports through &lt;code&gt;interface&lt;/code&gt;, and the engine &lt;b&gt;must&lt;/b&gt; return a descriptor to one of those interfaces through
         the &lt;code&gt;handle&lt;/code&gt;. If the engine &lt;b&gt;don't&lt;/b&gt; support any of those interfaces it should return &lt;code&gt;ENGINE_ENOTSUP&lt;/code&gt;.
     &lt;/p&gt;&lt;p&gt;         So let's go ahead and define a engine descriptor for our example engine and create an implementation for &lt;code&gt;create_instance&lt;/code&gt;:
     &lt;/p&gt;&lt;pre&gt;struct fs_engine {
  ENGINE_HANDLE_V1 engine;
  /* We're going to extend this structure later on */
};

MEMCACHED_PUBLIC_API
ENGINE_ERROR_CODE create_instance(uint64_t interface,
                                 GET_SERVER_API get_server_api,
                                 ENGINE_HANDLE **handle) {
  /*
   * Verify that the interface from the server is one we support. Right now
   * there is only one interface, so we would accept all of them (and it would
   * be up to the server to refuse us... I'm adding the test here so you
   * get the picture..
   */
  if (interface == 0) {
     return ENGINE_ENOTSUP;
  }

  /*
   * Allocate memory for the engine descriptor. I'm no big fan of using
   * global variables, because that might create problems later on if
   * we later on decide to create multiple instances of the same engine.
   * Better to be on the safe side from day one...
   */
  struct fs_engine *h = calloc(1, sizeof(*h));
  if (h == NULL) {
     return ENGINE_ENOMEM;
  }

  /*
   * We're going to implement the first version of the engine API, so
   * we need to inform the memcached core what kind of structure it should
   * expect
   */
  h-&gt;engine.interface.interface = 1;

  /*
   * Map the API entry points to our functions that implement them.
   */
  h-&gt;engine.initialize = fs_initialize;
  h-&gt;engine.destroy = fs_destroy;

  /* Pass the handle back to the core */
  *handle = (ENGINE_HANDLE*)h;

  return ENGINE_SUCCESS;
}
     &lt;/pre&gt;&lt;p&gt; If the interface we provide in &lt;code&gt;create_instance&lt;/code&gt; is dropped from the supported interfaces in memcached, the core will call &lt;code&gt;destroy()&lt;/code&gt; immediately.  The memcached core guarantees that it will never use &lt;b&gt;any&lt;/b&gt; pointers returned from the engine when &lt;code&gt;destroy()&lt;/code&gt; is called.
     &lt;/p&gt;&lt;p&gt; So let's go ahead and implement our &lt;code&gt;destroy()&lt;/code&gt; function. If you look at our implementation of
 &lt;code&gt;create_instance&lt;/code&gt; you will see that we mapped &lt;code&gt;destroy()&lt;/code&gt; to a function named &lt;code&gt;fs_destroy()&lt;/code&gt;:
     &lt;/p&gt;&lt;pre&gt;static void fs_destroy(ENGINE_HANDLE* handle) {
  /* Release the memory allocated for the engine descriptor */
  free(handle);
}
     &lt;/pre&gt;&lt;p&gt; If the core implements the interface we specify, the core will call a the &lt;code&gt;initialize()&lt;/code&gt; method. This is the time where you should do all sort of initialization in your engine (like connecting to a database, initializing mutexes etc). The &lt;code&gt;initialize&lt;/code&gt; function is called only once per instance
 returned from &lt;code&gt;create_instance&lt;/code&gt; (even if the memcached core use multiple threads). The core will &lt;b&gt;not&lt;/b&gt; call any other functions in the api before the initialization method returns.
     &lt;/p&gt;&lt;p&gt; We don't need any kind of initialization at this moment, so we can use the following initialization code:
     &lt;/p&gt;&lt;pre&gt;static ENGINE_ERROR_CODE fs_initialize(ENGINE_HANDLE* handle,
                                      const char* config_str) {
  return ENGINE_SUCCESS;
}
     &lt;/pre&gt;&lt;p&gt; If the engine returns anything else than &lt;code&gt;ENGINE_SUCCESS&lt;/code&gt;, the memcached core will
 refuse to use the engine and call &lt;code&gt;destroy()&lt;/code&gt;
     &lt;/p&gt;&lt;p&gt; In the next blog entry we will start adding functionality so that we can load our engine and handle commands from the client.
     &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-1359920490865517857?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/1359920490865517857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/10/writing-your-own-storage-engine-for.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1359920490865517857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1359920490865517857'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/10/writing-your-own-storage-engine-for.html' title='Writing your own storage engine for Memcached'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-9077632035358595181</id><published>2010-09-07T12:58:00.009+02:00</published><updated>2010-09-09T14:30:18.839+02:00</updated><title type='text'>Birkebeinerrittet</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_C3CKxYEfops/TIYdB2mS69I/AAAAAAAAADM/Z7r_0E8i37g/s1600/birken3.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 212px; height: 320px;" src="http://2.bp.blogspot.com/_C3CKxYEfops/TIYdB2mS69I/AAAAAAAAADM/Z7r_0E8i37g/s320/birken3.png" alt="" id="BLOGGER_PHOTO_ID_5514126711434963922" border="0" /&gt;&lt;/a&gt;I got inspired to attend the worlds largest cross country bicycle race named &lt;a href="http://www.birkebeiner.no/English/Birkebeinerrittet/"&gt;Birkebeinerrittet&lt;/a&gt; when I saw a TV show from the race a couple of years back. Everyone that knows me knows that I'm probably as far as you can get from a top athlete (I'm a hacker ;-), so I borrowed a bike from a good friend of mine earlier this summer and started to prepare for the 96km bike ride over the mountain from Rena to Lillehammer.
&lt;p&gt;
I had a nice trip over the mountain, but unfortunately it rained most of the time up there so I didn't get to see much of the nature. On the bright side I was one of the lucky ones that chose to attend the race on Friday instead of the main event on Saturday since it kept on raining that night and the next day causing the track to be even more muddy (and the temperature kept on dropping).
&lt;p&gt;
Because this was the first time I attended the race I didn't know what to expect, so I was a bit afraid if I would run out of energy. After the race I felt I had more to give, so I am really motivated for attending next year as well. There is a separate quota for foreigners who want to attend the race, so please join me next year!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-9077632035358595181?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/9077632035358595181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/09/birkebeinerrittet.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/9077632035358595181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/9077632035358595181'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/09/birkebeinerrittet.html' title='Birkebeinerrittet'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_C3CKxYEfops/TIYdB2mS69I/AAAAAAAAADM/Z7r_0E8i37g/s72-c/birken3.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-7714757824465513252</id><published>2010-07-28T19:32:00.006+02:00</published><updated>2010-07-29T10:47:48.619+02:00</updated><title type='text'>libmemcached on win32</title><content type='html'>&lt;p&gt;
It's a fact that people love their development platform and want to stick with it. I'm a die hard Solaris fan, and would never dream of switching to something else. I've heard that there is a crowd out there that likes to work on other systems like Windows, MacOSX, BSD and Linux. That the developer use a platform during development doesn't necessary mean that the target product will run on the platform, but the developer is more productive on that platform.
&lt;/p&gt;
&lt;p&gt;
People can argue as much as they want, but there is a large crowd of developers using Windows. There is also a large number of systems running some version of Windows, so enabling them to use the projects I'm working on is a good thing. Earlier today I pushed a &lt;a href="https://code.launchpad.net/~trond-norbye/libmemcached/mingw32"&gt;branch &lt;/a&gt;that adds support for building libmemcached into a dll on Windows.
&lt;/p&gt;
&lt;p&gt;
90% of the source code in libmemcached is just "logic" that applies for all platforms, but there is a small part of the code that interacts tightly with the operating system. "Everything" is a file descriptor on Unix systems, but Windows got their own subsystem for sockets called WinSock. In order to avoid getting tonns of #ifdefs all over the code, I defined memcached_socket_t to represent a socket object, and used "the WinSock way" to implement the code. It is pretty easy to map the WinSock code to work on Unix systems with a couple of macros.
&lt;/p&gt;
&lt;p&gt;
Well, enough talk. If you're interested in the details you can check out the branch on Launchpad.
&lt;/p&gt;
&lt;p&gt;
The easiest way for you to test out the code is to install the &lt;a href="http://code.google.com/p/msysgit/downloads/list"&gt;fullinstall of msysgit&lt;/a&gt;. Unfortunately it doesn't come with all of the tools needed to build libmemcached (you can't generate a configure script and generate the documentation). This means that you cannot build the development branch unless you got another machine where you can generate the configure script. I am exporting parts of my ZFS filesystem via CIFS, so I generated the configure script on Solaris.
&lt;/p&gt;
&lt;p&gt;
Building libmemcached with mingw is just as easy as on your favorite platform:
&lt;/p&gt;
&lt;pre&gt;
$ &lt;b&gt;./configure --with-memcached=&lt;path to memcached.exe&gt; --without-docs&lt;/b&gt;
$ &lt;b&gt;make all install&lt;/b&gt;
&lt;/pre&gt;
&lt;p&gt;
I haven't fixed the test suite yet, so you have to wait a bit longer before you can run make test ;)
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-7714757824465513252?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/7714757824465513252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/07/libmemcached-on-win32.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/7714757824465513252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/7714757824465513252'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/07/libmemcached-on-win32.html' title='libmemcached on win32'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-5849959877789256488</id><published>2010-03-30T10:05:00.002+02:00</published><updated>2010-03-30T10:09:47.892+02:00</updated><title type='text'>Building Memcached on Windows</title><content type='html'>&lt;p&gt;
  I like to be able to compile my software on multiple platforms with
  different compilers, because it force me to write that complies to
  standards (if not you'll most likely have a spaghetti of #ifdefs all
  over your code). One of the bonuses you get from using multiple
  compilers is that you'll get "more eyes" looking at your code, and
  they may warn you on different things. Supporting multiple platforms
  will also make it easier to ensure that you don't have 'hidden bombs'
  in your code (byte order, alignment, assumptions that you can
  dereference a NIL pointer etc)...
&lt;/p&gt;
&lt;p&gt;
  Building software that runs on different flavors of Unix/Linux isn't
  that hard, but adding support for Microsoft Windows offers some
  challenges. Among the challenges are:
&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Microsoft Windows doesn't provide the headerfiles found on most Unix-like systems (unistd.h etc).&lt;/li&gt;
  &lt;li&gt;Sockets is implemented in a separate "subsystem" on windows. &lt;/li&gt;
  &lt;li&gt;Win32 provides another threads/mutex implementation than what most software written for Unix-like systems use (pthreads).&lt;/li&gt;
  &lt;li&gt;Microsoft doesn't supply a C99 compiler. One would think that it would be safe to assume that one could use a C standard that is more than 10 years old....&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
  I am really glad to announce that we just pushed a number of
  changesets to memcached to allow you to build memcached on windows
  "just as easy" as you would on your favorite Unix/Linux system. This
  means that as of today users using Microsoft Windows is no longer
  stuck with an ancient version!
&lt;/p&gt;
&lt;p&gt;
  `The first thing you need to do in order to build memcached on Windows
  is to install the "fullversion"
  of &lt;a href="http://code.google.com/p/msysgit/"&gt;msysgit&lt;/a&gt;. In
  addition to installing git, it also installs a compiler capable of
  building a 32 bit version of memcached.
&lt;/p&gt;
&lt;p&gt;
  So let's go ahead and build the dependencies we need to get our 32bit
  memcached version up and running!
&lt;/p&gt;
&lt;p&gt;
  The first thing up
  is &lt;a href="http://www.monkey.org/%7Eprovos/libevent/"&gt;libevent&lt;/a&gt;. You
  should download the latest 2.x release available (there is a bug in
  all versions up to (and including) 2.0.4, so unless there is a new one
  out there you can grab a development version I pushed
  to &lt;a href="http://www.norbye.org/libevent-2.0.4-alpha-dev.tar.gz"&gt;libevent-2.0.4-alpha-dev.tar.gz&lt;/a&gt;),
  and install it with the following commands (from within your
  msysgit-shell). Please note that I'm using /tmp because I've had
  problems using my "home directory" because of spaces in the directory
  name (or at least I guess that's the reason ;-) ):
&lt;/p&gt;
&lt;pre&gt;
$ cd /tmp
$ tar xfz libevent-2.0.4-alpha-dev.tar.gz
$ cd libevent-2.0.4-alpha-dev
$ ./configure --prefix=/usr/local
$ make all                    (this will fail with an error, but don't care about that.. it's is in the example code)
$ make install             (this will fail with an error, but don't care about that.. it's just an example)
&lt;/pre&gt;

&lt;p&gt;
  It's time to start build memcached!!! Let's check out the source
  code and build it!
&lt;p/&gt;
&lt;pre&gt;
$ git clone git://github.com/trondn/memcached.git
$ git checkout -t origin/engine
$ make -f win32/Makefile.mingw
&lt;/pre&gt;
&lt;p&gt;
You should now be able to start memcached with the following command:
&lt;/p&gt;
&lt;pre&gt;
$ ./memcached.exe -E ./libs/default_engine.so
&lt;/pre&gt;
&lt;p&gt;
  Go ahead and telnet to port 11211 and issue the "stats" command to
  verify that it works!
&lt;/p&gt;
&lt;p&gt;
  You don't need to install msysgit on the systems where you want to
  run memcached, but you do need to include pthreadGC2.dll from the
  msysgit distribution. That means that if you want to run memcached
  on another machine you need to copy the following files:
  memcached.exe .libs/default_engine.so and /mingw/bin/pthreadGC2.dll
  (Just place them in the same directory on the machine you want to
  run memcached on :-)
&lt;/p&gt;
&lt;h2&gt;But wait, the world is 64 bit by now.  Lets build a 64 bit binary instead&lt;/h2&gt;
&lt;p&gt;
  The msysgit we installed previously isn't capable of building a
  64-bit binary, so we need to install a new compiler. Download the a
  bundle
  from &lt;a href="http://sourceforge.net/projects/mingw-w64/files/"&gt;http://sourceforge.net/projects/mingw-w64/files/&lt;/a&gt;
  (I
  used &lt;a href="http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Automated%20Builds/mingw-w64-bin_i686-mingw_20100129.zip/download"&gt;mingw-w64-bin_i686-mingw_20100129.zip&lt;/a&gt;
  ). You can "install" it by running the following commands in our
  msysgit shell:
&lt;/p&gt;
&lt;pre&gt;
$ mkdir /mingw64
$ cd /mingw64
$ unzip &lt;path-to-zipfile&gt;/mingw-w64-bin_i686-minw_20100129.zip
$ export PATH=/mingw64/bin:/usr/local/bin:$PATH
&lt;/pre&gt;
&lt;p&gt;So let's start compiling libevent:&lt;/p&gt;
&lt;pre&gt;
$ cd /tmp
$ tar xfz libevent-2.0.4-alpha-dev.tar.gz
$ cd libevent-2.0.4-alpha-dev
$ ./configure --prefix=/usr/local --host=x86_64-w64-mingw32 --build=i686-pc-mingw32
$ make all
$ make install
&lt;/pre&gt;
&lt;p&gt;
  The compiler we just downloaded didn't come with a 64-bit version of
  pthreads, so we have to download and build that ourself. I've pushed
  a package to my machine
  at: &lt;a href="http://www.norbye.org/pthreads-w64-2-8-0-release.tar.gz"&gt;pthreads-w64-2-8-0-release.tar.gz&lt;/a&gt;
&lt;/p&gt;
&lt;pre&gt;
$ cd /tmp
$ tar xfz pthreads-w64-2-8-0-release.tar.gz
$ cd pthreads-w64-2-8-0-release
$ make clean GC CC=x86_64-w64-mingw32-gcc
$ cp pthread.h semaphore.h sched.h /usr/local/include
$ cp libpthreadGC2.a /usr/local/lib/libpthread.a
$ cp pthreadGC2.dll /usr/local/lib
$ cp pthreadGC2.dll /usr/local/bin
&lt;/pre&gt;

&lt;p&gt;And finally memcached with:&lt;/p&gt;
&lt;pre&gt;
$ git clone git://github.com/trondn/memcached.git
$ git checkout -t origin/engine
$ make -f win32/Makefile.mingw CC=x86_64-w64-mingw32-gcc
&lt;/pre&gt;

&lt;h2&gt;So how does it perform?&lt;/h2&gt;
&lt;p&gt;
  I'm pretty sure a lot of the "Linux fanboys" are ready to jump in and
  tell how much faster the Linux version is. I have to admit that I was
  a bit skeptical in the beginning if the layers we added to get "Unix
  compatibility" had any performance impact. The only way to know for
  sure is to run a benchmark to measure the performance.
&lt;/p&gt;
&lt;p&gt;
  I ran a small benchmark where I had my client create 512 connections
  to the server, and then randomly choosing a connection and perform a
  get or a set operation on one of the 100 000 items I had stored in
  the server. I ran 500 000 operation (33% of the operations are set
  operations), and calculated the average time. I can dual-boot one of
  my machines into Windows 7 and Red Hat Enterprise Linux, so my
  results shows the "out of the box"-numbers between Windows 7 and Red
  Hat Enterprise Linux running on the same hardware. I used my
  OpenSolaris box to drive the test (connected to the same
  switch). Posting numbers is always "dangerous", because people put
  too much into the absolute numbers. The intention with my graphs is
  to show that the version running on Microsoft Windows is pretty much
  "on par" with the Linux version.
&lt;/p&gt;
&lt;p&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 261px;" src="http://2.bp.blogspot.com/_C3CKxYEfops/S7GiXlfkw8I/AAAAAAAAACo/Uz1HQ3guJ58/s320/256.png" border="0" alt="256 byte userdata" id="BLOGGER_PHOTO_ID_5454319149807616962" /&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 261px;" src="http://4.bp.blogspot.com/_C3CKxYEfops/S7GinoNaKyI/AAAAAAAAACw/3m2K5KpqB-I/s320/512.png" border="0" alt="512 byte userdata" id="BLOGGER_PHOTO_ID_5454319425414638370" /&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 261px;" src="http://1.bp.blogspot.com/_C3CKxYEfops/S7Giux8aAyI/AAAAAAAAAC4/z3DvcNC246g/s320/1024.png" border="0" alt="1024 byte userdata" id="BLOGGER_PHOTO_ID_5454319548286763810" /&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-5849959877789256488?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/5849959877789256488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/03/building-memcached-windows.html#comment-form' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/5849959877789256488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/5849959877789256488'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/03/building-memcached-windows.html' title='Building Memcached on Windows'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_C3CKxYEfops/S7GiXlfkw8I/AAAAAAAAACo/Uz1HQ3guJ58/s72-c/256.png' height='72' width='72'/><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-3390082068574351010</id><published>2010-03-03T00:03:00.001+01:00</published><updated>2010-03-03T00:06:39.833+01:00</updated><title type='text'>Memcached with SASL on OpenSolaris - part 2</title><content type='html'>It turns out that some systems doesn't support shadow.h and fgetspent, so I just updated the patch to no longer require them.. To create the file, simply do:

echo "myuser:mypass" &gt;&gt; my_sasl_pwdb

Happy hacking&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-3390082068574351010?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/3390082068574351010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/03/memcached-with-sasl-on-opensolaris-part.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/3390082068574351010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/3390082068574351010'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/03/memcached-with-sasl-on-opensolaris-part.html' title='Memcached with SASL on OpenSolaris - part 2'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-1958940113391234469</id><published>2010-02-25T13:24:00.006+01:00</published><updated>2010-02-26T12:04:11.014+01:00</updated><title type='text'>Memcached with SASL on OpenSolaris</title><content type='html'>&lt;p&gt;
You may have tried to build memcached with SASL support on OpenSolaris with the following result:
&lt;/p&gt;&lt;pre&gt;trond@opensolaris&amp;lt; &lt;b&gt;./configure --enable-sasl&lt;/b&gt;
[ ... cut ... ]
checking sasl/sasl.h usability... yes
checking sasl/sasl.h presence... yes
checking for sasl/sasl.h... yes
checking for library containing sasl_server_init... no
configure: error: Failed to locate the library containing sasl_server_init
&lt;/pre&gt;&lt;p&gt;This is because configure only tries to look for sasl_server_init in libsasl2, and OpenSolaris use libsasl instead. Yesterday I pushed a &lt;a href="http://github.com/trondn/memcached/commit/9e5e681c1890d3c2cdf279344fd78a9b16deee5a"&gt;fix&lt;/a&gt; to my github repository that search for the symbol in libsasl as well.
&lt;/p&gt;&lt;p&gt;Configuring SASL may also be a challenge (I want to spend my time writing code, not be a system administrator), so I decided to add support for plaintext passwords as well. You probably don't want to use this on your production servers, but it comes in really handy if you just want to test your favorite client.
&lt;/p&gt;&lt;p&gt;You enable support for plaintext password by passing &lt;code&gt;--enable-sasl-pwdb&lt;/code&gt; to &lt;code&gt;configure&lt;/code&gt;. I didn't want to spend any time to writing a new parser or come up with a new file format, so I decided to use &lt;a href="http://docs.sun.com/app/docs/doc/819-2243/fgetspent-r-3c"&gt;fgetspent_r&lt;/a&gt; to read the password file. This means that as long as you follow the &lt;a href="http://docs.sun.com/app/docs/doc/819-2251/shadow-4"&gt;format&lt;/a&gt; for a shadow file, you're good to go :-) You have to set the name of the file to use as the password file in the environment variable &lt;code&gt;MEMCACHED_SASL_PWDB&lt;code&gt;:
&lt;/code&gt;&lt;/code&gt;&lt;/p&gt;&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;echo "myname:mypass:::::::" &amp;gt; /tmp/memcached-sasl-db&lt;/b&gt;
trond@opensolaris&amp;gt; export MEMCACHED_SASL_PWDB=/tmp/memcached-sasl-db
&lt;/pre&gt;&lt;p&gt;With a password file in place, you have to create a config file for SASL to instruct it to use plain text password authentication:
&lt;/p&gt;&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;echo "mech_list: plain" &amp;gt; memcached.conf&lt;/b&gt;
&lt;/pre&gt;&lt;p&gt;If you don't want to install this as the global configuration for memcached, you should specify the location of the file in &lt;code&gt;SASL_CONF_PATH&lt;/code&gt;:
&lt;/p&gt;&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;export SASL_CONF_PATH=`pwd`/memcached.conf&lt;/b&gt;
&lt;/pre&gt;&lt;p&gt;You then start the memcached deamon with "-S" to enable SASL authentication:
&lt;/p&gt;&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;./memcached -S -d&lt;/b&gt;
&lt;/pre&gt;&lt;p&gt;So let's run some commands to the server and see how this works. I'm using the SASL support I implemented in libmemcached (not integrated yet, but you may download it from &lt;a href="https://code.launchpad.net/%7Etrond-norbye/libmemcached/sasl"&gt; https://code.launchpad.net/~trond-norbye/libmemcached/sasl&lt;/a&gt;):
&lt;/p&gt;&lt;pre&gt;trond@opensolaris&amp;gt; &lt;b&gt;./memcp --servers=localhost:11211 --binary \
                                                    --username=myname --password=inncorrect \
                                                    memcp.c&lt;/b&gt;
memcp: memcp.c: memcache error AUTHENTICATION FAILURE
trond@opensolaris&amp;gt; &lt;b&gt;./memcp --servers=localhost:11211 --binary \
                                                    --username=myname --password=mypass \
                                                    memcp.c&lt;/b&gt;
trond@opensolaris&amp;gt; &lt;b&gt;./memcat --servers=localhost:11211 --binary \
                                                     --username=myname --password=mypass \
                                                     memcp.c&lt;/b&gt;
[ ... output of memcp.c ... ]
&lt;/pre&gt;
That's all for now. Happy hacking :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-1958940113391234469?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/1958940113391234469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/02/memcached-with-sasl-on-opensolaris.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1958940113391234469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/1958940113391234469'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/02/memcached-with-sasl-on-opensolaris.html' title='Memcached with SASL on OpenSolaris'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-2254824104590667027</id><published>2010-02-11T09:48:00.002+01:00</published><updated>2010-02-11T10:10:46.033+01:00</updated><title type='text'>New opportunities</title><content type='html'>I've been really quiet lately, so I guess I should come with a short update on what I'm up to. As of 2010 I am no longer a Sun Microsystems employee, so I spent January in Mountain View in the offices of my new employer: &lt;a href="http://www.northscale.com/"&gt;NorthScale&lt;/a&gt;.

I'm currently working on getting up to speed on my new tasks, so I'll be back with more information later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-2254824104590667027?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/2254824104590667027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2010/02/new-opportunities.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/2254824104590667027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/2254824104590667027'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2010/02/new-opportunities.html' title='New opportunities'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-304784545999901538</id><published>2009-12-24T13:53:00.015+01:00</published><updated>2009-12-24T22:13:06.653+01:00</updated><title type='text'>2009</title><content type='html'>2009 has been a really interesting year for me. We didn't have much snow during the holiday season, but during the first weeks in January we got &lt;b&gt;a lot&lt;/b&gt; causing a complete chaos  in the traffic. I spent two hours driving to work and  two hours driving home one day (I normally only use 20-30 minutes), so I decided to work from home until things got back to normal.&lt;div&gt;
&lt;/div&gt;&lt;div&gt;Eric Lambert visited from California in February to start planning our next sprint on our project. "Unfortunately" he didn't get to see any extreme winter weather, but it was nice to be able to work face to face with my team-mate instead of only discussing on IRC.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;&lt;img style="width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_C3CKxYEfops/SzNwLGKgiMI/AAAAAAAAACQ/JVVNAgut-no/s320/trond_users_conference.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5418798112592398530" /&gt;&lt;/div&gt;&lt;div&gt;Unfortunately for me I can't spend as much time as I want with my brother and his family, because they live in California. I have however been so lucky the last few years that I have been able to stay at his place for a month and work from there. Last year was no exception, but this time I was also giving a presentation on MySQL Users Conference. The users conference is a highlight of the year for me, because I meet so many good friends from all over the world there.&lt;/div&gt;

&lt;div&gt;&lt;img style="width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_C3CKxYEfops/SzNxGyfHs7I/AAAAAAAAACY/eu32wy0M_qs/s320/IMG_4145.JPG" border="0" alt="" id="BLOGGER_PHOTO_ID_5418799138102293426" /&gt;&lt;/div&gt;&lt;div&gt;I've seen a lot of movies and heard a lot about Alcatraz, but I have never found the time to go there. It was therefore a really pleasant surprise when Matt Ingenthron asked me if we should go there a weekend. Everything there was much smaller than I had imagined (the cells are &lt;b&gt;really&lt;/b&gt; tiny). The weather was really nice that day, but it was kind of cold there anyway.. I can really imagine myself how it must have been to stay there on a winters night...&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;During the Users Conference I also left Suns Database Group, and joined a fantastic team lead by Lee Bieber (aka the Drizzle team). &lt;/div&gt;&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;&lt;img style="width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_C3CKxYEfops/SzNuEa28VbI/AAAAAAAAACI/OjSkmjkujvs/s320/hotel_view_small.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5418795798865139122" /&gt;&lt;/div&gt;&lt;div&gt;We spent the summer vacation on Oslo this year, going to amusement parks and museums. One of the highlights this years was my cousins wedding in Geiranger. We went on a 3 hour long boat trip in the fjords before we arrived at the hotel, so I got to see "syv søstre" and "friaren" up close. Thats a memory for life. The view out my hotel window was really awesome!&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;In September I went to Seattle for a team meeting, and it was really great to see the rest of the team again (You can see the team at &lt;a href="http://www.flickr.com/photos/brianaker/3834956962/"&gt;http://www.flickr.com/photos/brianaker/3834956962/&lt;/a&gt; ). Working from Norway I spend a lot of time talking with them on telephone / IRC, so it is really great to meet them face to face once in a while. Luckily for me I got to meet other friends during my stay there. Dustin Sallings, Steve Yen and Patrick Galbraith from NorthScale joined in on the open events to discuss the community work on Drizzle / Gearman and Memcached.&lt;/div&gt;&lt;div&gt;
&lt;img style="width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_C3CKxYEfops/SzNyRXdyZLI/AAAAAAAAACg/iADSo_I4OFg/s320/renovation.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5418800419339134130" /&gt;&lt;/div&gt;&lt;div&gt;Back in Trondheim I continued the renovation om my house. I am going to build two rooms in the garage attached to my house, so I started by tearing down the wall and constructing a new wall. I'm 99% done with everything on the outside now, but I need to find time to finish up inside.&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;div&gt;This was a quick summary of 2009, and I am pretty sure that 2010 will become even more exciting!!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-304784545999901538?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/304784545999901538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2009/12/2009.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/304784545999901538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/304784545999901538'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2009/12/2009.html' title='2009'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_C3CKxYEfops/SzNwLGKgiMI/AAAAAAAAACQ/JVVNAgut-no/s72-c/trond_users_conference.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7638694470910246382.post-2746938893229672967</id><published>2009-12-20T18:35:00.019+01:00</published><updated>2009-12-20T21:16:06.238+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='storage engine'/><category scheme='http://www.blogger.com/atom/ns#' term='memcached'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlite'/><title type='text'>Persistent storage engine</title><content type='html'>A lot of people keep asking about persistent storage engines for &lt;a href="http://www.memcached.org/"&gt;memcached&lt;/a&gt;, so I thought that I should create an example to show you how easy it is to create a storage engine that stores the items on disk for persistence. Please note that this is an &lt;span style="font-weight: bold;"&gt;example&lt;/span&gt; on how to do it, not a highly tuned version for performance (that would be up to you to implement ;-))

You wouldn't want to access the filesystem every time you want to access an item, so I'm going to beef the example up a bit by creating a two-level cache. All items will eventually be stored down to disk, but I will serve all of my items from memory. You might think that this sounds like a lot of work to implement, but you couldn't be more wrong about that. The way the default storage engine in memcached is implemented makes it a perfect starting point for us.

I've pushed the source code I'm going to discuss to &lt;a href="http://github.com/trondn/memcached-engines"&gt;git://github.com/trondn/memcached-engines.git&lt;/a&gt;, and you should look at the source code in &lt;code&gt;src/persistent&lt;/code&gt;.

So how does this thing work?
The short answer there is that whenever you store an item into the cache, I will also store the item in my persistent layer. We don't want to create a dead slow cache, so I am going to do the actual storing to the persistent media asynchronously. To speed up things more, I'm not going to let the application have to wait for the data to be written to the persistent media. The drawback for this is that the application will never know if anything failed while I tried to write the object to a persistent media. Whenever the user tries to get an object from the cache, I'll search the memory table first and if it isn't there, I'll read it from the persistent media and return it to the caller.

I've decided to use &lt;a href="http://www.sqlite.org/"&gt;SQLite&lt;/a&gt; for my persistence layer, so I created two extra threads in my engine:
&lt;ul&gt;&lt;li&gt;&lt;code&gt;SQLiteReader&lt;/code&gt; to read items from the database and store them in memory&lt;/li&gt;&lt;li&gt;&lt;code&gt;SQLiteWriter&lt;/code&gt; to write items from memory to the database&lt;/li&gt;&lt;/ul&gt;
Now let's look at the details.. When you try to store (add, set, replace etc) an item in memcached, you will eventually end up in &lt;code&gt;do_store_item&lt;/code&gt; in &lt;code&gt;items.c&lt;/code&gt;. This is the first place we are going to make some modifications. To keep the example simple I am not going to implement a proper add, append, prepend and replace function (to be specific, I am not going to check if they are in the persistent media if they aren't located in memory when you call the specific command. It should be pretty obvious how to implement that if you want it, so let's rather keep the example easy to understand. At the end of &lt;code&gt;do_store_item&lt;/code&gt; we know if the item was successfully inserted into memcached, and this is where I ship the item to my persistent layer.:
&lt;pre&gt;
if (stored == ENGINE_SUCCESS) {
   *cas = item_get_cas(&amp;amp;it-&amp;gt;item);
   if (notify) {
       &lt;b&gt;sqlite_io_store_item(engine, it);&lt;/b&gt;
   }
}
&lt;/pre&gt;
You will find the implementation for &lt;code&gt;sqlite_io_store_item&lt;/code&gt; in &lt;code&gt;sqlite.cc&lt;/code&gt;, and if you look at the code there you will see that all it does is to bump the reference counter for the item (to ensure that the object isn't evicted from the cache), and put the item into the work queue for the writer thread.

With this simple modification to the default engine, I was able to always store the items in the SQLite database when I was storing items to the cache. To verify that this worked, I created the
&lt;code&gt;SQLiteCacheWarmup&lt;/code&gt; class that does a simple "&lt;code&gt;&lt;b&gt;select * from kv;&lt;/b&gt;&lt;/code&gt;" and inserts the content into the database during startup.

The above code works perfectly if your entire cache set fits in memory, but if you start to evict items you would probably want to be able to asynchronously get items back into the cache again. What I did here was to add the following code snippet to my implementation of the get function in the engine API:
&lt;pre&gt;
hash_item *it = item_get(engine, key, nkey);
if (it != NULL) {
   *item = &amp;amp;it-&amp;gt;item;
   return ENGINE_SUCCESS;
} else {
    sqlite_io_get_item(engine, cookie, key, nkey);
    return ENGINE_EWOULDBLOCK;
}
&lt;/pre&gt;
The real magic here is the &lt;code&gt;sqlite_io_get_item()&lt;/code&gt; function. What it does is to put a request in the &lt;code&gt;SQLiteReader&lt;/code&gt; threads queue to load the item identified by the key. The &lt;code&gt;SQLiteReader&lt;/code&gt; thread will try to read the item from the database and insert it into memory before it will call the function &lt;code&gt;notify_io_complete()&lt;/code&gt; from the engine interface when it is done (with either &lt;code&gt;ENGINE_SUCCESS&lt;/code&gt;, or &lt;code&gt;ENGINE_KEY_ENOENT&lt;/code&gt; if the key isn't in the database either).

Please note that the asynchronous interface in the core memcached server isn't fully implemented yet, so you need to pull my engine branch for the memcached server in order to try it out.

Happy hacking :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7638694470910246382-2746938893229672967?l=trondn.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trondn.blogspot.com/feeds/2746938893229672967/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trondn.blogspot.com/2009/12/persistent-storage-engine.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/2746938893229672967'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7638694470910246382/posts/default/2746938893229672967'/><link rel='alternate' type='text/html' href='http://trondn.blogspot.com/2009/12/persistent-storage-engine.html' title='Persistent storage engine'/><author><name>Trond Norbye</name><uri>https://profiles.google.com/110529046755652980001</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh3.googleusercontent.com/-qkmrJGqJJSQ/AAAAAAAAAAI/AAAAAAAAAFQ/JAxEftOU6F8/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry></feed>
