Merge in updates for server failure testing.
authorBrian Aker <brian@tangent.org>
Fri, 19 Aug 2011 22:33:44 +0000 (15:33 -0700)
committerBrian Aker <brian@tangent.org>
Fri, 19 Aug 2011 22:33:44 +0000 (15:33 -0700)
31 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/server_container.cc
libtest/server_container.h
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 f90cb6b057958ec3db6feedd35cbbf9c1914a7af..589c40c51dd06fff0f9230a18eaf4e768e42dade 100644 (file)
@@ -37,6 +37,8 @@
 
 
 #include <libmemcached/common.h>
+
+#include <cassert>
 #include <ctime>
 #include <sys/time.h>
 
@@ -343,7 +345,7 @@ static void set_socket_options(memcached_server_st *server)
 static memcached_return_t unix_socket_connect(memcached_server_st *server)
 {
 #ifndef WIN32
-  WATCHPOINT_ASSERT(server->fd == -1);
+  WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
 
   if ((server->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
   {
@@ -398,7 +400,7 @@ static memcached_return_t network_connect(memcached_server_st *server)
   WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
   WATCHPOINT_ASSERT(server->cursor_active == 0);
 
-  if (not server->address_info)
+  if (server->address_info == NULL or server->address_info_next == NULL)
   {
     WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
     memcached_return_t rc;
@@ -421,11 +423,13 @@ static memcached_return_t network_connect(memcached_server_st *server)
     }
 
     if (memcached_failed(rc))
+    {
       return rc;
+    }
   }
 
   /* Create the socket */
-  while (server->address_info_next && server->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 (server->type == MEMCACHED_CONNECTION_UDP && server->address_info_next->ai_family != AF_INET)
@@ -510,16 +514,6 @@ static memcached_return_t network_connect(memcached_server_st *server)
   }
 
   WATCHPOINT_STRING("Never got a good file descriptor");
-  /* Failed to connect. schedule next retry */
-  if (server->root->retry_timeout)
-  {
-    struct timeval next_time;
-
-    if (gettimeofday(&next_time, NULL) == 0)
-    {
-      server->next_retry= next_time.tv_sec + server->root->retry_timeout;
-    }
-  }
 
   if (memcached_has_current_error(*server))
   {
@@ -534,58 +528,81 @@ static memcached_return_t network_connect(memcached_server_st *server)
   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 server)
+  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.
 
-  if (server->fd != INVALID_SOCKET)
+    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)
   {
-    return MEMCACHED_SUCCESS;
-  }
-
-  LIBMEMCACHED_MEMCACHED_CONNECT_START();
-
-  /* both retry_timeout and server_failure_limit must be set in order to delay retrying a server on error. */
-  WATCHPOINT_ASSERT(server->root);
-  if (server->root->retry_timeout and server->next_retry)
-  {
-    struct timeval curr_time;
-
-    gettimeofday(&curr_time, NULL);
-
-    // We should optimize this to remove the allocation if the server was
-    // the last server to die
-    if (server->next_retry > curr_time.tv_sec)
+    /*
+      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);
 
       return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
     }
+
+    server->state= MEMCACHED_SERVER_STATE_IN_TIMEOUT;
+
+    // Sanity check/setting
+    if (server->next_retry == 0)
+    {
+      server->next_retry= 1;
+    }
   }
 
-  // If we are over the counter failure, we just fail. Reject host only
-  // works if you have a set number of failures.
-  if (server->root->server_failure_limit and server->server_failure_counter >= server->root->server_failure_limit)
+  if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT)
   {
-    set_last_disconnected_host(server);
+    struct timeval curr_time;
+    bool _gettime_success= (gettimeofday(&curr_time, NULL) == 0);
 
-    // @todo fix this by fixing behavior to no longer make use of
-    // memcached_st
-    if (_is_auto_eject_host(server->root))
+    /*
+      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)
     {
-      run_distribution((memcached_st *)server->root);
+      server->state= MEMCACHED_SERVER_STATE_NEW;
+    }
+    else
+    {
+      return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
     }
 
-    return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
+    in_timeout= true;
+  }
+
+  return MEMCACHED_SUCCESS;
+}
+
+memcached_return_t memcached_connect(memcached_server_write_instance_st server)
+{
+  if (server->fd != INVALID_SOCKET)
+  {
+    return MEMCACHED_SUCCESS;
+  }
+
+  LIBMEMCACHED_MEMCACHED_CONNECT_START();
+
+  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 */
@@ -594,6 +611,7 @@ memcached_return_t memcached_connect(memcached_server_write_instance_st server)
   case MEMCACHED_CONNECTION_UDP:
   case MEMCACHED_CONNECTION_TCP:
     rc= network_connect(server);
+
     if (LIBMEMCACHED_WITH_SASL_SUPPORT)
     {
       if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks)
@@ -616,22 +634,28 @@ memcached_return_t memcached_connect(memcached_server_write_instance_st server)
 
   if (memcached_success(rc))
   {
-    server->server_failure_counter= 0;
-    server->next_retry= 0;
+    memcached_mark_server_as_clean(server);
+    return rc;
   }
-  else if (memcached_has_current_error(*server))
+
+  set_last_disconnected_host(server);
+  if (memcached_has_current_error(*server))
   {
-    server->server_failure_counter++;
-    set_last_disconnected_host(server);
+    memcached_mark_server_for_timeout(server);
+    assert(memcached_failed(memcached_server_error_return(server)));
   }
   else
   {
     memcached_set_error(*server, rc, MEMCACHED_AT);
-    server->server_failure_counter++;
-    set_last_disconnected_host(server);
+    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..7a84b9a80abb0b81360c21a85accca29b2e652be 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 2
+#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 0bf8c61988132f2b5e6b2b6fa2c4d4eaf2654a86..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;
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..022dda4f70be04d891746275c6cbe6318c34b0e1 100644 (file)
@@ -69,17 +69,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..e6f3a30a2884a1820ee5a2cd6e89e811c30a8a9f 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 {
@@ -63,6 +64,7 @@ struct memcached_server_st {
   uint32_t io_bytes_sent; /* # bytes sent since last read */
   uint32_t server_failure_counter;
   uint32_t weight;
+  uint32_t version;
   enum memcached_server_state_t state;
   struct {
     uint32_t read;
index 05b51f4fcbf098fbdda139b7a2f96a8f4b9c55d5..320da1152fe0f07aea280dbfda3ad98e3a29c0cc 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;
+    server->server_failure_counter++;
+    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 f747dc229b4e47d620f1b9390a214a261c18f4db..bba1240cabc5019022c6e5f3250a55604776669d 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++)
   {
-    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++)
+  {
+    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
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 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..185f9c7
--- /dev/null
@@ -0,0 +1,186 @@
+/*  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 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;
+}
+
+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 }
+};
+
+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 },
+  { 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 492467ca31a33518333cf4076dfb6cacdabfbc77..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;
 }
@@ -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,9 +2616,7 @@ 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);
 
@@ -2633,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)
@@ -3219,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);
   }
 
@@ -5259,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);
@@ -5285,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,
@@ -5857,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 },
@@ -5903,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}
 };
@@ -5933,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}
 };
 
@@ -6216,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"