Merge from trunk.
authorPadraig O'Sullivan <osullivan.padraig@gmail.com>
Tue, 20 Oct 2009 17:23:50 +0000 (13:23 -0400)
committerPadraig O'Sullivan <osullivan.padraig@gmail.com>
Tue, 20 Oct 2009 17:23:50 +0000 (13:23 -0400)
34 files changed:
AUTHORS
ChangeLog
clients/memcapable.c
configure.ac
docs/Makefile.am
docs/memcached_get.pod
docs/memcached_servers.pod
example/interface_v0.c
example/memcached_light.c
example/memcached_light.h
libmemcached/common.h
libmemcached/memcached.h
libmemcached/memcached_connect.c
libmemcached/memcached_delete.c
libmemcached/memcached_fetch.c
libmemcached/memcached_get.c
libmemcached/memcached_get.h
libmemcached/memcached_hosts.c
libmemcached/memcached_io.c
libmemcached/memcached_key.c
libmemcached/memcached_purge.c
libmemcached/memcached_quit.c
libmemcached/memcached_response.c
libmemcached/memcached_server.c
libmemcached/memcached_server.h
libmemcached/memcached_stats.c
libmemcached/memcached_types.h
support/libmemcached.spec.in
tests/atomsmasher.c
tests/function.c
tests/plus.cpp
tests/test.c
tests/test.h
tests/udp.c

diff --git a/AUTHORS b/AUTHORS
index 110afe2595347b87f27b564ec8ae0060df568e8f..24dbad1dfc11e0ea59c622306f480d1975ecb1aa 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,7 +1,10 @@
 Brian Aker, brian@tangent.org -- Client Library, Tools
 Mark Atwood, -- Tools, Docs
 Patrick Galbraith, -- C++ Interface
+Padraig O'Sullivan, -- C++ Interface (current one)
 Tim Bunce, -- Docs
 Trond Norbye, trond.norbye@sun.com -- Binary protocol, Misc
 Yin Chen, -- Ketama Work
 Toru Maesaka, dev@torum.net -- Stats analysis
+Eric Lambert, -- UDP work
+Monty Taylor, -- Build Releated (Pandora)
index bb373dc8d66cfe9e411eae97d490c8e8c20ceb83..0da08589c1b17937324c56b87e37f177efe5ca33 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+0.34 Tue Oct 13 08:39:51 PDT 2009
+  * Added support for setting behavior flags on a connection pool.
+  * Don't increment server_failure_counter on normal disconnects.
+  * Added prototype for a callback based protocol parser (server side)
+    with examples so that you could let your own application speak the
+    memcached protocol
+  * Updated memcapable to test ASCII protocol.
+  * Changed behavior so that server can be removed at first sign of failure.
+  * Added memcached_server_get_last_disconnect() call
+
 0.33 Wed Sep 23 10:11:58 PDT 2009
   * Added memcapable to test servers for binary compatibility.
   * Updated C++ interface. Added basic support for C++ exceptions. Added 
index 4b10777721754d8daf26c6ff2d013d9ad5c6c323..f1d7d6a4d7330f8b91e969fe951870b35a98dc38 100644 (file)
@@ -19,6 +19,7 @@
 #include <stdbool.h>
 #include <unistd.h>
 #include <poll.h>
+#include <ctype.h>
 
 #include <libmemcached/memcached/protocol_binary.h>
 #include <libmemcached/byteorder.h>
@@ -168,7 +169,7 @@ static ssize_t timeout_io_op(int fd, short direction, void *buf, size_t len)
     ret= read(fd, buf, len);
 
   if (ret == -1 && errno == EWOULDBLOCK) {
-    struct pollfd fds = {
+    struct pollfd fds= {
       .events= direction,
       .fd= fd
     };
@@ -183,7 +184,7 @@ static ssize_t timeout_io_op(int fd, short direction, void *buf, size_t len)
     }
     else if (err == 0)
     {
-      errno = ETIMEDOUT;
+      errno= ETIMEDOUT;
     }
     else
     {
@@ -206,7 +207,7 @@ static enum test_return ensure(bool val, const char *expression, const char *fil
   if (!val)
   {
     if (verbose)
-      fprintf(stderr, "%s:%u: %s\n", file, line, expression);
+      fprintf(stderr, "\n%s:%u: %s", file, line, expression);
 
     if (do_core)
       abort();
@@ -639,7 +640,7 @@ static enum test_return test_binary_set_impl(const char* key, uint8_t cc)
     cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SET;
     execute(resend_packet(&cmd));
     execute(recv_packet(&rsp));
-    verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET, 
+    verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
                                     PROTOCOL_BINARY_RESPONSE_SUCCESS));
     cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ;
   }
@@ -720,7 +721,7 @@ static enum test_return test_binary_addq(void)
   return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
 }
 
-static enum test_return set_item(const char *key, const char *value)
+static enum test_return binary_set_item(const char *key, const char *value)
 {
   command cmd;
   response rsp;
@@ -761,7 +762,7 @@ static enum test_return test_binary_replace_impl(const char* key, uint8_t cc)
       verify(validate_response_header(&rsp, cc, expected_result));
 
       if (ii == 0)
-        execute(set_item(key, key));
+        execute(binary_set_item(key, key));
     }
     else
       execute(test_binary_noop());
@@ -810,7 +811,7 @@ static enum test_return test_binary_delete_impl(const char *key, uint8_t cc)
   execute(send_packet(&cmd));
   execute(recv_packet(&rsp));
   verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
-  execute(set_item(key, key));
+  execute(binary_set_item(key, key));
 
   /* The item should be present now, resend*/
   execute(resend_packet(&cmd));
@@ -851,7 +852,7 @@ static enum test_return test_binary_get_impl(const char *key, uint8_t cc)
   else
     execute(test_binary_noop());
 
-  execute(set_item(key, key));
+  execute(binary_set_item(key, key));
   execute(resend_packet(&cmd));
   execute(recv_packet(&rsp));
   verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
@@ -993,7 +994,7 @@ static enum test_return test_binary_flush_impl(const char *key, uint8_t cc)
 
   for (int ii= 0; ii < 2; ++ii)
   {
-    execute(set_item(key, key));
+    execute(binary_set_item(key, key));
     flush_command(&cmd, cc, 0, ii == 0);
     execute(send_packet(&cmd));
 
@@ -1036,7 +1037,7 @@ static enum test_return test_binary_concat_impl(const char *key, uint8_t cc)
   else
     value=" world";
 
-  execute(set_item(key, value));
+  execute(binary_set_item(key, value));
 
   if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
     value=" world";
@@ -1117,9 +1118,652 @@ static enum test_return test_binary_illegal(void)
     ++cc;
   }
 
