Trond Norbye's Weblog

« Previous month (Nov 2008) | Main | Next month (Jan 2009) »

http://blogs.sun.com/trond/date/20090127 Tuesday January 27, 2009

Noreply support for binary protocol in libmemcached

Just a short notice that I pushed a patch that implements support for noreply in the binary protocol implementation in libmemcached. In order to try it out you need to get a recent build of memcached:

trond@razor> hg clone http://hg.tangent.org/libmemcached 
destination directory: libmemcached
requesting all changes
adding changesets
adding manifests
adding file changes
added 623 changesets with 2375 changes to 210 files
updating working directory
144 files updated, 0 files merged, 0 files removed, 0 files unresolved
trond@razor> cd libmemcached
trond@razor> ./config/bootstap
./config/bootstrap: running `aclocal-1.10' 
./config/bootstrap: running `autoheader' 
./config/bootstrap: running `libtoolize --automake --copy --force' 
./config/bootstrap: running `automake-1.10 --add-missing --copy --force' 
configure.ac:42: installing `config/missing'
configure.ac:42: installing `config/install-sh'
clients/Makefile.am: installing `config/depcomp'
Makefile.am: installing `./INSTALL'
./config/bootstrap: running `autoconf' 
trond@razor> ./configure --enable-64bit --prefix=/opt/memcached
[ a lot of output ]
trond@razor> make all install
[ a lot of output ]
trond@razor> cd ..
trond@razor> git clone git://github.com/dustin/memcached.git
Initialized empty Git repository in /export/home/trond/khepri38/foobar/memcached/.git/
remote: Counting objects: 3451, done.
remote: Compressing objects: 100% (1162/1162), done.
remote: Total 3451 (delta 2377), reused 3286 (delta 2261)
Receiving objects: 100% (3451/3451), 1.04 MiB | 163 KiB/s, done.
Resolving deltas: 100% (2377/2377), done.
trond@razor> cd memcached
trond@razor> git checkout -b rewritten-bin origin/rewritten-bin
Branch rewritten-bin set up to track remote branch refs/remotes/origin/rewritten-bin.
Switched to a new branch "rewritten-bin"
trond@razor> ./autogen.sh
aclocal...
autoheader...
automake...
configure.ac:10: installing `./compile'
configure.ac:3: installing `./config.sub'
configure.ac:5: installing `./missing'
configure.ac:5: installing `./install-sh'
configure.ac:3: installing `./config.guess'
Makefile.am: installing `./depcomp'
autoconf...
trond@razor> ./configure --prefix=/opt/memcached --enable-64bit --enable-dtrace
[ a lot of output ]
trond@razor> make all test install
[ a lot of output ]

So unless you got a compilation error on your platform, you should now be able to start /opt/memcached/bin/memcached, and link your application with the libmemcached installed in /opt/memcached/lib.

Happy hacking :-)

http://blogs.sun.com/trond/date/20090123 Friday January 23, 2009

libmemcached cleanup and enhancements

I have been pretty busy this morning applying patches and cleaning up the code in libmemcached. I started out with a patch from Toru Maesaka adding stat analysis from your memcached cluster:

trond@razor> memstat -a --server=server1,server2,server3
Memcached Cluster Analysis Report

	Number of Servers Analyzed         : 3
	Average Item Size (incl/overhead)  : 567 bytes

	Node with most memory consumption  : server1:11211 (7572852 bytes)
	Node with least free space         : server1:11211 (59536012 bytes remaining)
	Node with longest uptime           : server1:11211 (667s)
	Pool-wide Hit Ratio                : 0%

I have also pushed a small optimization for parsing the buffer returned from the client (in the text protocol) that operates directly on the receive buffer instead of calling memcached_io_read to read out a single byte.

When the source have compiler warnings it makes it hard to spot if you introduce a new one, so I went through the source and removed all of the compiler warnings. With a warning free source code I toggled the compiler flags, so that warnings should be treated as errors. So the next time someone tries to push a patch introducing a warning, the Solaris build bots will scream out on #memcached on irc.freenode.net.

I also improved the test I had written for the noreply

support, and discovered that there was a bug in the implementation. Luckily it was pretty easy to fix, so I have pushed a patch for that as well.

I feel that we have made a lot of progress on libmemcached lately, so I'm looking forward to the next release!!!

http://blogs.sun.com/trond/date/20090121 Wednesday January 21, 2009

noreply support in libmemcached - part two

In my previous post I described the implementation of noreply support in libmemcached, but Brian Aker commented that we should buffer the noreply packets as well to avoid sending a lot of packets over the wire.

Earlier today I pushed a patch that introduce a new function: memcached_return memcached_flush_buffers(memcached_st *).

So what does this mean for you as a developer? If we look at the following example:

  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1);
  for (int x= 0; x < 100; ++x) {
    char key[10];
    size_t len= sprintf(key, "%d", x);
    memcached_response ret;
    ret= memcached_set(memc, key, len, key, len, 0, 0);
    if (ret != MEMCACHED_SUCCESS && ret != MEMCACHED_BUFFERED)
      abort();
  }
  [ ... Perform application logic ... ]
  
  char* value= memcached_get(memc, key, strlen(key),
                             &value_length, &flags, &rc);

Without the patch I added today, libmemcached would send out the command to the server for each invocation of memcached_set. With the the patch I added today, all of the commands would be sent to the server in chunks when the user calls memcached_get. Now this doesn't buy us much from what we had before I added the noreply support (just that we don't have to parse the reply codes from the server). Well, lets talk about the new function I added today: memcached_flush_buffers. Let's look at some source:

  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1);
  for (int x= 0; x < 100; ++x) {
    char key[10];
    size_t len= sprintf(key, "%d", x);
    memcached_response ret;
    ret= memcached_set(memc, key, len, key, len, 0, 0);
    if (ret != MEMCACHED_SUCCESS && ret != MEMCACHED_BUFFERED)
      abort();
  }
  
  if (memcached_flush_buffers(memc) != MEMCACHED_SUCCESS)
    abort();
  

  [ ... Perform application logic ... ]
  
  char* value= memcached_get(memc, key, strlen(key),
                             &value_length, &flags, &rc);

Now memcached_flush_buffers will send all of the commands in the buffers to the memcached servers, so that the memcached server may start executing them. This means that the memcached server may start processing the commands while we perform application logic. When we invoke memcached_get the memcached server may already be finish executing the commands in the buffer, and process the get request immediately.

http://blogs.sun.com/trond/date/20090120 Tuesday January 20, 2009

noreply support in libmemcached

The memcached textual protocol allows you to execute commands on the memcached server without sending the result back to you. Why would you want to do this? One case could be that you just want to store the item in the cache, and you don't really care if it is successful or not. Up until today you had two modes you could use to insert items in the cache: synchronous (default) and asynchronous.

If we look at the following code snippet:

  for (int x= 0; x < 100; ++x) {
    char key[10];
    size_t len= sprintf(key, "%d", x);
    if (memcached_set(memc, key, len, key, len, 0, 0) != MEMCACHED_SUCCESS)
      abort();
  }
  memcached_quit(memc);

During the execution of the above loop, libmemcached will send the set command to the server and wait for the response from the server before it sends the next command to the server etc. This is not very efficient, so you may use the asynchronous mode to queue up commands. To do that, we modify the code:

  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
  for (int x= 0; x < 100; ++x) {
    char key[10];
    size_t len= sprintf(key, "%d", x);
    memcached_response ret;
    ret= memcached_set(memc, key, len, key, len, 0, 0);
    if (ret != MEMCACHED_SUCCESS && ret != MEMCACHED_BUFFERED)
      abort();
  }
  memcached_quit(memc);

With this modification, libmemcached will buffer multiple requests to the server and send them to the server when the buffer exceeds a configurable threshold.

Now this sounds pretty neat doesn't it? Well it is one problem with the model, and that is if we try to issue a get command. The problem now is that we need to flush the send buffer and let the server execute all of the commands in the send buffer before we can send the get request. If we are lucky the buffer is empty (or just contain a few commands), but it could potentially contain a lot of set commands and the memcached server could use some time processing all of those commands.

To solve this problem I implemented use of the noreply command (If you are interested in the glory details, take a look at the patch). So let me modify the source code once more:

  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1);
  for (int x= 0; x < 100; ++x) {
    char key[10];
    size_t len= sprintf(key, "%d", x);
    memcached_response ret;
    ret= memcached_set(memc, key, len, key, len, 0, 0);
    if (ret != MEMCACHED_SUCCESS && ret != MEMCACHED_BUFFERED)
      abort();
  }
  memcached_quit(memc);

With this modification libmemcached will send the command to the memcached server immediately, but it also tells the server that it doesn't want a reply from the server so it doesn't wait for a response before it sends the next command. When I implemented this patch I noticed that the binary protocol didn't have support for all of the "noreply" variants of the commands, so I filed a bug report on memcached (and implemented it as well ;-)).

http://blogs.sun.com/trond/date/20090118 Sunday January 18, 2009

Trondheim OpenSolaris User Group

Det er med stor glede jeg kan fortelle at vi har startet opp Trondheim OpenSolaris User Group (TrOSUG). Vi (Knut Anders, Jan Ståle og meg selv) har noen tanker om hva vi vil oppnå med brukergruppen, men vi er veldig interessert i å høre hva du er interessert i. (Jan Ståle skrev en intro-mail til gruppen med noen forslag).

http://blogs.sun.com/trond/date/20090107 Wednesday January 07, 2009

DTrace probes in libmemcached on Mac OSX?

I have just pushed a patch to libmemcached that allows you to build libmemcached with the static DTrace probes. Please note that I haven't had the time yet to look at probes it contains (and if they are useful), but I am going to look into that :-)

So why don't you just go ahead and grab the latest source and try it out?

trond@mac > hg clone http://hg.tangent.org/libmemcached
trond@mac > cd libmemcached
trond@mac > ./config/bootstrap && ./configure --enable-dtrace --enable-64bit


Valid HTML! Valid CSS!

This is a personal weblog, I do not speak for my employer.