Fix for lp:777672
authorBrian Aker <brian@tangent.org>
Sat, 20 Aug 2011 07:04:21 +0000 (00:04 -0700)
committerBrian Aker <brian@tangent.org>
Sat, 20 Aug 2011 07:04:21 +0000 (00:04 -0700)
36 files changed:
.bzrignore
docs/memcached_behavior.rst
libhashkit/digest.cc
libmemcached/behavior.cc
libmemcached/common.h
libmemcached/connect.cc
libmemcached/constants.h
libmemcached/get.cc
libmemcached/hash.cc
libmemcached/hosts.cc
libmemcached/io.cc
libmemcached/memcached.cc
libmemcached/memcached.h
libmemcached/quit.cc
libmemcached/return.h
libmemcached/server.cc
libmemcached/server.h
libmemcached/server.hpp
libmemcached/server_list.cc
libmemcached/strerror.cc
libtest/comparison.hpp
libtest/framework.h
libtest/include.am
libtest/port.cc [new file with mode: 0644]
libtest/port.h [new file with mode: 0644]
libtest/server_container.cc
libtest/server_container.h
libtest/test.cc
libtest/test.hpp
m4/pandora_optimize.m4
tests/atomsmasher.cc
tests/failure.cc [new file with mode: 0644]
tests/include.am
tests/libmemcached_world.h
tests/mem_functions.cc
tests/mem_udp.cc

index c28f4fd1ef443fbfcfe8f52303859a4793477511..3a6e517ac449971e835d39648434e163996baed6 100644 (file)
@@ -110,6 +110,7 @@ tests/atomsmasher
 tests/c_sasl_test
 tests/c_test
 tests/cycle
+tests/failure
 tests/hash_plus
 tests/hashplus
 tests/internals
index baa2eacd698585ebde104c14e4120d77d08352f2..3480d2511ca3817b9631bd733c854957b148b1d5 100644 (file)
@@ -252,7 +252,7 @@ Find the current size of SO_RCVBUF. A value of 0 means either an error occured o
 
 .. c:type:: MEMCACHED_BEHAVIOR_RETRY_TIMEOUT
  
-When enabled a host which is problematic will only be checked for usage based on the amount of time set by this behavior.  
+When enabled a host which is problematic will only be checked for usage based on the amount of time set by this behavior. The value is in seconds.
 
 
 .. c:type:: MEMCACHED_BEHAVIOR_HASH_WITH_PREFIX_KEY
index e1559819db8faff2d3f6d4e6e7b2f3e079ea449b..ce94cc76d3905c19de525825bb73a70d6195f54c 100644 (file)
@@ -48,11 +48,12 @@ uint32_t libhashkit_digest(const char *key, size_t key_length, hashkit_hash_algo
   case HASHKIT_HASH_CUSTOM:
   case HASHKIT_HASH_MAX:
   default:
-#ifdef HAVE_DEBUG
-    fprintf(stderr, "hashkit_hash_t was extended but libhashkit_generate_value was not updated\n");
-    fflush(stderr);
-    assert(0);
-#endif
+    if (DEBUG)
+    {
+      fprintf(stderr, "hashkit_hash_t was extended but libhashkit_generate_value was not updated\n");
+      fflush(stderr);
+      assert(0);
+    }
     break;
   }
 
index a6988a225c15a1541766af723365a9c6b3711056..2a1e75372f89c71f7296a56338460f88b8c88b27 100644 (file)
@@ -53,7 +53,9 @@ memcached_return_t memcached_behavior_set(memcached_st *ptr,
                                           uint64_t data)
 {
   if (not ptr)
+  {
     return MEMCACHED_INVALID_ARGUMENTS;
+  }
 
   switch (flag)
   {
@@ -85,7 +87,12 @@ memcached_return_t memcached_behavior_set(memcached_st *ptr,
     ptr->flags.auto_eject_hosts= bool(data);
 
   case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT:
-    ptr->server_failure_limit= (uint32_t)data;
+    if (data == 0)
+    {
+      return memcached_set_error(*ptr, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
+                                        memcached_literal_param("MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT requires a value greater then zero."));
+    }
+    ptr->server_failure_limit= uint32_t(data);
     break;
 
   case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL:
@@ -189,7 +196,12 @@ memcached_return_t memcached_behavior_set(memcached_st *ptr,
     break;
 
   case MEMCACHED_BEHAVIOR_RETRY_TIMEOUT:
-    ptr->retry_timeout= (int32_t)data;
+    if (data == 0)
+    {
+      return memcached_set_error(*ptr, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
+                                        memcached_literal_param("MEMCACHED_BEHAVIOR_RETRY_TIMEOUT requires a value greater then zero."));
+    }
+    ptr->retry_timeout= int32_t(data);
     break;
 
   case MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE:
@@ -461,8 +473,10 @@ memcached_return_t memcached_behavior_set_distribution(memcached_st *ptr, memcac
     {
       ptr->ketama.weighted= false;
     }
+
     ptr->distribution= type;
     run_distribution(ptr);
+
     return MEMCACHED_SUCCESS;
   }
 
index f45eaffd4474894390f8d1142eb2796cdcefb61d..6de9bbd1e24298026597a8de9d31353b1c8d82e8 100644 (file)
@@ -155,9 +155,6 @@ memcached_return_t run_distribution(memcached_st *ptr);
 #define memcached_server_response_decrement(A) (A)->cursor_active--
 #define memcached_server_response_reset(A) (A)->cursor_active=0
 
-LIBMEMCACHED_LOCAL
-void set_last_disconnected_host(memcached_server_write_instance_st ptr);
-
 #ifdef __cplusplus
 LIBMEMCACHED_LOCAL
 memcached_return_t memcached_key_test(const memcached_st& memc,
index fa35d84e5af49cd35edbbd89598fe09c948eb7e5..589c40c51dd06fff0f9230a18eaf4e768e42dade 100644 (file)
 
 
 #include <libmemcached/common.h>
+
+#include <cassert>
 #include <ctime>
 #include <sys/time.h>
 
-static memcached_return_t connect_poll(memcached_server_st *ptr)
+static memcached_return_t connect_poll(memcached_server_st *server)
 {
   struct pollfd fds[1];
-  fds[0].fd = ptr->fd;
-  fds[0].events = POLLOUT;
+  fds[0].fd= server->fd;
+  fds[0].events= POLLOUT;
 
   size_t loop_max= 5;
 
-  if (ptr->root->poll_timeout == 0)
+  if (server->root->poll_timeout == 0)
   {
-    return memcached_set_error(*ptr, MEMCACHED_TIMEOUT, MEMCACHED_AT);
+    return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
   }
 
   while (--loop_max) // Should only loop on cases of ERESTART or EINTR
   {
-    int error= poll(fds, 1, ptr->root->connect_timeout);
+    int error= poll(fds, 1, server->root->connect_timeout);
     switch (error)
     {
     case 1:
       {
         int err;
         socklen_t len= sizeof (err);
-        (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len);
+        (void)getsockopt(server->fd, SOL_SOCKET, SO_ERROR, &err, &len);
 
         // We check the value to see what happened wth the socket.
         if (err == 0)
@@ -70,11 +72,11 @@ static memcached_return_t connect_poll(memcached_server_st *ptr)
           return MEMCACHED_SUCCESS;
         }
 
-        return memcached_set_errno(*ptr, err, MEMCACHED_AT);
+        return memcached_set_errno(*server, err, MEMCACHED_AT);
       }
     case 0:
       {
-        return memcached_set_error(*ptr, MEMCACHED_TIMEOUT, MEMCACHED_AT);
+        return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
       }
 
     default: // A real error occurred and we need to completely bail
@@ -88,36 +90,36 @@ static memcached_return_t connect_poll(memcached_server_st *ptr)
 
       case EFAULT:
       case ENOMEM:
-        return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
+        return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
 
       case EINVAL:
-        return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid"));
+        return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid"));
 
       default: // This should not happen
         if (fds[0].revents & POLLERR)
         {
           int err;
           socklen_t len= sizeof (err);
-          (void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len);
-          memcached_set_errno(*ptr, (err == 0) ? get_socket_errno() : err, MEMCACHED_AT);
+          (void)getsockopt(server->fd, SOL_SOCKET, SO_ERROR, &err, &len);
+          memcached_set_errno(*server, (err == 0) ? get_socket_errno() : err, MEMCACHED_AT);
         }
         else
         {
-          memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT);
+          memcached_set_errno(*server, get_socket_errno(), MEMCACHED_AT);
         }
 
-        assert_msg(ptr->fd != INVALID_SOCKET, "poll() was passed an invalid file descriptor");
-        (void)closesocket(ptr->fd);
-        ptr->fd= INVALID_SOCKET;
-        ptr->state= MEMCACHED_SERVER_STATE_NEW;
+        assert_msg(server->fd != INVALID_SOCKET, "poll() was passed an invalid file descriptor");
+        (void)closesocket(server->fd);
+        server->fd= INVALID_SOCKET;
+        server->state= MEMCACHED_SERVER_STATE_NEW;
 
-        return memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT);
+        return memcached_set_errno(*server, get_socket_errno(), MEMCACHED_AT);
       }
     }
   }
 
   // This should only be possible from ERESTART or EINTR;
-  return memcached_set_errno(*ptr, get_socket_errno(), MEMCACHED_AT);
+  return memcached_set_errno(*server, get_socket_errno(), MEMCACHED_AT);
 }
 
 static memcached_return_t set_hostinfo(memcached_server_st *server)
@@ -164,7 +166,7 @@ static memcached_return_t set_hostinfo(memcached_server_st *server)
     return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT, memcached_string_make_from_cstr(gai_strerror(errcode)));
 
   case EAI_SYSTEM:
-      return memcached_set_errno(*server, errno, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_SYSTEM)"));
+    return memcached_set_errno(*server, errno, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_SYSTEM)"));
 
   case EAI_BADFLAGS:
     return memcached_set_error(*server, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_BADFLAGS)"));
@@ -183,25 +185,25 @@ static memcached_return_t set_hostinfo(memcached_server_st *server)
   return MEMCACHED_SUCCESS;
 }
 