+  return TEST_PASS_RECONNECT;
+}
+
+static enum test_return send_string(const char *cmd)
+{
+  execute(retry_write(cmd, strlen(cmd)));
+  return TEST_PASS;
+}
+
+static enum test_return receive_line(char *buffer, size_t size)
+{
+  size_t offset= 0;
+  while (offset < size)
+  {
+    execute(retry_read(buffer + offset, 1));
+    if (buffer[offset] == '\n')
+    {
+      if (offset + 1 < size)
+      {
+        buffer[offset + 1]= '\0';
+        return TEST_PASS;
+      }
+      else
+        return TEST_FAIL;
+    }
+    ++offset;
+  }
+
+  return TEST_FAIL;
+}
+
+static enum test_return receive_response(const char *msg) {
+  char buffer[80];
+  execute(receive_line(buffer, sizeof(buffer)));
+  verify(strcmp(msg, buffer) == 0);
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_quit(void)
+{
+  /* Verify that quit handles unknown options */
+  execute(send_string("quit foo bar\r\n"));
+  execute(receive_response("ERROR\r\n"));
+
+  /* quit doesn't support noreply */
+  execute(send_string("quit noreply\r\n"));
+  execute(receive_response("ERROR\r\n"));
+
+  /* Verify that quit works */
+  execute(send_string("quit\r\n"));
+
+  /* Socket should be closed now, read should return 0 */
+  char buffer[80];
+  verify(timeout_io_op(sock, POLLIN, buffer, sizeof(buffer)) == 0);
+  return TEST_PASS_RECONNECT;
+
+}
+
+static enum test_return test_ascii_version(void)
+{
+  /* Verify that version command handles unknown options */
+  execute(send_string("version foo bar\r\n"));
+  execute(receive_response("ERROR\r\n"));
+
+  /* version doesn't support noreply */
+  execute(send_string("version noreply\r\n"));
+  execute(receive_response("ERROR\r\n"));
+
+  /* Verify that verify works */
+  execute(send_string("version\r\n"));
+  char buffer[256];
+  execute(receive_line(buffer, sizeof(buffer)));
+  verify(strncmp(buffer, "VERSION ", 8) == 0);
+
   return TEST_PASS;
 }
 
+static enum test_return test_ascii_verbosity(void)
+{
+  /* This command does not adhere to the spec! */
+  execute(send_string("verbosity foo bar my\r\n"));
+  execute(receive_response("ERROR\r\n"));
+
+  execute(send_string("verbosity noreply\r\n"));
+  execute(test_ascii_version());
+
+  execute(send_string("verbosity 0 noreply\r\n"));
+  execute(test_ascii_version());
+
+  execute(send_string("verbosity\r\n"));
+  execute(receive_response("ERROR\r\n"));
+
+  execute(send_string("verbosity 1\r\n"));
+  execute(receive_response("OK\r\n"));
+
+  execute(send_string("verbosity 0\r\n"));
+  execute(receive_response("OK\r\n"));
+
+  return TEST_PASS;
+}
+
+
+
+static enum test_return test_ascii_set_impl(const char* key, bool noreply)
+{
+  /* @todo add tests for bogus format! */
+  char buffer[1024];
+  sprintf(buffer, "set %s 0 0 5%s\r\nvalue\r\n", key,
+          noreply ? " noreply" : "");
+  execute(send_string(buffer));
+
+  if (!noreply)
+    execute(receive_response("STORED\r\n"));
+
+  return test_ascii_version();
+}
+
+static enum test_return test_ascii_set(void)
+{
+  return test_ascii_set_impl("test_ascii_set", false);
+}
+
+static enum test_return test_ascii_set_noreply(void)
+{
+  return test_ascii_set_impl("test_ascii_set_noreply", true);
+}
+
+static enum test_return test_ascii_add_impl(const char* key, bool noreply)
+{
+  /* @todo add tests for bogus format! */
+  char buffer[1024];
+  sprintf(buffer, "add %s 0 0 5%s\r\nvalue\r\n", key,
+          noreply ? " noreply" : "");
+  execute(send_string(buffer));
+
+  if (!noreply)
+    execute(receive_response("STORED\r\n"));
+
+  execute(send_string(buffer));
+
+  if (!noreply)
+    execute(receive_response("NOT_STORED\r\n"));
+
+  return test_ascii_version();
+}
+
+static enum test_return test_ascii_add(void)
+{
+  return test_ascii_add_impl("test_ascii_add", false);
+}
+
+static enum test_return test_ascii_add_noreply(void)
+{
+  return test_ascii_add_impl("test_ascii_add_noreply", true);
+}
+
+static enum test_return ascii_get_value(const char *key, const char *value)
+{
+
+  char buffer[1024];
+  size_t datasize= strlen(value);
+
+  verify(datasize < sizeof(buffer));
+  execute(receive_line(buffer, sizeof(buffer)));
+  verify(strncmp(buffer, "VALUE ", 6) == 0);
+  verify(strncmp(buffer + 6, key, strlen(key)) == 0);
+  char *ptr= buffer + 6 + strlen(key) + 1;
+  char *end;
+
+  unsigned long val= strtoul(ptr, &end, 10); /* flags */
+  verify(ptr != end);
+  verify(val == 0);
+  verify(end != NULL);
+  val= strtoul(end, &end, 10); /* size */
+  verify(ptr != end);
+  verify(val == datasize);
+  verify(end != NULL);
+  while (*end != '\n' && isspace(*end))
+    ++end;
+  verify(*end == '\n');
+
+  execute(retry_read(buffer, datasize));
+  verify(memcmp(buffer, value, datasize) == 0);
+
+  execute(retry_read(buffer, 2));
+  verify(memcmp(buffer, "\r\n", 2) == 0);
+
+  return TEST_PASS;
+}
+
+static enum test_return ascii_get_item(const char *key, const char *value,
+                                       bool exist)
+{
+  char buffer[1024];
+  size_t datasize= 0;
+  if (value != NULL)
+    datasize= strlen(value);
+
+  verify(datasize < sizeof(buffer));
+  sprintf(buffer, "get %s\r\n", key);
+  execute(send_string(buffer));
+
+  if (exist)
+    execute(ascii_get_value(key, value));
+
+  execute(retry_read(buffer, 5));
+  verify(memcmp(buffer, "END\r\n", 5) == 0);
+
+  return TEST_PASS;
+}
+
+static enum test_return ascii_gets_value(const char *key, const char *value,
+                                         unsigned long *cas)
+{
+
+  char buffer[1024];
+  size_t datasize= strlen(value);
+
+  verify(datasize < sizeof(buffer));
+  execute(receive_line(buffer, sizeof(buffer)));
+  verify(strncmp(buffer, "VALUE ", 6) == 0);
+  verify(strncmp(buffer + 6, key, strlen(key)) == 0);
+  char *ptr= buffer + 6 + strlen(key) + 1;
+  char *end;
+
+  unsigned long val= strtoul(ptr, &end, 10); /* flags */
+  verify(ptr != end);
+  verify(val == 0);
+  verify(end != NULL);
+  val= strtoul(end, &end, 10); /* size */
+  verify(ptr != end);
+  verify(val == datasize);
+  verify(end != NULL);
+  *cas= strtoul(end, &end, 10); /* cas */
+  verify(ptr != end);
+  verify(val == datasize);
+  verify(end != NULL);
+
+  while (*end != '\n' && isspace(*end))
+    ++end;
+  verify(*end == '\n');
+
+  execute(retry_read(buffer, datasize));
+  verify(memcmp(buffer, value, datasize) == 0);
+
+  execute(retry_read(buffer, 2));
+  verify(memcmp(buffer, "\r\n", 2) == 0);
+
+  return TEST_PASS;
+}
+
+static enum test_return ascii_gets_item(const char *key, const char *value,
+                                        bool exist, unsigned long *cas)
+{
+  char buffer[1024];
+  size_t datasize= 0;
+  if (value != NULL)
+    datasize= strlen(value);
+
+  verify(datasize < sizeof(buffer));
+  sprintf(buffer, "gets %s\r\n", key);
+  execute(send_string(buffer));
+
+  if (exist)
+    execute(ascii_gets_value(key, value, cas));
+
+  execute(retry_read(buffer, 5));
+  verify(memcmp(buffer, "END\r\n", 5) == 0);
+
+  return TEST_PASS;
+}
+
+static enum test_return ascii_set_item(const char *key, const char *value)
+{
+  char buffer[300];
+  size_t len= strlen(value);
+  sprintf(buffer, "set %s 0 0 %u\r\n", key, (unsigned int)len);
+  execute(send_string(buffer));
+  execute(retry_write(value, len));
+  execute(send_string("\r\n"));
+  execute(receive_response("STORED\r\n"));
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_replace_impl(const char* key, bool noreply)
+{
+  char buffer[1024];
+  sprintf(buffer, "replace %s 0 0 5%s\r\nvalue\r\n", key,
+          noreply ? " noreply" : "");
+  execute(send_string(buffer));
+
+  if (noreply)
+    execute(test_ascii_version());
+  else
+    execute(receive_response("NOT_STORED\r\n"));
+
+  execute(ascii_set_item(key, "value"));
+  execute(ascii_get_item(key, "value", true));
+
+
+  execute(send_string(buffer));
+
+  if (noreply)
+    execute(test_ascii_version());
+  else
+    execute(receive_response("STORED\r\n"));
+
+  return test_ascii_version();
+}
+
+static enum test_return test_ascii_replace(void)
+{
+  return test_ascii_replace_impl("test_ascii_replace", false);
+}
+
+static enum test_return test_ascii_replace_noreply(void)
+{
+  return test_ascii_replace_impl("test_ascii_replace_noreply", true);
+}
+
+static enum test_return test_ascii_cas_impl(const char* key, bool noreply)
+{
+  char buffer[1024];
+  unsigned long cas;
+
+  execute(ascii_set_item(key, "value"));
+  execute(ascii_gets_item(key, "value", true, &cas));
+
+  sprintf(buffer, "cas %s 0 0 6 %lu%s\r\nvalue2\r\n", key, cas,
+          noreply ? " noreply" : "");
+  execute(send_string(buffer));
+
+  if (noreply)
+    execute(test_ascii_version());
+  else
+    execute(receive_response("STORED\r\n"));
+
+  /* reexecute the same command should fail due to illegal cas */
+  execute(send_string(buffer));
+
+  if (noreply)
+    execute(test_ascii_version());
+  else
+    execute(receive_response("EXISTS\r\n"));
+
+  return test_ascii_version();
+}
+
+static enum test_return test_ascii_cas(void)
+{
+  return test_ascii_cas_impl("test_ascii_cas", false);
+}
+
+static enum test_return test_ascii_cas_noreply(void)
+{
+  return test_ascii_cas_impl("test_ascii_cas_noreply", true);
+}
+
+static enum test_return test_ascii_delete_impl(const char *key, bool noreply)
+{
+  execute(ascii_set_item(key, "value"));
+
+  execute(send_string("delete\r\n"));
+  execute(receive_response("ERROR\r\n"));
+  /* BUG: the server accepts delete a b */
+  execute(send_string("delete a b c d e\r\n"));
+  execute(receive_response("ERROR\r\n"));
+
+  char buffer[1024];
+  sprintf(buffer, "delete %s%s\r\n", key, noreply ? " noreply" : "");
+  execute(send_string(buffer));
+
+  if (noreply)
+    execute(test_ascii_version());
+  else
+    execute(receive_response("DELETED\r\n"));
+
+  execute(ascii_get_item(key, "value", false));
+  execute(send_string(buffer));
+  if (noreply)
+    execute(test_ascii_version());
+  else
+    execute(receive_response("NOT_FOUND\r\n"));
+
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_delete(void)
+{
+  return test_ascii_delete_impl("test_ascii_delete", false);
+}
+
+static enum test_return test_ascii_delete_noreply(void)
+{
+  return test_ascii_delete_impl("test_ascii_delete_noreply", true);
+}
+
+static enum test_return test_ascii_get(void)
+{
+  execute(ascii_set_item("test_ascii_get", "value"));
+
+  execute(send_string("get\r\n"));
+  execute(receive_response("ERROR\r\n"));
+  execute(ascii_get_item("test_ascii_get", "value", true));
+  execute(ascii_get_item("test_ascii_get_notfound", "value", false));
+
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_gets(void)
+{
+  execute(ascii_set_item("test_ascii_gets", "value"));
+
+  execute(send_string("gets\r\n"));
+  execute(receive_response("ERROR\r\n"));
+  unsigned long cas;
+  execute(ascii_gets_item("test_ascii_gets", "value", true, &cas));
+  execute(ascii_gets_item("test_ascii_gets_notfound", "value", false, &cas));
+
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_mget(void)
+{
+  execute(ascii_set_item("test_ascii_mget1", "value"));
+  execute(ascii_set_item("test_ascii_mget2", "value"));
+  execute(ascii_set_item("test_ascii_mget3", "value"));
+  execute(ascii_set_item("test_ascii_mget4", "value"));
+  execute(ascii_set_item("test_ascii_mget5", "value"));
+
+  execute(send_string("get test_ascii_mget1 test_ascii_mget2 test_ascii_mget3 "
+                      "test_ascii_mget4 test_ascii_mget5 "
+                      "test_ascii_mget6\r\n"));
+  execute(ascii_get_value("test_ascii_mget1", "value"));
+  execute(ascii_get_value("test_ascii_mget2", "value"));
+  execute(ascii_get_value("test_ascii_mget3", "value"));
+  execute(ascii_get_value("test_ascii_mget4", "value"));
+  execute(ascii_get_value("test_ascii_mget5", "value"));
+
+  char buffer[5];
+  execute(retry_read(buffer, 5));
+  verify(memcmp(buffer, "END\r\n", 5) == 0);
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_incr_impl(const char* key, bool noreply)
+{
+  char cmd[300];
+  sprintf(cmd, "incr %s 1%s\r\n", key, noreply ? " noreply" : "");
+
+  execute(ascii_set_item(key, "0"));
+  for (int x= 1; x < 11; ++x)
+  {
+    execute(send_string(cmd));
+
+    if (noreply)
+      execute(test_ascii_version());
+    else
+    {
+      char buffer[80];
+      execute(receive_line(buffer, sizeof(buffer)));
+      int val= atoi(buffer);
+      verify(val == x);
+    }
+  }
+
+  execute(ascii_get_item(key, "10", true));
+
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_incr(void)
+{
+  return test_ascii_incr_impl("test_ascii_incr", false);
+}
+
+static enum test_return test_ascii_incr_noreply(void)
+{
+  return test_ascii_incr_impl("test_ascii_incr_noreply", true);
+}
+
+static enum test_return test_ascii_decr_impl(const char* key, bool noreply)
+{
+  char cmd[300];
+  sprintf(cmd, "decr %s 1%s\r\n", key, noreply ? " noreply" : "");
+
+  execute(ascii_set_item(key, "9"));
+  for (int x= 8; x > -1; --x)
+  {
+    execute(send_string(cmd));
+
+    if (noreply)
+      execute(test_ascii_version());
+    else
+    {
+      char buffer[80];
+      execute(receive_line(buffer, sizeof(buffer)));
+      int val= atoi(buffer);
+      verify(val == x);
+    }
+  }
+
+  execute(ascii_get_item(key, "0", true));
+
+  /* verify that it doesn't wrap */
+  execute(send_string(cmd));
+  if (noreply)
+    execute(test_ascii_version());
+  else
+  {
+    char buffer[80];
+    execute(receive_line(buffer, sizeof(buffer)));
+  }
+  execute(ascii_get_item(key, "0", true));
+
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_decr(void)
+{
+  return test_ascii_decr_impl("test_ascii_decr", false);
+}
+
+static enum test_return test_ascii_decr_noreply(void)
+{
+  return test_ascii_decr_impl("test_ascii_decr_noreply", true);
+}
+
+
+static enum test_return test_ascii_flush_impl(const char *key, bool noreply)
+{
+#if 0
+  /* Verify that the flush_all command handles unknown options */
+  /* Bug in the current memcached server! */
+  execute(send_string("flush_all foo bar\r\n"));
+  execute(receive_response("ERROR\r\n"));
+#endif
+
+  execute(ascii_set_item(key, key));
+  execute(ascii_get_item(key, key, true));
+
+  if (noreply)
+  {
+    execute(send_string("flush_all noreply\r\n"));
+    execute(test_ascii_version());
+  }
+  else
+  {
+    execute(send_string("flush_all\r\n"));
+    execute(receive_response("OK\r\n"));
+  }
+
+  execute(ascii_get_item(key, key, false));
+
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_flush(void)
+{
+  return test_ascii_flush_impl("test_ascii_flush", false);
+}
+
+static enum test_return test_ascii_flush_noreply(void)
+{
+  return test_ascii_flush_impl("test_ascii_flush_noreply", true);
+}
+
+static enum test_return test_ascii_concat_impl(const char *key,
+                                               bool append,
+                                               bool noreply)
+{
+  const char *value;
+
+  if (append)
+    value="hello";
+  else
+    value=" world";
+
+  execute(ascii_set_item(key, value));
+
+  if (append)
+    value=" world";
+  else
+    value="hello";
+
+  char cmd[400];
+  sprintf(cmd, "%s %s 0 0 %u%s\r\n%s\r\n",
+          append ? "append" : "prepend",
+          key, (unsigned int)strlen(value), noreply ? " noreply" : "",
+          value);
+  execute(send_string(cmd));
+
+  if (noreply)
+    execute(test_ascii_version());
+  else
+    execute(receive_response("STORED\r\n"));
+
+  execute(ascii_get_item(key, "hello world", true));
+
+  sprintf(cmd, "%s %s_notfound 0 0 %u%s\r\n%s\r\n",
+          append ? "append" : "prepend",
+          key, (unsigned int)strlen(value), noreply ? " noreply" : "",
+          value);
+  execute(send_string(cmd));
+
+  if (noreply)
+    execute(test_ascii_version());
+  else
+    execute(receive_response("NOT_STORED\r\n"));
+
+  return TEST_PASS;
+}
+
+static enum test_return test_ascii_append(void)
+{
+  return test_ascii_concat_impl("test_ascii_append", true, false);
+}
+
+static enum test_return test_ascii_prepend(void)
+{
+  return test_ascii_concat_impl("test_ascii_prepend", false, false);
+}
+
+static enum test_return test_ascii_append_noreply(void)
+{
+  return test_ascii_concat_impl("test_ascii_append_noreply", true, true);
+}
+
+static enum test_return test_ascii_prepend_noreply(void)
+{
+  return test_ascii_concat_impl("test_ascii_prepend_noreply", false, true);
+}
+
+static enum test_return test_ascii_stat(void)
+{
+  execute(send_string("stats noreply\r\n"));
+  execute(receive_response("ERROR\r\n"));
+  execute(send_string("stats\r\n"));
+  char buffer[1024];
+  do {
+    execute(receive_line(buffer, sizeof(buffer)));
+  } while (strcmp(buffer, "END\r\n") != 0);
+
+  return TEST_PASS_RECONNECT;
+}
+
 typedef enum test_return(*TEST_FUNC)(void);
 
 struct testcase
@@ -1129,34 +1773,61 @@ struct testcase
 };
 
 struct testcase testcases[]= {
-  { "noop", test_binary_noop},
-  { "quit", test_binary_quit},
-  { "quitq", test_binary_quitq},
-  { "set", test_binary_set},
-  { "setq", test_binary_setq},
-  { "flush", test_binary_flush},
-  { "flushq", test_binary_flushq},
-  { "add", test_binary_add},
-  { "addq", test_binary_addq},
-  { "replace", test_binary_replace},
-  { "replaceq", test_binary_replaceq},
-  { "delete", test_binary_delete},
-  { "deleteq", test_binary_deleteq},
-  { "get", test_binary_get},
-  { "getq", test_binary_getq},
-  { "getk", test_binary_getk},
-  { "getkq", test_binary_getkq},
-  { "incr", test_binary_incr},
-  { "incrq", test_binary_incrq},
-  { "decr", test_binary_decr},
-  { "decrq", test_binary_decrq},
-  { "version", test_binary_version},
-  { "append", test_binary_append},
-  { "appendq", test_binary_appendq},
-  { "prepend", test_binary_prepend},
-  { "prependq", test_binary_prependq},
-  { "stat", test_binary_stat},
-  { "illegal", test_binary_illegal},
+  { "ascii quit", test_ascii_quit },
+  { "ascii version", test_ascii_version },
+  { "ascii verbosity", test_ascii_verbosity },
+  { "ascii set", test_ascii_set },
+  { "ascii set noreply", test_ascii_set_noreply },
+  { "ascii get", test_ascii_get },
+  { "ascii gets", test_ascii_gets },
+  { "ascii mget", test_ascii_mget },
+  { "ascii flush", test_ascii_flush },
+  { "ascii flush noreply", test_ascii_flush_noreply },
+  { "ascii add", test_ascii_add },
+  { "ascii add noreply", test_ascii_add_noreply },
+  { "ascii replace", test_ascii_replace },
+  { "ascii replace noreply", test_ascii_replace_noreply },
+  { "ascii cas", test_ascii_cas },
+  { "ascii cas noreply", test_ascii_cas_noreply },
+  { "ascii delete", test_ascii_delete },
+  { "ascii delete noreply", test_ascii_delete_noreply },
+  { "ascii incr", test_ascii_incr },
+  { "ascii incr noreply", test_ascii_incr_noreply },
+  { "ascii decr", test_ascii_decr },
+  { "ascii decr noreply", test_ascii_decr_noreply },
+  { "ascii append", test_ascii_append },
+  { "ascii append noreply", test_ascii_append_noreply },
+  { "ascii prepend", test_ascii_prepend },
+  { "ascii prepend noreply", test_ascii_prepend_noreply },
+  { "ascii stat", test_ascii_stat },
+  { "binary noop", test_binary_noop },
+  { "binary quit", test_binary_quit },
+  { "binary quitq", test_binary_quitq },
+  { "binary set", test_binary_set },
+  { "binary setq", test_binary_setq },
+  { "binary flush", test_binary_flush },
+  { "binary flushq", test_binary_flushq },
+  { "binary add", test_binary_add },
+  { "binary addq", test_binary_addq },
+  { "binary replace", test_binary_replace },
+  { "binary replaceq", test_binary_replaceq },
+  { "binary delete", test_binary_delete },
+  { "binary deleteq", test_binary_deleteq },
+  { "binary get", test_binary_get },
+  { "binary getq", test_binary_getq },
+  { "binary getk", test_binary_getk },
+  { "binary getkq", test_binary_getkq },
+  { "binary incr", test_binary_incr },
+  { "binary incrq", test_binary_incrq },
+  { "binary decr", test_binary_decr },
+  { "binary decrq", test_binary_decrq },
+  { "binary version", test_binary_version },
+  { "binary append", test_binary_append },
+  { "binary appendq", test_binary_appendq },
+  { "binary prepend", test_binary_prepend },
+  { "binary prependq", test_binary_prependq },
+  { "binary stat", test_binary_stat },
+  { "binary illegal", test_binary_illegal },
   { NULL, NULL}
 };
 
@@ -1209,20 +1880,22 @@ int main(int argc, char **argv)
   for (int ii= 0; testcases[ii].description != NULL; ++ii)
   {
     ++total;
-    fprintf(stdout, "%s\t\t", testcases[ii].description);
+    fprintf(stdout, "%-40s", testcases[ii].description);
     fflush(stdout);
 
     bool reconnect= false;
     enum test_return ret= testcases[ii].function();
-    fprintf(stderr, "%s\n", status_msg[ret]);
     if (ret == TEST_FAIL)
     {
       reconnect= true;
       ++failed;
+      if (verbose)
+        fprintf(stderr, "\n");
     }
     else if (ret == TEST_PASS_RECONNECT)
       reconnect= true;
 
+    fprintf(stderr, "%s\n", status_msg[ret]);
     if (reconnect)
     {
       (void) close(sock);
index 951edfbfb44eb0c4530d138aef2713bbca9077cb..f1333afad7b0c34c1aef6dc296e195ac621b6c8e 100644 (file)
@@ -6,7 +6,7 @@
 # the COPYING file in this directory for full text.
 
 AC_PREREQ(2.59)
-AC_INIT([libmemcached],[0.33],[http://tangent.org/552/libmemcached.html])
+AC_INIT([libmemcached],[0.34],[http://tangent.org/552/libmemcached.html])
 AC_CONFIG_SRCDIR([libmemcached/memcached.c])
 AC_CONFIG_AUX_DIR(config)
 AM_CONFIG_HEADER([config.h])
index 772b03443025d8d17650ab7ea7ef297d83407baa..9a4ee49ac8cdc28ed71a234833acc6517603e586 100644 (file)
@@ -74,6 +74,8 @@ man_MANS = libmemcached.3\
        memcached_increment_with_initial.3\
        memcached_mget.3\
        memcached_mget_by_key.3\
+        memcached_mget_execute.3 \
+        memcached_mget_execute_by_key.3 \
        memcached_prepend.3\
        memcached_prepend_by_key.3\
        memcached_replace.3\
@@ -212,6 +214,12 @@ memcached_mget.3: memcached_get.pod
 memcached_mget_by_key.3: memcached_get.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_get.pod > memcached_mget_by_key.3
 
+memcached_mget_execute.3: memcached_get.pod
+       ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_get.pod > memcached_mget_execute.3
+
+memcached_mget_execute_by_key.3: memcached_get.pod
+       ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_get.pod > memcached_mget_execute_by_key.3
+
 memcached_fetch.3: memcached_get.pod
        ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_get.pod > memcached_fetch.3
 
index 1fe7298f1ebe6ff541f69c1825af65d5879d6990..db2c9b5ae9d8295b665bf1c0fd18758366d42b9a 100644 (file)
@@ -1,6 +1,7 @@
 =head1 NAME
 
-memcached_get, memcached_mget, memcached_fetch - Get a value
+memcached_get, memcached_mget, memcached_fetch, memcached_mget_execute,
+memcached_mget_execute_by_key - Get a value
 
 =head1 LIBRARY
 
@@ -23,7 +24,8 @@ C Client Library for memcached (libmemcached, -lmemcached)
 
   memcached_return
   memcached_mget (memcached_st *ptr, 
-                  char **keys, size_t *key_length, 
+                  const char * const *keys, 
+                  const size_t *key_length, 
                   size_t number_of_keys);
   char *
   memcached_get_by_key(memcached_st *ptr, 
@@ -36,7 +38,8 @@ C Client Library for memcached (libmemcached, -lmemcached)
   memcached_return 
   memcached_mget_by_key(memcached_st *ptr, 
                         const char *master_key, size_t master_key_length,
-                        char **keys, size_t *key_length, 
+                        const char * const *keys, 
+                        const size_t *key_length, 
                         size_t number_of_keys);
 
   char *memcached_fetch (memcached_st *ptr,
@@ -44,12 +47,35 @@ C Client Library for memcached (libmemcached, -lmemcached)
                          size_t *value_length,
                          uint32_t *flags, 
                          memcached_return *error);
+
   memcached_return 
   memcached_fetch_execute(memcached_st *ptr, 
                           memcached_return (*callback[])(memcached_st *ptr, memcached_result_st *result, void *context),
                           void *context,
                           unsigned int number_of_callbacks);
 
+
+  memcached_return
+  memcached_mget_execute(memcached_st *ptr,
+                         const char * const *keys,
+                         const size_t *key_length,
+                         size_t number_of_keys,
+                         memcached_execute_function *callback,
+                         void *context,
+                         unsigned int number_of_callbacks);
+
+  memcached_return 
+  memcached_mget_execute_by_key(memcached_st *ptr,
+                                const char *master_key,
+                                size_t master_key_length,
+                                const char * const *keys,
+                                const size_t *key_length,
+                                size_t number_of_keys,
+                                memcached_execute_function *callback,
+                                void *context,
+                                unsigned int number_of_callbacks);
+
+
 =head1 DESCRIPTION
 
 memcached_get() is used to fetch an individual value from the server. You
@@ -99,6 +125,16 @@ you supply the calling function. Currently only one value is being passed
 to each function call. In the future there will be an option to allow this
 to be an array.
 
+memcached_mget_execute() and memcached_mget_execute_by_key() is
+similar to memcached_mget(), but it may trigger the supplied callbacks
+with result sets while sending out the queries. If you try to perform
+a really large multiget with memcached_mget() you may encounter a
+deadlock in the OS kernel (we fail to write data to the socket because
+the input buffer is full). memcached_mget_execute() solves this
+problem by processing some of the results before continuing sending
+out requests. Please note that this function is only available in the
+binary protocol.
+
 memcached_get_by_key() and memcached_mget_by_key() behave in a similar nature
 as memcached_get() and memcached_mget(). The difference is that they take
 a master key that is used for determining which server an object was stored
index 125e1f4b1ba169c33d6fa3c7df885fd3fe2305f9..a4495382683a7c32e89311e28e6fbe1f7746c320 100644 (file)
@@ -1,6 +1,6 @@
 =head1 NAME
 
-memcached_server_count, memcached_server_list, memcached_server_add, memcached_server_push - Manage server list
+memcached_server_count, memcached_server_list, memcached_server_add, memcached_server_push, memcached_server_get_last_disconnect - Manage server list
 
 =head1 LIBRARY
 
@@ -37,6 +37,9 @@ C Client Library for memcached (libmemcached, -lmemcached)
     memcached_server_by_key (memcached_st *ptr,  
                              const char *key, size_t key_length, 
                              memcached_return *error);
+  memcached_server_st *                          
+    memcached_server_get_last_disconnect (memcached_st *ptr)
+    
 
 =head1 DESCRIPTION
 
@@ -81,6 +84,11 @@ from its original structure and must be freed. If NULL is returned you
 should consult *error. The returning structure should be freed with
 memcached_server_free().
 
+memcached_server_get_last_disconnect() returns a pointer to the last server 
+for which there was a connection problem. It does not mean this particular 
+server is currently dead but if the library is reporting a server is, 
+the returned server is a very good candidate.
+
 =head1 RETURN
 
 Varies, see particular functions.
index 85dd2c7d426dad28caafe73b5c2cdf0c3761ca34..596313305fb5f055ea14706f3e4be393e47a4b79 100644 (file)
@@ -559,7 +559,7 @@ memcached_binary_protocol_callback_st interface_v0_impl= {
 #endif
 };
 
-void initialize_iterface_v0_handler(void)
+void initialize_interface_v0_handler(void)
 {
   interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GET]= get_command_handler;
   interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_SET]= set_command_handler;
index 86087c5a9b7010140a4de95a0ade3b1e2c5fb72f..c4495b5946802eca82b3e4ae11ada8573d9bf9ac 100644 (file)
@@ -234,7 +234,7 @@ int main(int argc, char **argv)
    * We need to initialize the handlers manually due to a bug in the
    * warnings generated by struct initialization in gcc (all the way up to 4.4)
    */
-  initialize_iterface_v0_handler();
+  initialize_interface_v0_handler();
 
   while ((cmd= getopt(argc, argv, "v1p:?")) != EOF)
   {
index e1ca41526a8898bce06e5fe2d9d50282d464982c..529169393fefd2e92215bc68b516cafd6aa67094 100644 (file)
@@ -2,6 +2,6 @@
 #ifndef MEMCACHED_LIGHT_H
 #define MEMCACHED_LIGHT_H
 
-extern void initialize_iterface_v0_handler(void);
+extern void initialize_interface_v0_handler(void);
 
 #endif
index 96b834632ccfaefa23d3ce5f1bd97acf1e84703d..30fc4e12a9fbfb6203075b55b948dcd4af523b3d 100644 (file)
@@ -136,7 +136,8 @@ LIBMEMCACHED_LOCAL
 void server_list_free(memcached_st *ptr, memcached_server_st *servers);
 
 LIBMEMCACHED_LOCAL
-memcached_return memcached_key_test(const char **keys, size_t *key_length,
+memcached_return memcached_key_test(const char * const *keys, 
+                                    const size_t *key_length,
                                     size_t number_of_keys);
 
 
index 5068f98b6294ba0e5d802c8584900fe7adc8424f..225b20990d1f2d6a4bec4de5cb1e8781b3fc8908 100644 (file)
@@ -33,7 +33,7 @@ extern "C" {
 #endif
 
 #define MEMCACHED_VERSION_STRING_LENGTH 24
-#define LIBMEMCACHED_VERSION_STRING "0.33"
+#define LIBMEMCACHED_VERSION_STRING "0.34"
 
 struct memcached_analysis_st {
   uint32_t average_item_size;
@@ -80,6 +80,7 @@ struct memcached_st {
   uint8_t hash;
   uint32_t continuum_points_counter;
   memcached_server_st *hosts;
+  memcached_server_st *last_disconnected_server;
   int32_t snd_timeout;
   int32_t rcv_timeout;
   uint32_t server_failure_limit;
@@ -112,6 +113,7 @@ struct memcached_st {
   memcached_trigger_delete_key delete_trigger;
   char prefix_key[MEMCACHED_PREFIX_KEY_MAX_SIZE];
   uint32_t number_of_replicas;
+  memcached_callback_st *callbacks;
 };
 
 LIBMEMCACHED_API
@@ -133,12 +135,12 @@ LIBMEMCACHED_API
 memcached_return memcached_delete(memcached_st *ptr, const char *key, size_t key_length,
                                   time_t expiration);
 LIBMEMCACHED_API
-memcached_return memcached_increment(memcached_st *ptr, 
+memcached_return memcached_increment(memcached_st *ptr,
                                      const char *key, size_t key_length,
                                      uint32_t offset,
                                      uint64_t *value);
 LIBMEMCACHED_API
-memcached_return memcached_decrement(memcached_st *ptr, 
+memcached_return memcached_decrement(memcached_st *ptr,
                                      const char *key, size_t key_length,
                                      uint32_t offset,
                                      uint64_t *value);
@@ -163,7 +165,7 @@ void memcached_stat_free(memcached_st *, memcached_stat_st *);
 LIBMEMCACHED_API
 memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_return *error);
 LIBMEMCACHED_API
-memcached_return memcached_stat_servername(memcached_stat_st *memc_stat, char *args, 
+memcached_return memcached_stat_servername(memcached_stat_st *memc_stat, char *args,
                                            char *hostname, unsigned int port);
 LIBMEMCACHED_API
 memcached_return memcached_flush(memcached_st *ptr, time_t expiration);
@@ -190,27 +192,27 @@ memcached_return memcached_flush_buffers(memcached_st *mem);
 /* Server Public functions */
 
 LIBMEMCACHED_API
-memcached_return memcached_server_add_udp(memcached_st *ptr, 
+memcached_return memcached_server_add_udp(memcached_st *ptr,
                                           const char *hostname,
                                           unsigned int port);
 LIBMEMCACHED_API
-memcached_return memcached_server_add_unix_socket(memcached_st *ptr, 
+memcached_return memcached_server_add_unix_socket(memcached_st *ptr,
                                                   const char *filename);
 LIBMEMCACHED_API
-memcached_return memcached_server_add(memcached_st *ptr, const char *hostname, 
+memcached_return memcached_server_add(memcached_st *ptr, const char *hostname,
                                       unsigned int port);
 
 LIBMEMCACHED_API
-memcached_return memcached_server_add_udp_with_weight(memcached_st *ptr, 
+memcached_return memcached_server_add_udp_with_weight(memcached_st *ptr,
                                                       const char *hostname,
                                                       unsigned int port,
                                                       uint32_t weight);
 LIBMEMCACHED_API
-memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr, 
+memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr,
                                                               const char *filename,
                                                               uint32_t weight);
 LIBMEMCACHED_API
-memcached_return memcached_server_add_with_weight(memcached_st *ptr, const char *hostname, 
+memcached_return memcached_server_add_with_weight(memcached_st *ptr, const char *hostname,
                                                   unsigned int port,
                                                   uint32_t weight);
 LIBMEMCACHED_API
@@ -219,14 +221,14 @@ LIBMEMCACHED_API
 memcached_return memcached_server_push(memcached_st *ptr, memcached_server_st *list);
 
 LIBMEMCACHED_API
-memcached_server_st *memcached_server_list_append(memcached_server_st *ptr, 
-                                                  const char *hostname, 
-                                                  unsigned int port, 
+memcached_server_st *memcached_server_list_append(memcached_server_st *ptr,
+                                                  const char *hostname,
+                                                  unsigned int port,
                                                   memcached_return *error);
 LIBMEMCACHED_API
-memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr, 
-                                                              const char *hostname, 
-                                                              unsigned int port, 
+memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr,
+                                                              const char *hostname,
+                                                              unsigned int port,
                                                               uint32_t weight,
                                                               memcached_return *error);
 LIBMEMCACHED_API
@@ -235,30 +237,30 @@ LIBMEMCACHED_API
 memcached_server_st *memcached_servers_parse(const char *server_strings);
 
 LIBMEMCACHED_API
-char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *memc_stat, 
+char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *memc_stat,
                                const char *key, memcached_return *error);
 LIBMEMCACHED_API
-char ** memcached_stat_get_keys(memcached_st *ptr, memcached_stat_st *memc_stat, 
+char ** memcached_stat_get_keys(memcached_st *ptr, memcached_stat_st *memc_stat,
                                 memcached_return *error);
 
 LIBMEMCACHED_API
-memcached_return memcached_delete_by_key(memcached_st *ptr, 
+memcached_return memcached_delete_by_key(memcached_st *ptr,
                                          const char *master_key, size_t master_key_length,
                                          const char *key, size_t key_length,
                                          time_t expiration);
 
 LIBMEMCACHED_API
-memcached_return memcached_fetch_execute(memcached_st *ptr, 
+memcached_return memcached_fetch_execute(memcached_st *ptr,
                                              memcached_execute_function *callback,
                                              void *context,
                                              unsigned int number_of_callbacks);
 
 LIBMEMCACHED_API
-memcached_return memcached_callback_set(memcached_st *ptr, 
-                                        memcached_callback flag, 
+memcached_return memcached_callback_set(memcached_st *ptr,
+                                        memcached_callback flag,
                                         void *data);
 LIBMEMCACHED_API
-void *memcached_callback_get(memcached_st *ptr, 
+void *memcached_callback_get(memcached_st *ptr,
                              memcached_callback flag,
                              memcached_return *error);
 
index 833ad0efba97f9b9eb126d7f177ab1bb4147aff9..f3ee14810081af272b06c092005410bbaee131d8 100644 (file)
@@ -60,7 +60,7 @@ static memcached_return set_socket_options(memcached_server_st *ptr)
     waittime.tv_sec= 0;
     waittime.tv_usec= ptr->root->snd_timeout;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, 
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO,
                       &waittime, (socklen_t)sizeof(struct timeval));
     WATCHPOINT_ASSERT(error == 0);
   }
@@ -75,7 +75,7 @@ static memcached_return set_socket_options(memcached_server_st *ptr)
     waittime.tv_sec= 0;
     waittime.tv_usec= ptr->root->rcv_timeout;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, 
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO,
                       &waittime, (socklen_t)sizeof(struct timeval));
     WATCHPOINT_ASSERT(error == 0);
   }
@@ -86,9 +86,9 @@ static memcached_return set_socket_options(memcached_server_st *ptr)
     int error;
     struct linger linger;
 
-    linger.l_onoff= 1; 
-    linger.l_linger= 0; /* By default on close() just drop the socket */ 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, 
+    linger.l_onoff= 1;
+    linger.l_linger= 0; /* By default on close() just drop the socket */
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER,
                       &linger, (socklen_t)sizeof(struct linger));
     WATCHPOINT_ASSERT(error == 0);
   }
@@ -98,7 +98,7 @@ static memcached_return set_socket_options(memcached_server_st *ptr)
     int flag= 1;
     int error;
 
-    error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, 
+    error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY,
                       &flag, (socklen_t)sizeof(int));
     WATCHPOINT_ASSERT(error == 0);
   }
@@ -107,7 +107,7 @@ static memcached_return set_socket_options(memcached_server_st *ptr)
   {
     int error;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, 
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF,
                       &ptr->root->send_size, (socklen_t)sizeof(int));
     WATCHPOINT_ASSERT(error == 0);
   }
@@ -116,21 +116,30 @@ static memcached_return set_socket_options(memcached_server_st *ptr)
   {
     int error;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF, 
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF,
                       &ptr->root->recv_size, (socklen_t)sizeof(int));
     WATCHPOINT_ASSERT(error == 0);
   }
 
-  /* For the moment, not getting a nonblocking mode will not be fatal */
-  if ((ptr->root->flags & MEM_NO_BLOCK) || ptr->root->connect_timeout)
-  {
-    int flags;
+  /* libmemcached will always use nonblocking IO to avoid write deadlocks */
+  int flags;
 
+  do
     flags= fcntl(ptr->fd, F_GETFL, 0);
-    unlikely (flags != -1)
-    {
-      (void)fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
-    }
+  while (flags == -1 && (errno == EINTR || errno == EAGAIN));
+
+  unlikely (flags == -1)
+    return MEMCACHED_CONNECTION_FAILURE;
+  else if ((flags & O_NONBLOCK) == 0)
+  {
+    int rval;
+
+    do
+      rval= fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
+    while (rval == -1 && (errno == EINTR || errno == EAGAIN));
+
+    unlikely (rval == -1)
+      return MEMCACHED_CONNECTION_FAILURE;
   }
 
   return MEMCACHED_SUCCESS;
@@ -156,7 +165,7 @@ static memcached_return unix_socket_connect(memcached_server_st *ptr)
     addrlen= (socklen_t) (strlen(servAddr.sun_path) + sizeof(servAddr.sun_family));
 
 test_connect:
-    if (connect(ptr->fd, 
+    if (connect(ptr->fd,
                 (struct sockaddr *)&servAddr,
                 sizeof(servAddr)) < 0)
     {
@@ -208,8 +217,8 @@ static memcached_return network_connect(memcached_server_st *ptr)
         continue;
       }
 
-      if ((ptr->fd= socket(use->ai_family, 
-                           use->ai_socktype, 
+      if ((ptr->fd= socket(use->ai_family,
+                           use->ai_socktype,
                            use->ai_protocol)) < 0)
       {
         ptr->cached_errno= errno;
@@ -219,16 +228,8 @@ static memcached_return network_connect(memcached_server_st *ptr)
 
       (void)set_socket_options(ptr);
 
-      int flags= 0;
-      if (ptr->root->connect_timeout)
-      {
-        flags= fcntl(ptr->fd, F_GETFL, 0);
-        if (flags != -1 && !(flags & O_NONBLOCK))
-          (void)fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
-      }
-
       /* connect to server */
-      while (ptr->fd != -1 && 
+      while (ptr->fd != -1 &&
              connect(ptr->fd, use->ai_addr, use->ai_addrlen) < 0)
       {
         ptr->cached_errno= errno;
@@ -253,25 +254,21 @@ static memcached_return network_connect(memcached_server_st *ptr)
             (void)close(ptr->fd);
             ptr->fd= -1;
           }
-        } 
+        }
         else if (errno == EISCONN) /* we are connected :-) */
         {
           break;
-        } 
+        }
         else if (errno != EINTR)
         {
           (void)close(ptr->fd);
           ptr->fd= -1;
           break;
-        } 
+        }
       }
 
       if (ptr->fd != -1)
       {
-        /* restore flags */ 
-        if (ptr->root->connect_timeout && (ptr->root->flags & MEM_NO_BLOCK) == 0) 
-          (void)fcntl(ptr->fd, F_SETFL, flags & ~O_NONBLOCK);
-
         WATCHPOINT_ASSERT(ptr->cursor_active == 0);
         ptr->server_failure_counter= 0;
         return MEMCACHED_SUCCESS;
@@ -290,9 +287,10 @@ static memcached_return network_connect(memcached_server_st *ptr)
       if (gettimeofday(&next_time, NULL) == 0)
         ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
     }
-    ptr->server_failure_counter+= 1;
+    ptr->server_failure_counter++;
     if (ptr->cached_errno == 0)
       return MEMCACHED_TIMEOUT;
+
     return MEMCACHED_ERRNO; /* The last error should be from connect() */
   }
 
@@ -310,22 +308,23 @@ memcached_return memcached_connect(memcached_server_st *ptr)
   WATCHPOINT_ASSERT(ptr->root);
   if (ptr->root->retry_timeout && ptr->root->server_failure_limit)
   {
-    struct timeval next_time;
+    struct timeval curr_time;
 
-    gettimeofday(&next_time, NULL);
+    gettimeofday(&curr_time, NULL);
 
     /* if we've had too many consecutive errors on this server, mark it dead. */
-    if (ptr->server_failure_counter > ptr->root->server_failure_limit)
+    if (ptr->server_failure_counter >= ptr->root->server_failure_limit)
     {
-      ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
+      ptr->next_retry= curr_time.tv_sec + ptr->root->retry_timeout;
       ptr->server_failure_counter= 0;
     }
 
-    if (next_time.tv_sec < ptr->next_retry)
+    if (curr_time.tv_sec < ptr->next_retry)
     {
       if (memcached_behavior_get(ptr->root, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS))
         run_distribution(ptr->root);
 
+      ptr->root->last_disconnected_server = ptr;
       return MEMCACHED_SERVER_MARKED_DEAD;
     }
   }
@@ -348,6 +347,8 @@ memcached_return memcached_connect(memcached_server_st *ptr)
     WATCHPOINT_ASSERT(0);
   }
 
+  unlikely ( rc != MEMCACHED_SUCCESS) ptr->root->last_disconnected_server = ptr;
+
   LIBMEMCACHED_MEMCACHED_CONNECT_END();
 
   return rc;
index 8e732834199ef4667fed3d93299f5bde247c3597..3fc7c037f696542e03c90d87c60728c91375b19e 100644 (file)
@@ -36,7 +36,7 @@ memcached_return memcached_delete_by_key(memcached_st *ptr,
     return MEMCACHED_NO_SERVERS;
 
   server_key= memcached_generate_hash(ptr, master_key, master_key_length);
-  to_write= (uint8_t) (ptr->flags & MEM_BUFFER_REQUESTS) ? 0 : 1;
+  to_write= (uint8_t)((ptr->flags & MEM_BUFFER_REQUESTS) ? 0 : 1);
   bool no_reply= (ptr->flags & MEM_NOREPLY);
      
   if (ptr->flags & MEM_BINARY_PROTOCOL) 
index d3f012197d6a177a30356b90161650c4739c7653..9c31e2b01a6cc6e260080eaaa4c7e79a6535cc3b 100644 (file)
@@ -64,7 +64,7 @@ memcached_result_st *memcached_fetch_result(memcached_st *ptr,
       return result;
     else if (*error == MEMCACHED_END)
       memcached_server_response_reset(server);
-    else
+    else if (*error != MEMCACHED_NOTFOUND)
       break;
   }
 
index 176be6ad5af2d388b10dc3978c673a409750d7bb..3436186f24a4803a32bf107e9c0f773489f8f53f 100644 (file)
@@ -15,12 +15,12 @@ char *memcached_get(memcached_st *ptr, const char *key,
 }
 
 static memcached_return memcached_mget_by_key_real(memcached_st *ptr, 
-                                       const char *master_key, 
-                                       size_t master_key_length,
-                                       const char **keys, 
-                                       size_t *key_length, 
-                                       size_t number_of_keys,
-                                      bool mget_mode);
+                                                   const char *master_key, 
+                                                   size_t master_key_length,
+                                                   const char * const *keys, 
+                                                   const size_t *key_length, 
+                                                   size_t number_of_keys,
+                                                   bool mget_mode);
 
 char *memcached_get_by_key(memcached_st *ptr, 
                            const char *master_key, 
@@ -42,10 +42,9 @@ char *memcached_get_by_key(memcached_st *ptr,
   }
 
   /* Request the key */
-  *error= memcached_mget_by_key_real(ptr, 
-                                master_key, 
-                                master_key_length, 
-                                (const char **)&key, &key_length, 1, false);
+  *error= memcached_mget_by_key_real(ptr, master_key, master_key_length, 
+                                     (const char * const *)&key, 
+                                     &key_length, 1, false);
 
   value= memcached_fetch(ptr, NULL, NULL, 
                          value_length, flags, error);
@@ -110,7 +109,8 @@ char *memcached_get_by_key(memcached_st *ptr,
 }
 
 memcached_return memcached_mget(memcached_st *ptr, 
-                                const char **keys, size_t *key_length, 
+                                const char * const *keys, 
+                                const size_t *key_length, 
                                 size_t number_of_keys)
 {
   return memcached_mget_by_key(ptr, NULL, 0, keys, key_length, number_of_keys);
@@ -119,17 +119,18 @@ memcached_return memcached_mget(memcached_st *ptr,
 static memcached_return binary_mget_by_key(memcached_st *ptr,
                                            unsigned int master_server_key,
                                            bool is_master_key_set,
-                                           const char **keys, size_t *key_length,
+                                           const char * const *keys, 
+                                           const size_t *key_length,
                                            size_t number_of_keys,
                                           bool mget_mode);
 
 static memcached_return memcached_mget_by_key_real(memcached_st *ptr, 
-                                       const char *master_key, 
-                                       size_t master_key_length,
-                                       const char **keys, 
-                                       size_t *key_length, 
-                                       size_t number_of_keys,
-                                      bool mget_mode)
+                                                   const char *master_key, 
+                                                   size_t master_key_length,
+                                                   const char * const *keys, 
+                                                   const size_t *key_length, 
+                                                   size_t number_of_keys,
+                                                   bool mget_mode)
 {
   unsigned int x;
   memcached_return rc= MEMCACHED_NOTFOUND;
@@ -155,7 +156,7 @@ static memcached_return memcached_mget_by_key_real(memcached_st *ptr,
 
   if (master_key && master_key_length)
   {
-    if ((ptr->flags & MEM_VERIFY_KEY) && (memcached_key_test((const char **)&master_key, &master_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED))
+    if ((ptr->flags & MEM_VERIFY_KEY) && (memcached_key_test((const char * const *)&master_key, &master_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED))
       return MEMCACHED_BAD_KEY_PROVIDED;
     master_server_key= memcached_generate_hash(ptr, master_key, master_key_length);
     is_master_key_set= true;
@@ -269,18 +270,60 @@ static memcached_return memcached_mget_by_key_real(memcached_st *ptr,
 memcached_return memcached_mget_by_key(memcached_st *ptr, 
                                        const char *master_key, 
                                        size_t master_key_length,
-                                       const char **keys, 
-                                       size_t *key_length, 
+                                       const char * const *keys, 
+                                       const size_t *key_length, 
                                        size_t number_of_keys)
 {
   return memcached_mget_by_key_real(ptr, master_key, master_key_length, keys, 
                                     key_length, number_of_keys, true);
 }
 
+memcached_return memcached_mget_execute(memcached_st *ptr,
+                                        const char * const *keys,
+                                        const size_t *key_length,
+                                        size_t number_of_keys,
+                                        memcached_execute_function *callback,
+                                        void *context,
+                                        unsigned int number_of_callbacks)
+{
+  return memcached_mget_execute_by_key(ptr, NULL, 0, keys, key_length,
+                                       number_of_keys, callback,
+                                       context, number_of_callbacks);
+}
+
+memcached_return memcached_mget_execute_by_key(memcached_st *ptr,
+                                               const char *master_key,
+                                               size_t master_key_length,
+                                               const char * const *keys,
+                                               const size_t *key_length,
+                                               size_t number_of_keys,
+                                               memcached_execute_function *callback,
+                                               void *context,
+                                               unsigned int number_of_callbacks)
+{
+  if ((ptr->flags & MEM_BINARY_PROTOCOL) == 0)
+    return MEMCACHED_NOT_SUPPORTED;
+
+  memcached_return rc;
+  memcached_callback_st *original_callbacks= ptr->callbacks;
+  memcached_callback_st cb= {
+    .callback= callback,
+    .context= context,
+    .number_of_callback= number_of_callbacks
+  };
+
+  ptr->callbacks= &cb;
+  rc= memcached_mget_by_key(ptr, master_key, master_key_length, keys,
+                            key_length, number_of_keys);
+  ptr->callbacks= original_callbacks;
+  return rc;
+}
+
 static memcached_return simple_binary_mget(memcached_st *ptr,
                                            unsigned int master_server_key,
                                            bool is_master_key_set,
-                                           const char **keys, size_t *key_length, 
+                                           const char * const *keys, 
+                                           const size_t *key_length, 
                                            size_t number_of_keys, bool mget_mode)
 {
   memcached_return rc= MEMCACHED_NOTFOUND;
@@ -338,6 +381,9 @@ static memcached_return simple_binary_mget(memcached_st *ptr,
       rc= MEMCACHED_SOME_ERRORS;
       continue;
     }
+    
+    /* We just want one pending response per server */
+    memcached_server_response_reset(&ptr->hosts[server_key]); 
     memcached_server_response_increment(&ptr->hosts[server_key]);    
     if ((x > 0 && x == ptr->io_key_prefetch) &&
         memcached_flush_buffers(ptr) != MEMCACHED_SUCCESS)
@@ -371,7 +417,6 @@ static memcached_return simple_binary_mget(memcached_st *ptr,
           memcached_io_reset(&ptr->hosts[x]);
           rc= MEMCACHED_SOME_ERRORS;
         }
-        memcached_server_response_increment(&ptr->hosts[x]);    
       }
     }
 
@@ -380,19 +425,19 @@ static memcached_return simple_binary_mget(memcached_st *ptr,
 }
 
 static memcached_return replication_binary_mget(memcached_st *ptr,
-                                             uint32_t* hash, bool* dead_servers,
-                                             const char **keys, size_t *key_length,
-                                             size_t number_of_keys, bool mget_mode)
+                                                uint32_t* hash, 
+                                                bool* dead_servers,
+                                                const char *const *keys, 
+                                                const size_t *key_length,
+                                                size_t number_of_keys)
 {
   memcached_return rc= MEMCACHED_NOTFOUND;
   uint32_t x;
 
-  int flush= number_of_keys == 1;
-
   for (uint32_t replica= 0; replica <= ptr->number_of_replicas; ++replica)
   {
     bool success= true;    
-    
+
     for (x= 0; x < number_of_keys; ++x)
     {
       if (hash[x] == ptr->number_of_hosts)
@@ -417,57 +462,39 @@ static memcached_return replication_binary_mget(memcached_st *ptr,
         }
       }
 
-      protocol_binary_request_getk request= {.bytes= {0}};
-      request.message.header.request.magic= PROTOCOL_BINARY_REQ;
-      if (mget_mode)
-        request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ;
-      else
-        request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
-
-      request.message.header.request.keylen= htons((uint16_t)key_length[x]);
-      request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
-      request.message.header.request.bodylen= htonl((uint32_t) key_length[x]);
+      protocol_binary_request_getk request= {
+        .message.header.request= {
+          .magic= PROTOCOL_BINARY_REQ,
+          .opcode= PROTOCOL_BINARY_CMD_GETK,
+          .keylen= htons((uint16_t)key_length[x]),
+          .datatype= PROTOCOL_BINARY_RAW_BYTES,
+          .bodylen= htonl((uint32_t)key_length[x])
+        }
+      };
 
+      /*
+       * We need to disable buffering to actually know that the request was
+       * successfully sent to the server (so that we should expect a result
+       * back). It would be nice to do this in buffered mode, but then it
+       * would be complex to handle all error situations if we got to send
+       * some of the messages, and then we failed on writing out some others
+       * and we used the callback interface from memcached_mget_execute so
+       * that we might have processed some of the responses etc. For now,
+       * just make sure we work _correctly_
+       */
       if ((memcached_io_write(&ptr->hosts[server], request.bytes,
                               sizeof(request.bytes), 0) == -1) ||
           (memcached_io_write(&ptr->hosts[server], keys[x],
-                              key_length[x], (char) flush) == -1))
+                              key_length[x], 1) == -1))
       {
         memcached_io_reset(&ptr->hosts[server]);
         dead_servers[server]= true;
         success= false;
         continue;
       }
-      memcached_server_response_increment(&ptr->hosts[server]);
-    }
 
-    if (mget_mode)
-    {
-      /*
-       * Send a noop command to flush the buffers
-       */
-      protocol_binary_request_noop request= {.bytes= {0}};
-      request.message.header.request.magic= PROTOCOL_BINARY_REQ;
-      request.message.header.request.opcode= PROTOCOL_BINARY_CMD_NOOP;
-      request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
-
-      for (x= 0; x < ptr->number_of_hosts; x++)
-        if (memcached_server_response_count(&ptr->hosts[x]))
-        {
-          if (memcached_io_write(&ptr->hosts[x], request.bytes,
-                                 sizeof(request.bytes), 1) == -1)
-          {
-            memcached_io_reset(&ptr->hosts[x]);
-            dead_servers[x]= true;
-            success= false;
-          }
-          memcached_server_response_increment(&ptr->hosts[x]);
-
-          /* mark all of the messages bound for this server as sent! */
-          for (x= 0; x < number_of_keys; ++x)
-            if (hash[x] == x)
-              hash[x]= ptr->number_of_hosts;
-        }
+      memcached_server_response_increment(&ptr->hosts[server]);
+      hash[x]= ptr->number_of_hosts;
     }
 
     if (success)
@@ -480,8 +507,10 @@ static memcached_return replication_binary_mget(memcached_st *ptr,
 static memcached_return binary_mget_by_key(memcached_st *ptr,
                                            unsigned int master_server_key,
                                            bool is_master_key_set,
-                                           const char **keys, size_t *key_length,
-                                           size_t number_of_keys, bool mget_mode)
+                                           const char * const *keys, 
+                                           const size_t *key_length,
+                                           size_t number_of_keys, 
+                                           bool mget_mode)
 {
   memcached_return rc;
 
@@ -513,7 +542,7 @@ static memcached_return binary_mget_by_key(memcached_st *ptr,
         hash[x]= memcached_generate_hash(ptr, keys[x], key_length[x]);
 
     rc= replication_binary_mget(ptr, hash, dead_servers, keys, 
-                                key_length, number_of_keys, mget_mode);
+                                key_length, number_of_keys);
 
     ptr->call_free(ptr, hash);
     ptr->call_free(ptr, dead_servers);
index 4ea04a9457b10ff05ac2071d9eda7f2f6a2e04de..bf3aeb5333728459bf243ee4af50c0daa13004bb 100644 (file)
@@ -6,8 +6,8 @@
  * Author: Brian Aker
  */
 
-#ifndef __MEMCACHED_GET_H__
-#define __MEMCACHED_GET_H__
+#ifndef LIBMEMCACHED_MEMCACHED_GET_H
+#define LIBMEMCACHED_MEMCACHED_GET_H
 
 #ifdef __cplusplus
 extern "C" {
@@ -23,7 +23,8 @@ char *memcached_get(memcached_st *ptr,
 
 LIBMEMCACHED_API
 memcached_return memcached_mget(memcached_st *ptr, 
-                                const char **keys, size_t *key_length, 
+                                const char * const *keys, 
+                                const size_t *key_length, 
                                 size_t number_of_keys);
 
 LIBMEMCACHED_API
@@ -38,8 +39,8 @@ LIBMEMCACHED_API
 memcached_return memcached_mget_by_key(memcached_st *ptr, 
                                        const char *master_key, size_t 
                                        master_key_length,
-                                       const char **keys, 
-                                       size_t *key_length, 
+                                       const char * const *keys, 
+                                       const size_t *key_length, 
                                        size_t number_of_keys);
 
 LIBMEMCACHED_API
@@ -53,10 +54,28 @@ memcached_result_st *memcached_fetch_result(memcached_st *ptr,
                                             memcached_result_st *result,
                                             memcached_return *error);
 
+LIBMEMCACHED_API
+memcached_return memcached_mget_execute(memcached_st *ptr,
+                                        const char * const *keys,
+                                        const size_t *key_length,
+                                        size_t number_of_keys,
+                                        memcached_execute_function *callback,
+                                        void *context,
+                                        unsigned int number_of_callbacks);
 
+LIBMEMCACHED_API
+memcached_return memcached_mget_execute_by_key(memcached_st *ptr,
+                                               const char *master_key,
+                                               size_t master_key_length,
+                                               const char * const *keys,
+                                               const size_t *key_length,
+                                               size_t number_of_keys,
+                                               memcached_execute_function *callback,
+                                               void *context,
+                                               unsigned int number_of_callbacks);
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* __MEMCACHED_GET_H__ */
+#endif /* LIBMEMCACHED_MEMCACHED_GET_H */
index f56c89f17c5e2c1bc58b59f4732829e7da095421..d664ab2b5a7b0e7eefa89d88f2b5f28e1d9971be 100644 (file)
@@ -2,7 +2,7 @@
 #include <math.h>
 
 /* Protoypes (static) */
-static memcached_return server_add(memcached_st *ptr, const char *hostname, 
+static memcached_return server_add(memcached_st *ptr, const char *hostname,
                                    unsigned int port,
                                    uint32_t weight,
                                    memcached_connection type);
@@ -36,7 +36,7 @@ static void sort_hosts(memcached_st *ptr)
 
 memcached_return run_distribution(memcached_st *ptr)
 {
-  switch (ptr->distribution) 
+  switch (ptr->distribution)
   {
   case MEMCACHED_DISTRIBUTION_CONSISTENT:
   case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA:
@@ -51,6 +51,8 @@ memcached_return run_distribution(memcached_st *ptr)
     WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */
   }
 
+  ptr->last_disconnected_server = NULL;
+
   return MEMCACHED_SUCCESS;
 }
 
@@ -77,7 +79,7 @@ void server_list_free(memcached_st *ptr, memcached_server_st *servers)
 static uint32_t ketama_server_hash(const char *key, unsigned int key_length, int alignment)
 {
   unsigned char results[16];
-  
+
   md5_signature((unsigned char*)key, key_length, results);
   return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24)
     | ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16)
@@ -155,7 +157,7 @@ memcached_return update_continuum(memcached_st *ptr)
   {
     memcached_continuum_item_st *new_ptr;
 
-    new_ptr= ptr->call_realloc(ptr, ptr->continuum, 
+    new_ptr= ptr->call_realloc(ptr, ptr->continuum,
                                sizeof(memcached_continuum_item_st) * (live_servers + MEMCACHED_CONTINUUM_ADDITION) * points_per_server);
 
     if (new_ptr == 0)
@@ -165,9 +167,9 @@ memcached_return update_continuum(memcached_st *ptr)
     ptr->continuum_count= live_servers + MEMCACHED_CONTINUUM_ADDITION;
   }
 
-  if (is_ketama_weighted) 
+  if (is_ketama_weighted)
   {
-    for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index) 
+    for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index)
     {
       if (list[host_index].weight == 0)
       {
@@ -178,27 +180,27 @@ memcached_return update_continuum(memcached_st *ptr)
     }
   }
 
-  for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index) 
+  for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index)
   {
     if (is_auto_ejecting && list[host_index].next_retry > now.tv_sec)
       continue;
 
-    if (is_ketama_weighted) 
+    if (is_ketama_weighted)
     {
         float pct = (float)list[host_index].weight / (float)total_weight;
         pointer_per_server= (uint32_t) ((floorf((float) (pct * MEMCACHED_POINTS_PER_SERVER_KETAMA / 4 * (float)live_servers + 0.0000000001))) * 4);
         pointer_per_hash= 4;
 #ifdef DEBUG
-        printf("ketama_weighted:%s|%d|%llu|%u\n", 
-               list[host_index].hostname, 
-               list[host_index].port,  
-               (unsigned long long)list[host_index].weight, 
+        printf("ketama_weighted:%s|%d|%llu|%u\n",
+               list[host_index].hostname,
+               list[host_index].port,
+               (unsigned long long)list[host_index].weight,
                pointer_per_server);
 #endif
     }
     for (pointer_index= 1;
          pointer_index <= pointer_per_server / pointer_per_hash;
-         ++pointer_index) 
+         ++pointer_index)
     {
       char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= "";
       size_t sort_host_length;
@@ -214,7 +216,7 @@ memcached_return update_continuum(memcached_st *ptr)
       else
       {
         sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH,
-                                            "%s:%d-%d", 
+                                            "%s:%d-%d",
                                             list[host_index].hostname,
                                             list[host_index].port, pointer_index - 1);
       }
@@ -247,7 +249,7 @@ memcached_return update_continuum(memcached_st *ptr)
   qsort(ptr->continuum, ptr->continuum_points_counter, sizeof(memcached_continuum_item_st), continuum_item_cmp);
 
 #ifdef DEBUG
-  for (pointer_index= 0; ptr->number_of_hosts && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++) 
+  for (pointer_index= 0; ptr->number_of_hosts && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++)
   {
     WATCHPOINT_ASSERT(ptr->continuum[pointer_index].value <= ptr->continuum[pointer_index + 1].value);
   }
@@ -267,7 +269,7 @@ memcached_return memcached_server_push(memcached_st *ptr, memcached_server_st *l
     return MEMCACHED_SUCCESS;
 
   count= list[0].count;
-  new_host_list= ptr->call_realloc(ptr, ptr->hosts, 
+  new_host_list= ptr->call_realloc(ptr, ptr->hosts,
                                    sizeof(memcached_server_st) * (count + ptr->number_of_hosts));
 
   if (!new_host_list)
@@ -285,7 +287,7 @@ memcached_return memcached_server_push(memcached_st *ptr, memcached_server_st *l
     WATCHPOINT_ASSERT(list[x].hostname[0] != 0);
     memcached_server_create(ptr, &ptr->hosts[ptr->number_of_hosts]);
     /* TODO check return type */
-    (void)memcached_server_create_with(ptr, &ptr->hosts[ptr->number_of_hosts], list[x].hostname, 
+    (void)memcached_server_create_with(ptr, &ptr->hosts[ptr->number_of_hosts], list[x].hostname,
                                        list[x].port, list[x].weight, list[x].type);
     ptr->number_of_hosts++;
   }
@@ -294,14 +296,14 @@ memcached_return memcached_server_push(memcached_st *ptr, memcached_server_st *l
   return run_distribution(ptr);
 }
 
-memcached_return memcached_server_add_unix_socket(memcached_st *ptr, 
+memcached_return memcached_server_add_unix_socket(memcached_st *ptr,
                                                   const char *filename)
 {
   return memcached_server_add_unix_socket_with_weight(ptr, filename, 0);
 }
 
-memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr, 
-                                                              const char *filename, 
+memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr,
+                                                              const char *filename,
                                                               uint32_t weight)
 {
   if (!filename)
@@ -310,41 +312,41 @@ memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr,
   return server_add(ptr, filename, 0, weight, MEMCACHED_CONNECTION_UNIX_SOCKET);
 }
 
-memcached_return memcached_server_add_udp(memcached_st *ptr, 
+memcached_return memcached_server_add_udp(memcached_st *ptr,
                                           const char *hostname,
                                           unsigned int port)
 {
   return memcached_server_add_udp_with_weight(ptr, hostname, port, 0);
 }
 
-memcached_return memcached_server_add_udp_with_weight(memcached_st *ptr, 
+memcached_return memcached_server_add_udp_with_weight(memcached_st *ptr,
                                                       const char *hostname,
                                                       unsigned int port,
                                                       uint32_t weight)
 {
   if (!port)
-    port= MEMCACHED_DEFAULT_PORT; 
+    port= MEMCACHED_DEFAULT_PORT;
 
   if (!hostname)
-    hostname= "localhost"; 
+    hostname= "localhost";
 
   return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_UDP);
 }
 
-memcached_return memcached_server_add(memcached_st *ptr, 
-                                      const char *hostname, 
+memcached_return memcached_server_add(memcached_st *ptr,
+                                      const char *hostname,
                                       unsigned int port)
 {
   return memcached_server_add_with_weight(ptr, hostname, port, 0);
 }
 
-memcached_return memcached_server_add_with_weight(memcached_st *ptr, 
-                                                  const char *hostname, 
+memcached_return memcached_server_add_with_weight(memcached_st *ptr,
+                                                  const char *hostname,
                                                   unsigned int port,
                                                   uint32_t weight)
 {
   if (!port)
-    port= MEMCACHED_DEFAULT_PORT; 
+    port= MEMCACHED_DEFAULT_PORT;
 
   if (!hostname)
     hostname= "localhost";
@@ -352,7 +354,7 @@ memcached_return memcached_server_add_with_weight(memcached_st *ptr,
   return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_TCP);
 }
 
-static memcached_return server_add(memcached_st *ptr, const char *hostname, 
+static memcached_return server_add(memcached_st *ptr, const char *hostname,
                                    unsigned int port,
                                    uint32_t weight,
                                    memcached_connection type)
@@ -362,8 +364,8 @@ static memcached_return server_add(memcached_st *ptr, const char *hostname,
   if ( (ptr->flags & MEM_USE_UDP && type != MEMCACHED_CONNECTION_UDP)
       || ( (type == MEMCACHED_CONNECTION_UDP) && !(ptr->flags & MEM_USE_UDP) ) )
     return MEMCACHED_INVALID_HOST_PROTOCOL;
-  
-  new_host_list= ptr->call_realloc(ptr, ptr->hosts, 
+
+  new_host_list= ptr->call_realloc(ptr, ptr->hosts,
                                    sizeof(memcached_server_st) * (ptr->number_of_hosts+1));
 
   if (new_host_list == NULL)
@@ -385,18 +387,18 @@ memcached_return memcached_server_remove(memcached_server_st *st_ptr)
   memcached_st *ptr= st_ptr->root;
   memcached_server_st *list= ptr->hosts;
 
-  for (x= 0, host_index= 0; x < ptr->number_of_hosts; x++) 
+  for (x= 0, host_index= 0; x < ptr->number_of_hosts; x++)
   {
-    if (strncmp(list[x].hostname, st_ptr->hostname, MEMCACHED_MAX_HOST_LENGTH) != 0 || list[x].port != st_ptr->port) 
+    if (strncmp(list[x].hostname, st_ptr->hostname, MEMCACHED_MAX_HOST_LENGTH) != 0 || list[x].port != st_ptr->port)
     {
       if (host_index != x)
         memcpy(list+host_index, list+x, sizeof(memcached_server_st));
       host_index++;
-    } 
+    }
   }
   ptr->number_of_hosts= host_index;
 
-  if (st_ptr->address_info) 
+  if (st_ptr->address_info)
   {
     freeaddrinfo(st_ptr->address_info);
     st_ptr->address_info= NULL;
@@ -406,16 +408,16 @@ memcached_return memcached_server_remove(memcached_server_st *st_ptr)
   return MEMCACHED_SUCCESS;
 }
 
-memcached_server_st *memcached_server_list_append(memcached_server_st *ptr, 
+memcached_server_st *memcached_server_list_append(memcached_server_st *ptr,
                                                   const char *hostname, unsigned int port,
                                                   memcached_return *error)
 {
   return memcached_server_list_append_with_weight(ptr, hostname, port, 0, error);
 }
 
-memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr, 
+memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr,
                                                               const char *hostname, unsigned int port,
-                                                              uint32_t weight, 
+                                                              uint32_t weight,
                                                               memcached_return *error)
 {
   unsigned int count;
@@ -425,14 +427,14 @@ memcached_server_st *memcached_server_list_append_with_weight(memcached_server_s
     return NULL;
 
   if (!port)
-    port= MEMCACHED_DEFAULT_PORT; 
+    port= MEMCACHED_DEFAULT_PORT;
 
   /* Increment count for hosts */
   count= 1;
   if (ptr != NULL)
   {
     count+= ptr[0].count;
-  } 
+  }
 
   new_host_list= (memcached_server_st *)realloc(ptr, sizeof(memcached_server_st) * count);
   if (!new_host_list)
index 411040f68e6adccaa7bf602c021b84523fcb25c9..693ce95c54ddc052642acc7fd98137c54fb8ebbc 100644 (file)
@@ -9,7 +9,7 @@
 
 typedef enum {
   MEM_READ,
-  MEM_WRITE,
+  MEM_WRITE
 } memc_read_or_write;
 
 static ssize_t io_flush(memcached_server_st *ptr, memcached_return *error);
@@ -18,18 +18,14 @@ static void increment_udp_message_id(memcached_server_st *ptr);
 static memcached_return io_wait(memcached_server_st *ptr,
                                 memc_read_or_write read_or_write)
 {
-  struct pollfd fds[1];
-  short flags= 0;
+  struct pollfd fds= {
+     .fd= ptr->fd,
+     .events = POLLIN
+  };
   int error;
 
-  if (read_or_write == MEM_WRITE) /* write */
-    flags= POLLOUT;
-  else
-    flags= POLLIN;
-
-  memset(&fds, 0, sizeof(struct pollfd));
-  fds[0].fd= ptr->fd;
-  fds[0].events= flags;
+  unlikely (read_or_write == MEM_WRITE) /* write */
+    fds.events= POLLOUT;
 
   /*
   ** We are going to block on write, but at least on Solaris we might block
@@ -41,26 +37,109 @@ static memcached_return io_wait(memcached_server_st *ptr,
   */
   if (read_or_write == MEM_WRITE)
   {
-    memcached_return rc=memcached_purge(ptr);
+    memcached_return rc= memcached_purge(ptr);
     if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED)
-       return MEMCACHED_FAILURE;
+      return MEMCACHED_FAILURE;
   }
 
-  error= poll(fds, 1, ptr->root->poll_timeout);
+  int timeout= ptr->root->poll_timeout;
+  if ((ptr->root->flags & MEM_NO_BLOCK) == 0)
+    timeout= -1;
+
+  error= poll(&fds, 1, timeout);
 
   if (error == 1)
     return MEMCACHED_SUCCESS;
   else if (error == 0)
-  {
     return MEMCACHED_TIMEOUT;
-  }
 
   /* Imposssible for anything other then -1 */
   WATCHPOINT_ASSERT(error == -1);
   memcached_quit_server(ptr, 1);
 
   return MEMCACHED_FAILURE;
+}
+
+/**
+ * Try to fill the input buffer for a server with as much
+ * data as possible.
+ *
+ * @param ptr the server to pack
+ */
+static bool repack_input_buffer(memcached_server_st *ptr)
+{
+  if (ptr->read_ptr != ptr->read_buffer)
+  {
+    /* Move all of the data to the beginning of the buffer so
+    ** that we can fit more data into the buffer...
+    */
+    memmove(ptr->read_buffer, ptr->read_ptr, ptr->read_buffer_length);
+    ptr->read_ptr= ptr->read_buffer;
+    ptr->read_data_length= ptr->read_buffer_length;
+  }
+
+  /* There is room in the buffer, try to fill it! */
+  if (ptr->read_buffer_length != MEMCACHED_MAX_BUFFER)
+  {
+    /* Just try a single read to grab what's available */
+    ssize_t nr= read(ptr->fd,
+                     ptr->read_ptr + ptr->read_data_length,
+                     MEMCACHED_MAX_BUFFER - ptr->read_data_length);
 
+    if (nr > 0)
+    {
+      ptr->read_data_length+= (size_t)nr;
+      ptr->read_buffer_length+= (size_t)nr;
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * If the we have callbacks connected to this server structure
+ * we may start process the input queue and fire the callbacks
+ * for the incomming messages. This function is _only_ called
+ * when the input buffer is full, so that we _know_ that we have
+ * at least _one_ message to process.
+ *
+ * @param ptr the server to star processing iput messages for
+ * @return true if we processed anything, false otherwise
+ */
+static bool process_input_buffer(memcached_server_st *ptr)
+{
+  /*
+  ** We might be able to process some of the response messages if we
+  ** have a callback set up
+  */
+  if (ptr->root->callbacks != NULL && (ptr->root->flags & MEM_USE_UDP) == 0)
+  {
+    /*
+     * We might have responses... try to read them out and fire
+     * callbacks
+     */
+    memcached_callback_st cb= *ptr->root->callbacks;
+
+    char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
+    memcached_return error;
+    error= memcached_response(ptr, buffer, sizeof(buffer),
+                              &ptr->root->result);
+    if (error == MEMCACHED_SUCCESS)
+    {
+      for (unsigned int x= 0; x < cb.number_of_callback; x++)
+      {
+        error= (*cb.callback[x])(ptr->root, &ptr->root->result, cb.context);
+        if (error != MEMCACHED_SUCCESS)
+          break;
+      }
+
+      /* @todo what should I do with the error message??? */
+    }
+    /* @todo what should I do with other error messages?? */
+    return true;
+  }
+
+  return false;
 }
 
 #ifdef UNUSED
@@ -385,6 +464,16 @@ static ssize_t io_flush(memcached_server_st *ptr,
         continue;
       case EAGAIN:
       {
+        /*
+         * We may be blocked on write because the input buffer
+         * is full. Let's check if we have room in our input
+         * buffer for more data and retry the write before
+         * waiting..
+         */
+        if (repack_input_buffer(ptr) ||
+            process_input_buffer(ptr))
+          continue;
+
         memcached_return rc;
         rc= io_wait(ptr, MEM_WRITE);
 
@@ -429,7 +518,7 @@ static ssize_t io_flush(memcached_server_st *ptr,
   return (ssize_t) return_length;
 }
 
-/* 
+/*
   Eventually we will just kill off the server with the problem.
 */
 void memcached_io_reset(memcached_server_st *ptr)
@@ -439,7 +528,7 @@ void memcached_io_reset(memcached_server_st *ptr)
 
 /**
  * Read a given number of bytes from the server and place it into a specific
- * buffer. Reset the IO channel on this server if an error occurs. 
+ * buffer. Reset the IO channel on this server if an error occurs.
  */
 memcached_return memcached_safe_read(memcached_server_st *ptr,
                                      void *dta,
@@ -526,7 +615,7 @@ static void increment_udp_message_id(memcached_server_st *ptr)
   uint16_t cur_req= get_udp_datagram_request_id(header);
   int msg_num= get_msg_num_from_request_id(cur_req);
   int thread_id= get_thread_id_from_request_id(cur_req);
-  
+
   if (((++msg_num) & UDP_REQUEST_ID_THREAD_MASK) != 0)
     msg_num= 0;
 
index c8a3a0eb4f5d59d325f88614adc7ce3dad099827..9aac1ebcfd24f0828b66e8d75ce1e5cd8f35716a 100644 (file)
@@ -1,6 +1,7 @@
 #include "common.h"
 
-memcached_return memcached_key_test(const char **keys, size_t *key_length, 
+memcached_return memcached_key_test(const char * const *keys, 
+                                    const size_t *key_length, 
                                     size_t number_of_keys)
 {
   uint32_t x;
@@ -14,8 +15,6 @@ memcached_return memcached_key_test(const char **keys, size_t *key_length,
     if (rc != MEMCACHED_SUCCESS)
       return rc;
     
-
-
     for (y= 0; y < *(key_length + x); y++)
     {
       if ((isgraph(keys[x][y])) == 0)
index 40c8ad61a5af5d0e956e2a4c246601d9fcf1acf3..9e5e31aba04e3f9d1416b15794ab35ea6067883d 100644 (file)
@@ -10,7 +10,7 @@ memcached_return memcached_purge(memcached_server_st *ptr)
   if (ptr->root->purging || /* already purging */
       (memcached_server_response_count(ptr) < ptr->root->io_msg_watermark &&
       ptr->io_bytes_sent < ptr->root->io_bytes_watermark) ||
-      (ptr->io_bytes_sent > ptr->root->io_bytes_watermark &&
+      (ptr->io_bytes_sent >= ptr->root->io_bytes_watermark &&
       memcached_server_response_count(ptr) < 2))
   {
     return MEMCACHED_SUCCESS;
index 9a200a1ee935041b6386a82aacb6d00e505bdd25..8ac76d48aeb97b8d2323fd24526f58223108d701 100644 (file)
@@ -2,10 +2,10 @@
 
 /*
   This closes all connections (forces flush of input as well).
-  
-  Maybe add a host specific, or key specific version? 
-  
-  The reason we send "quit" is that in case we have buffered IO, this 
+
+  Maybe add a host specific, or key specific version?
+
+  The reason we send "quit" is that in case we have buffered IO, this
   will force data to be completed.
 */
 
@@ -18,7 +18,7 @@ void memcached_quit_server(memcached_server_st *ptr, uint8_t io_death)
       memcached_return rc;
       char buffer[MEMCACHED_MAX_BUFFER];
 
-      if (ptr->root->flags & MEM_BINARY_PROTOCOL) 
+      if (ptr->root->flags & MEM_BINARY_PROTOCOL)
       {
         protocol_binary_request_quit request = {.bytes= {0}};
         request.message.header.request.magic = PROTOCOL_BINARY_REQ;
@@ -30,7 +30,7 @@ void memcached_quit_server(memcached_server_st *ptr, uint8_t io_death)
         rc= memcached_do(ptr, "quit\r\n", 6, 1);
 
       WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_FETCH_NOTFINISHED);
-      
+
       /* read until socket is closed, or there is an error
        * closing the socket before all data is read
        * results in server throwing away all data which is
@@ -39,6 +39,15 @@ void memcached_quit_server(memcached_server_st *ptr, uint8_t io_death)
       ssize_t nread;
       while (memcached_io_read(ptr, buffer, sizeof(buffer)/sizeof(*buffer),
                                &nread) == MEMCACHED_SUCCESS);
+
+      /*
+       * memcached_io_read may call memcached_quit_server with io_death if
+       * it encounters problems, but we don't care about those occurences.
+       * The intention of that loop is to drain the data sent from the
+       * server to ensure that the server processed all of the data we
+       * sent to the server.
+       */
+      ptr->server_failure_counter= 0;
     }
     memcached_io_close(ptr);
 
@@ -49,14 +58,14 @@ void memcached_quit_server(memcached_server_st *ptr, uint8_t io_death)
     memcached_server_response_reset(ptr);
   }
 
-  ptr->server_failure_counter++;
+  if(io_death) ptr->server_failure_counter++;
 }
 
 void memcached_quit(memcached_st *ptr)
 {
   unsigned int x;
 
-  if (ptr->hosts == NULL || 
+  if (ptr->hosts == NULL ||
       ptr->number_of_hosts == 0)
     return;
 
index ea13bb151ce09065cd37f4d958206a7140d1465b..fe13ddd53f8045efabdfb51e8fd4fbf583f21555 100644 (file)
@@ -356,8 +356,14 @@ static memcached_return binary_read_one_response(memcached_server_st *ptr,
   {
     switch (header.response.opcode)
     {
-    case PROTOCOL_BINARY_CMD_GETK:
     case PROTOCOL_BINARY_CMD_GETKQ:
+      /*
+       * We didn't increment the response counter for the GETKQ packet
+       * (only the final NOOP), so we need to increment the counter again.
+       */ 
+      memcached_server_response_increment(ptr); 
+      /* FALLTHROUGH */
+    case PROTOCOL_BINARY_CMD_GETK:
       {
         uint16_t keylen= header.response.keylen;
         memcached_result_reset(result);
index 7b9c2117bff972b893f17eca23e1d0ae05b4aaf9..2ecbe214d9fb416f60b9fca98290bb70ee37977f 100644 (file)
@@ -16,14 +16,14 @@ memcached_server_st *memcached_server_create(memcached_st *memc, memcached_serve
   }
   else
     memset(ptr, 0, sizeof(memcached_server_st));
-  
+
   ptr->root= memc;
 
   return ptr;
 }
 
-memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host, 
-                                                  const char *hostname, unsigned int port, 
+memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host,
+                                                  const char *hostname, unsigned int port,
                                                   uint32_t weight, memcached_connection type)
 {
   host= memcached_server_create(memc, host);
@@ -76,7 +76,7 @@ memcached_server_st *memcached_server_clone(memcached_server_st *clone, memcache
   if (ptr == NULL)
     return NULL;
 
-  rv = memcached_server_create_with(ptr->root, clone, 
+  rv = memcached_server_create_with(ptr->root, clone,
                                     ptr->hostname, ptr->port, ptr->weight,
                                     ptr->type);
   if (rv != NULL)
@@ -90,7 +90,7 @@ memcached_server_st *memcached_server_clone(memcached_server_st *clone, memcache
 
 }
 
-memcached_return memcached_server_cursor(memcached_st *ptr, 
+memcached_return memcached_server_cursor(memcached_st *ptr,
                                          memcached_server_function *callback,
                                          void *context,
                                          unsigned int number_of_callbacks)
@@ -119,7 +119,7 @@ memcached_server_st *memcached_server_by_key(memcached_st *ptr,  const char *key
 {
   uint32_t server_key;
 
-  *error= memcached_validate_key_length(key_length, 
+  *error= memcached_validate_key_length(key_length,
                                         ptr->flags & MEM_BINARY_PROTOCOL);
   unlikely (*error != MEMCACHED_SUCCESS)
     return NULL;
@@ -146,7 +146,7 @@ const char *memcached_server_error(memcached_server_st *ptr)
 {
   if (ptr)
     return ptr->cached_server_error;
-  else 
+  else
     return NULL;
 }
 
@@ -154,3 +154,8 @@ void memcached_server_error_reset(memcached_server_st *ptr)
 {
   ptr->cached_server_error[0]= 0;
 }
+
+memcached_server_st *memcached_server_get_last_disconnect(memcached_st *ptr)
+{
+  return ptr->last_disconnected_server;
+}
index ec1014a61b1aa85f955305e676dcca3c35acae88..de57eaec3534cf69cba4fd5cd91707cf666d4740 100644 (file)
@@ -49,13 +49,13 @@ struct memcached_server_st {
 #define memcached_server_response_count(A) (A)->cursor_active
 
 LIBMEMCACHED_API
-memcached_return memcached_server_cursor(memcached_st *ptr, 
+memcached_return memcached_server_cursor(memcached_st *ptr,
                                          memcached_server_function *callback,
                                          void *context,
                                          unsigned int number_of_callbacks);
 
 LIBMEMCACHED_API
-memcached_server_st *memcached_server_by_key(memcached_st *ptr,  const char *key, 
+memcached_server_st *memcached_server_by_key(memcached_st *ptr,  const char *key,
                                              size_t key_length, memcached_return *error);
 
 LIBMEMCACHED_API
@@ -71,8 +71,8 @@ LIBMEMCACHED_API
 memcached_server_st *memcached_server_create(memcached_st *memc, memcached_server_st *ptr);
 
 LIBMEMCACHED_API
-memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host, 
-                                                  const char *hostname, unsigned int port, 
+memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host,
+                                                  const char *hostname, unsigned int port,
                                                   uint32_t weight, memcached_connection type);
 
 LIBMEMCACHED_API
@@ -86,6 +86,9 @@ memcached_analysis_st *memcached_analyze(memcached_st *memc, memcached_stat_st *
 LIBMEMCACHED_API
 memcached_return memcached_server_remove(memcached_server_st *st_ptr);
 
+LIBMEMCACHED_API
+memcached_server_st *memcached_server_get_last_disconnect(memcached_st *ptr);
+
 #ifdef __cplusplus
 }
 #endif
index 0f4a97f75675446f1e5f8e00c06494f47c166634..04928f042eeeb799617a92fdeb14ea3362fd29f0 100644 (file)
@@ -185,8 +185,6 @@ char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *memc_stat,
     length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_items);
   else if (!memcmp("total_items", key, strlen("total_items")))
     length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_items);
-  else if (!memcmp("bytes", key, strlen("bytes")))
-    length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
   else if (!memcmp("curr_connections", key, strlen("curr_connections")))
     length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_connections);
   else if (!memcmp("total_connections", key, strlen("total_connections")))
@@ -207,6 +205,8 @@ char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *memc_stat,
     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read);
   else if (!memcmp("bytes_written", key, strlen("bytes_written")))
     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written);
+  else if (!memcmp("bytes", key, strlen("bytes")))
+    length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
   else if (!memcmp("limit_maxbytes", key, strlen("limit_maxbytes")))
     length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes);
   else if (!memcmp("threads", key, strlen("threads")))
index d7bb470ab915d9868df4133e8b7f26260f267294..9d2537d4064f8a5804c0584a7c4e087238f35617 100644 (file)
@@ -6,8 +6,8 @@
  * Author: Brian Aker
  */
 
-#ifndef __MEMCACHED_TYPES_H__
-#define __MEMCACHED_TYPES_H__
+#ifndef LIBMEMCACHED_MEMCACHED_TYPES_H
+#define LIBMEMCACHED_MEMCACHED_TYPES_H
 
 #ifdef __cplusplus
 extern "C" {
@@ -37,8 +37,14 @@ typedef memcached_return (*memcached_trigger_delete_key)(memcached_st *ptr,
 typedef memcached_return (*memcached_dump_func)(memcached_st *ptr,  
                                                 const char *key, size_t key_length, void *context);
 
+typedef struct {
+  memcached_execute_function *callback;
+  void *context;
+  unsigned int number_of_callback;
+} memcached_callback_st;
+
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* __MEMCACHED_TYPES_H__ */
+#endif /* LIBMEMCACHED_MEMCACHED_TYPES_H */
index 36fd047dba243b0d9d0ef54b1c3d3b90cef66fbf..1f340e468b85e6855f3a12a38f6e159a7a95c73d 100644 (file)
@@ -80,9 +80,12 @@ you will need to install %{name}-devel.
 %exclude %{_libdir}/libmemcached.a
 %exclude %{_libdir}/libmemcached.la
 %exclude %{_libdir}/libmemcachedutil.a
+%exclude %{_libdir}/libmemcachedprotocol.a
 %exclude %{_libdir}/libmemcachedutil.la
+%exclude %{_libdir}/libmemcachedprotocol.la
 %{_libdir}/libmemcached.so.*
 %{_libdir}/libmemcachedutil.so.*
+%{_libdir}/libmemcachedprotocol.so.*
 %{_mandir}/man1/mem*
 
 
@@ -92,6 +95,7 @@ you will need to install %{name}-devel.
 %{_includedir}/libmemcached
 %{_libdir}/libmemcached.so
 %{_libdir}/libmemcachedutil.so
+%{_libdir}/libmemcachedprotocol.so
 %{_libdir}/pkgconfig/libmemcached.pc
 %{_mandir}/man3/libmemcached*.3.gz
 %{_mandir}/man3/memcached_*.3.gz
index 3b4879df4bef1c1c260935867fbfbae4ad42f94f..19ad086ad624565330890fcdf0030e1ae39267f0 100644 (file)
@@ -37,14 +37,14 @@ static pairs_st *global_pairs;
 static char *global_keys[GLOBAL_COUNT];
 static size_t global_keys_length[GLOBAL_COUNT];
 
-static test_return cleanup_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t cleanup_pairs(memcached_st *memc __attribute__((unused)))
 {
   pairs_free(global_pairs);
 
   return 0;
 }
 
-static test_return generate_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t generate_pairs(memcached_st *memc __attribute__((unused)))
 {
   unsigned long long x;
   global_pairs= pairs_generate(GLOBAL_COUNT, 400);
@@ -59,7 +59,7 @@ static test_return generate_pairs(memcached_st *memc __attribute__((unused)))
   return 0;
 }
 
-static test_return drizzle(memcached_st *memc)
+static test_return_t drizzle(memcached_st *memc)
 {
   unsigned int x;
   memcached_return rc;
@@ -153,7 +153,7 @@ static memcached_return enable_consistent(memcached_st *memc)
   Set the value, then quit to make sure it is flushed.
   Come back in and test that add fails.
 */
-static test_return add_test(memcached_st *memc)
+static test_return_t add_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -184,7 +184,7 @@ static test_return add_test(memcached_st *memc)
  * repeating add_tests many times
  * may show a problem in timing
  */
-static test_return many_adds(memcached_st *memc)
+static test_return_t many_adds(memcached_st *memc)
 {
   unsigned int i;
   for (i = 0; i < TEST_COUNTER; i++)
index 844666d17b9772837990e1bec699171d07ce1a8c..2bc0192b3f0cf39f510eefcb729ae4b2852e2ce2 100644 (file)
@@ -11,6 +11,7 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <signal.h>
 #include <unistd.h>
 #include <time.h>
 #include "server.h"
@@ -41,7 +42,7 @@ static pairs_st *global_pairs;
 static const char *global_keys[GLOBAL_COUNT];
 static size_t global_keys_length[GLOBAL_COUNT];
 
-static test_return  init_test(memcached_st *not_used __attribute__((unused)))
+static test_return_t  init_test(memcached_st *not_used __attribute__((unused)))
 {
   memcached_st memc;
 
@@ -51,7 +52,7 @@ static test_return  init_test(memcached_st *not_used __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return  server_list_null_test(memcached_st *ptr __attribute__((unused)))
+static test_return_t  server_list_null_test(memcached_st *ptr __attribute__((unused)))
 {
   memcached_server_st *server_list;
   memcached_return rc;
@@ -81,7 +82,7 @@ static memcached_return  server_display_function(memcached_st *ptr __attribute__
   return MEMCACHED_SUCCESS;
 }
 
-static test_return  server_sort_test(memcached_st *ptr __attribute__((unused)))
+static test_return_t  server_sort_test(memcached_st *ptr __attribute__((unused)))
 {
   uint32_t x;
   uint32_t bigger= 0; /* Prime the value for the assert in server_display_function */
@@ -111,7 +112,7 @@ static test_return  server_sort_test(memcached_st *ptr __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return  server_sort2_test(memcached_st *ptr __attribute__((unused)))
+static test_return_t  server_sort2_test(memcached_st *ptr __attribute__((unused)))
 {
   uint32_t bigger= 0; /* Prime the value for the assert in server_display_function */
   memcached_return rc;
@@ -152,7 +153,7 @@ static memcached_return  server_display_unsort_function(memcached_st *ptr __attr
   return MEMCACHED_SUCCESS;
 }
 
-static test_return  server_unsort_test(memcached_st *ptr __attribute__((unused)))
+static test_return_t  server_unsort_test(memcached_st *ptr __attribute__((unused)))
 {
   uint32_t x;
   uint32_t counter= 0; /* Prime the value for the assert in server_display_function */
@@ -187,7 +188,7 @@ static test_return  server_unsort_test(memcached_st *ptr __attribute__((unused))
   return TEST_SUCCESS;
 }
 
-static test_return  allocation_test(memcached_st *not_used __attribute__((unused)))
+static test_return_t  allocation_test(memcached_st *not_used __attribute__((unused)))
 {
   memcached_st *memc;
   memc= memcached_create(NULL);
@@ -197,7 +198,7 @@ static test_return  allocation_test(memcached_st *not_used __attribute__((unused
   return TEST_SUCCESS;
 }
 
-static test_return  clone_test(memcached_st *memc)
+static test_return_t  clone_test(memcached_st *memc)
 {
   /* All null? */
   {
@@ -264,7 +265,7 @@ static test_return  clone_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return userdata_test(memcached_st *memc)
+static test_return_t userdata_test(memcached_st *memc)
 {
   void* foo= NULL;
   assert(memcached_set_user_data(memc, foo) == NULL);
@@ -274,7 +275,7 @@ static test_return userdata_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  connection_test(memcached_st *memc)
+static test_return_t  connection_test(memcached_st *memc)
 {
   memcached_return rc;
 
@@ -284,7 +285,7 @@ static test_return  connection_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  error_test(memcached_st *memc)
+static test_return_t  error_test(memcached_st *memc)
 {
   memcached_return rc;
   uint32_t values[] = { 851992627U, 2337886783U, 3196981036U, 4001849190U,
@@ -312,7 +313,7 @@ static test_return  error_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  set_test(memcached_st *memc)
+static test_return_t  set_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -326,7 +327,7 @@ static test_return  set_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  append_test(memcached_st *memc)
+static test_return_t  append_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "fig";
@@ -363,7 +364,7 @@ static test_return  append_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  append_binary_test(memcached_st *memc)
+static test_return_t  append_binary_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "numbers";
@@ -409,7 +410,7 @@ static test_return  append_binary_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  cas2_test(memcached_st *memc)
+static test_return_t  cas2_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"fudge", "son", "food"};
@@ -453,7 +454,7 @@ static test_return  cas2_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  cas_test(memcached_st *memc)
+static test_return_t  cas_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "fun";
@@ -513,7 +514,7 @@ static test_return  cas_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  prepend_test(memcached_st *memc)
+static test_return_t  prepend_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "fig";
@@ -554,7 +555,7 @@ static test_return  prepend_test(memcached_st *memc)
   Set the value, then quit to make sure it is flushed.
   Come back in and test that add fails.
 */
-static test_return  add_test(memcached_st *memc)
+static test_return_t  add_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -588,7 +589,7 @@ static test_return  add_test(memcached_st *memc)
 ** because the connects starts to time out (the test doesn't do much
 ** anyway, so just loop 10 iterations)
 */
-static test_return  add_wrapper(memcached_st *memc)
+static test_return_t  add_wrapper(memcached_st *memc)
 {
   unsigned int x;
   unsigned int max= 10000;
@@ -605,7 +606,7 @@ static test_return  add_wrapper(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  replace_test(memcached_st *memc)
+static test_return_t  replace_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -625,7 +626,7 @@ static test_return  replace_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  delete_test(memcached_st *memc)
+static test_return_t  delete_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -642,7 +643,7 @@ static test_return  delete_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  flush_test(memcached_st *memc)
+static test_return_t  flush_test(memcached_st *memc)
 {
   memcached_return rc;
 
@@ -661,7 +662,7 @@ static memcached_return  server_function(memcached_st *ptr __attribute__((unused
   return MEMCACHED_SUCCESS;
 }
 
-static test_return  memcached_server_cursor_test(memcached_st *memc)
+static test_return_t  memcached_server_cursor_test(memcached_st *memc)
 {
   char context[8];
   strcpy(context, "foo bad");
@@ -672,7 +673,7 @@ static test_return  memcached_server_cursor_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  bad_key_test(memcached_st *memc)
+static test_return_t  bad_key_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo bad";
@@ -774,7 +775,7 @@ static memcached_return  read_through_trigger(memcached_st *memc __attribute__((
   return memcached_result_set_value(result, READ_THROUGH_VALUE, strlen(READ_THROUGH_VALUE));
 }
 
-static test_return  read_through(memcached_st *memc)
+static test_return_t  read_through(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -822,7 +823,7 @@ static memcached_return  delete_trigger(memcached_st *ptr __attribute__((unused)
   return MEMCACHED_SUCCESS;
 }
 
-static test_return  delete_through(memcached_st *memc)
+static test_return_t  delete_through(memcached_st *memc)
 {
   memcached_trigger_delete_key callback;
   memcached_return rc;
@@ -835,7 +836,7 @@ static test_return  delete_through(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  get_test(memcached_st *memc)
+static test_return_t  get_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -856,7 +857,7 @@ static test_return  get_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  get_test2(memcached_st *memc)
+static test_return_t  get_test2(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -883,7 +884,7 @@ static test_return  get_test2(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  set_test2(memcached_st *memc)
+static test_return_t  set_test2(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -902,7 +903,7 @@ static test_return  set_test2(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  set_test3(memcached_st *memc)
+static test_return_t  set_test3(memcached_st *memc)
 {
   memcached_return rc;
   char *value;
@@ -933,7 +934,7 @@ static test_return  set_test3(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  get_test3(memcached_st *memc)
+static test_return_t  get_test3(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -969,7 +970,7 @@ static test_return  get_test3(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  get_test4(memcached_st *memc)
+static test_return_t  get_test4(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -1013,7 +1014,7 @@ static test_return  get_test4(memcached_st *memc)
  * dereference a NIL-pointer if you issue a multi-get and don't read out all
  * responses before you execute a storage command.
  */
-static test_return get_test5(memcached_st *memc)
+static test_return_t get_test5(memcached_st *memc)
 {
   /*
   ** Request the same key twice, to ensure that we hash to the same server
@@ -1053,7 +1054,7 @@ static test_return get_test5(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  mget_end(memcached_st *memc)
+static test_return_t  mget_end(memcached_st *memc)
 {
   const char *keys[]= { "foo", "foo2" };
   size_t lengths[]= { 3, 4 };
@@ -1118,7 +1119,7 @@ static test_return  mget_end(memcached_st *memc)
 }
 
 /* Do not copy the style of this code, I just access hosts to testthis function */
-static test_return  stats_servername_test(memcached_st *memc)
+static test_return_t  stats_servername_test(memcached_st *memc)
 {
   memcached_return rc;
   memcached_stat_st memc_stat;
@@ -1129,7 +1130,7 @@ static test_return  stats_servername_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  increment_test(memcached_st *memc)
+static test_return_t  increment_test(memcached_st *memc)
 {
   uint64_t new_number;
   memcached_return rc;
@@ -1154,7 +1155,7 @@ static test_return  increment_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  increment_with_initial_test(memcached_st *memc)
+static test_return_t  increment_with_initial_test(memcached_st *memc)
 {
   if (memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) != 0)
   {
@@ -1176,7 +1177,7 @@ static test_return  increment_with_initial_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  decrement_test(memcached_st *memc)
+static test_return_t  decrement_test(memcached_st *memc)
 {
   uint64_t new_number;
   memcached_return rc;
@@ -1201,7 +1202,7 @@ static test_return  decrement_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  decrement_with_initial_test(memcached_st *memc)
+static test_return_t  decrement_with_initial_test(memcached_st *memc)
 {
   if (memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) != 0)
   {
@@ -1223,7 +1224,7 @@ static test_return  decrement_with_initial_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  quit_test(memcached_st *memc)
+static test_return_t  quit_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "fudge";
@@ -1243,7 +1244,7 @@ static test_return  quit_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  mget_result_test(memcached_st *memc)
+static test_return_t  mget_result_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"fudge", "son", "food"};
@@ -1300,7 +1301,7 @@ static test_return  mget_result_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  mget_result_alloc_test(memcached_st *memc)
+static test_return_t  mget_result_alloc_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"fudge", "son", "food"};
@@ -1362,7 +1363,7 @@ static memcached_return callback_counter(memcached_st *ptr __attribute__((unused
   return MEMCACHED_SUCCESS;
 }
 
-static test_return  mget_result_function(memcached_st *memc)
+static test_return_t  mget_result_function(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"fudge", "son", "food"};
@@ -1393,7 +1394,7 @@ static test_return  mget_result_function(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  mget_test(memcached_st *memc)
+static test_return_t  mget_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"fudge", "son", "food"};
@@ -1448,7 +1449,71 @@ static test_return  mget_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  get_stats_keys(memcached_st *memc)
+static test_return_t mget_execute(memcached_st *memc)
+{
+  bool binary= false;
+  if (memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) != 0)
+    binary= true;
+
+  /*
+   * I only want to hit _one_ server so I know the number of requests I'm
+   * sending in the pipeline.
+   */
+  uint32_t number_of_hosts= memc->number_of_hosts;
+  memc->number_of_hosts= 1;
+
+  int max_keys= binary ? 20480 : 1;
+  
+
+  char **keys= calloc((size_t)max_keys, sizeof(char*));
+  size_t *key_length=calloc((size_t)max_keys, sizeof(size_t));
+
+  /* First add all of the items.. */
+  char blob[1024] = {0};
+  memcached_return rc;
+  for (int x= 0; x < max_keys; ++x)
+  {
+    char k[251];
+    key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
+    keys[x]= strdup(k);
+    assert(keys[x] != NULL);
+    rc= memcached_add(memc, keys[x], key_length[x], blob, sizeof(blob), 0, 0);
+    assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+  }
+
+  /* Try to get all of them with a large multiget */
+  unsigned int counter= 0;
+  memcached_execute_function callbacks[1]= { [0]= &callback_counter };
+  rc= memcached_mget_execute(memc, (const char**)keys, key_length,
+                             (size_t)max_keys, callbacks, &counter, 1);
+
+  if (binary)
+  {
+    assert(rc == MEMCACHED_SUCCESS);
+
+    rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+    assert(rc == MEMCACHED_END);
+
+    /* Verify that we got all of the items */
+    assert(counter == (unsigned int)max_keys);
+  }
+  else
+  {
+    assert(rc == MEMCACHED_NOT_SUPPORTED);
+    assert(counter == 0);
+  }
+
+  /* Release all allocated resources */
+  for (int x= 0; x < max_keys; ++x)
+    free(keys[x]);
+  free(keys);
+  free(key_length);
+
+  memc->number_of_hosts= number_of_hosts;
+  return TEST_SUCCESS;
+}
+
+static test_return_t  get_stats_keys(memcached_st *memc)
 {
  char **list;
  char **ptr;
@@ -1466,7 +1531,7 @@ static test_return  get_stats_keys(memcached_st *memc)
  return TEST_SUCCESS;
 }
 
-static test_return  version_string_test(memcached_st *memc __attribute__((unused)))
+static test_return_t  version_string_test(memcached_st *memc __attribute__((unused)))
 {
   const char *version_string;
 
@@ -1477,7 +1542,7 @@ static test_return  version_string_test(memcached_st *memc __attribute__((unused
   return TEST_SUCCESS;
 }
 
-static test_return  get_stats(memcached_st *memc)
+static test_return_t  get_stats(memcached_st *memc)
 {
  unsigned int x;
  char **list;
@@ -1505,7 +1570,7 @@ static test_return  get_stats(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  add_host_test(memcached_st *memc)
+static test_return_t  add_host_test(memcached_st *memc)
 {
   unsigned int x;
   memcached_server_st *servers;
@@ -1547,7 +1612,7 @@ static memcached_return  cleanup_test_callback(memcached_st *ptr __attribute__((
   return MEMCACHED_SUCCESS;
 }
 
-static test_return  callback_test(memcached_st *memc)
+static test_return_t  callback_test(memcached_st *memc)
 {
   /* Test User Data */
   {
@@ -1594,7 +1659,7 @@ static test_return  callback_test(memcached_st *memc)
 }
 
 /* We don't test the behavior itself, we test the switches */
-static test_return  behavior_test(memcached_st *memc)
+static test_return_t  behavior_test(memcached_st *memc)
 {
   uint64_t value;
   uint32_t set= 1;
@@ -1644,8 +1709,28 @@ static test_return  behavior_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
+static test_return_t fetch_all_results(memcached_st *memc)
+{
+  memcached_return rc= MEMCACHED_SUCCESS;
+  char return_key[MEMCACHED_MAX_KEY];
+  size_t return_key_length;
+  char *return_value;
+  size_t return_value_length;
+  uint32_t flags;
+
+  while ((return_value= memcached_fetch(memc, return_key, &return_key_length,
+                                        &return_value_length, &flags, &rc)))
+  {
+    assert(return_value);
+    assert(rc == MEMCACHED_SUCCESS);
+    free(return_value);
+  }
+
+  return ((rc == MEMCACHED_END) || (rc == MEMCACHED_SUCCESS)) ? TEST_SUCCESS : TEST_FAILURE;
+}
+
 /* Test case provided by Cal Haldenbrand */
-static test_return  user_supplied_bug1(memcached_st *memc)
+static test_return_t  user_supplied_bug1(memcached_st *memc)
 {
   unsigned int setter= 1;
   unsigned int x;
@@ -1693,7 +1778,7 @@ static test_return  user_supplied_bug1(memcached_st *memc)
 }
 
 /* Test case provided by Cal Haldenbrand */
-static test_return  user_supplied_bug2(memcached_st *memc)
+static test_return_t  user_supplied_bug2(memcached_st *memc)
 {
   int errors;
   unsigned int setter;
@@ -1748,7 +1833,7 @@ static test_return  user_supplied_bug2(memcached_st *memc)
 
 /* Do a large mget() over all the keys we think exist */
 #define KEY_COUNT 3000 // * 1024576
-static test_return  user_supplied_bug3(memcached_st *memc)
+static test_return_t  user_supplied_bug3(memcached_st *memc)
 {
   memcached_return rc;
   unsigned int setter;
@@ -1768,9 +1853,8 @@ static test_return  user_supplied_bug3(memcached_st *memc)
   getter = memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE);
 #endif
 
-  keys= (char **)malloc(sizeof(char *) * KEY_COUNT);
+  keys= calloc(KEY_COUNT, sizeof(char *));
   assert(keys);
-  memset(keys, 0, (sizeof(char *) * KEY_COUNT));
   for (x= 0; x < KEY_COUNT; x++)
   {
     char buffer[30];
@@ -1783,22 +1867,7 @@ static test_return  user_supplied_bug3(memcached_st *memc)
   rc= memcached_mget(memc, (const char **)keys, key_lengths, KEY_COUNT);
   assert(rc == MEMCACHED_SUCCESS);
 
-  /* Turn this into a help function */
-  {
-    char return_key[MEMCACHED_MAX_KEY];
-    size_t return_key_length;
-    char *return_value;
-    size_t return_value_length;
-    uint32_t flags;
-
-    while ((return_value= memcached_fetch(memc, return_key, &return_key_length,
-                                          &return_value_length, &flags, &rc)))
-    {
-      assert(return_value);
-      assert(rc == MEMCACHED_SUCCESS);
-      free(return_value);
-    }
-  }
+  assert(fetch_all_results(memc) == TEST_SUCCESS);
 
   for (x= 0; x < KEY_COUNT; x++)
     free(keys[x]);
@@ -1808,7 +1877,7 @@ static test_return  user_supplied_bug3(memcached_st *memc)
 }
 
 /* Make sure we behave properly if server list has no values */
-static test_return  user_supplied_bug4(memcached_st *memc)
+static test_return_t  user_supplied_bug4(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"fudge", "son", "food"};
@@ -1871,7 +1940,7 @@ static test_return  user_supplied_bug4(memcached_st *memc)
 }
 
 #define VALUE_SIZE_BUG5 1048064
-static test_return  user_supplied_bug5(memcached_st *memc)
+static test_return_t  user_supplied_bug5(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"036790384900", "036790384902", "036790384904", "036790384906"};
@@ -1929,7 +1998,7 @@ static test_return  user_supplied_bug5(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  user_supplied_bug6(memcached_st *memc)
+static test_return_t  user_supplied_bug6(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"036790384900", "036790384902", "036790384904", "036790384906"};
@@ -1994,7 +2063,7 @@ static test_return  user_supplied_bug6(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  user_supplied_bug8(memcached_st *memc __attribute__((unused)))
+static test_return_t  user_supplied_bug8(memcached_st *memc __attribute__((unused)))
 {
   memcached_return rc;
   memcached_st *mine;
@@ -2025,7 +2094,7 @@ static test_return  user_supplied_bug8(memcached_st *memc __attribute__((unused)
 }
 
 /* Test flag store/retrieve */
-static test_return  user_supplied_bug7(memcached_st *memc)
+static test_return_t  user_supplied_bug7(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys= "036790384900";
@@ -2069,7 +2138,7 @@ static test_return  user_supplied_bug7(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  user_supplied_bug9(memcached_st *memc)
+static test_return_t  user_supplied_bug9(memcached_st *memc)
 {
   memcached_return rc;
   const char *keys[]= {"UDATA:edevil@sapo.pt", "fudge&*@#", "for^#@&$not"};
@@ -2114,7 +2183,7 @@ static test_return  user_supplied_bug9(memcached_st *memc)
 }
 
 /* We are testing with aggressive timeout to get failures */
-static test_return  user_supplied_bug10(memcached_st *memc)
+static test_return_t  user_supplied_bug10(memcached_st *memc)
 {
   const char *key= "foo";
   char *value;
@@ -2157,7 +2226,7 @@ static test_return  user_supplied_bug10(memcached_st *memc)
 /*
   We are looking failures in the async protocol
 */
-static test_return  user_supplied_bug11(memcached_st *memc)
+static test_return_t  user_supplied_bug11(memcached_st *memc)
 {
   const char *key= "foo";
   char *value;
@@ -2198,7 +2267,7 @@ static test_return  user_supplied_bug11(memcached_st *memc)
 /*
   Bug found where incr was not returning MEMCACHED_NOTFOUND when object did not exist.
 */
-static test_return  user_supplied_bug12(memcached_st *memc)
+static test_return_t  user_supplied_bug12(memcached_st *memc)
 {
   memcached_return rc;
   uint32_t flags;
@@ -2241,7 +2310,7 @@ static test_return  user_supplied_bug12(memcached_st *memc)
   Bug found where command total one more than MEMCACHED_MAX_BUFFER
   set key34567890 0 0 8169 \r\n is sent followed by buffer of size 8169, followed by 8169
  */
-static test_return  user_supplied_bug13(memcached_st *memc)
+static test_return_t  user_supplied_bug13(memcached_st *memc)
 {
   char key[] = "key34567890";
   char *overflow;
@@ -2279,7 +2348,7 @@ static test_return  user_supplied_bug13(memcached_st *memc)
   set key34567890 0 0 8169 \r\n
   is sent followed by buffer of size 8169, followed by 8169
  */
-static test_return  user_supplied_bug14(memcached_st *memc)
+static test_return_t  user_supplied_bug14(memcached_st *memc)
 {
   size_t setter= 1;
   memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, setter);
@@ -2324,7 +2393,7 @@ static test_return  user_supplied_bug14(memcached_st *memc)
 /*
   Look for zero length value problems
   */
-static test_return  user_supplied_bug15(memcached_st *memc)
+static test_return_t  user_supplied_bug15(memcached_st *memc)
 {
   uint32_t x;
   memcached_return rc;
@@ -2362,7 +2431,7 @@ static test_return  user_supplied_bug15(memcached_st *memc)
 }
 
 /* Check the return sizes on FLAGS to make sure it stores 32bit unsigned values correctly */
-static test_return  user_supplied_bug16(memcached_st *memc)
+static test_return_t  user_supplied_bug16(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "mykey";
@@ -2389,7 +2458,7 @@ static test_return  user_supplied_bug16(memcached_st *memc)
 
 #ifndef __sun
 /* Check the validity of chinese key*/
-static test_return  user_supplied_bug17(memcached_st *memc)
+static test_return_t  user_supplied_bug17(memcached_st *memc)
 {
     memcached_return rc;
     const char *key= "豆瓣";
@@ -2420,7 +2489,7 @@ static test_return  user_supplied_bug17(memcached_st *memc)
   From Andrei on IRC
 */
 
-static test_return user_supplied_bug19(memcached_st *memc)
+static test_return_t user_supplied_bug19(memcached_st *memc)
 {
   memcached_st *m;
   memcached_server_st *s;
@@ -2441,7 +2510,7 @@ static test_return user_supplied_bug19(memcached_st *memc)
 }
 
 /* CAS test from Andei */
-static test_return user_supplied_bug20(memcached_st *memc)
+static test_return_t user_supplied_bug20(memcached_st *memc)
 {
   memcached_return status;
   memcached_result_st *result, result_obj;
@@ -2473,7 +2542,7 @@ static test_return user_supplied_bug20(memcached_st *memc)
 }
 
 #include "ketama_test_cases.h"
-static test_return user_supplied_bug18(memcached_st *trash)
+static test_return_t user_supplied_bug18(memcached_st *trash)
 {
   memcached_return rc;
   uint64_t value;
@@ -2532,7 +2601,93 @@ static test_return user_supplied_bug18(memcached_st *trash)
   return TEST_SUCCESS;
 }
 
-static test_return auto_eject_hosts(memcached_st *trash)
+/* Large mget() of missing keys with binary proto
+ *
+ * If many binary quiet commands (such as getq's in an mget) fill the output
+ * buffer and the server chooses not to respond, memcached_flush hangs. See
+ * http://lists.tangent.org/pipermail/libmemcached/2009-August/000918.html
+ */
+
+/* sighandler_t function that always asserts false */
+static void fail(int unused __attribute__((unused)))
+{
+  assert(0);
+}
+
+
+static test_return_t  _user_supplied_bug21(memcached_st* memc, size_t key_count)
+{
+  memcached_return rc;
+  unsigned int x;
+  char **keys;
+  size_t* key_lengths;
+  void (*oldalarm)(int);
+  memcached_st *memc_clone;
+
+  memc_clone= memcached_clone(NULL, memc);
+  assert(memc_clone);
+
+  /* only binproto uses getq for mget */
+  memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1);
+
+  /* empty the cache to ensure misses (hence non-responses) */
+  rc= memcached_flush(memc_clone, 0);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  key_lengths= calloc(key_count, sizeof(size_t));
+  keys= calloc(key_count, sizeof(char *));
+  assert(keys);
+  for (x= 0; x < key_count; x++)
+  {
+    char buffer[30];
+
+    snprintf(buffer, 30, "%u", x);
+    keys[x]= strdup(buffer);
+    key_lengths[x]= strlen(keys[x]);
+  }
+
+  oldalarm= signal(SIGALRM, fail);
+  alarm(5);
+
+  rc= memcached_mget(memc_clone, (const char **)keys, key_lengths, key_count);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  alarm(0);
+  signal(SIGALRM, oldalarm);
+
+  assert(fetch_all_results(memc) == TEST_SUCCESS);
+
+  for (x= 0; x < key_count; x++)
+    free(keys[x]);
+  free(keys);
+  free(key_lengths);
+
+  memcached_free(memc_clone);
+
+  return TEST_SUCCESS;
+}
+
+static memcached_return pre_binary(memcached_st *memc);
+
+static test_return_t user_supplied_bug21(memcached_st *memc)
+{
+  if (pre_binary(memc) != MEMCACHED_SUCCESS)
+    return TEST_SKIPPED;
+
+  test_return_t rc;
+
+  /* should work as of r580 */
+  rc= _user_supplied_bug21(memc, 10);
+  assert(rc == TEST_SUCCESS);
+
+  /* should fail as of r580 */
+  rc= _user_supplied_bug21(memc, 1000);
+  assert(rc == TEST_SUCCESS);
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t auto_eject_hosts(memcached_st *trash)
 {
   (void) trash;
 
@@ -2601,7 +2756,7 @@ static test_return auto_eject_hosts(memcached_st *trash)
   return TEST_SUCCESS;
 }
 
-static test_return  result_static(memcached_st *memc)
+static test_return_t  result_static(memcached_st *memc)
 {
   memcached_result_st result;
   memcached_result_st *result_ptr;
@@ -2614,7 +2769,7 @@ static test_return  result_static(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  result_alloc(memcached_st *memc)
+static test_return_t  result_alloc(memcached_st *memc)
 {
   memcached_result_st *result;
 
@@ -2625,7 +2780,7 @@ static test_return  result_alloc(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  string_static_null(memcached_st *memc)
+static test_return_t  string_static_null(memcached_st *memc)
 {
   memcached_string_st string;
   memcached_string_st *string_ptr;
@@ -2638,7 +2793,7 @@ static test_return  string_static_null(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  string_alloc_null(memcached_st *memc)
+static test_return_t  string_alloc_null(memcached_st *memc)
 {
   memcached_string_st *string;
 
@@ -2649,7 +2804,7 @@ static test_return  string_alloc_null(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  string_alloc_with_size(memcached_st *memc)
+static test_return_t  string_alloc_with_size(memcached_st *memc)
 {
   memcached_string_st *string;
 
@@ -2660,7 +2815,7 @@ static test_return  string_alloc_with_size(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  string_alloc_with_size_toobig(memcached_st *memc)
+static test_return_t  string_alloc_with_size_toobig(memcached_st *memc)
 {
   memcached_string_st *string;
 
@@ -2670,7 +2825,7 @@ static test_return  string_alloc_with_size_toobig(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  string_alloc_append(memcached_st *memc)
+static test_return_t  string_alloc_append(memcached_st *memc)
 {
   unsigned int x;
   char buffer[SMALL_STRING_LEN];
@@ -2693,7 +2848,7 @@ static test_return  string_alloc_append(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  string_alloc_append_toobig(memcached_st *memc)
+static test_return_t  string_alloc_append_toobig(memcached_st *memc)
 {
   memcached_return rc;
   unsigned int x;
@@ -2718,14 +2873,14 @@ static test_return  string_alloc_append_toobig(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  cleanup_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t  cleanup_pairs(memcached_st *memc __attribute__((unused)))
 {
   pairs_free(global_pairs);
 
   return TEST_SUCCESS;
 }
 
-static test_return  generate_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t  generate_pairs(memcached_st *memc __attribute__((unused)))
 {
   unsigned long long x;
   global_pairs= pairs_generate(GLOBAL_COUNT, 400);
@@ -2740,7 +2895,7 @@ static test_return  generate_pairs(memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return  generate_large_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t  generate_large_pairs(memcached_st *memc __attribute__((unused)))
 {
   unsigned long long x;
   global_pairs= pairs_generate(GLOBAL2_COUNT, MEMCACHED_MAX_BUFFER+10);
@@ -2755,14 +2910,14 @@ static test_return  generate_large_pairs(memcached_st *memc __attribute__((unuse
   return TEST_SUCCESS;
 }
 
-static test_return  generate_data(memcached_st *memc)
+static test_return_t  generate_data(memcached_st *memc)
 {
   execute_set(memc, global_pairs, global_count);
 
   return TEST_SUCCESS;
 }
 
-static test_return  generate_data_with_stats(memcached_st *memc)
+static test_return_t  generate_data_with_stats(memcached_st *memc)
 {
   memcached_stat_st *stat_p;
   memcached_return rc;
@@ -2786,7 +2941,7 @@ static test_return  generate_data_with_stats(memcached_st *memc)
 
   return TEST_SUCCESS;
 }
-static test_return  generate_buffer_data(memcached_st *memc)
+static test_return_t  generate_buffer_data(memcached_st *memc)
 {
   size_t latch= 0;
 
@@ -2797,7 +2952,7 @@ static test_return  generate_buffer_data(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  get_read_count(memcached_st *memc)
+static test_return_t  get_read_count(memcached_st *memc)
 {
   unsigned int x;
   memcached_return rc;
@@ -2825,7 +2980,6 @@ static test_return  get_read_count(memcached_st *memc)
           free(return_value);
       }
     }
-    fprintf(stderr, "\t%u -> %u", global_count, count);
   }
 
   memcached_free(memc_clone);
@@ -2833,7 +2987,7 @@ static test_return  get_read_count(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  get_read(memcached_st *memc)
+static test_return_t  get_read(memcached_st *memc)
 {
   unsigned int x;
   memcached_return rc;
@@ -2859,33 +3013,18 @@ static test_return  get_read(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  mget_read(memcached_st *memc)
+static test_return_t  mget_read(memcached_st *memc)
 {
   memcached_return rc;
 
   rc= memcached_mget(memc, global_keys, global_keys_length, global_count);
   assert(rc == MEMCACHED_SUCCESS);
-  /* Turn this into a help function */
-  {
-    char return_key[MEMCACHED_MAX_KEY];
-    size_t return_key_length;
-    char *return_value;
-    size_t return_value_length;
-    uint32_t flags;
-
-    while ((return_value= memcached_fetch(memc, return_key, &return_key_length,
-                                          &return_value_length, &flags, &rc)))
-    {
-      assert(return_value);
-      assert(rc == MEMCACHED_SUCCESS);
-      free(return_value);
-    }
-  }
+  assert(fetch_all_results(memc) == TEST_SUCCESS);
 
   return TEST_SUCCESS;
 }
 
-static test_return  mget_read_result(memcached_st *memc)
+static test_return_t  mget_read_result(memcached_st *memc)
 {
   memcached_return rc;
 
@@ -2910,7 +3049,7 @@ static test_return  mget_read_result(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  mget_read_function(memcached_st *memc)
+static test_return_t  mget_read_function(memcached_st *memc)
 {
   memcached_return rc;
   unsigned int counter;
@@ -2926,7 +3065,7 @@ static test_return  mget_read_function(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  delete_generate(memcached_st *memc)
+static test_return_t  delete_generate(memcached_st *memc)
 {
   unsigned int x;
 
@@ -2938,7 +3077,7 @@ static test_return  delete_generate(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  delete_buffer_generate(memcached_st *memc)
+static test_return_t  delete_buffer_generate(memcached_st *memc)
 {
   size_t latch= 0;
   unsigned int x;
@@ -2954,7 +3093,7 @@ static test_return  delete_buffer_generate(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return  add_host_test1(memcached_st *memc)
+static test_return_t  add_host_test1(memcached_st *memc)
 {
   unsigned int x;
   memcached_return rc;
@@ -3136,23 +3275,24 @@ static memcached_return  pre_binary(memcached_st *memc)
   }
 
   memcached_free(memc_clone);
+
   return rc;
 }
 
 static memcached_return pre_replication(memcached_st *memc)
 {
-  memcached_return rc= MEMCACHED_FAILURE;
-  if (pre_binary(memc) == MEMCACHED_SUCCESS)
-  {
-    /*
-     * Make sure that we store the item on all servers
-     * (master + replicas == number of servers)
-     */
-    rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS,
-                               memc->number_of_hosts - 1);
-    assert(rc == MEMCACHED_SUCCESS);
-    assert(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS) == memc->number_of_hosts - 1);
-  }
+  if (pre_binary(memc) != MEMCACHED_SUCCESS)
+    return MEMCACHED_FAILURE;
+
+  /*
+   * Make sure that we store the item on all servers
+   * (master + replicas == number of servers)
+   */
+  memcached_return rc;
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS,
+                             memc->number_of_hosts - 1);
+  assert(rc == MEMCACHED_SUCCESS);
+  assert(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS) == memc->number_of_hosts - 1);
 
   return rc;
 }
@@ -3426,7 +3566,7 @@ static memcached_return  poll_timeout(memcached_st *memc)
   return MEMCACHED_SUCCESS;
 }
 
-static test_return noreply_test(memcached_st *memc)
+static test_return_t noreply_test(memcached_st *memc)
 {
   memcached_return ret;
   ret= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1);
@@ -3552,7 +3692,7 @@ static test_return noreply_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return analyzer_test(memcached_st *memc)
+static test_return_t analyzer_test(memcached_st *memc)
 {
   memcached_return rc;
   memcached_stat_st *memc_stat;
@@ -3585,12 +3725,12 @@ static memcached_return callback_dump_counter(memcached_st *ptr __attribute__((u
   return MEMCACHED_SUCCESS;
 }
 
-static test_return dump_test(memcached_st *memc)
+static test_return_t dump_test(memcached_st *memc)
 {
   memcached_return rc;
   uint32_t counter= 0;
   memcached_dump_func callbacks[1];
-  test_return main_rc;
+  test_return_t main_rc;
 
   callbacks[0]= &callback_dump_counter;
 
@@ -3623,7 +3763,7 @@ static void* connection_release(void *arg) {
   return arg;
 }
 
-static test_return connection_pool_test(memcached_st *memc)
+static test_return_t connection_pool_test(memcached_st *memc)
 {
   memcached_pool_st* pool= memcached_pool_create(memc, 5, 10);
   assert(pool != NULL);
@@ -3695,7 +3835,7 @@ static test_return connection_pool_test(memcached_st *memc)
 }
 #endif
 
-static test_return replication_set_test(memcached_st *memc)
+static test_return_t replication_set_test(memcached_st *memc)
 {
   memcached_return rc;
   memcached_st *memc_clone= memcached_clone(NULL, memc);
@@ -3742,7 +3882,7 @@ static test_return replication_set_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return replication_get_test(memcached_st *memc)
+static test_return_t replication_get_test(memcached_st *memc)
 {
   memcached_return rc;
 
@@ -3774,7 +3914,7 @@ static test_return replication_get_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return replication_mget_test(memcached_st *memc)
+static test_return_t replication_mget_test(memcached_st *memc)
 {
   memcached_return rc;
   memcached_st *memc_clone= memcached_clone(NULL, memc);
@@ -3842,7 +3982,7 @@ static test_return replication_mget_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return replication_delete_test(memcached_st *memc)
+static test_return_t replication_delete_test(memcached_st *memc)
 {
   memcached_return rc;
   memcached_st *memc_clone= memcached_clone(NULL, memc);
@@ -3919,7 +4059,7 @@ static uint16_t *get_udp_request_ids(memcached_st *memc)
   return ids;
 }
 
-static test_return post_udp_op_check(memcached_st *memc, uint16_t *expected_req_ids)
+static test_return_t post_udp_op_check(memcached_st *memc, uint16_t *expected_req_ids)
 {
   unsigned int x;
   memcached_server_st *cur_server = memc->hosts;
@@ -3973,7 +4113,7 @@ static memcached_return binary_init_udp(memcached_st *memc)
 }
 
 /* Make sure that I cant add a tcp server to a udp client */
-static test_return add_tcp_server_udp_client_test(memcached_st *memc)
+static test_return_t add_tcp_server_udp_client_test(memcached_st *memc)
 {
   memcached_server_st server;
   memcached_server_clone(&server, &memc->hosts[0]);
@@ -3983,7 +4123,7 @@ static test_return add_tcp_server_udp_client_test(memcached_st *memc)
 }
 
 /* Make sure that I cant add a udp server to a tcp client */
-static test_return add_udp_server_tcp_client_test(memcached_st *memc)
+static test_return_t add_udp_server_tcp_client_test(memcached_st *memc)
 {
   memcached_server_st server;
   memcached_server_clone(&server, &memc->hosts[0]);
@@ -3995,7 +4135,7 @@ static test_return add_udp_server_tcp_client_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return set_udp_behavior_test(memcached_st *memc)
+static test_return_t set_udp_behavior_test(memcached_st *memc)
 {
 
   memcached_quit(memc);
@@ -4014,7 +4154,7 @@ static test_return set_udp_behavior_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return udp_set_test(memcached_st *memc)
+static test_return_t udp_set_test(memcached_st *memc)
 {
   unsigned int x= 0;
   unsigned int num_iters= 1025; //request id rolls over at 1024
@@ -4052,13 +4192,13 @@ static test_return udp_set_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return udp_buffered_set_test(memcached_st *memc)
+static test_return_t udp_buffered_set_test(memcached_st *memc)
 {
   memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
   return udp_set_test(memc);
 }
 
-static test_return udp_set_too_big_test(memcached_st *memc)
+static test_return_t udp_set_too_big_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "bar";
@@ -4071,7 +4211,7 @@ static test_return udp_set_too_big_test(memcached_st *memc)
   return post_udp_op_check(memc,expected_ids);
 }
 
-static test_return udp_delete_test(memcached_st *memc)
+static test_return_t udp_delete_test(memcached_st *memc)
 {
   unsigned int x= 0;
   unsigned int num_iters= 1025; //request id rolls over at 1024
@@ -4098,13 +4238,13 @@ static test_return udp_delete_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return udp_buffered_delete_test(memcached_st *memc)
+static test_return_t udp_buffered_delete_test(memcached_st *memc)
 {
   memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
   return udp_delete_test(memc);
 }
 
-static test_return udp_verbosity_test(memcached_st *memc)
+static test_return_t udp_verbosity_test(memcached_st *memc)
 {
   memcached_return rc;
   uint16_t *expected_ids= get_udp_request_ids(memc);
@@ -4117,14 +4257,14 @@ static test_return udp_verbosity_test(memcached_st *memc)
   return post_udp_op_check(memc,expected_ids);
 }
 
-static test_return udp_quit_test(memcached_st *memc)
+static test_return_t udp_quit_test(memcached_st *memc)
 {
   uint16_t *expected_ids= get_udp_request_ids(memc);
   memcached_quit(memc);
   return post_udp_op_check(memc, expected_ids);
 }
 
-static test_return udp_flush_test(memcached_st *memc)
+static test_return_t udp_flush_test(memcached_st *memc)
 {
   memcached_return rc;
   uint16_t *expected_ids= get_udp_request_ids(memc);
@@ -4137,7 +4277,7 @@ static test_return udp_flush_test(memcached_st *memc)
   return post_udp_op_check(memc,expected_ids);
 }
 
-static test_return udp_incr_test(memcached_st *memc)
+static test_return_t udp_incr_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "incr";
@@ -4156,7 +4296,7 @@ static test_return udp_incr_test(memcached_st *memc)
   return post_udp_op_check(memc, expected_ids);
 }
 
-static test_return udp_decr_test(memcached_st *memc)
+static test_return_t udp_decr_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "decr";
@@ -4176,7 +4316,7 @@ static test_return udp_decr_test(memcached_st *memc)
 }
 
 
-static test_return udp_stat_test(memcached_st *memc)
+static test_return_t udp_stat_test(memcached_st *memc)
 {
   memcached_stat_st * rv= NULL;
   memcached_return rc;
@@ -4188,7 +4328,7 @@ static test_return udp_stat_test(memcached_st *memc)
   return post_udp_op_check(memc, expected_ids);
 }
 
-static test_return udp_version_test(memcached_st *memc)
+static test_return_t udp_version_test(memcached_st *memc)
 {
   memcached_return rc;
   uint16_t *expected_ids = get_udp_request_ids(memc);
@@ -4197,7 +4337,7 @@ static test_return udp_version_test(memcached_st *memc)
   return post_udp_op_check(memc, expected_ids);
 }
 
-static test_return udp_get_test(memcached_st *memc)
+static test_return_t udp_get_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";
@@ -4209,7 +4349,7 @@ static test_return udp_get_test(memcached_st *memc)
   return post_udp_op_check(memc, expected_ids);
 }
 
-static test_return udp_mixed_io_test(memcached_st *memc)
+static test_return_t udp_mixed_io_test(memcached_st *memc)
 {
   test_st current_op;
   test_st mixed_io_ops [] ={
@@ -4232,7 +4372,7 @@ static test_return udp_mixed_io_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return hsieh_avaibility_test (memcached_st *memc)
+static test_return_t hsieh_avaibility_test (memcached_st *memc)
 {
   memcached_return expected_rc= MEMCACHED_FAILURE;
 #ifdef HAVE_HSIEH_HASH
@@ -4274,7 +4414,7 @@ static const char *list[]=
   NULL
 };
 
-static test_return md5_run (memcached_st *memc __attribute__((unused)))
+static test_return_t md5_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4298,7 +4438,7 @@ static test_return md5_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return crc_run (memcached_st *memc __attribute__((unused)))
+static test_return_t crc_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4318,7 +4458,7 @@ static test_return crc_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return fnv1_64_run (memcached_st *memc __attribute__((unused)))
+static test_return_t fnv1_64_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4341,7 +4481,7 @@ static test_return fnv1_64_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return fnv1a_64_run (memcached_st *memc __attribute__((unused)))
+static test_return_t fnv1a_64_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4364,7 +4504,7 @@ static test_return fnv1a_64_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return fnv1_32_run (memcached_st *memc __attribute__((unused)))
+static test_return_t fnv1_32_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4388,7 +4528,7 @@ static test_return fnv1_32_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return fnv1a_32_run (memcached_st *memc __attribute__((unused)))
+static test_return_t fnv1a_32_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4411,7 +4551,7 @@ static test_return fnv1a_32_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return hsieh_run (memcached_st *memc __attribute__((unused)))
+static test_return_t hsieh_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4436,7 +4576,7 @@ static test_return hsieh_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return murmur_run (memcached_st *memc __attribute__((unused)))
+static test_return_t murmur_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4459,7 +4599,7 @@ static test_return murmur_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return jenkins_run (memcached_st *memc __attribute__((unused)))
+static test_return_t jenkins_run (memcached_st *memc __attribute__((unused)))
 {
   uint32_t x;
   const char **ptr;
@@ -4483,10 +4623,10 @@ static test_return jenkins_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
-static test_return regression_bug_434484(memcached_st *memc)
+static test_return_t regression_bug_434484(memcached_st *memc)
 {
-  if (pre_binary(memc) != TEST_SUCCESS)
-    return TEST_SUCCESS;
+  if (pre_binary(memc) != MEMCACHED_SUCCESS)
+    return TEST_SKIPPED;
 
   memcached_return ret;
   const char *key= "regression_bug_434484";
@@ -4496,7 +4636,7 @@ static test_return regression_bug_434484(memcached_st *memc)
   assert(ret == MEMCACHED_NOTSTORED);
 
   size_t size= 2048 * 1024;
-  void *data= malloc(size);
+  void *data= calloc(1, size);
   assert(data != NULL);
   ret= memcached_set(memc, key, keylen, data, size, 0, 0);
   assert(ret == MEMCACHED_E2BIG);
@@ -4505,6 +4645,363 @@ static test_return regression_bug_434484(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
+static test_return_t regression_bug_434843(memcached_st *memc)
+{
+  if (pre_binary(memc) != MEMCACHED_SUCCESS)
+    return TEST_SKIPPED;
+
+  memcached_return rc;
+  unsigned int counter= 0;
+  memcached_execute_function callbacks[1]= { [0]= &callback_counter };
+
+  /*
+   * I only want to hit only _one_ server so I know the number of requests I'm
+   * sending in the pipleine to the server. Let's try to do a multiget of
+   * 1024 (that should satisfy most users don't you think?). Future versions
+   * will include a mget_execute function call if you need a higher number.
+   */
+  uint32_t number_of_hosts= memc->number_of_hosts;
+  memc->number_of_hosts= 1;
+  const size_t max_keys= 1024;
+  char **keys= calloc(max_keys, sizeof(char*));
+  size_t *key_length=calloc(max_keys, sizeof(size_t));
+
+  for (int x= 0; x < (int)max_keys; ++x)
+  {
+     char k[251];
+     key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
+     keys[x]= strdup(k);
+     assert(keys[x] != NULL);
+  }
+
+  /*
+   * Run two times.. the first time we should have 100% cache miss,
+   * and the second time we should have 100% cache hits
+   */
+  for (int y= 0; y < 2; ++y)
+  {
+    rc= memcached_mget(memc, (const char**)keys, key_length, max_keys);
+    assert(rc == MEMCACHED_SUCCESS);
+    rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+    if (y == 0)
+    {
+      /* The first iteration should give me a 100% cache miss. verify that*/
+      assert(counter == 0);
+      char blob[1024]= { 0 };
+      for (int x= 0; x < (int)max_keys; ++x)
+      {
+        rc= memcached_add(memc, keys[x], key_length[x],
+                          blob, sizeof(blob), 0, 0);
+        assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+      }
+    }
+    else
+    {
+      /* Verify that we received all of the key/value pairs */
+       assert(counter == (unsigned int)max_keys);
+    }
+  }
+
+  /* Release allocated resources */
+  for (size_t x= 0; x < max_keys; ++x)
+    free(keys[x]);
+  free(keys);
+  free(key_length);
+
+  memc->number_of_hosts= number_of_hosts;
+  return TEST_SUCCESS;
+}
+
+static test_return_t regression_bug_434843_buffered(memcached_st *memc)
+{
+  memcached_return rc;
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  return regression_bug_434843(memc);
+}
+
+static test_return_t regression_bug_421108(memcached_st *memc)
+{
+  memcached_return rc;
+  memcached_stat_st *memc_stat= memcached_stat(memc, NULL, &rc);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  char *bytes= memcached_stat_get_value(memc, memc_stat, "bytes", &rc);
+  assert(rc == MEMCACHED_SUCCESS);
+  assert(bytes != NULL);
+  char *bytes_read= memcached_stat_get_value(memc, memc_stat,
+                                             "bytes_read", &rc);
+  assert(rc == MEMCACHED_SUCCESS);
+  assert(bytes_read != NULL);
+
+  char *bytes_written= memcached_stat_get_value(memc, memc_stat,
+                                                "bytes_written", &rc);
+  assert(rc == MEMCACHED_SUCCESS);
+  assert(bytes_written != NULL);
+
+  assert(strcmp(bytes, bytes_read) != 0);
+  assert(strcmp(bytes, bytes_written) != 0);
+
+  /* Release allocated resources */
+  free(bytes);
+  free(bytes_read);
+  free(bytes_written);
+  memcached_stat_free(NULL, memc_stat);
+  return TEST_SUCCESS;
+}
+
+/*
+ * The test case isn't obvious so I should probably document why
+ * it works the way it does. Bug 442914 was caused by a bug
+ * in the logic in memcached_purge (it did not handle the case
+ * where the number of bytes sent was equal to the watermark).
+ * In this test case, create messages so that we hit that case
+ * and then disable noreply mode and issue a new command to
+ * verify that it isn't stuck. If we change the format for the
+ * delete command or the watermarks, we need to update this
+ * test....
+ */
+static test_return_t regression_bug_442914(memcached_st *memc)
+{
+  memcached_return rc;
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1);
+  assert(rc == MEMCACHED_SUCCESS);
+  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1);
+
+  uint32_t number_of_hosts= memc->number_of_hosts;
+  memc->number_of_hosts= 1;
+
+  char k[250];
+  size_t len;
+
+  for (int x= 0; x < 250; ++x)
+  {
+     len= (size_t)snprintf(k, sizeof(k), "%0250u", x);
+     rc= memcached_delete(memc, k, len, 0);
+     assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+  }
+
+  len= (size_t)snprintf(k, sizeof(k), "%037u", 251);
+  rc= memcached_delete(memc, k, len, 0);
+  assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 0);
+  assert(rc == MEMCACHED_SUCCESS);
+  rc= memcached_delete(memc, k, len, 0);
+  assert(rc == MEMCACHED_NOTFOUND);
+
+  memc->number_of_hosts= number_of_hosts;
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t regression_bug_447342(memcached_st *memc)
+{
+  if (memc->number_of_hosts < 3 || pre_replication(memc) != MEMCACHED_SUCCESS)
+    return TEST_SKIPPED;
+
+  memcached_return rc;
+
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 2);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  const size_t max_keys= 100;
+  char **keys= calloc(max_keys, sizeof(char*));
+  size_t *key_length=calloc(max_keys, sizeof(size_t));
+
+  for (int x= 0; x < (int)max_keys; ++x)
+  {
+    char k[251];
+    key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
+    keys[x]= strdup(k);
+    assert(keys[x] != NULL);
+    rc= memcached_set(memc, k, key_length[x], k, key_length[x], 0, 0);
+    assert(rc == MEMCACHED_SUCCESS);
+  }
+
+  /*
+  ** We are using the quiet commands to store the replicas, so we need
+  ** to ensure that all of them are processed before we can continue.
+  ** In the test we go directly from storing the object to trying to
+  ** receive the object from all of the different servers, so we
+  ** could end up in a race condition (the memcached server hasn't yet
+  ** processed the quiet command from the replication set when it process
+  ** the request from the other client (created by the clone)). As a
+  ** workaround for that we call memcached_quit to send the quit command
+  ** to the server and wait for the response ;-) If you use the test code
+  ** as an example for your own code, please note that you shouldn't need
+  ** to do this ;-)
+  */
+  memcached_quit(memc);
+  
+  /* Verify that all messages are stored, and we didn't stuff too much 
+   * into the servers
+   */
+  rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  unsigned int counter= 0;
+  memcached_execute_function callbacks[1]= { [0]= &callback_counter };
+  rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+  /* Verify that we received all of the key/value pairs */
+  assert(counter == (unsigned int)max_keys);
+
+  memcached_quit(memc);
+  /*
+   * Don't do the following in your code. I am abusing the internal details
+   * within the library, and this is not a supported interface.
+   * This is to verify correct behavior in the library. Fake that two servers
+   * are dead..
+   */
+  unsigned int port0= memc->hosts[0].port;
+  unsigned int port2= memc->hosts[2].port;
+  memc->hosts[0].port= 0;
+  memc->hosts[2].port= 0;
+
+  rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  counter= 0;
+  rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+  assert(counter == (unsigned int)max_keys);
+
+  /* restore the memc handle */
+  memc->hosts[0].port= port0;
+  memc->hosts[2].port= port2;
+
+  memcached_quit(memc);
+
+  /* Remove half of the objects */
+  for (int x= 0; x < (int)max_keys; ++x)
+    if (x & 1)
+    {
+      rc= memcached_delete(memc, keys[x], key_length[x], 0);
+      assert(rc == MEMCACHED_SUCCESS);
+    }
+
+  memcached_quit(memc);
+  memc->hosts[0].port= 0;
+  memc->hosts[2].port= 0;
+
+  /* now retry the command, this time we should have cache misses */
+  rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  counter= 0;
+  rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+  assert(counter == (unsigned int)(max_keys >> 1));
+
+  /* Release allocated resources */
+  for (size_t x= 0; x < max_keys; ++x)
+    free(keys[x]);
+  free(keys);
+  free(key_length);
+
+  /* restore the memc handle */
+  memc->hosts[0].port= port0;
+  memc->hosts[2].port= port2;
+  return TEST_SUCCESS;
+}
+
+/* Test memcached_server_get_last_disconnect
+ * For a working server set, shall be NULL
+ * For a set of non existing server, shall not be NULL
+ */
+static test_return_t  test_get_last_disconnect(memcached_st *memc)
+{
+  memcached_return rc;
+  memcached_server_st *disconnected_server;
+
+  /* With the working set of server */
+  const char *key= "marmotte";
+  const char *value= "milka";
+
+  rc= memcached_set(memc, key, strlen(key),
+                    value, strlen(value),
+                    (time_t)0, (uint32_t)0);
+  assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+
+  disconnected_server = memcached_server_get_last_disconnect(memc);
+  assert(disconnected_server == NULL);
+
+  /* With a non existing server */
+  memcached_st *mine;
+  memcached_server_st *servers;
+
+  const char *server_list= "localhost:9";
+
+  servers= memcached_servers_parse(server_list);
+  assert(servers);
+  mine= memcached_create(NULL);
+  rc= memcached_server_push(mine, servers);
+  assert(rc == MEMCACHED_SUCCESS);
+  memcached_server_list_free(servers);
+  assert(mine);
+
+  rc= memcached_set(mine, key, strlen(key),
+                    value, strlen(value),
+                    (time_t)0, (uint32_t)0);
+  assert(rc != MEMCACHED_SUCCESS);
+
+  disconnected_server = memcached_server_get_last_disconnect(mine);
+  assert(disconnected_server != NULL);
+  assert(disconnected_server->port == 9);
+  assert(strncmp(disconnected_server->hostname,"localhost",9) == 0);
+
+  memcached_quit(mine);
+  memcached_free(mine);
+
+  return TEST_SUCCESS;
+}
+
+/*
+ * This test ensures that the failure counter isn't incremented during
+ * normal termination of the memcached instance.
+ */
+static test_return_t wrong_failure_counter_test(memcached_st *memc)
+{
+  memcached_return rc;
+
+  /* Set value to force connection to the server */
+  const char *key= "marmotte";
+  const char *value= "milka";
+
+  /*
+   * Please note that I'm abusing the internal structures in libmemcached
+   * in a non-portable way and you shouldn't be doing this. I'm only
+   * doing this in order to verify that the library works the way it should
+   */
+  uint32_t number_of_hosts= memc->number_of_hosts;
+  memc->number_of_hosts= 1;
+
+  /* Ensure that we are connected to the server by setting a value */
+  rc= memcached_set(memc, key, strlen(key),
+                    value, strlen(value),
+                    (time_t)0, (uint32_t)0);
+  assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+
+
+  /* The test is to see that the memcached_quit doesn't increase the
+   * the server failure conter, so let's ensure that it is zero
+   * before sending quit
+   */
+  memc->hosts[0].server_failure_counter= 0;
+
+  memcached_quit(memc);
+
+  /* Verify that it memcached_quit didn't increment the failure counter
+   * Please note that this isn't bullet proof, because an error could
+   * occur...
+   */
+  assert(memc->hosts[0].server_failure_counter == 0);
+
+  /* restore the instance */
+  memc->number_of_hosts= number_of_hosts;
+
+  return TEST_SUCCESS;
+}
+
 test_st udp_setup_server_tests[] ={
   {"set_udp_behavior_test", 0, set_udp_behavior_test},
   {"add_tcp_server_udp_client_test", 0, add_tcp_server_udp_client_test},
@@ -4567,6 +5064,7 @@ test_st tests[] ={
   {"mget_result", 1, mget_result_test },
   {"mget_result_alloc", 1, mget_result_alloc_test },
   {"mget_result_function", 1, mget_result_function },
+  {"mget_execute", 1, mget_execute },
   {"mget_end", 0, mget_end },
   {"get_stats", 0, get_stats },
   {"add_host_test", 0, add_host_test },
@@ -4584,6 +5082,7 @@ test_st tests[] ={
 #ifdef HAVE_LIBMEMCACHEDUTIL
   {"connectionpool", 1, connection_pool_test },
 #endif
+  {"test_get_last_disconnect", 1, test_get_last_disconnect},
   {0, 0, 0}
 };
 
@@ -4647,6 +5146,8 @@ test_st user_tests[] ={
   {"user_supplied_bug18", 1, user_supplied_bug18 },
   {"user_supplied_bug19", 1, user_supplied_bug19 },
   {"user_supplied_bug20", 1, user_supplied_bug20 },
+  {"user_supplied_bug21", 1, user_supplied_bug21 },
+  {"wrong_failure_counter_test", 1, wrong_failure_counter_test},
   {0, 0, 0}
 };
 
@@ -4666,6 +5167,11 @@ test_st replication_tests[]= {
  */
 test_st regression_tests[]= {
   {"lp:434484", 1, regression_bug_434484 },
+  {"lp:434843", 1, regression_bug_434843 },
+  {"lp:434843 buffered", 1, regression_bug_434843_buffered },
+  {"lp:421108", 1, regression_bug_421108 },
+  {"lp:442914", 1, regression_bug_442914 },
+  {"lp:447342", 1, regression_bug_447342 },
   {0, 0, 0}
 };
 
@@ -4791,8 +5297,7 @@ void *world_create(void)
 {
   server_startup_st *construct;
 
-  construct= (server_startup_st *)malloc(sizeof(server_startup_st));
-  memset(construct, 0, sizeof(server_startup_st));
+  construct= calloc(sizeof(server_startup_st), 1);
   construct->count= SERVERS_TO_CREATE;
   construct->udp= 0;
   server_startup(construct);
index 7ede21564c93d00eb07c7ce2fec097a8a5ed258a..d988334fb89459b8df47671bb66dae37c3aaa043 100644 (file)
@@ -23,11 +23,11 @@ using namespace std;
 using namespace memcache;
 
 extern "C" {
-   test_return basic_test(memcached_st *memc);
-   test_return increment_test(memcached_st *memc);
-   test_return basic_master_key_test(memcached_st *memc);
-   test_return mget_result_function(memcached_st *memc);
-   test_return mget_test(memcached_st *memc);
+   test_return_t basic_test(memcached_st *memc);
+   test_return_t increment_test(memcached_st *memc);
+   test_return_t basic_master_key_test(memcached_st *memc);
+   test_return_t mget_result_function(memcached_st *memc);
+   test_return_t mget_test(memcached_st *memc);
    memcached_return callback_counter(memcached_st *,
                                      memcached_result_st *, 
                                      void *context);
@@ -50,7 +50,7 @@ static void copy_vec_to_string(vector<char> &vec, string &str)
   }
 }
 
-test_return basic_test(memcached_st *memc)
+test_return_t basic_test(memcached_st *memc)
 {
   Memcache foo(memc);
   const string value_set("This is some data");
@@ -80,7 +80,7 @@ test_return basic_test(memcached_st *memc)
   return TEST_FAILURE;
 }
 
-test_return increment_test(memcached_st *memc)
+test_return_t increment_test(memcached_st *memc)
 {
   Memcache mcach(memc);
   bool rc;
@@ -125,7 +125,7 @@ test_return increment_test(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-test_return basic_master_key_test(memcached_st *memc)
+test_return_t basic_master_key_test(memcached_st *memc)
 {
   Memcache foo(memc);
   const string value_set("Data for server A");
@@ -162,7 +162,7 @@ memcached_return callback_counter(memcached_st *,
   return MEMCACHED_SUCCESS;
 }
 
-test_return mget_result_function(memcached_st *memc)
+test_return_t mget_result_function(memcached_st *memc)
 {
   Memcache mc(memc);
   bool rc;
@@ -205,7 +205,7 @@ test_return mget_result_function(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-test_return mget_test(memcached_st *memc)
+test_return_t mget_test(memcached_st *memc)
 {
   Memcache mc(memc);
   bool rc;
index 78fe4a70ff8b284ee4db96e3256ef4eed46135bb..7f7879dc14f65c2cedd108ee6764776b15afe12f 100644 (file)
@@ -25,8 +25,28 @@ static long int timedif(struct timeval a, struct timeval b)
   return s + us;
 }
 
+static const char *test_strerror(test_return_t code)
+{
+  switch (code) {
+  case TEST_SUCCESS:
+    return "ok";
+  case TEST_FAILURE:
+    return "failed";
+  case TEST_MEMORY_ALLOCATION_FAILURE:
+    return "memory allocation";
+  case TEST_SKIPPED:
+    return "skipped";
+  case TEST_MAXIMUM_RETURN:
+  default:
+    fprintf(stderr, "Unknown return value\n");
+    abort();
+  }
+
+}
+
 int main(int argc, char *argv[])
 {
+  test_return_t failed;
   unsigned int x;
   char *collection_to_run= NULL;
   char *wildcard= NULL;
@@ -35,7 +55,6 @@ int main(int argc, char *argv[])
   world_st world;
   collection_st *collection;
   collection_st *next;
-  uint8_t failed;
   void *world_ptr;
 
   memset(&world, 0, sizeof(world_st));
@@ -44,7 +63,7 @@ int main(int argc, char *argv[])
 
   if (world.create)
     world_ptr= world.create();
-  else 
+  else
     world_ptr= NULL;
 
   startup_ptr= (server_startup_st *)world_ptr;
@@ -112,12 +131,9 @@ int main(int argc, char *argv[])
       failed= run->function(memc);
       gettimeofday(&end_time, NULL);
       load_time= timedif(end_time, start_time);
-      if (failed)
-        fprintf(stderr, "\t\t\t\t\t %ld.%03ld [ failed ]\n", load_time / 1000, 
-                load_time % 1000);
-      else
-        fprintf(stderr, "\t\t\t\t\t %ld.%03ld [ ok ]\n", load_time / 1000, 
-                load_time % 1000);
+
+      fprintf(stderr, "\t\t\t\t\t %ld.%03ld [ %s ]\n", load_time / 1000,
+              load_time % 1000, test_strerror(failed));
 
       if (next->post)
         (void)next->post(memc);
index d0e089ddfcfa42f5b3d62762162ac64e360aa91f..28d29a41c79d118e47ab741c3f198baf6776b2ae 100644 (file)
@@ -16,13 +16,14 @@ typedef enum {
   TEST_SUCCESS= 0, /* Backwards compatibility */
   TEST_FAILURE,
   TEST_MEMORY_ALLOCATION_FAILURE,
+  TEST_SKIPPED,
   TEST_MAXIMUM_RETURN /* Always add new error code before */
-} test_return;
+} test_return_t;
 
 struct test_st {
   const char *name;
   unsigned int requires_flush;
-  test_return (*function)(memcached_st *memc);
+  test_return_t (*function)(memcached_st *memc);
 };
 
 struct collection_st {
index 2fc178f07f15bfe9833c7967994467957b92b910..dc33ae67360821c9af86f7fe742e417efe6749bc 100644 (file)
 #include "server.h"
 
 /* Prototypes */
-test_return set_test(memcached_st *memc);
+test_return_t set_test(memcached_st *memc);
 void *world_create(void);
 void world_destroy(void *p);
 
-test_return set_test(memcached_st *memc)
+test_return_t set_test(memcached_st *memc)
 {
   memcached_return rc;
   const char *key= "foo";