-static inline void set_socket_nonblocking(memcached_server_st *ptr)
+static inline void set_socket_nonblocking(memcached_server_st *server)
 {
 #ifdef WIN32
-  u_long arg = 1;
-  if (ioctlsocket(ptr->fd, FIONBIO, &arg) == SOCKET_ERROR)
+  u_long arg= 1;
+  if (ioctlsocket(server->fd, FIONBIO, &arg) == SOCKET_ERROR)
   {
-    memcached_set_errno(*ptr, get_socket_errno(), NULL);
+    memcached_set_errno(*server, get_socket_errno(), NULL);
   }
 #else
   int flags;
 
   do
   {
-    flags= fcntl(ptr->fd, F_GETFL, 0);
+    flags= fcntl(server->fd, F_GETFL, 0);
   } while (flags == -1 && (errno == EINTR || errno == EAGAIN));
 
   if (flags == -1)
   {
-    memcached_set_errno(*ptr, errno, NULL);
+    memcached_set_errno(*server, errno, NULL);
   }
   else if ((flags & O_NONBLOCK) == 0)
   {
@@ -209,51 +211,51 @@ static inline void set_socket_nonblocking(memcached_server_st *ptr)
 
     do
     {
-      rval= fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
+      rval= fcntl(server->fd, F_SETFL, flags | O_NONBLOCK);
     } while (rval == -1 && (errno == EINTR || errno == EAGAIN));
 
     unlikely (rval == -1)
     {
-      memcached_set_errno(*ptr, errno, NULL);
+      memcached_set_errno(*server, errno, NULL);
     }
   }
 #endif
 }
 
-static void set_socket_options(memcached_server_st *ptr)
+static void set_socket_options(memcached_server_st *server)
 {
-  assert_msg(ptr->fd != -1, "invalid socket was passed to set_socket_options()");
+  assert_msg(server->fd != -1, "invalid socket was passed to set_socket_options()");
 
-  if (ptr->type == MEMCACHED_CONNECTION_UDP)
+  if (server->type == MEMCACHED_CONNECTION_UDP)
   {
     return;
   }
 
 #ifdef HAVE_SNDTIMEO
-  if (ptr->root->snd_timeout)
+  if (server->root->snd_timeout)
   {
     int error;
     struct timeval waittime;
 
     waittime.tv_sec= 0;
-    waittime.tv_usec= ptr->root->snd_timeout;
+    waittime.tv_usec= server->root->snd_timeout;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO,
+    error= setsockopt(server->fd, SOL_SOCKET, SO_SNDTIMEO,
                       &waittime, (socklen_t)sizeof(struct timeval));
     WATCHPOINT_ASSERT(error == 0);
   }
 #endif
 
 #ifdef HAVE_RCVTIMEO
-  if (ptr->root->rcv_timeout)
+  if (server->root->rcv_timeout)
   {
     int error;
     struct timeval waittime;
 
     waittime.tv_sec= 0;
-    waittime.tv_usec= ptr->root->rcv_timeout;
+    waittime.tv_usec= server->root->rcv_timeout;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO,
+    error= setsockopt(server->fd, SOL_SOCKET, SO_RCVTIMEO,
                       &waittime, (socklen_t)sizeof(struct timeval));
     WATCHPOINT_ASSERT(error == 0);
   }
@@ -263,7 +265,7 @@ static void set_socket_options(memcached_server_st *ptr)
 #if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
   {
     int set= 1;
-    int error= setsockopt(ptr->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
+    int error= setsockopt(server->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
 
     // This is not considered a fatal error
     if (error == -1)
@@ -274,80 +276,80 @@ static void set_socket_options(memcached_server_st *ptr)
   }
 #endif
 
-  if (ptr->root->flags.no_block)
+  if (server->root->flags.no_block)
   {
     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,
+    error= setsockopt(server->fd, SOL_SOCKET, SO_LINGER,
                       &linger, (socklen_t)sizeof(struct linger));
     WATCHPOINT_ASSERT(error == 0);
   }
 
-  if (ptr->root->flags.tcp_nodelay)
+  if (server->root->flags.tcp_nodelay)
   {
     int flag= 1;
     int error;
 
-    error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY,
+    error= setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY,
                       &flag, (socklen_t)sizeof(int));
     WATCHPOINT_ASSERT(error == 0);
   }
 
-  if (ptr->root->flags.tcp_keepalive)
+  if (server->root->flags.tcp_keepalive)
   {
     int flag= 1;
     int error;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_KEEPALIVE,
+    error= setsockopt(server->fd, SOL_SOCKET, SO_KEEPALIVE,
                       &flag, (socklen_t)sizeof(int));
     WATCHPOINT_ASSERT(error == 0);
   }
 
 #ifdef TCP_KEEPIDLE
-  if (ptr->root->tcp_keepidle > 0)
+  if (server->root->tcp_keepidle > 0)
   {
     int error;
 
-    error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_KEEPIDLE,
-                      &ptr->root->tcp_keepidle, (socklen_t)sizeof(int));
+    error= setsockopt(server->fd, IPPROTO_TCP, TCP_KEEPIDLE,
+                      &server->root->tcp_keepidle, (socklen_t)sizeof(int));
     WATCHPOINT_ASSERT(error == 0);
   }
 #endif
 
-  if (ptr->root->send_size > 0)
+  if (server->root->send_size > 0)
   {
     int error;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF,
-                      &ptr->root->send_size, (socklen_t)sizeof(int));
+    error= setsockopt(server->fd, SOL_SOCKET, SO_SNDBUF,
+                      &server->root->send_size, (socklen_t)sizeof(int));
     WATCHPOINT_ASSERT(error == 0);
   }
 
-  if (ptr->root->recv_size > 0)
+  if (server->root->recv_size > 0)
   {
     int error;
 
-    error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF,
-                      &ptr->root->recv_size, (socklen_t)sizeof(int));
+    error= setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF,
+                      &server->root->recv_size, (socklen_t)sizeof(int));
     WATCHPOINT_ASSERT(error == 0);
   }
 
 
   /* libmemcached will always use nonblocking IO to avoid write deadlocks */
-  set_socket_nonblocking(ptr);
+  set_socket_nonblocking(server);
 }
 
-static memcached_return_t unix_socket_connect(memcached_server_st *ptr)
+static memcached_return_t unix_socket_connect(memcached_server_st *server)
 {
 #ifndef WIN32
-  WATCHPOINT_ASSERT(ptr->fd == -1);
+  WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
 
-  if ((ptr->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+  if ((server->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
   {
-    memcached_set_errno(*ptr, errno, NULL);
+    memcached_set_errno(*server, errno, NULL);
     return MEMCACHED_CONNECTION_FAILURE;
   }
 
@@ -355,10 +357,10 @@ static memcached_return_t unix_socket_connect(memcached_server_st *ptr)
 
   memset(&servAddr, 0, sizeof (struct sockaddr_un));
   servAddr.sun_family= AF_UNIX;
-  strncpy(servAddr.sun_path, ptr->hostname, sizeof(servAddr.sun_path)); /* Copy filename */
+  strncpy(servAddr.sun_path, server->hostname, sizeof(servAddr.sun_path)); /* Copy filename */
 
   do {
-    if (connect(ptr->fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
+    if (connect(server->fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
     {
       switch (errno)
       {
@@ -375,37 +377,37 @@ static memcached_return_t unix_socket_connect(memcached_server_st *ptr)
 
       default:
         WATCHPOINT_ERRNO(errno);
-        memcached_set_errno(*ptr, errno, MEMCACHED_AT);
+        memcached_set_errno(*server, errno, MEMCACHED_AT);
         return MEMCACHED_CONNECTION_FAILURE;
       }
     }
   } while (0);
-  ptr->state= MEMCACHED_SERVER_STATE_CONNECTED;
+  server->state= MEMCACHED_SERVER_STATE_CONNECTED;
 
-  WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET);
+  WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
 
   return MEMCACHED_SUCCESS;
 #else
-  (void)ptr;
+  (void)server;
   return MEMCACHED_NOT_SUPPORTED;
 #endif
 }
 
-static memcached_return_t network_connect(memcached_server_st *ptr)
+static memcached_return_t network_connect(memcached_server_st *server)
 {
   bool timeout_error_occured= false;
 
-  WATCHPOINT_ASSERT(ptr->fd == INVALID_SOCKET);
-  WATCHPOINT_ASSERT(ptr->cursor_active == 0);
+  WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
+  WATCHPOINT_ASSERT(server->cursor_active == 0);
 
-  if (not ptr->address_info)
+  if (server->address_info == NULL or server->address_info_next == NULL)
   {
-    WATCHPOINT_ASSERT(ptr->state == MEMCACHED_SERVER_STATE_NEW);
+    WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
     memcached_return_t rc;
     uint32_t counter= 5;
     while (--counter)
     {
-      if ((rc= set_hostinfo(ptr)) != MEMCACHED_TIMEOUT)
+      if ((rc= set_hostinfo(server)) != MEMCACHED_TIMEOUT)
       {
         break;
       }
@@ -421,32 +423,34 @@ static memcached_return_t network_connect(memcached_server_st *ptr)
     }
 
     if (memcached_failed(rc))
+    {
       return rc;
+    }
   }
 
   /* Create the socket */
-  while (ptr->address_info_next && ptr->fd == INVALID_SOCKET)
+  while (server->address_info_next and server->fd == INVALID_SOCKET)
   {
     /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */
-    if (ptr->type == MEMCACHED_CONNECTION_UDP && ptr->address_info_next->ai_family != AF_INET)
+    if (server->type == MEMCACHED_CONNECTION_UDP && server->address_info_next->ai_family != AF_INET)
     {
-      ptr->address_info_next= ptr->address_info_next->ai_next;
+      server->address_info_next= server->address_info_next->ai_next;
       continue;
     }
 
-    if ((ptr->fd= socket(ptr->address_info_next->ai_family,
-                         ptr->address_info_next->ai_socktype,
-                         ptr->address_info_next->ai_protocol)) < 0)
+    if ((server->fd= socket(server->address_info_next->ai_family,
+                            server->address_info_next->ai_socktype,
+                            server->address_info_next->ai_protocol)) < 0)
     {
-      return memcached_set_errno(*ptr, get_socket_errno(), NULL);
+      return memcached_set_errno(*server, get_socket_errno(), NULL);
     }
 
-    set_socket_options(ptr);
+    set_socket_options(server);
 
     /* connect to server */
-    if ((connect(ptr->fd, ptr->address_info_next->ai_addr, ptr->address_info_next->ai_addrlen) != SOCKET_ERROR))
+    if ((connect(server->fd, server->address_info_next->ai_addr, server->address_info_next->ai_addrlen) != SOCKET_ERROR))
     {
-      ptr->state= MEMCACHED_SERVER_STATE_CONNECTED;
+      server->state= MEMCACHED_SERVER_STATE_CONNECTED;
       return MEMCACHED_SUCCESS;
     }
 
@@ -461,12 +465,12 @@ static memcached_return_t network_connect(memcached_server_st *ptr)
     case EINPROGRESS: // nonblocking mode - first return
     case EALREADY: // nonblocking mode - subsequent returns
       {
-        ptr->state= MEMCACHED_SERVER_STATE_IN_PROGRESS;
-        memcached_return_t rc= connect_poll(ptr);
+        server->state= MEMCACHED_SERVER_STATE_IN_PROGRESS;
+        memcached_return_t rc= connect_poll(server);
 
         if (memcached_success(rc))
         {
-          ptr->state= MEMCACHED_SERVER_STATE_CONNECTED;
+          server->state= MEMCACHED_SERVER_STATE_CONNECTED;
           return MEMCACHED_SUCCESS;
         }
 
@@ -483,155 +487,175 @@ static memcached_return_t network_connect(memcached_server_st *ptr)
       break;
 
     case EINTR: // Special case, we retry ai_addr
-      WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET);
-      (void)closesocket(ptr->fd);
-      ptr->fd= INVALID_SOCKET;
+      WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
+      (void)closesocket(server->fd);
+      server->fd= INVALID_SOCKET;
       continue;
 
     default:
       break;
     }
 
-    WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET);
-    (void)closesocket(ptr->fd);
-    ptr->fd= INVALID_SOCKET;
-    ptr->address_info_next= ptr->address_info_next->ai_next;
+    WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
+    (void)closesocket(server->fd);
+    server->fd= INVALID_SOCKET;
+    server->address_info_next= server->address_info_next->ai_next;
   }
 
-  WATCHPOINT_ASSERT(ptr->fd == INVALID_SOCKET);
+  WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
 
   if (timeout_error_occured)
   {
-    if (ptr->fd != INVALID_SOCKET)
+    if (server->fd != INVALID_SOCKET)
     {
-      (void)closesocket(ptr->fd);
-      ptr->fd= INVALID_SOCKET;
+      (void)closesocket(server->fd);
+      server->fd= INVALID_SOCKET;
     }
   }
 
   WATCHPOINT_STRING("Never got a good file descriptor");
-  /* Failed to connect. schedule next retry */
-  if (ptr->root->retry_timeout)
-  {
-    struct timeval next_time;
 
-    if (gettimeofday(&next_time, NULL) == 0)
-    {
-      ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
-    }
-  }
-  
-  if (memcached_has_current_error(*ptr))
+  if (memcached_has_current_error(*server))
   {
-    return memcached_server_error_return(ptr);
+    return memcached_server_error_return(server);
   }
 
-  if (timeout_error_occured and ptr->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)
+  if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)
   {
-    return memcached_set_error(*ptr, MEMCACHED_TIMEOUT, MEMCACHED_AT);
+    return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
   }
 
-  return memcached_set_error(*ptr, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); /* The last error should be from connect() */
+  return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); /* The last error should be from connect() */
 }
 
-void set_last_disconnected_host(memcached_server_write_instance_st self)
-{
-  // const_cast
-  memcached_st *root= (memcached_st *)self->root;
 
-  memcached_server_free(root->last_disconnected_server);
-  root->last_disconnected_server= memcached_server_clone(NULL, self);
-}
+/*
+  backoff_handling()
 
-memcached_return_t memcached_connect(memcached_server_write_instance_st ptr)
+  Based on time/failure count fail the connect without trying. This prevents waiting in a state where
+  we get caught spending cycles just waiting.
+*/
+static memcached_return_t backoff_handling(memcached_server_write_instance_st server, bool& in_timeout)
 {
-  memcached_return_t rc= MEMCACHED_NO_SERVERS;
+  /* 
+    If we hit server_failure_limit then something is completely wrong about the server.
+
+    1) If autoeject is enabled we do that.
+    2) If not? We go into timeout again, there is much else to do :(
+  */
+  if (server->server_failure_counter >= server->root->server_failure_limit)
+  {
+    /*
+      We just auto_eject if we hit this point 
+    */
+    if (_is_auto_eject_host(server->root))
+    {
+      set_last_disconnected_host(server);
+      run_distribution((memcached_st *)server->root);
 
-  if (ptr->fd != INVALID_SOCKET)
-  {
-    return MEMCACHED_SUCCESS;
-  }
+      return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
+    }
 
-  LIBMEMCACHED_MEMCACHED_CONNECT_START();
+    server->state= MEMCACHED_SERVER_STATE_IN_TIMEOUT;
+
+    // Sanity check/setting
+    if (server->next_retry == 0)
+    {
+      server->next_retry= 1;
+    }
+  }
 
-  /* both retry_timeout and server_failure_limit must be set in order to delay retrying a server on error. */
-  WATCHPOINT_ASSERT(ptr->root);
-  if (ptr->root->retry_timeout && ptr->next_retry)
+  if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT)
   {
     struct timeval curr_time;
+    bool _gettime_success= (gettimeofday(&curr_time, NULL) == 0);
 
-    gettimeofday(&curr_time, NULL);
-
-    // We should optimize this to remove the allocation if the server was
-    // the last server to die
-    if (ptr->next_retry > curr_time.tv_sec)
+    /*
+      If next_retry is less then our current time, then we reset and try everything again.
+    */
+    if (_gettime_success and server->next_retry < curr_time.tv_sec)
     {
-      set_last_disconnected_host(ptr);
-
-      return memcached_set_error(*ptr, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
+      server->state= MEMCACHED_SERVER_STATE_NEW;
     }
+    else
+    {
+      return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
+    }
+
+    in_timeout= true;
   }
 
-  // If we are over the counter failure, we just fail. Reject host only
-  // works if you have a set number of failures.
-  if (ptr->root->server_failure_limit && ptr->server_failure_counter >= ptr->root->server_failure_limit)
+  return MEMCACHED_SUCCESS;
+}
+
+memcached_return_t memcached_connect(memcached_server_write_instance_st server)
+{
+  if (server->fd != INVALID_SOCKET)
   {
-    set_last_disconnected_host(ptr);
+    return MEMCACHED_SUCCESS;
+  }
 
-    // @todo fix this by fixing behavior to no longer make use of
-    // memcached_st
-    if (_is_auto_eject_host(ptr->root))
-    {
-      run_distribution((memcached_st *)ptr->root);
-    }
+  LIBMEMCACHED_MEMCACHED_CONNECT_START();
 
-    return memcached_set_error(*ptr, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
+  bool in_timeout= false;
+  memcached_return_t rc;
+  if (memcached_failed(rc= backoff_handling(server, in_timeout)))
+  {
+    set_last_disconnected_host(server);
+    return rc;
   }
 
   /* We need to clean up the multi startup piece */
-  switch (ptr->type)
+  switch (server->type)
   {
   case MEMCACHED_CONNECTION_UDP:
   case MEMCACHED_CONNECTION_TCP:
-    rc= network_connect(ptr);
+    rc= network_connect(server);
+
     if (LIBMEMCACHED_WITH_SASL_SUPPORT)
     {
-      if (ptr->fd != INVALID_SOCKET and ptr->root->sasl.callbacks)
+      if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks)
       {
-        rc= memcached_sasl_authenticate_connection(ptr);
-        if (memcached_failed(rc) and ptr->fd != INVALID_SOCKET)
+        rc= memcached_sasl_authenticate_connection(server);
+        if (memcached_failed(rc) and server->fd != INVALID_SOCKET)
         {
-          WATCHPOINT_ASSERT(ptr->fd != INVALID_SOCKET);
-          (void)closesocket(ptr->fd);
-          ptr->fd= INVALID_SOCKET;
+          WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
+          (void)closesocket(server->fd);
+          server->fd= INVALID_SOCKET;
         }
       }
     }
     break;
 
   case MEMCACHED_CONNECTION_UNIX_SOCKET:
-    rc= unix_socket_connect(ptr);
+    rc= unix_socket_connect(server);
     break;
   }
 
   if (memcached_success(rc))
   {
-    ptr->server_failure_counter= 0;
-    ptr->next_retry= 0;
+    memcached_mark_server_as_clean(server);
+    return rc;
   }
-  else if (memcached_has_current_error(*ptr))
+
+  set_last_disconnected_host(server);
+  if (memcached_has_current_error(*server))
   {
-    ptr->server_failure_counter++;
-    set_last_disconnected_host(ptr);
+    memcached_mark_server_for_timeout(server);
+    assert(memcached_failed(memcached_server_error_return(server)));
   }
   else
   {
-    memcached_set_error(*ptr, rc, MEMCACHED_AT);
-    ptr->server_failure_counter++;
-    set_last_disconnected_host(ptr);
+    memcached_set_error(*server, rc, MEMCACHED_AT);
+    memcached_mark_server_for_timeout(server);
   }
 
   LIBMEMCACHED_MEMCACHED_CONNECT_END();
 
+  if (in_timeout)
+  {
+    return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
+  }
+
   return rc;
 }
index e17e676ebebb31c58c238ebe63fe54cd077fdcc7..106aec496d5050f30c2d2c2da435810611e4350e 100644 (file)
@@ -54,6 +54,8 @@
 #define MEMCACHED_EXPIRATION_NOT_ADD 0xffffffffU
 #define MEMCACHED_VERSION_STRING_LENGTH 24
 #define MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH 20
+#define MEMCACHED_SERVER_FAILURE_LIMIT 5
+#define MEMCACHED_SERVER_FAILURE_RETRY_TIMEOUT 2
 
 
 enum memcached_server_distribution_t {
index 8c98d4ca0991e65583a8383d18992e1eff049ff4..211701027bd9072024e21c3594023fa84fac79ac 100644 (file)
@@ -469,7 +469,9 @@ static memcached_return_t simple_binary_mget(memcached_st *ptr,
     {
       rc= memcached_connect(instance);
       if (memcached_failed(rc))
+      {
         continue;
+      }
     }
 
     protocol_binary_request_getk request= { }; //= {.bytes= {0}};
@@ -589,16 +591,21 @@ static memcached_return_t replication_binary_mget(memcached_st *ptr,
         server += start;
 
       while (server >= memcached_server_count(ptr))
+      {
         server -= memcached_server_count(ptr);
+      }
 
       if (dead_servers[server])
+      {
         continue;
+      }
 
       memcached_server_write_instance_st instance= memcached_server_instance_fetch(ptr, server);
 
       if (memcached_server_response_count(instance) == 0)
       {
         rc= memcached_connect(instance);
+
         if (memcached_failed(rc))
         {
           memcached_io_reset(instance);
index ae07dfd0ea466829df85e16d09633f7e8e53d4fc..8edd839db1a97fc47ed6df3e0db8e431ca17e08d 100644 (file)
@@ -132,7 +132,7 @@ static inline void _regen_for_auto_eject(memcached_st *ptr)
   {
     struct timeval now;
 
-    if (gettimeofday(&now, NULL) == 0 &&
+    if (gettimeofday(&now, NULL) == 0 and
         now.tv_sec > ptr->ketama.next_distribution_rebuild)
     {
       run_distribution(ptr);
index 4a9a1033bdd51f0d71aad53fb8b6650ee49c6aa8..5c4cf175e095a9e08e3736bf51275def28fba2ad 100644 (file)
@@ -161,7 +161,7 @@ static memcached_return_t update_continuum(memcached_st *ptr)
       }
       else
       {
-        if (ptr->ketama.next_distribution_rebuild == 0 || list[host_index].next_retry < ptr->ketama.next_distribution_rebuild)
+        if (ptr->ketama.next_distribution_rebuild == 0 or list[host_index].next_retry < ptr->ketama.next_distribution_rebuild)
         {
           ptr->ketama.next_distribution_rebuild= list[host_index].next_retry;
         }
@@ -189,7 +189,9 @@ static memcached_return_t update_continuum(memcached_st *ptr)
                                                                             sizeof(memcached_continuum_item_st) * (live_servers + MEMCACHED_CONTINUUM_ADDITION) * points_per_server));
 
     if (new_ptr == 0)
+    {
       return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+    }
 
     ptr->ketama.continuum= new_ptr;
     ptr->ketama.continuum_count= live_servers + MEMCACHED_CONTINUUM_ADDITION;
@@ -200,7 +202,7 @@ static memcached_return_t update_continuum(memcached_st *ptr)
   {
     for (uint32_t host_index = 0; host_index < memcached_server_count(ptr); ++host_index)
     {
-      if (! is_auto_ejecting || list[host_index].next_retry <= now.tv_sec)
+      if (is_auto_ejecting == false or list[host_index].next_retry <= now.tv_sec)
       {
         total_weight += list[host_index].weight;
       }
@@ -209,21 +211,24 @@ static memcached_return_t update_continuum(memcached_st *ptr)
 
   for (uint32_t host_index= 0; host_index < memcached_server_count(ptr); ++host_index)
   {
-    if (is_auto_ejecting && list[host_index].next_retry > now.tv_sec)
+    if (is_auto_ejecting and list[host_index].next_retry > now.tv_sec)
+    {
       continue;
+    }
 
     if (is_ketama_weighted)
     {
-        float pct = (float)list[host_index].weight / (float)total_weight;
+        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,
-               pointer_per_server);
-#endif
+        if (DEBUG)
+        {
+          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);
+        }
     }
 
 
@@ -249,9 +254,12 @@ static memcached_return_t update_continuum(memcached_st *ptr)
           return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, 
                                      memcached_literal_param("snprintf(MEMCACHED_DEFAULT_COMMAND_SIZE)"));
         }
-#ifdef DEBUG
-        printf("update_continuum: key is %s\n", sort_host);
-#endif
+
+        if (DEBUG)
+        {
+          fprintf(stdout, "update_continuum: key is %s\n", sort_host);
+        }
+
         if (is_ketama_weighted)
         {
           for (uint32_t x= 0; x < pointer_per_hash; x++)
@@ -327,12 +335,13 @@ static memcached_return_t update_continuum(memcached_st *ptr)
   ptr->ketama.continuum_points_counter= pointer_counter;
   qsort(ptr->ketama.continuum, ptr->ketama.continuum_points_counter, sizeof(memcached_continuum_item_st), continuum_item_cmp);
 
-#ifdef DEBUG
-  for (uint32_t pointer_index= 0; memcached_server_count(ptr) && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++)
+  if (DEBUG)
   {
-    WATCHPOINT_ASSERT(ptr->ketama.continuum[pointer_index].value <= ptr->ketama.continuum[pointer_index + 1].value);
+    for (uint32_t pointer_index= 0; memcached_server_count(ptr) && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++)
+    {
+      WATCHPOINT_ASSERT(ptr->ketama.continuum[pointer_index].value <= ptr->ketama.continuum[pointer_index + 1].value);
+    }
   }
-#endif
 
   return MEMCACHED_SUCCESS;
 }
index 0ed565d7e28a436e7d3bd9b26ba6598175253430..d7268940543cedeb0e7d8bc84145b442cfdcde74 100644 (file)
@@ -547,8 +547,8 @@ memcached_return_t memcached_io_read(memcached_server_write_instance_st ptr,
     }
   }
 
-  ptr->server_failure_counter= 0;
   *nread = (ssize_t)(buffer_ptr - (char*)buffer);
+
   return MEMCACHED_SUCCESS;
 }
 
index ce3fb8e39ce612b3c451162dbcd3e4db03bab0fe..e81f19ee6fabb94687202a1be7d201d8fb34b0f1 100644 (file)
@@ -90,7 +90,11 @@ static inline bool _memcached_init(memcached_st *self)
   self->distribution= MEMCACHED_DISTRIBUTION_MODULA;
 
   if (not hashkit_create(&self->hashkit))
+  {
     return false;
+  }
+
+  self->server_info.version= 0;
 
   self->ketama.continuum= NULL;
   self->ketama.continuum_count= 0;
@@ -104,7 +108,7 @@ static inline bool _memcached_init(memcached_st *self)
 
   self->snd_timeout= 0;
   self->rcv_timeout= 0;
-  self->server_failure_limit= 0;
+  self->server_failure_limit= MEMCACHED_SERVER_FAILURE_LIMIT;
   self->query_id= 1; // 0 is considered invalid
 
   /* TODO, Document why we picked these defaults */
@@ -116,7 +120,7 @@ static inline bool _memcached_init(memcached_st *self)
   self->io_key_prefetch= 0;
   self->poll_timeout= MEMCACHED_DEFAULT_TIMEOUT;
   self->connect_timeout= MEMCACHED_DEFAULT_CONNECT_TIMEOUT;
-  self->retry_timeout= 0;
+  self->retry_timeout= MEMCACHED_SERVER_FAILURE_RETRY_TIMEOUT;
 
   self->send_size= -1;
   self->recv_size= -1;
@@ -156,7 +160,9 @@ static void _free(memcached_st *ptr, bool release_st)
   memcached_server_free(ptr->last_disconnected_server);
 
   if (ptr->on_cleanup)
+  {
     ptr->on_cleanup(ptr);
+  }
 
   libmemcached_free(ptr, ptr->ketama.continuum);
 
@@ -205,13 +211,13 @@ memcached_st *memcached_create(memcached_st *ptr)
   memcached_set_processing_input(ptr, false);
 #endif
 
-  if (! _memcached_init(ptr))
+  if (_memcached_init(ptr) == false)
   {
     memcached_free(ptr);
     return NULL;
   }
 
-  if (! memcached_result_create(ptr, &ptr->result))
+  if (memcached_result_create(ptr, &ptr->result) == NULL)
   {
     memcached_free(ptr);
     return NULL;
@@ -232,7 +238,9 @@ memcached_st *memcached(const char *string, size_t length)
   }
 
   if (not length)
+  {
     return self;
+  }
 
   memcached_return_t rc= memcached_parse_configuration(self, string, length);
 
@@ -255,7 +263,9 @@ memcached_return_t memcached_reset(memcached_st *ptr)
 {
   WATCHPOINT_ASSERT(ptr);
   if (not ptr)
+  {
     return MEMCACHED_INVALID_ARGUMENTS;
+  }
 
   bool stored_is_allocated= memcached_is_allocated(ptr);
   uint64_t query_id= ptr->query_id;
@@ -274,33 +284,32 @@ memcached_return_t memcached_reset(memcached_st *ptr)
 
 void memcached_servers_reset(memcached_st *self)
 {
-  if (not self)
-    return;
-
-  memcached_server_list_free(memcached_server_list(self));
+  if (self)
+  {
+    memcached_server_list_free(memcached_server_list(self));
 
-  memcached_server_list_set(self, NULL);
-  self->number_of_hosts= 0;
-  memcached_server_free(self->last_disconnected_server);
-  self->last_disconnected_server= NULL;
-  self->server_failure_limit= 0;
+    memcached_server_list_set(self, NULL);
+    self->number_of_hosts= 0;
+    memcached_server_free(self->last_disconnected_server);
+    self->last_disconnected_server= NULL;
+  }
 }
 
 void memcached_reset_last_disconnected_server(memcached_st *self)
 {
-  if (not self)
-    return;
-
-  memcached_server_free(self->last_disconnected_server);
-  self->last_disconnected_server= NULL;
+  if (self)
+  {
+    memcached_server_free(self->last_disconnected_server);
+    self->last_disconnected_server= NULL;
+  }
 }
 
 void memcached_free(memcached_st *ptr)
 {
-  if (not ptr)
-    return;
-
-  _free(ptr, true);
+  if (ptr)
+  {
+    _free(ptr, true);
+  }
 }
 
 /*
@@ -359,9 +368,11 @@ memcached_st *memcached_clone(memcached_st *clone, const memcached_st *source)
   new_clone->tcp_keepidle= source->tcp_keepidle;
 
   if (memcached_server_count(source))
+  {
     rc= memcached_push(new_clone, source);
+  }
 
-  if (rc != MEMCACHED_SUCCESS)
+  if (memcached_failed(rc))
   {
     memcached_free(new_clone);
 
@@ -374,16 +385,14 @@ memcached_st *memcached_clone(memcached_st *clone, const memcached_st *source)
 
   if (LIBMEMCACHED_WITH_SASL_SUPPORT and source->sasl.callbacks)
   {
-    if (memcached_clone_sasl(new_clone, source) != MEMCACHED_SUCCESS)
+    if (memcached_failed(memcached_clone_sasl(new_clone, source)))
     {
       memcached_free(new_clone);
       return NULL;
     }
   }
 
-  rc= run_distribution(new_clone);
-
-  if (rc != MEMCACHED_SUCCESS)
+  if (memcached_failed(run_distribution(new_clone)))
   {
     memcached_free(new_clone);
 
@@ -391,7 +400,9 @@ memcached_st *memcached_clone(memcached_st *clone, const memcached_st *source)
   }
 
   if (source->on_clone)
+  {
     source->on_clone(new_clone, source);
+  }
 
   return new_clone;
 }
index 740edfc73a8583b9e4ad9e65cc28fb5d81cd3f23..3c29a5e14c899442068cf4df38fec92078a8e989 100644 (file)
@@ -113,6 +113,9 @@ struct memcached_st {
 
   memcached_server_distribution_t distribution;
   hashkit_st hashkit;
+  struct {
+    unsigned int version;
+  } server_info;
   uint32_t number_of_hosts;
   memcached_server_st *servers;
   memcached_server_st *last_disconnected_server;
@@ -124,7 +127,7 @@ struct memcached_st {
   uint32_t io_key_prefetch;
   uint32_t tcp_keepidle;
   int32_t poll_timeout;
-  int32_t connect_timeout;
+  int32_t connect_timeout; // How long we will wait on connect() before we will timeout
   int32_t retry_timeout;
   int send_size;
   int recv_size;
index fa7599ede2a0f5d4b15e858930ed65531f9444de..0162700d1a1737b7194eff9e3b0c602076adad94 100644 (file)
@@ -112,8 +112,7 @@ void memcached_quit_server(memcached_server_st *ptr, bool io_death)
 
   if (io_death)
   {
-    ptr->server_failure_counter++;
-    set_last_disconnected_host(ptr);
+    memcached_mark_server_for_timeout(ptr);
   }
 }
 
index d45d2e853e570d0ca24a8c6559fec5fba92f558b..378194fd9fe08d33aeb1d4ffa39ba0373dd54b77 100644 (file)
@@ -84,6 +84,7 @@ enum memcached_return_t {
   MEMCACHED_PARSE_USER_ERROR,
   MEMCACHED_DEPRECATED,
   MEMCACHED_IN_PROGRESS,
+  MEMCACHED_SERVER_TEMPORARILY_DISABLED,
   MEMCACHED_MAXIMUM_RETURN /* Always add new error code before */
 };
 
index 073972dd0e2afbdc7730884ca3f644690979d1f5..ed360ee31aaa2f14f560df9e5e92059dcc6562a1 100644 (file)
@@ -53,6 +53,7 @@ static inline void _server_init(memcached_server_st *self, memcached_st *root,
   self->fd= -1;
   self->io_bytes_sent= 0;
   self->server_failure_counter= 0;
+  self->server_failure_counter_query_id= 0;
   self->weight= weight ? weight : 1; // 1 is the default weight value
   WATCHPOINT_SET(self->io_wait_count.read= 0);
   WATCHPOINT_SET(self->io_wait_count.write= 0);
@@ -69,17 +70,17 @@ static inline void _server_init(memcached_server_st *self, memcached_st *root,
   self->address_info_next= NULL;
 
   self->state= MEMCACHED_SERVER_STATE_NEW;
+  self->next_retry= 0;
 
+  self->root= root;
   if (root)
   {
-    self->next_retry= root->retry_timeout;
+    self->version= ++root->server_info.version;
   }
   else
   {
-    self->next_retry= 0;
+    self->version= UINT_MAX;
   }
-
-  self->root= root;
   self->limit_maxbytes= 0;
   memcpy(self->hostname, hostname.c_str, hostname.size);
   self->hostname[hostname.size]= 0;
index 40328ab6e6cbda2d2209b505a9f6e9bf007cb6c8..3afb5202f6eb95dcabf55cf0a73f688dd931ec13 100644 (file)
@@ -46,7 +46,8 @@ enum memcached_server_state_t {
   MEMCACHED_SERVER_STATE_NEW, // fd == -1, no address lookup has been done
   MEMCACHED_SERVER_STATE_ADDRINFO, // ADDRRESS information has been gathered
   MEMCACHED_SERVER_STATE_IN_PROGRESS,
-  MEMCACHED_SERVER_STATE_CONNECTED
+  MEMCACHED_SERVER_STATE_CONNECTED,
+  MEMCACHED_SERVER_STATE_IN_TIMEOUT
 };
 
 struct memcached_server_st {
@@ -62,7 +63,9 @@ struct memcached_server_st {
   memcached_socket_t fd;
   uint32_t io_bytes_sent; /* # bytes sent since last read */
   uint32_t server_failure_counter;
+  uint64_t server_failure_counter_query_id;
   uint32_t weight;
+  uint32_t version;
   enum memcached_server_state_t state;
   struct {
     uint32_t read;
index 05b51f4fcbf098fbdda139b7a2f96a8f4b9c55d5..8cfcbdba229fa75112d9acd83a3953e8fa869ef8 100644 (file)
 
 #include <libmemcached/basic_string.h>
 
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <cassert>
+
 static inline bool memcached_is_valid_servername(const memcached_string_t& arg)
 {
   return arg.size > 0 or arg.size < NI_MAXHOST;
 }
 
+static inline void memcached_mark_server_as_clean(memcached_server_write_instance_st server)
+{
+  server->server_failure_counter= 0;
+  server->next_retry= 0;
+}
+
+
+static inline void set_last_disconnected_host(memcached_server_write_instance_st self)
+{
+  assert(self->root);
+  if (self->root == NULL)
+  {
+    return;
+  }
+
+  if (self->root->last_disconnected_server and self->root->last_disconnected_server->version == self->version)
+  {
+    return;
+  }
+
+  // const_cast
+  memcached_st *root= (memcached_st *)self->root;
+
+  memcached_server_free(root->last_disconnected_server);
+  root->last_disconnected_server= memcached_server_clone(NULL, self);
+  root->last_disconnected_server->version= self->version;
+}
+
+static inline void memcached_mark_server_for_timeout(memcached_server_write_instance_st server)
+{
+  if (server->state != MEMCACHED_SERVER_STATE_IN_TIMEOUT)
+  {
+    struct timeval next_time;
+    if (gettimeofday(&next_time, NULL) == 0)
+    {
+      server->next_retry= next_time.tv_sec +server->root->retry_timeout;
+    }
+    else
+    {
+      server->next_retry= 1; // Setting the value to 1 causes the timeout to occur immediatly
+    }
+
+    server->state= MEMCACHED_SERVER_STATE_IN_TIMEOUT;
+    if (server->server_failure_counter_query_id != server->root->query_id)
+    {
+      server->server_failure_counter++;
+      server->server_failure_counter_query_id= server->root->query_id;
+    }
+    set_last_disconnected_host(server);
+  }
+}
+
 LIBMEMCACHED_LOCAL
   memcached_server_st *__server_create_with(memcached_st *memc,
                                             memcached_server_write_instance_st host,
index a4a617c0be71cde21a90bbe62cef4ca3854025bf..d5994e852560b03bb165e527bef9455dc4c4ffee 100644 (file)
@@ -118,7 +118,12 @@ uint32_t memcached_server_list_count(const memcached_server_list_st self)
 
 memcached_server_st *memcached_server_list(const memcached_st *self)
 {
-  return self->servers;
+  if (self)
+  {
+    return self->servers;
+  }
+
+  return NULL;
 }
 
 void memcached_server_list_set(memcached_st *self, memcached_server_st *list)
index 3688a66bcc678bf50ecd9764fdf4bacedc49ca53..ebb90056a9efd83f16166af7b7b5eea673dbeb8b 100644 (file)
@@ -182,6 +182,9 @@ const char *memcached_strerror(memcached_st *, memcached_return_t rc)
   case MEMCACHED_IN_PROGRESS:
     return "OPERATION IN PROCESS";
 
+  case MEMCACHED_SERVER_TEMPORARILY_DISABLED:
+    return "SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY";
+
   default:
   case MEMCACHED_MAXIMUM_RETURN:
     return "INVALID memcached_return_t";
index 5c687fc3a6500b5d153b00802b73ca51934f7cbc..09106d9f62134faf7f04e2a1babc40ce8fe0652f 100644 (file)
@@ -62,7 +62,7 @@ bool _compare(const char *file, int line, const char *func, const T_comparable _
         << got_str
         << "\"";
     }
-#if (defined(HAVE_LIBMEMCACHED) && HAVE_LIBMEMCACHED)
+#if defined(HAVE_LIBMEMCACHED) && HAVE_LIBMEMCACHED
     else if (typeid(__expected) == typeid(memcached_return_t))
     {
       libtest::stream::make_cerr(file, line, func) << "Expected \"" 
@@ -107,8 +107,43 @@ bool _compare_hint(const char *file, int line, const char *func, T_comparable __
 {
   if (__expected != __actual)
   {
-    libtest::stream::make_cerr(file, line, func) << "Expected \"" << __expected << "\" got \"" << __actual << "\" Additionally: \"" << __hint << "\"";
+    if (typeid(__expected) == typeid(test_return_t))
+    {
+      const char *expected_str= test_strerror(test_return_t(__expected));
+      const char *got_str= test_strerror(test_return_t(__actual));
 
+      libtest::stream::make_cerr(file, line, func) << "Expected \"" 
+        << expected_str
+        << "\" got \"" 
+        << got_str
+        << "\""
+        << " Additionally: \"" << __hint << "\"";
+    }
+#if defined(HAVE_LIBMEMCACHED) && HAVE_LIBMEMCACHED
+    else if (typeid(__expected) == typeid(memcached_return_t))
+    {
+      libtest::stream::make_cerr(file, line, func) << "Expected \"" 
+        << memcached_strerror(NULL, memcached_return_t(__expected)) 
+        << "\" got \"" 
+        << memcached_strerror(NULL, memcached_return_t(__actual)) << "\""
+        << " Additionally: \"" << __hint << "\"";
+    }
+#endif
+#if defined(HAVE_LIBGEARMAN) && HAVE_LIBGEARMAN
+    else if (typeid(__expected) == typeid(gearman_return_t))
+    {
+      libtest::stream::make_cerr(file, line, func) << "Expected \"" 
+        << gearman_strerror(gearman_return_t(__expected)) 
+        << "\" got \"" 
+        << gearman_strerror(gearman_return_t(__actual)) << "\""
+        << " Additionally: \"" << __hint << "\"";
+    }
+#endif
+    else
+    {
+      libtest::stream::make_cerr(file, line, func) << "Expected \"" << __expected << "\" got \"" << __actual << "\""
+        << " Additionally: \"" << __hint << "\"";
+    }
     return false;
   }
 
index 90908244c47506a655c50b3bcf64dbbeb9a0ab7d..c5ca7a1bf7da945f2ccfcc634de785e309c958c0 100644 (file)
@@ -148,6 +148,11 @@ public:
   {
     _servers.set_sasl(username_arg, password_arg);
   }
+
+  libtest::server_startup_st& servers()
+  {
+    return _servers;
+  }
   
   /**
     Runner represents the callers for the tests. If not implemented we will use
index d7394fd090c9e78ccabc092322acf611bcfe2250..044ca4f8e7bf9e2b8cbcda171936d8cba8c2ca54 100644 (file)
@@ -69,6 +69,7 @@ noinst_HEADERS+= \
                 libtest/libtool.hpp \
                 libtest/memcached.h \
                 libtest/runner.h \
+                libtest/port.h \
                 libtest/server.h \
                 libtest/server_container.h \
                 libtest/signal.h \
@@ -88,6 +89,7 @@ libtest_libtest_la_SOURCES= \
                            libtest/killpid.cc \
                            libtest/libtool.cc \
                            libtest/runner.cc \
+                           libtest/port.cc \
                            libtest/server.cc \
                            libtest/server_container.cc \
                            libtest/signal.cc \
diff --git a/libtest/port.cc b/libtest/port.cc
new file mode 100644 (file)
index 0000000..9bd7717
--- /dev/null
@@ -0,0 +1,73 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  libtest
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 3 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <libtest/common.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <ctime>
+#include <fnmatch.h>
+#include <iostream>
+
+#include <signal.h>
+
+#include <libtest/stats.h>
+#include <libtest/signal.h>
+
+#ifndef __INTEL_COMPILER
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+#endif
+
+using namespace libtest;
+
+static in_port_t global_port= 0;
+static in_port_t global_max_port= 0;
+
+in_port_t default_port()
+{
+  return global_port;
+}
+void set_default_port(in_port_t port)
+{
+  global_port= port;
+}
+
+in_port_t max_port()
+{
+  return global_max_port;
+}
+void set_max_port(in_port_t port)
+{
+  if (port > global_max_port)
+  {
+    global_max_port= port;
+  }
+
+  global_max_port= port;
+}
diff --git a/libtest/port.h b/libtest/port.h
new file mode 100644 (file)
index 0000000..63b2992
--- /dev/null
@@ -0,0 +1,39 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  libtest
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 3 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+/*
+  Structures for generic tests.
+*/
+
+#pragma once
+
+LIBTEST_API
+in_port_t default_port();
+
+LIBTEST_API
+void set_default_port(in_port_t port);
+
+LIBTEST_API
+in_port_t max_port();
+
+LIBTEST_API
+void set_max_port(in_port_t port);
index f747dc229b4e47d620f1b9390a214a261c18f4db..684303a23934ef09e7a4e62352cc44219fc8a60d 100644 (file)
@@ -75,31 +75,55 @@ Server* server_startup_st::pop_server()
   return tmp;
 }
 
-void server_startup_st::shutdown(bool remove)
+bool server_startup_st::shutdown(uint32_t number_of_host)
 {
-  if (remove)
+  assert(servers.size() > number_of_host);
+  if (servers.size() > number_of_host)
   {
-    for (std::vector<Server *>::iterator iter= servers.begin(); iter != servers.end(); iter++)
+    Server* tmp= servers[number_of_host];
+
+    if (tmp and tmp->has_pid() and not tmp->kill(tmp->pid()))
+    { }
+    else
     {
-      delete *iter;
+      return true;
     }
-    servers.clear();
   }
-  else
+
+  return false;
+}
+
+void server_startup_st::shutdown_and_remove()
+{
+  for (std::vector<Server *>::iterator iter= servers.begin(); iter != servers.end(); iter++)
+  {
+    delete *iter;
+  }
+  servers.clear();
+}
+
+void server_startup_st::shutdown()
+{
+  for (std::vector<Server *>::iterator iter= servers.begin(); iter != servers.end(); iter++)
   {
-    for (std::vector<Server *>::iterator iter= servers.begin(); iter != servers.end(); iter++)
+    if ((*iter)->has_pid() and not (*iter)->kill((*iter)->pid()))
     {
-      if ((*iter)->has_pid() and not (*iter)->kill((*iter)->pid()))
-      {
-        Error << "Unable to kill:" <<  *(*iter);
-      }
+      Error << "Unable to kill:" <<  *(*iter);
     }
   }
 }
 
+void server_startup_st::restart()
+{
+  for (std::vector<Server *>::iterator iter= servers.begin(); iter != servers.end(); iter++)
+  {
+    (*iter)->start();
+  }
+}
+
 server_startup_st::~server_startup_st()
 {
-  shutdown(true);
+  shutdown_and_remove();
 }
 
 bool server_startup_st::is_debug() const
@@ -123,6 +147,8 @@ bool server_startup(server_startup_st& construct, const std::string& server_type
   Outn();
   (void)try_port;
 
+  set_max_port(try_port);
+
   // Look to see if we are being provided ports to use
   {
     char variable_buffer[1024];
index 9c9922ed0ed6b30c1a58b51e052826d7b6affaa9..4e81dafd27af310f22db1bf91ddd62160adebacb 100644 (file)
@@ -38,6 +38,7 @@ private:
   std::string server_list;
   bool _socket;
   bool _sasl;
+  uint32_t _count;
   std::string _username;
   std::string _password;
 
@@ -49,18 +50,26 @@ public:
   server_startup_st() :
     _socket(false),
     _sasl(false),
+    _count(5),
     udp(0)
   { }
 
   bool start_socket_server(const std::string& server_type, const in_port_t try_port, int argc, const char *argv[]);
 
-  std::string option_string() const;
+  uint32_t count() const
+  {
+    return _count;
+  }
 
-  size_t count() const
+  void set_count(uint32_t arg)
   {
-    return servers.size();
+    _count= arg;
   }
 
+  void restart();
+
+  std::string option_string() const;
+
   const std::string& password() const
   {
     return _password;
@@ -99,7 +108,10 @@ public:
   }
 
 
-  void shutdown(bool remove= false);
+  void shutdown_and_remove();
+  void shutdown();
+  bool shutdown(uint32_t number_of_host);
+
   void push_server(Server *);
   Server *pop_server();
 
index 5c43e40b35d9f0211d076c19d24c6864fe7dfa59..89e7c07112e674c6a8a2be1e9c40cbf5c2a9733b 100644 (file)
 
 using namespace libtest;
 
-static in_port_t global_port= 0;
 static char global_socket[1024];
 
-in_port_t default_port()
-{
-  return global_port;
-}
-void set_default_port(in_port_t port)
-{
-  global_port= port;
-}
-
 const char *default_socket()
 {
   assert(global_socket[0]);
index a10aa30dd2f927adc3a9ace9a1bdfe10eb4685f6..047dd8ce03390f245e5c6c71f95c0672786d154a 100644 (file)
@@ -41,6 +41,7 @@
 #include <libtest/strerror.h>
 #include <libtest/core.h>
 #include <libtest/runner.h>
+#include <libtest/port.h>
 #include <libtest/stats.h>
 #include <libtest/collection.h>
 #include <libtest/framework.h>
index fb2cd7769bf1a50fd78bdf8e5466789e6e2f61a5..b0fd5ad011dde2d68689bc0918506a5ff79aeb04 100644 (file)
@@ -67,9 +67,11 @@ AC_DEFUN([PANDORA_OPTIMIZE],[
     # Debugging. No optimization.
     AM_CFLAGS="${AM_CFLAGS} ${DEBUG_CFLAGS} -DDEBUG"
     AM_CXXFLAGS="${AM_CXXFLAGS} ${DEBUG_CXXFLAGS} -DDEBUG"
+    AC_DEFINE(DEBUG, [ 1 ], [Define to 1 to enable debugging code.])
   ],[
     # Optimized version. No debug
     AM_CFLAGS="${AM_CFLAGS} ${OPTIMIZE_CFLAGS}"
     AM_CXXFLAGS="${AM_CXXFLAGS} ${OPTIMIZE_CXXFLAGS}"
+    AC_DEFINE(DEBUG, [ 0 ], [Define to 1 to enable debugging code.])
   ])
 ])
index 6949bcd6f2c184d0c37966ad8ec5459525f74f79..eebe0f38311162ed75bd847a1c32370183d41f07 100644 (file)
@@ -282,7 +282,6 @@ collection_st collection[] ={
 
 
 #define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT +10
-#define SERVERS_TO_CREATE 5
 
 #include "libmemcached_world.h"
 
diff --git a/tests/failure.cc b/tests/failure.cc
new file mode 100644 (file)
index 0000000..c616a26
--- /dev/null
@@ -0,0 +1,231 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *
+ *      * Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ *      * Redistributions in binary form must reproduce the above
+ *  copyright notice, this list of conditions and the following disclaimer
+ *  in the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ *      * The names of its contributors may not be used to endorse or
+ *  promote products derived from this software without specific prior
+ *  written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <config.h>
+
+/*
+  C++ interface test
+*/
+#include <libmemcached/memcached.hpp>
+#include <libmemcached/server_instance.h>
+#include <libtest/test.hpp>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctime>
+
+#include <string>
+#include <iostream>
+
+using namespace std;
+using namespace memcache;
+using namespace libtest;
+
+Framework *global_framework= NULL;
+
+static test_return_t shutdown_servers(memcached_st *memc)
+{
+  test_compare(memcached_server_count(memc), 1U);
+
+  // Disable a single server, just the first
+  global_framework->servers().shutdown(0);
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t add_shutdown_servers(memcached_st *memc)
+{
+  while (memcached_server_count(memc) < 2)
+  {
+    const char *argv[1]= { "add_shutdown_server" };
+    in_port_t port= max_port() +1;
+    test_true(global_framework->servers().start_socket_server("memcached", port, 1, argv));
+    test_compare(MEMCACHED_SUCCESS, memcached_server_add(memc, "localhost", port));
+  }
+
+  // Disable a single server, just the first
+  global_framework->servers().shutdown(0);
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t restart_servers(memcached_st *)
+{
+  // Restart the servers
+  global_framework->servers().restart();
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t cull_TEST(memcached_st *memc)
+{
+  uint32_t count= memcached_server_count(memc);
+
+  // Do not do this in your code, it is not supported.
+  memc->servers[0].options.is_dead= true;
+  memc->state.is_time_for_rebuild= true;
+
+  uint32_t new_count= memcached_server_count(memc);
+  test_compare(count, new_count);
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t MEMCACHED_SERVER_TEMPORARILY_DISABLED_TEST(memcached_st *memc)
+{
+  test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, 30));
+  test_compare_got(MEMCACHED_CONNECTION_FAILURE,
+                   memcached_set(memc,
+                                 test_literal_param("foo"),
+                                 NULL, 0, time_t(0), uint32_t(0)),
+                   memcached_last_error_message(memc));
+
+  /*
+    Setting server_failure_counter==0 should not influence the timeout that we set above,
+    since we check the timeout that is created by the failure before we check how many times
+    a server has failed.
+  */
+  test_compare(MEMCACHED_SERVER_TEMPORARILY_DISABLED,
+               memcached_set(memc, test_literal_param("foo"), NULL, 0, time_t(0), uint32_t(0)));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t MEMCACHED_SERVER_TEMPORARILY_DISABLED_to_success_TEST(memcached_st *memc)
+{
+  test_compare_got(MEMCACHED_CONNECTION_FAILURE,
+                   memcached_set(memc,
+                                 test_literal_param("foo"),
+                                 NULL, 0, time_t(0), uint32_t(0)),
+                   memcached_last_error_message(memc));
+
+  /*
+    Setting server_failure_counter==0 should not influence the timeout that we set above,
+    since we check the timeout that is created by the failure before we check how many times
+    a server has failed.
+  */
+  test_compare(MEMCACHED_SERVER_TEMPORARILY_DISABLED,
+               memcached_set(memc, test_literal_param("foo"), NULL, 0, time_t(0), uint32_t(0)));
+
+  global_framework->servers().restart();
+
+  memcached_return_t ret;
+  do {
+    sleep(3);
+    ret= memcached_set(memc, test_literal_param("foo"), NULL, 0, time_t(0), uint32_t(0));
+  } while (ret == MEMCACHED_SERVER_TEMPORARILY_DISABLED);
+
+  test_compare_got(MEMCACHED_SUCCESS, ret, memcached_last_error_message(memc));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t MEMCACHED_SERVER_MARKED_DEAD_TEST(memcached_st *memc)
+{
+  test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, 30));
+  test_compare(MEMCACHED_SUCCESS, memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS, true));
+
+  memcached_return_t ret;
+  do {
+    ret= memcached_set(memc,
+                       test_literal_param("foo"),
+                       NULL, 0, time_t(0), uint32_t(0));
+  } while (ret == MEMCACHED_SUCCESS or ret == MEMCACHED_CONNECTION_FAILURE);
+  test_compare(MEMCACHED_SERVER_TEMPORARILY_DISABLED, ret);
+
+  do {
+    sleep(3);
+    ret= memcached_set(memc, test_literal_param("foo"), NULL, 0, time_t(0), uint32_t(0));
+  } while (ret == MEMCACHED_SERVER_TEMPORARILY_DISABLED or ret == MEMCACHED_SUCCESS);
+
+  test_compare_got(MEMCACHED_SERVER_MARKED_DEAD, ret, memcached_last_error_message(memc));
+
+  return TEST_SUCCESS;
+}
+
+test_st cull_TESTS[] ={
+  { "cull servers", true, (test_callback_fn*)cull_TEST },
+  { 0, 0, 0 }
+};
+
+test_st server_temporarily_disabled_TESTS[] ={
+  { "memcached_set(MEMCACHED_SERVER_TEMPORARILY_DISABLED)", true, (test_callback_fn*)MEMCACHED_SERVER_TEMPORARILY_DISABLED_TEST },
+  { "memcached_set(MEMCACHED_SERVER_TEMPORARILY_DISABLED -> MEMCACHED_SUCCESS)", true, (test_callback_fn*)MEMCACHED_SERVER_TEMPORARILY_DISABLED_to_success_TEST },
+  { 0, 0, 0 }
+};
+
+test_st server_permanently_disabled_TESTS[] ={
+  { "memcached_set(MEMCACHED_SERVER_MARKED_DEAD)", true, (test_callback_fn*)MEMCACHED_SERVER_MARKED_DEAD_TEST },
+  { 0, 0, 0 }
+};
+
+collection_st collection[] ={
+  { "cull", (test_callback_fn*)shutdown_servers, (test_callback_fn*)restart_servers, cull_TESTS },
+  { "server failed", (test_callback_fn*)shutdown_servers, (test_callback_fn*)restart_servers, server_temporarily_disabled_TESTS },
+  { "server eject", (test_callback_fn*)add_shutdown_servers, (test_callback_fn*)restart_servers, server_permanently_disabled_TESTS },
+  { 0, 0, 0, 0 }
+};
+
+#define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT +10
+#include "libmemcached_world.h"
+
+void get_world(Framework *world)
+{
+  world->servers().set_count(1);
+
+  world->collections= collection;
+
+  world->_create= (test_callback_create_fn*)world_create;
+  world->_destroy= (test_callback_destroy_fn*)world_destroy;
+
+  world->item._startup= (test_callback_fn*)world_test_startup;
+  world->item.set_pre((test_callback_fn*)world_pre_run);
+  world->item.set_flush((test_callback_fn*)world_flush);
+  world->item.set_post((test_callback_fn*)world_post_run);
+  world->_on_error= (test_callback_error_fn*)world_on_error;
+
+  world->collection_startup= (test_callback_fn*)world_container_startup;
+  world->collection_shutdown= (test_callback_fn*)world_container_shutdown;
+
+  world->set_runner(&defualt_libmemcached_runner);
+
+  global_framework= world;
+}
index ed345c927679fb4dcc08fbeae8929b2ced0dd98d..38a07f9912bcb230e43e992913d501a82a8675fc 100644 (file)
@@ -130,6 +130,13 @@ tests_testplus_LDADD= $(tests_testplus_DEPENDENCIES)
 check_PROGRAMS+= tests/testplus
 noinst_PROGRAMS+= tests/testplus
 
+tests_failure_SOURCES= tests/failure.cc
+tests_failure_CXXFLAGS = $(AM_CXXFLAGS)
+tests_failure_DEPENDENCIES= $(TESTS_LDADDS)
+tests_failure_LDADD= $(tests_failure_DEPENDENCIES)
+check_PROGRAMS+= tests/failure
+noinst_PROGRAMS+= tests/failure
+
 tests_atomsmasher_SOURCES= \
                           tests/atomsmasher.cc \
                           tests/debug.cc \
@@ -355,12 +362,18 @@ gdb-hashplus: tests/hash_plus
 gdb-cycle: tests/cycle
        @$(DEBUG_COMMAND) tests/cycle
 
+gdb-failure: tests/failure
+       @$(DEBUG_COMMAND) tests/failure
+
 valgrind-cycle: tests/cycle
        $(VALGRIND_COMMAND) tests/cycle
 
 valgrind-mem: tests/testapp
        @$(VALGRIND_COMMAND) tests/testapp
 
+valgrind-failure: tests/failure
+       @$(VALGRIND_COMMAND) tests/failure
+
 valgrind-atom: tests/atomsmasher
        $(VALGRIND_COMMAND) tests/atomsmasher
 
index e2f0c27d2e4b9de3df69d6ce482c8c8b8bfc5d18..9f38c02498b54220f40db8330c99f82cfdd62a68 100644 (file)
@@ -27,8 +27,6 @@ struct libmemcached_test_container_st
   { }
 };
 
-#define SERVERS_TO_CREATE 5
-
 static void *world_create(server_startup_st& servers, test_return_t& error)
 {
   if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0)
@@ -45,8 +43,8 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
   }
 
 
-  in_port_t max_port;
-  for (uint32_t x= 0; x < SERVERS_TO_CREATE; x++)
+  in_port_t max_port= TEST_PORT_BASE;
+  for (uint32_t x= 0; x < servers.count(); x++)
   {
     in_port_t port;
 
index d691cbc57dd5f6186c686d3bb211c776d152bb33..2c29c0487cd522e30b2ed34180bceb2fdf4a4192 100644 (file)
@@ -422,7 +422,7 @@ static test_return_t libmemcached_string_distribution_test(memcached_st *)
   return TEST_SUCCESS;
 }
 
-static test_return_t error_test(memcached_st *memc)
+static test_return_t memcached_return_t_TEST(memcached_st *memc)
 {
   uint32_t values[] = { 851992627U, 2337886783U, 4109241422U, 4001849190U,
                         982370485U, 1263635348U, 4242906218U, 3829656100U,
@@ -435,7 +435,8 @@ static test_return_t error_test(memcached_st *memc)
                         54481931U, 4186304426U, 1741088401U, 2979625118U,
                         4159057246U, 3425930182U, 2593724503U,  1868899624U,
                         1769812374U, 2302537950U, 1110330676U, 3365377466U, 
-                        1336171666U, 3021258493U, 2334992265U, 3365377466U };
+                        1336171666U, 3021258493U, 2334992265U, 3861994737U, 
+                        3365377466U };
 
   // You have updated the memcache_error messages but not updated docs/tests.
   for (int rc= int(MEMCACHED_SUCCESS); rc < int(MEMCACHED_MAXIMUM_RETURN); ++rc)
@@ -446,12 +447,12 @@ static test_return_t error_test(memcached_st *memc)
                                             MEMCACHED_HASH_JENKINS);
     if (values[rc] != hash_val)
     {
-      fprintf(stderr, "\n\nYou have updated memcached_return_t without updating the error_test\n");
+      fprintf(stderr, "\n\nYou have updated memcached_return_t without updating the memcached_return_t_TEST\n");
       fprintf(stderr, "%u, %s, (%u)\n\n", (uint32_t)rc, memcached_strerror(memc, memcached_return_t(rc)), hash_val);
     }
     test_compare(values[rc], hash_val);
   }
-  test_compare(47, int(MEMCACHED_MAXIMUM_RETURN));
+  test_compare(48, int(MEMCACHED_MAXIMUM_RETURN));
 
   return TEST_SUCCESS;
 }
@@ -1278,7 +1279,7 @@ static test_return_t mget_end(memcached_st *memc)
 
   // this should indicate end
   string= memcached_fetch(memc, key, &key_length, &string_length, &flags, &rc);
-  test_true(rc == MEMCACHED_END);
+  test_compare(MEMCACHED_END, rc);
 
   return TEST_SUCCESS;
 }
@@ -1522,6 +1523,7 @@ static test_return_t binary_increment_with_prefix_test(memcached_st *orig_memc)
                                                       test_literal_param("number"),
                                                       1, &new_number));
   test_compare(uint64_t(2), new_number);
+  memcached_free(memc);
 
   return TEST_SUCCESS;
 }
@@ -2614,16 +2616,13 @@ static test_return_t user_supplied_bug9(memcached_st *memc)
 /* We are testing with aggressive timeout to get failures */
 static test_return_t user_supplied_bug10(memcached_st *memc)
 {
-  const char *key= "foo";
   size_t value_length= 512;
-  size_t key_len= 3;
   unsigned int set= 1;
   memcached_st *mclone= memcached_clone(NULL, memc);
 
   memcached_behavior_set(mclone, MEMCACHED_BEHAVIOR_NO_BLOCK, set);
   memcached_behavior_set(mclone, MEMCACHED_BEHAVIOR_TCP_NODELAY, set);
-  int32_t timeout= 0;
-  memcached_behavior_set(mclone, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, (uint64_t)timeout);
+  memcached_behavior_set(mclone, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, uint64_t(0));
 
   char *value= (char*)malloc(value_length * sizeof(char));
 
@@ -2634,9 +2633,12 @@ static test_return_t user_supplied_bug10(memcached_st *memc)
 
   for (unsigned int x= 1; x <= 100000; ++x)
   {
-    memcached_return_t rc= memcached_set(mclone, key, key_len,value, value_length, 0, 0);
+    memcached_return_t rc= memcached_set(mclone, 
+                                         test_literal_param("foo"),
+                                         value, value_length, 0, 0);
 
-    test_true_got(rc == MEMCACHED_SUCCESS or rc == MEMCACHED_WRITE_FAILURE or rc == MEMCACHED_BUFFERED or rc == MEMCACHED_TIMEOUT or rc == MEMCACHED_CONNECTION_FAILURE, 
+    test_true_got((rc == MEMCACHED_SUCCESS or rc == MEMCACHED_WRITE_FAILURE or rc == MEMCACHED_BUFFERED or rc == MEMCACHED_TIMEOUT or rc == MEMCACHED_CONNECTION_FAILURE 
+                  or rc == MEMCACHED_SERVER_TEMPORARILY_DISABLED), 
                   memcached_strerror(NULL, rc));
 
     if (rc == MEMCACHED_WRITE_FAILURE or rc == MEMCACHED_TIMEOUT)
@@ -3220,12 +3222,13 @@ static test_return_t generate_data_with_stats(memcached_st *memc)
   for (host_index= 0; host_index < SERVERS_TO_CREATE; host_index++)
   {
     /* This test was changes so that "make test" would work properlly */
-#ifdef DEBUG
-    memcached_server_instance_st instance=
-      memcached_server_instance_by_position(memc, host_index);
+    if (DEBUG)
+    {
+      memcached_server_instance_st instance=
+        memcached_server_instance_by_position(memc, host_index);
 
-    printf("\nserver %u|%s|%u bytes: %llu\n", host_index, instance->hostname, instance->port, (unsigned long long)(stat_p + host_index)->bytes);
-#endif
+      printf("\nserver %u|%s|%u bytes: %llu\n", host_index, instance->hostname, instance->port, (unsigned long long)(stat_p + host_index)->bytes);
+    }
     test_true((unsigned long long)(stat_p + host_index)->bytes);
   }
 
@@ -5260,7 +5263,7 @@ static test_return_t test_multiple_get_last_disconnect(memcached_st *)
     {
       const char *msg=  memcached_strerror(memc, memcached_return_t(x));
       memcached_return_t ret= memcached_set(memc, msg, strlen(msg), NULL, 0, (time_t)0, (uint32_t)0);
-      test_compare_got(MEMCACHED_CONNECTION_FAILURE, ret, memcached_last_error_message(memc));
+      test_true_got((ret == MEMCACHED_CONNECTION_FAILURE or ret == MEMCACHED_SERVER_TEMPORARILY_DISABLED), memcached_last_error_message(memc));
 
       memcached_server_instance_st disconnected_server= memcached_server_get_last_disconnect(memc);
       test_true(disconnected_server);
@@ -5286,66 +5289,6 @@ static test_return_t test_verbosity(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
-static test_return_t test_server_failure(memcached_st *memc)
-{
-  if (memcached_server_count(memc) < 2)
-    return TEST_SKIPPED;
-
-  memcached_server_instance_st instance= memcached_server_instance_by_position(memc, 0);
-
-  memcached_st *local_memc= memcached_create(NULL);
-
-  memcached_server_add(local_memc, memcached_server_name(instance), memcached_server_port(instance));
-  memcached_behavior_set(local_memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, 2);
-
-  uint32_t server_count= memcached_server_count(local_memc);
-  test_compare(1U, server_count);
-
-  // Disable the server
-  instance= memcached_server_instance_by_position(local_memc, 0);
-  ((memcached_server_write_instance_st)instance)->server_failure_counter= 2;
-
-  memcached_return_t rc;
-  test_compare_got(MEMCACHED_SERVER_MARKED_DEAD,
-                   rc= memcached_set(local_memc, "foo", strlen("foo"), NULL, 0, (time_t)0, (uint32_t)0),
-                   memcached_last_error_message(local_memc));
-
-  ((memcached_server_write_instance_st)instance)->server_failure_counter= 0;
-  test_compare(MEMCACHED_SUCCESS,
-               memcached_set(local_memc, "foo", strlen("foo"), NULL, 0, (time_t)0, (uint32_t)0));
-#if 0
-  memcached_last_error_message(local_memc));
-#endif
-
-
-  memcached_free(local_memc);
-
-  return TEST_SUCCESS;
-}
-
-static test_return_t test_cull_servers(memcached_st *memc)
-{
-  uint32_t count= memcached_server_count(memc);
-
-  if (count < 2)
-  {
-    return TEST_SKIPPED;
-  }
-
-  // Do not do this in your code, it is not supported.
-  memc->servers[1].options.is_dead= true;
-  memc->state.is_time_for_rebuild= true;
-
-  uint32_t new_count= memcached_server_count(memc);
-  test_compare(count, new_count);
-
-#if 0
-  test_true(count == new_count + 1 );
-#endif
-
-  return TEST_SUCCESS;
-}
-
 
 static memcached_return_t stat_printer(memcached_server_instance_st server,
                                        const char *key, size_t key_length,
@@ -5858,7 +5801,6 @@ test_st tests[] ={
   {"connection_test", false, (test_callback_fn*)connection_test},
   {"callback_test", false, (test_callback_fn*)callback_test},
   {"userdata_test", false, (test_callback_fn*)userdata_test},
-  {"error", false, (test_callback_fn*)error_test },
   {"set", false, (test_callback_fn*)set_test },
   {"set2", false, (test_callback_fn*)set_test2 },
   {"set3", false, (test_callback_fn*)set_test3 },
@@ -5904,8 +5846,6 @@ test_st tests[] ={
   {"memcached_pool_test", true, (test_callback_fn*)memcached_pool_test },
   {"test_get_last_disconnect", true, (test_callback_fn*)test_get_last_disconnect},
   {"verbosity", true, (test_callback_fn*)test_verbosity},
-  {"test_server_failure", true, (test_callback_fn*)test_server_failure},
-  {"cull_servers", true, (test_callback_fn*)test_cull_servers},
   {"memcached_stat_execute", true, (test_callback_fn*)memcached_stat_execute_test},
   {0, 0, 0}
 };
@@ -5934,6 +5874,7 @@ test_st basic_tests[] ={
   {"reset heap", true, (test_callback_fn*)basic_reset_heap_test},
   {"reset stack clone", true, (test_callback_fn*)basic_reset_stack_clone_test},
   {"reset heap clone", true, (test_callback_fn*)basic_reset_heap_clone_test},
+  {"memcached_return_t", false, (test_callback_fn*)memcached_return_t_TEST },
   {0, 0, 0}
 };
 
@@ -6217,7 +6158,7 @@ collection_st collection[] ={
   {"async", (test_callback_fn*)pre_nonblock, 0, async_tests},
   {"async(BINARY)", (test_callback_fn*)pre_nonblock_binary, 0, async_tests},
   {"Cal Haldenbrand's tests", 0, 0, haldenbrand_tests},
-  {"user", 0, 0, user_tests},
+  {"user written tests", 0, 0, user_tests},
   {"generate", 0, 0, generate_tests},
   {"generate_hsieh", (test_callback_fn*)pre_hsieh, 0, generate_tests},
   {"generate_ketama", (test_callback_fn*)pre_behavior_ketama, 0, generate_tests},
index 4c81cadfa81bb9f11b8ae2d672c72725080d69d0..077f8c99815f7a9c24f437a4c8c1a18d49d32d2c 100644 (file)
@@ -33,8 +33,6 @@ using namespace libtest;
 
 #include <libtest/server.h>
 
-#define SERVERS_TO_CREATE 5
-
 #ifndef __INTEL_COMPILER
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
@@ -491,8 +489,6 @@ collection_st collection[] ={
   {0, 0, 0, 0}
 };
 
-#define SERVERS_TO_CREATE 5
-
 #define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT +10
 #include "libmemcached_world.h"