Added MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE
authorTrond Norbye <trond.norbye@sun.com>
Tue, 3 Nov 2009 13:09:13 +0000 (14:09 +0100)
committerTrond Norbye <trond.norbye@sun.com>
Tue, 3 Nov 2009 13:09:13 +0000 (14:09 +0100)
docs/memcached_behavior.pod
libmemcached/memcached_behavior.c
libmemcached/memcached_constants.h
libmemcached/memcached_hash.c
libmemcached/memcached_hosts.c
tests/function.c
tests/ketama_test_cases.h
tests/ketama_test_cases_spy.h [new file with mode: 0644]

index 00d762825d2da1f4757e2000b7843820843ba11a..dd7184541743adf05d6dcdcbe15649db76ffb497 100644 (file)
@@ -32,7 +32,7 @@ that behavior is currently enabled in the client.
 
 memcached_behavior_set() changes the value of a particular option of the
 client. It takes both a flag (listed below) and a value. For simple on or
-off options you just need to pass in a value of 1. Calls to 
+off options you just need to pass in a value of 1. Calls to
 memcached_behavior_set() will flush and reset all connections.
 
 =over 4
@@ -40,7 +40,7 @@ memcached_behavior_set() will flush and reset all connections.
 =item MEMCACHED_BEHAVIOR_USE_UDP
 
 Causes libmemcached(3) to use the UDP transport when communicating
-with a memcached server. Not all I/O operations are supported 
+with a memcached server. Not all I/O operations are supported
 when this behavior is enababled. The following operations will return
 C<MEMCACHED_NOT_SUPPORTED> when executed with the MEMCACHED_BEHAVIOR_USE_UDP
 enabled: memcached_version(), memcached_stat(), memcached_get(),
@@ -50,7 +50,7 @@ memcached_fetch(), memcached_fetch_result(), memcached_value_fetch().
 All other operations are supported but are executed in a 'fire-and-forget'
 mode, in which once the client has executed the operation, no attempt
 will be made to ensure the operation has been received and acted on by the
-server. 
+server.
 
 libmemcached(3) does not allow TCP and UDP servers to be shared within
 the same libmemached(3) client 'instance'. An attempt to add a TCP server
@@ -82,19 +82,19 @@ environments).
 =item MEMCACHED_BEHAVIOR_HASH
 
 Makes the default hashing algorithm for keys use MD5. The value can be set
-to either MEMCACHED_HASH_DEFAULT, MEMCACHED_HASH_MD5, MEMCACHED_HASH_CRC, MEMCACHED_HASH_FNV1_64, MEMCACHED_HASH_FNV1A_64, MEMCACHED_HASH_FNV1_32, MEMCACHED_HASH_FNV1A_32, MEMCACHED_HASH_JENKINS, MEMCACHED_HASH_HSIEH, and MEMCACHED_HASH_MURMUR. 
+to either MEMCACHED_HASH_DEFAULT, MEMCACHED_HASH_MD5, MEMCACHED_HASH_CRC, MEMCACHED_HASH_FNV1_64, MEMCACHED_HASH_FNV1A_64, MEMCACHED_HASH_FNV1_32, MEMCACHED_HASH_FNV1A_32, MEMCACHED_HASH_JENKINS, MEMCACHED_HASH_HSIEH, and MEMCACHED_HASH_MURMUR.
 Each hash has it's advantages and it's weaknesses. If you dont know or dont care, just go with the default.
-Support for MEMCACHED_HASH_HSIEH is a compile time option that is disabled by default. To enable support for this hashing algorithm, configure and build libmemcached with the --enable-hash_hsieh. 
+Support for MEMCACHED_HASH_HSIEH is a compile time option that is disabled by default. To enable support for this hashing algorithm, configure and build libmemcached with the --enable-hash_hsieh.
 
 =item MEMCACHED_BEHAVIOR_DISTRIBUTION
 
 Using this you can enable different means of distributing values to servers.
 The default method is MEMCACHED_DISTRIBUTION_MODULA. You can enable
-consistent hashing by setting MEMCACHED_DISTRIBUTION_CONSISTENT. 
-Consistent hashing delivers better distribution and allows servers to be 
+consistent hashing by setting MEMCACHED_DISTRIBUTION_CONSISTENT.
+Consistent hashing delivers better distribution and allows servers to be
 added to the cluster with minimal cache losses. Currently
 MEMCACHED_DISTRIBUTION_CONSISTENT is an alias for the value
-MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA.  
+MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA.
 
 =item MEMCACHED_BEHAVIOR_CACHE_LOOKUPS
 
@@ -117,7 +117,14 @@ and the hash to MEMCACHED_HASH_MD5.
 =item MEMCACHED_BEHAVIOR_KETAMA_HASH
 
 Sets the hashing algorithm for host mapping on continuum. The value can be set
-to either MEMCACHED_HASH_DEFAULT, MEMCACHED_HASH_MD5, MEMCACHED_HASH_CRC, MEMCACHED_HASH_FNV1_64, MEMCACHED_HASH_FNV1A_64, MEMCACHED_HASH_FNV1_32, and MEMCACHED_HASH_FNV1A_32. 
+to either MEMCACHED_HASH_DEFAULT, MEMCACHED_HASH_MD5, MEMCACHED_HASH_CRC, MEMCACHED_HASH_FNV1_64, MEMCACHED_HASH_FNV1A_64, MEMCACHED_HASH_FNV1_32, and MEMCACHED_HASH_FNV1A_32.
+
+=item MEMCACHED_BEHAVIOR_KETAMA_COMPAT
+
+Sets the compatibility mode. The value can be set to either
+MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED (this is the default) or
+MEMCACHED_KETAMA_COMPAT_SPY to be compatible with the SPY Memcached client
+for Java.
 
 =item MEMCACHED_BEHAVIOR_POLL_TIMEOUT
 
@@ -136,16 +143,16 @@ of memcached_callback_set(3). This will be removed in 0.15.
 Enabling buffered IO causes commands to "buffer" instead of being sent. Any
 action that gets data causes this buffer to be be sent to the remote
 connection. Quiting the connection or closing down the connection will also
-cause the buffered data to be pushed to the remote connection. 
+cause the buffered data to be pushed to the remote connection.
 
 =item MEMCACHED_BEHAVIOR_VERIFY_KEY
 
-Enabling this will cause libmemcached(3) to test all keys to verify that they 
+Enabling this will cause libmemcached(3) to test all keys to verify that they
 are valid keys.
 
 =item MEMCACHED_BEHAVIOR_SORT_HOSTS
 
-Enabling this will cause hosts that are added to be placed in the host list in 
+Enabling this will cause hosts that are added to be placed in the host list in
 sorted order. This will defeat consisten hashing.
 
 =item MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT
@@ -165,7 +172,7 @@ times connection failure.
 
 =item MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK
 
-Set this value to tune the number of messages that may be sent before 
+Set this value to tune the number of messages that may be sent before
 libmemcached should start to automatically drain the input queue. Setting
 this value to high, may cause libmemcached to deadlock (trying to send data,
 but the send will block because the input buffer in the kernel is full).
@@ -175,7 +182,7 @@ but the send will block because the input buffer in the kernel is full).
 Set this value to tune the number of bytes that may be sent before
 libmemcached should start to automatically drain the input queue (need
 at least 10 IO requests sent without reading the input buffer). Setting
-this value to high, may cause libmemcached to deadlock (trying to send 
+this value to high, may cause libmemcached to deadlock (trying to send
 data, but the send will block because the input buffer in the kernel is full).
 
 =item MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH
@@ -192,7 +199,7 @@ sent to the server.
 =item MEMCACHED_BEHAVIOR_NOREPLY
 
 Set this value to specify that you really don't care about the result
-from your storage commands (set, add, replace, append, prepend). 
+from your storage commands (set, add, replace, append, prepend).
 
 =item MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS
 
@@ -213,7 +220,7 @@ returns failure or success.
 =head1 NOTES
 
 memcached_behavior_set() in version .17 was changed from taking a pointer
-to data value, to taking a uin64_t. 
+to data value, to taking a uin64_t.
 
 =head1 HOME
 
index dd9884892ca89ef8d0bf822015e8ae365053e823..72b0d5ccada8418f613fee07248e1cffec0b74ea 100644 (file)
@@ -1,10 +1,10 @@
-#include "common.h" 
+#include "common.h"
 #include <time.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/tcp.h>
 
-/* 
+/*
   This function is used to modify the behavior of running client.
 
   We quit all connections so we can reset the sockets.
@@ -18,8 +18,8 @@ static void set_behavior_flag(memcached_st *ptr, memcached_flags temp_flag, uint
     ptr->flags&= ~temp_flag;
 }
 
-memcached_return memcached_behavior_set(memcached_st *ptr, 
-                                        memcached_behavior flag, 
+memcached_return memcached_behavior_set(memcached_st *ptr,
+                                        memcached_behavior flag,
                                         uint64_t data)
 {
   switch (flag)
@@ -38,18 +38,18 @@ memcached_return memcached_behavior_set(memcached_st *ptr,
     break;
   case MEMCACHED_BEHAVIOR_SND_TIMEOUT:
     ptr->snd_timeout= (int32_t)data;
-    break;     
+    break;
   case MEMCACHED_BEHAVIOR_RCV_TIMEOUT:
     ptr->rcv_timeout= (int32_t)data;
-    break;     
+    break;
   case MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT:
     ptr->server_failure_limit= (uint32_t)data;
-    break;     
+    break;
   case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL:
     if (data)
         set_behavior_flag(ptr, MEM_VERIFY_KEY, 0);
     set_behavior_flag(ptr, MEM_BINARY_PROTOCOL, data);
-    break;     
+    break;
   case MEMCACHED_BEHAVIOR_SUPPORT_CAS:
     set_behavior_flag(ptr, MEM_SUPPORT_CAS, data);
     break;
@@ -105,6 +105,22 @@ memcached_return memcached_behavior_set(memcached_st *ptr,
       run_distribution(ptr);
       break;
     }
+  case MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE:
+    switch (data)
+    {
+    case MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED:
+      ptr->hash= MEMCACHED_HASH_MD5;
+      ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA;
+      break;
+    case MEMCACHED_KETAMA_COMPAT_SPY:
+      ptr->hash= MEMCACHED_HASH_MD5;
+      ptr->distribution= MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY;
+      break;
+    default:
+       return MEMCACHED_FAILURE;
+    }
+    run_distribution(ptr);
+    break;
   case MEMCACHED_BEHAVIOR_HASH:
 #ifndef HAVE_HSIEH_HASH
     if ((memcached_hash)(data) == MEMCACHED_HASH_HSIEH)
@@ -169,7 +185,7 @@ memcached_return memcached_behavior_set(memcached_st *ptr,
   return MEMCACHED_SUCCESS;
 }
 
-uint64_t memcached_behavior_get(memcached_st *ptr, 
+uint64_t memcached_behavior_get(memcached_st *ptr,
                                 memcached_behavior flag)
 {
   memcached_flags temp_flag= MEM_NO_BLOCK;
@@ -186,7 +202,7 @@ uint64_t memcached_behavior_get(memcached_st *ptr,
     return ptr->io_key_prefetch;
   case MEMCACHED_BEHAVIOR_BINARY_PROTOCOL:
     temp_flag= MEM_BINARY_PROTOCOL;
-    break;     
+    break;
   case MEMCACHED_BEHAVIOR_SUPPORT_CAS:
     temp_flag= MEM_SUPPORT_CAS;
     break;
@@ -215,6 +231,17 @@ uint64_t memcached_behavior_get(memcached_st *ptr,
     return ptr->distribution;
   case MEMCACHED_BEHAVIOR_KETAMA:
     return (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA) ? (uint64_t) 1 : 0;
+  case MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE:
+    switch (ptr->distribution)
+    {
+    case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA:
+      return MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED;
+    case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY:
+      return MEMCACHED_KETAMA_COMPAT_SPY;
+    default:
+      return (uint64_t)-1;
+    }
+    /* NOTREACHED */
   case MEMCACHED_BEHAVIOR_HASH:
     return ptr->hash;
   case MEMCACHED_BEHAVIOR_KETAMA_HASH:
@@ -244,7 +271,7 @@ uint64_t memcached_behavior_get(memcached_st *ptr,
       if ((memcached_connect(&ptr->hosts[0])) != MEMCACHED_SUCCESS)
         return 0;
 
-      if (getsockopt(ptr->hosts[0].fd, SOL_SOCKET, 
+      if (getsockopt(ptr->hosts[0].fd, SOL_SOCKET,
                      SO_SNDBUF, &sock_size, &sock_length))
         return 0; /* Zero means error */
 
@@ -260,7 +287,7 @@ uint64_t memcached_behavior_get(memcached_st *ptr,
       if ((memcached_connect(&ptr->hosts[0])) != MEMCACHED_SUCCESS)
         return 0;
 
-      if (getsockopt(ptr->hosts[0].fd, SOL_SOCKET, 
+      if (getsockopt(ptr->hosts[0].fd, SOL_SOCKET,
                      SO_RCVBUF, &sock_size, &sock_length))
         return 0; /* Zero means error */
 
index 1705bb6e57883ed71f0972b6e015faa68e81051a..3a91ca017b51abc160ef9624d6900d7b0359d406 100644 (file)
@@ -17,7 +17,7 @@
 #define MEMCACHED_MAX_HOST_SORT_LENGTH 86 /* Used for Ketama */
 #define MEMCACHED_POINTS_PER_SERVER 100
 #define MEMCACHED_POINTS_PER_SERVER_KETAMA 160
-#define MEMCACHED_CONTINUUM_SIZE MEMCACHED_POINTS_PER_SERVER*100 /* This would then set max hosts to 100 */ 
+#define MEMCACHED_CONTINUUM_SIZE MEMCACHED_POINTS_PER_SERVER*100 /* This would then set max hosts to 100 */
 #define MEMCACHED_STRIDE 4
 #define MEMCACHED_DEFAULT_TIMEOUT 1000
 #define MEMCACHED_CONTINUUM_ADDITION 10 /* How many extra slots we should build for in the continuum */
@@ -70,7 +70,8 @@ typedef enum {
   MEMCACHED_DISTRIBUTION_MODULA,
   MEMCACHED_DISTRIBUTION_CONSISTENT,
   MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA,
-  MEMCACHED_DISTRIBUTION_RANDOM
+  MEMCACHED_DISTRIBUTION_RANDOM,
+  MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY
 } memcached_server_distribution;
 
 typedef enum {
@@ -103,9 +104,13 @@ typedef enum {
   MEMCACHED_BEHAVIOR_NOREPLY,
   MEMCACHED_BEHAVIOR_USE_UDP,
   MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS,
-  MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS
+  MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS,
+  MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE
 } memcached_behavior;
 
+#define MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED 0
+#define MEMCACHED_KETAMA_COMPAT_SPY 1
+
 typedef enum {
   MEMCACHED_CALLBACK_PREFIX_KEY = 0,
   MEMCACHED_CALLBACK_USER_DATA = 1,
index 129d76127e8e7271a5d19861892da6b4c474addd..959fc296af562c3f815d0e7a48952759564c9848 100644 (file)
@@ -31,13 +31,13 @@ uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memca
       hash= 1;
     break;
     /* FNV hash'es lifted from Dustin Sallings work */
-  case MEMCACHED_HASH_FNV1_64: 
+  case MEMCACHED_HASH_FNV1_64:
     {
       /* Thanks to pierre@demartines.com for the pointer */
       uint64_t temp_hash;
 
       temp_hash= FNV_64_INIT;
-      for (x= 0; x < key_length; x++) 
+      for (x= 0; x < key_length; x++)
       {
         temp_hash *= FNV_64_PRIME;
         temp_hash ^= (uint64_t)key[x];
@@ -45,10 +45,10 @@ uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memca
       hash= (uint32_t)temp_hash;
     }
     break;
-  case MEMCACHED_HASH_FNV1A_64: 
+  case MEMCACHED_HASH_FNV1A_64:
     {
       hash= (uint32_t) FNV_64_INIT;
-      for (x= 0; x < key_length; x++) 
+      for (x= 0; x < key_length; x++)
       {
         uint32_t val= (uint32_t)key[x];
         hash ^= val;
@@ -56,10 +56,10 @@ uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memca
       }
     }
     break;
-  case MEMCACHED_HASH_FNV1_32: 
+  case MEMCACHED_HASH_FNV1_32:
     {
       hash= FNV_32_INIT;
-      for (x= 0; x < key_length; x++) 
+      for (x= 0; x < key_length; x++)
       {
         uint32_t val= (uint32_t)key[x];
         hash *= FNV_32_PRIME;
@@ -67,10 +67,10 @@ uint32_t memcached_generate_hash_value(const char *key, size_t key_length, memca
       }
     }
     break;
-  case MEMCACHED_HASH_FNV1A_32: 
+  case MEMCACHED_HASH_FNV1A_32:
     {
       hash= FNV_32_INIT;
-      for (x= 0; x < key_length; x++) 
+      for (x= 0; x < key_length; x++)
       {
         uint32_t val= (uint32_t)key[x];
         hash ^= val;
@@ -121,10 +121,11 @@ uint32_t generate_hash(memcached_st *ptr, const char *key, size_t key_length)
 
 static uint32_t dispatch_host(memcached_st *ptr, uint32_t hash)
 {
-  switch (ptr->distribution) 
+  switch (ptr->distribution)
   {
   case MEMCACHED_DISTRIBUTION_CONSISTENT:
   case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA:
+  case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY:
     {
       uint32_t num= ptr->continuum_points_counter;
       WATCHPOINT_ASSERT(ptr->continuum);
@@ -145,7 +146,7 @@ static uint32_t dispatch_host(memcached_st *ptr, uint32_t hash)
       if (right == end)
         right= begin;
       return right->index;
-    } 
+    }
   case MEMCACHED_DISTRIBUTION_MODULA:
     return hash % ptr->number_of_hosts;
   case MEMCACHED_DISTRIBUTION_RANDOM:
@@ -158,8 +159,8 @@ static uint32_t dispatch_host(memcached_st *ptr, uint32_t hash)
   /* NOTREACHED */
 }
 
-/* 
-  One day make this public, and have it return the actual memcached_server_st 
+/*
+  One day make this public, and have it return the actual memcached_server_st
   to the calling application.
 */
 uint32_t memcached_generate_hash(memcached_st *ptr, const char *key, size_t key_length)
@@ -206,7 +207,7 @@ static uint32_t internal_generate_hash(const char *key, size_t key_length)
   const char *ptr= key;
   uint32_t value= 0;
 
-  while (key_length--) 
+  while (key_length--)
   {
     uint32_t val= (uint32_t) *ptr++;
     value += val;
@@ -215,7 +216,7 @@ static uint32_t internal_generate_hash(const char *key, size_t key_length)
   }
   value += (value << 3);
   value ^= (value >> 11);
-  value += (value << 15); 
+  value += (value << 15);
 
   return value == 0 ? 1 : (uint32_t) value;
 }
index f0af383ab09b9a500322979812757ae66df48dd8..d7f78dea4061ced1a2883ca195b305acabdae265 100644 (file)
@@ -40,6 +40,7 @@ memcached_return run_distribution(memcached_st *ptr)
   {
   case MEMCACHED_DISTRIBUTION_CONSISTENT:
   case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA:
+  case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY:
     return update_continuum(ptr);
   case MEMCACHED_DISTRIBUTION_MODULA:
     if (ptr->flags & MEM_USE_SORT_HOSTS)
@@ -198,43 +199,93 @@ memcached_return update_continuum(memcached_st *ptr)
                pointer_per_server);
 #endif
     }
-    for (pointer_index= 0;
-         pointer_index < pointer_per_server / pointer_per_hash;
-         pointer_index++)
+
+
+    if (ptr->distribution == MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY)
     {
-      char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= "";
-      size_t sort_host_length;
-
-      // Spymemcached ketema key format is: hostname/ip:port-index
-      // If hostname is not available then: /ip:port-index
-      sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH,
-                                          "/%s:%d-%d",
-                                          list[host_index].hostname,
-                                          list[host_index].port,
-                                          pointer_index);
+      for (pointer_index= 0;
+           pointer_index < pointer_per_server / pointer_per_hash;
+           pointer_index++)
+      {
+        char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= "";
+        size_t sort_host_length;
+
+        // Spymemcached ketema key format is: hostname/ip:port-index
+        // If hostname is not available then: /ip:port-index
+        sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH,
+                                            "/%s:%d-%d",
+                                            list[host_index].hostname,
+                                            list[host_index].port,
+                                            pointer_index);
 #ifdef DEBUG
-      printf("update_continuum: key is %s\n", sort_host);
+        printf("update_continuum: key is %s\n", sort_host);
 #endif
 
-      WATCHPOINT_ASSERT(sort_host_length);
+        WATCHPOINT_ASSERT(sort_host_length);
 
-      if (is_ketama_weighted)
-      {
-        unsigned int i;
-        for (i = 0; i < pointer_per_hash; i++)
+        if (is_ketama_weighted)
+        {
+          unsigned int i;
+          for (i = 0; i < pointer_per_hash; i++)
+          {
+             value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i);
+             ptr->continuum[continuum_index].index= host_index;
+             ptr->continuum[continuum_index++].value= value;
+          }
+        }
+        else
         {
-          value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i);
+          value= memcached_generate_hash_value(sort_host, sort_host_length, ptr->hash_continuum);
           ptr->continuum[continuum_index].index= host_index;
           ptr->continuum[continuum_index++].value= value;
         }
       }
-      else
+    }
+    else
+    {
+      for (pointer_index= 1;
+           pointer_index <= pointer_per_server / pointer_per_hash;
+           pointer_index++)
       {
-        value= memcached_generate_hash_value(sort_host, sort_host_length, ptr->hash_continuum);
-        ptr->continuum[continuum_index].index= host_index;
-        ptr->continuum[continuum_index++].value= value;
+        char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= "";
+        size_t sort_host_length;
+
+        if (list[host_index].port == MEMCACHED_DEFAULT_PORT)
+        {
+          sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH,
+                                              "%s-%d",
+                                              list[host_index].hostname,
+                                              pointer_index - 1);
+        }
+        else
+        {
+          sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH,
+                                              "%s:%d-%d",
+                                              list[host_index].hostname,
+                                              list[host_index].port, pointer_index - 1);
+        }
+
+        WATCHPOINT_ASSERT(sort_host_length);
+
+        if (is_ketama_weighted)
+        {
+          unsigned int i;
+          for (i = 0; i < pointer_per_hash; i++)
+          {
+             value= ketama_server_hash(sort_host, (uint32_t) sort_host_length, (int) i);
+             ptr->continuum[continuum_index].index= host_index;
+             ptr->continuum[continuum_index++].value= value;
+          }
+        }
+        else
+        {
+          value= memcached_generate_hash_value(sort_host, sort_host_length, ptr->hash_continuum);
+          ptr->continuum[continuum_index].index= host_index;
+          ptr->continuum[continuum_index++].value= value;
+        }
       }
     }
+
     pointer_counter+= pointer_per_server;
   }
 
index affa176ff80653c488b25eabdc230438a0c3770e..0d7b2d1ab1a51cbe6d172c18cbf28cad3b022e3a 100644 (file)
@@ -1463,7 +1463,7 @@ static test_return_t mget_execute(memcached_st *memc)
   memc->number_of_hosts= 1;
 
   int max_keys= binary ? 20480 : 1;
-  
+
 
   char **keys= calloc((size_t)max_keys, sizeof(char*));
   size_t *key_length=calloc((size_t)max_keys, sizeof(size_t));
@@ -2590,9 +2590,9 @@ static test_return_t user_supplied_bug18(memcached_st *trash)
   /* verify the standard ketama set. */
   for (x= 0; x < 99; x++)
   {
-    uint32_t server_idx = memcached_generate_hash(memc, test_cases[x].key, strlen(test_cases[x].key));
+    uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
     char *hostname = memc->hosts[server_idx].hostname;
-    assert(strcmp(hostname, test_cases[x].server) == 0);
+    assert(strcmp(hostname, ketama_test_cases[x].server) == 0);
   }
 
   memcached_server_list_free(server_pool);
@@ -2735,7 +2735,7 @@ static test_return_t auto_eject_hosts(memcached_st *trash)
 
   for (int x= 0; x < 99; x++)
   {
-    uint32_t server_idx = memcached_generate_hash(memc, test_cases[x].key, strlen(test_cases[x].key));
+    uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
     assert(server_idx != 2);
   }
 
@@ -2745,9 +2745,9 @@ static test_return_t auto_eject_hosts(memcached_st *trash)
   run_distribution(memc);
   for (int x= 0; x < 99; x++)
   {
-    uint32_t server_idx = memcached_generate_hash(memc, test_cases[x].key, strlen(test_cases[x].key));
+    uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
     char *hostname = memc->hosts[server_idx].hostname;
-    assert(strcmp(hostname, test_cases[x].server) == 0);
+    assert(strcmp(hostname, ketama_test_cases[x].server) == 0);
   }
 
   memcached_server_list_free(server_pool);
@@ -2764,6 +2764,7 @@ static test_return_t output_ketama_weighted_keys(memcached_st *trash)
   memcached_st *memc= memcached_create(NULL);
   assert(memc);
 
+
   rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1);
   assert(rc == MEMCACHED_SUCCESS);
 
@@ -2776,6 +2777,10 @@ static test_return_t output_ketama_weighted_keys(memcached_st *trash)
   value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH);
   assert(value == MEMCACHED_HASH_MD5);
 
+
+  assert(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE,
+                                MEMCACHED_KETAMA_COMPAT_SPY) == MEMCACHED_SUCCESS);
+
   memcached_server_st *server_pool;
   server_pool = memcached_servers_parse("10.0.1.1:11211,10.0.1.2:11211,10.0.1.3:11211,10.0.1.4:11211,10.0.1.5:11211,10.0.1.6:11211,10.0.1.7:11211,10.0.1.8:11211,192.168.1.1:11211,192.168.100.1:11211");
   memcached_server_push(memc, server_pool);
@@ -2793,7 +2798,7 @@ static test_return_t output_ketama_weighted_keys(memcached_st *trash)
   {
     char key[10];
     sprintf(key, "%d", x);
-      
+
     uint32_t server_idx = memcached_generate_hash(memc, key, strlen(key));
     char *hostname = memc->hosts[server_idx].hostname;
     unsigned int port = memc->hosts[server_idx].port;
@@ -4674,6 +4679,125 @@ static test_return_t jenkins_run (memcached_st *memc __attribute__((unused)))
   return TEST_SUCCESS;
 }
 
+
+static test_return_t ketama_compatibility_libmemcached(memcached_st *trash)
+{
+  memcached_return rc;
+  uint64_t value;
+  int x;
+  memcached_server_st *server_pool;
+  memcached_st *memc;
+
+  (void)trash;
+
+  memc= memcached_create(NULL);
+  assert(memc);
+
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED);
+  assert(value == 1);
+
+  assert(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE,
+                                MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED) == MEMCACHED_SUCCESS);
+
+  assert(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE) ==
+         MEMCACHED_KETAMA_COMPAT_LIBMEMCACHED);
+
+  server_pool = memcached_servers_parse("10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100");
+  memcached_server_push(memc, server_pool);
+
+  /* verify that the server list was parsed okay. */
+  assert(memc->number_of_hosts == 8);
+  assert(strcmp(server_pool[0].hostname, "10.0.1.1") == 0);
+  assert(server_pool[0].port == 11211);
+  assert(server_pool[0].weight == 600);
+  assert(strcmp(server_pool[2].hostname, "10.0.1.3") == 0);
+  assert(server_pool[2].port == 11211);
+  assert(server_pool[2].weight == 200);
+  assert(strcmp(server_pool[7].hostname, "10.0.1.8") == 0);
+  assert(server_pool[7].port == 11211);
+  assert(server_pool[7].weight == 100);
+
+  /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
+   * us test the boundary wraparound.
+   */
+  assert(memcached_generate_hash(memc, (char *)"VDEAAAAA", 8) == memc->continuum[0].index);
+
+  /* verify the standard ketama set. */
+  for (x= 0; x < 99; x++)
+  {
+    uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
+    char *hostname = memc->hosts[server_idx].hostname;
+    assert(strcmp(hostname, ketama_test_cases[x].server) == 0);
+  }
+
+  memcached_server_list_free(server_pool);
+  memcached_free(memc);
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t ketama_compatibility_spymemcached(memcached_st *trash)
+{
+  memcached_return rc;
+  uint64_t value;
+  int x;
+  memcached_server_st *server_pool;
+  memcached_st *memc;
+
+  (void)trash;
+
+  memc= memcached_create(NULL);
+  assert(memc);
+
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED);
+  assert(value == 1);
+
+  assert(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE,
+                                MEMCACHED_KETAMA_COMPAT_SPY) == MEMCACHED_SUCCESS);
+
+  assert(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_COMPAT_MODE) ==
+         MEMCACHED_KETAMA_COMPAT_SPY);
+
+  server_pool = memcached_servers_parse("10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100");
+  memcached_server_push(memc, server_pool);
+
+  /* verify that the server list was parsed okay. */
+  assert(memc->number_of_hosts == 8);
+  assert(strcmp(server_pool[0].hostname, "10.0.1.1") == 0);
+  assert(server_pool[0].port == 11211);
+  assert(server_pool[0].weight == 600);
+  assert(strcmp(server_pool[2].hostname, "10.0.1.3") == 0);
+  assert(server_pool[2].port == 11211);
+  assert(server_pool[2].weight == 200);
+  assert(strcmp(server_pool[7].hostname, "10.0.1.8") == 0);
+  assert(server_pool[7].port == 11211);
+  assert(server_pool[7].weight == 100);
+
+  /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
+   * us test the boundary wraparound.
+   */
+  assert(memcached_generate_hash(memc, (char *)"VDEAAAAA", 8) == memc->continuum[0].index);
+
+  /* verify the standard ketama set. */
+  for (x= 0; x < 99; x++)
+  {
+    uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases_spy[x].key, strlen(ketama_test_cases_spy[x].key));
+    char *hostname = memc->hosts[server_idx].hostname;
+    assert(strcmp(hostname, ketama_test_cases_spy[x].server) == 0);
+  }
+
+  memcached_server_list_free(server_pool);
+  memcached_free(memc);
+
+  return TEST_SUCCESS;
+}
+
 static test_return_t regression_bug_434484(memcached_st *memc)
 {
   if (pre_binary(memc) != MEMCACHED_SUCCESS)
@@ -4885,8 +5009,8 @@ static test_return_t regression_bug_447342(memcached_st *memc)
   ** to do this ;-)
   */
   memcached_quit(memc);
-  
-  /* Verify that all messages are stored, and we didn't stuff too much 
+
+  /* Verify that all messages are stored, and we didn't stuff too much
    * into the servers
    */
   rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
@@ -5226,6 +5350,12 @@ test_st regression_tests[]= {
   {0, 0, 0}
 };
 
+test_st ketama_compatibility[]= {
+  {"libmemcached", 1, ketama_compatibility_libmemcached },
+  {"spymemcached", 1, ketama_compatibility_spymemcached },
+  {0, 0, 0}
+};
+
 test_st generate_tests[] ={
   {"generate_pairs", 1, generate_pairs },
   {"generate_data", 1, generate_data },
@@ -5332,6 +5462,7 @@ collection_st collection[] ={
   {"consistent_not", 0, 0, consistent_tests},
   {"consistent_ketama", pre_behavior_ketama, 0, consistent_tests},
   {"consistent_ketama_weighted", pre_behavior_ketama_weighted, 0, consistent_weighted_tests},
+  {"ketama_compat", 0, 0, ketama_compatibility},
   {"test_hashes", 0, 0, hash_tests},
   {"replication", pre_replication, 0, replication_tests},
   {"replication_noblock", pre_replication_noblock, 0, replication_tests},
index fb6f03d82e1dc3b0869e75b4e0512c7ee5b7b68e..3d770f169773f4f977ff27c9484c6f63da946e8c 100644 (file)
-typedef struct {
+#ifndef TESTS_KETAMA_TEST_CASES_H
+#define TESTS_KETAMA_TEST_CASES_H
+
+static struct {
     const char *key;
     unsigned long hash1;
     unsigned long hash2;
     const char *server;
-} TestCase;
-
-static TestCase test_cases[99] = {
-  { "SVa_]_V41)", 443691461UL, 445379617UL, "10.0.1.2" },
-  { "*/Z;?V(.\\8", 1422915503UL, 1428303028UL, "10.0.1.4" },
+} ketama_test_cases[99]= {
+  { "SVa_]_V41)", 443691461UL, 445379617UL, "10.0.1.7" },
+  { "*/Z;?V(.\\8", 1422915503UL, 1428303028UL, "10.0.1.1" },
   { "30C1*Z*S/_", 1473165754UL, 1480075959UL, "10.0.1.2" },
   { "ERR:EC58G>", 2148406511UL, 2168579133UL, "10.0.1.7" },
-  { "1I=cTMNTKF", 2882686667UL, 2885206587UL, "10.0.1.4" },
+  { "1I=cTMNTKF", 2882686667UL, 2885206587UL, "10.0.1.5" },
   { "]VG<`I*Z8)", 1103544263UL, 1104827657UL, "10.0.1.5" },
-  { "UUTC`-V159", 3716288206UL, 3727224240UL, "10.0.1.7" },
-  { "@7RU6C6T+Z", 3862737685UL, 3871917949UL, "10.0.1.6" },
-  { "/XLN0@+36;", 1623269830UL, 1627683651UL, "10.0.1.7" },
-  { "4(`X;\\V.^c", 373546328UL, 383925769UL, "10.0.1.6" },
-  { "726bW=9*a4", 4213440020UL, 4213950705UL, "10.0.1.3" },
-  { "\\`)<B)UE,c", 951096736UL, 955226069UL, "10.0.1.8" },
-  { "P1[Ma3=K1/", 1989324036UL, 1994028240UL, "10.0.1.1" },
-  { "C89I.-V?cT", 1604239957UL, 1606398093UL, "10.0.1.5" },
-  { "D[HE+cFXDK", 2117036136UL, 2117124014UL, "10.0.1.5" },
-  { "P1L?NAB[)K", 2129972569UL, 2132542634UL, "10.0.1.7" },
-  { "cDT0)Z5P6,", 176485284UL, 178675413UL, "10.0.1.1" },
-  { "@JW`+[WAO8", 2720940826UL, 2743975456UL, "10.0.1.2" },
-  { "\\39DKW^)N_", 3548879868UL, 3550704865UL, "10.0.1.6" },
-  { "EM75N0+[X1", 1558531507UL, 1559308507UL, "10.0.1.5" },
+  { "UUTC`-V159", 3716288206UL, 3727224240UL, "10.0.1.5" },
+  { "@7RU6C6T+Z", 3862737685UL, 3871917949UL, "10.0.1.5" },
+  { "/XLN0@+36;", 1623269830UL, 1627683651UL, "10.0.1.1" },
+  { "4(`X;\\V.^c", 373546328UL, 383925769UL, "10.0.1.1" },
+  { "726bW=9*a4", 4213440020UL, 4213950705UL, "10.0.1.7" },
+  { "\\`)<B)UE,c", 951096736UL, 955226069UL, "10.0.1.1" },
+  { "P1[Ma3=K1/", 1989324036UL, 1994028240UL, "10.0.1.8" },
+  { "C89I.-V?cT", 1604239957UL, 1606398093UL, "10.0.1.8" },
+  { "D[HE+cFXDK", 2117036136UL, 2117124014UL, "10.0.1.3" },
+  { "P1L?NAB[)K", 2129972569UL, 2132542634UL, "10.0.1.1" },
+  { "cDT0)Z5P6,", 176485284UL, 178675413UL, "10.0.1.5" },
+  { "@JW`+[WAO8", 2720940826UL, 2743975456UL, "10.0.1.5" },
+  { "\\39DKW^)N_", 3548879868UL, 3550704865UL, "10.0.1.3" },
+  { "EM75N0+[X1", 1558531507UL, 1559308507UL, "10.0.1.4" },
   { "`,SS]NBP,b", 1883545960UL, 1884847278UL, "10.0.1.1" },
-  { "XX1a9LT+F?", 653487707UL, 656410408UL, "10.0.1.6" },
-  { "Zc\\-,F-c6V", 1160802451UL, 1171575728UL, "10.0.1.6" },
-  { "1*RTMC7,03", 1602398012UL, 1606398093UL, "10.0.1.5" },
+  { "XX1a9LT+F?", 653487707UL, 656410408UL, "10.0.1.5" },
+  { "Zc\\-,F-c6V", 1160802451UL, 1171575728UL, "10.0.1.5" },
+  { "1*RTMC7,03", 1602398012UL, 1606398093UL, "10.0.1.8" },
   { "*Xc+V0P>32", 536016577UL, 539988520UL, "10.0.1.7" },
   { "U))Fb-(`,.", 4128682289UL, 4136854163UL, "10.0.1.7" },
   { "R-08RNTaRT", 3718170086UL, 3727224240UL, "10.0.1.5" },
-  { "(LHcO203I3", 1007779411UL, 1014643570UL, "10.0.1.1" },
-  { "=256P+;Qc8", 3976201210UL, 3976304873UL, "10.0.1.3" },
-  { "OI5XZ_BBT(", 2155922164UL, 2168579133UL, "10.0.1.5" },
-  { "2TLRL/UL;:", 1086800909UL, 1095659802UL, "10.0.1.2" },
-  { "WHD\\O1`ZRW", 3087923411UL, 3095471560UL, "10.0.1.1" },
-  { ".=54)_c;=T", 2497691631UL, 2502731301UL, "10.0.1.6" },
-  { ";G<W-XWZ@b", 2888169733UL, 2888728739UL, "10.0.1.7" },
-  { "(,>E`)FT\\4", 580747448UL, 581063326UL, "10.0.1.5" },
-  { "HZAU*;P*N]", 2564670474UL, 2565697267UL, "10.0.1.1" },
+  { "(LHcO203I3", 1007779411UL, 1014643570UL, "10.0.1.5" },
+  { "=256P+;Qc8", 3976201210UL, 3976304873UL, "10.0.1.5" },
+  { "OI5XZ_BBT(", 2155922164UL, 2168579133UL, "10.0.1.7" },
+  { "2TLRL/UL;:", 1086800909UL, 1095659802UL, "10.0.1.7" },
+  { "WHD\\O1`ZRW", 3087923411UL, 3095471560UL, "10.0.1.5" },
+  { ".=54)_c;=T", 2497691631UL, 2502731301UL, "10.0.1.1" },
+  { ";G<W-XWZ@b", 2888169733UL, 2888728739UL, "10.0.1.5" },
+  { "(,>E`)FT\\4", 580747448UL, 581063326UL, "10.0.1.2" },
+  { "HZAU*;P*N]", 2564670474UL, 2565697267UL, "10.0.1.7" },
   { "NZ@ZE=O84_", 533335275UL, 539988520UL, "10.0.1.7" },
-  { "6,cEI`F_P>", 3972869246UL, 3974773167UL, "10.0.1.3" },
-  { "c,5AQ/T5)6", 2835605783UL, 2847870057UL, "10.0.1.7" },
-  { ".O,>>BT)RX", 3857978174UL, 3871917949UL, "10.0.1.7" },
-  { "XY\\X::LX50", 1749241099UL, 1752196488UL, "10.0.1.7" },
-  { "+550F^/.01", 3781824099UL, 3783248219UL, "10.0.1.2" },
+  { "6,cEI`F_P>", 3972869246UL, 3974773167UL, "10.0.1.6" },
+  { "c,5AQ/T5)6", 2835605783UL, 2847870057UL, "10.0.1.8" },
+  { ".O,>>BT)RX", 3857978174UL, 3871917949UL, "10.0.1.5" },
+  { "XY\\X::LX50", 1749241099UL, 1752196488UL, "10.0.1.6" },
+  { "+550F^/.01", 3781824099UL, 3783248219UL, "10.0.1.6" },
   { "<.X9E2S5+9", 3232479481UL, 3234387706UL, "10.0.1.7" },
-  { "]\\.UH8_0a1", 2419699252UL, 2423002920UL, "10.0.1.6" },
-  { "8(6=(T0/Z0", 728266737UL, 729026070UL, "10.0.1.6" },
-  { "8*6a;Sc*X+", 4223431086UL, 4230156966UL, "10.0.1.5" },
-  { "<QW:;3K6;H", 2731158143UL, 2743975456UL, "10.0.1.7" },
+  { "]\\.UH8_0a1", 2419699252UL, 2423002920UL, "10.0.1.4" },
+  { "8(6=(T0/Z0", 728266737UL, 729026070UL, "10.0.1.7" },
+  { "8*6a;Sc*X+", 4223431086UL, 4230156966UL, "10.0.1.2" },
+  { "<QW:;3K6;H", 2731158143UL, 2743975456UL, "10.0.1.5" },
   { "7C@EY@-Y?_", 760770733UL, 761576669UL, "10.0.1.5" },
-  { "aPb3E1WD4K", 2500489218UL, 2502731301UL, "10.0.1.2" },
-  { "?@12R<=1BH", 1494795329UL, 1502505505UL, "10.0.1.1" },
-  { "QR(a+Q=1FU", 3238535074UL, 3238996435UL, "10.0.1.5" },
-  { "`C9^FV,960", 2628553463UL, 2628733766UL, "10.0.1.3" },
-  { "UNHVP..^8H", 977096483UL, 977319837UL, "10.0.1.6" },
-  { ":Y.2W2[(35", 2777083668UL, 2784182515UL, "10.0.1.6" },
-  { "M/HV^_HZ4O", 3623390946UL, 3624445007UL, "10.0.1.4" },
-  { "ZY16KQ<ICD", 1831153193UL, 1838563516UL, "10.0.1.7" },
-  { "bV2,`a.PY9", 1962228869UL, 1962648654UL, "10.0.1.7" },
-  { "U;9:-+5N]9", 269504649UL, 277560877UL, "10.0.1.2" },
-  { "1S/:aJ[1(;", 578069729UL, 581063326UL, "10.0.1.5" },
-  { "Nb-X^]M)I:", 330865696UL, 331009896UL, "10.0.1.3" },
-  { "2;M;ES>J5/", 2776949824UL, 2784182515UL, "10.0.1.6" },
-  { "[>RZHG97Q9", 71954686UL, 72034069UL, "10.0.1.4" },
-  { "J3/G[)9<^Z", 2799896459UL, 2805183696UL, "10.0.1.6" },
-  { "N-)88>[O`,", 50404102UL, 51792557UL, "10.0.1.2" },
+  { "aPb3E1WD4K", 2500489218UL, 2502731301UL, "10.0.1.1" },
+  { "?@12R<=1BH", 1494795329UL, 1502505505UL, "10.0.1.8" },
+  { "QR(a+Q=1FU", 3238535074UL, 3238996435UL, "10.0.1.6" },
+  { "`C9^FV,960", 2628553463UL, 2628733766UL, "10.0.1.6" },
+  { "UNHVP..^8H", 977096483UL, 977319837UL, "10.0.1.4" },
+  { ":Y.2W2[(35", 2777083668UL, 2784182515UL, "10.0.1.7" },
+  { "M/HV^_HZ4O", 3623390946UL, 3624445007UL, "10.0.1.7" },
+  { "ZY16KQ<ICD", 1831153193UL, 1838563516UL, "10.0.1.4" },
+  { "bV2,`a.PY9", 1962228869UL, 1962648654UL, "10.0.1.1" },
+  { "U;9:-+5N]9", 269504649UL, 277560877UL, "10.0.1.1" },
+  { "1S/:aJ[1(;", 578069729UL, 581063326UL, "10.0.1.2" },
+  { "Nb-X^]M)I:", 330865696UL, 331009896UL, "10.0.1.6" },
+  { "2;M;ES>J5/", 2776949824UL, 2784182515UL, "10.0.1.7" },
+  { "[>RZHG97Q9", 71954686UL, 72034069UL, "10.0.1.6" },
+  { "J3/G[)9<^Z", 2799896459UL, 2805183696UL, "10.0.1.7" },
+  { "N-)88>[O`,", 50404102UL, 51792557UL, "10.0.1.5" },
   { "NP:=FR\\OaA", 3837333776UL, 3837792034UL, "10.0.1.7" },
-  { "`@L+W;a,O[", 1512157148UL, 1522285852UL, "10.0.1.5" },
-  { "W2`P:-+1T[", 2945171975UL, 2946196424UL, "10.0.1.7" },
-  { "-6G7K^YDIN", 3168617340UL, 3170513015UL, "10.0.1.5" },
-  { "U>*>9ZI6V5", 668514946UL, 674097631UL, "10.0.1.5" },
+  { "`@L+W;a,O[", 1512157148UL, 1522285852UL, "10.0.1.6" },
+  { "W2`P:-+1T[", 2945171975UL, 2946196424UL, "10.0.1.5" },
+  { "-6G7K^YDIN", 3168617340UL, 3170513015UL, "10.0.1.7" },
+  { "U>*>9ZI6V5", 668514946UL, 674097631UL, "10.0.1.6" },
   { ".I?^6Ic9RK", 938419020UL, 942832691UL, "10.0.1.6" },
-  { "0OZH^9BKM[", 3682518606UL, 3686781297UL, "10.0.1.2" },
-  { "5?50UGZ:ML", 868610882UL, 869425986UL, "10.0.1.6" },
-  { "?K2NF@3=IU", 381218851UL, 383925769UL, "10.0.1.6" },
-  { "YI@G-2X?UB", 3688706179UL, 3693197681UL, "10.0.1.6" },
-  { "7cY</BSaL=", 3976870223UL, 3978903843UL, "10.0.1.7" },
+  { "0OZH^9BKM[", 3682518606UL, 3686781297UL, "10.0.1.8" },
+  { "5?50UGZ:ML", 868610882UL, 869425986UL, "10.0.1.5" },
+  { "?K2NF@3=IU", 381218851UL, 383925769UL, "10.0.1.1" },
+  { "YI@G-2X?UB", 3688706179UL, 3693197681UL, "10.0.1.5" },
+  { "7cY</BSaL=", 3976870223UL, 3978903843UL, "10.0.1.6" },
   { "A(`KF:[RH8", 3292979676UL, 3294849139UL, "10.0.1.6" },
-  { ";=ZT\\W^P+H", 1401102653UL, 1416290674UL, "10.0.1.6" },
-  { "b2?WFF56;R", 480494704UL, 486971192UL, "10.0.1.7" },
-  { "CTR74,J+N.", 137446045UL, 146633907UL, "10.0.1.7" },
+  { ";=ZT\\W^P+H", 1401102653UL, 1416290674UL, "10.0.1.4" },
+  { "b2?WFF56;R", 480494704UL, 486971192UL, "10.0.1.4" },
+  { "CTR74,J+N.", 137446045UL, 146633907UL, "10.0.1.8" },
   { "<b;*R+QDST", 1304985302UL, 1308223778UL, "10.0.1.5" },
-  { "\\R^7=9UCG`", 126218373UL, 129199837UL, "10.0.1.6" },
-  { "1bQS5]WOXB", 1853470245UL, 1855329369UL, "10.0.1.7" },
-  { "M(@X^b[L:K", 3019630308UL, 3022260113UL, "10.0.1.4" },
-  { "431cBF8,YO", 1679726993UL, 1685224295UL, "10.0.1.1" },
-  { "(bEIQJ:E./", 2922607787UL, 2925521819UL, "10.0.1.7" },
-  { "WS/3H*)7F;", 419488232UL, 422140585UL, "10.0.1.3" },
-  { "ZJF[Ia6Q)+", 3960568056UL, 3962489998UL, "10.0.1.5" },
-  { "<]*QCK8U,>", 2590140172UL, 2598117636UL, "10.0.1.5" },
-  { "\\[a\\^=V_M0", 689410119UL, 698690782UL, "10.0.1.7" },
+  { "\\R^7=9UCG`", 126218373UL, 129199837UL, "10.0.1.5" },
+  { "1bQS5]WOXB", 1853470245UL, 1855329369UL, "10.0.1.4" },
+  { "M(@X^b[L:K", 3019630308UL, 3022260113UL, "10.0.1.1" },
+  { "431cBF8,YO", 1679726993UL, 1685224295UL, "10.0.1.7" },
+  { "(bEIQJ:E./", 2922607787UL, 2925521819UL, "10.0.1.6" },
+  { "WS/3H*)7F;", 419488232UL, 422140585UL, "10.0.1.5" },
+  { "ZJF[Ia6Q)+", 3960568056UL, 3962489998UL, "10.0.1.7" },
+  { "<]*QCK8U,>", 2590140172UL, 2598117636UL, "10.0.1.7" },
+  { "\\[a\\^=V_M0", 689410119UL, 698690782UL, "10.0.1.6" },
   { "7;RM+8J9YC", 1530175299UL, 1531107082UL, "10.0.1.7" },
-  { "4*=.SPR[AV", 3928582722UL, 3928853792UL, "10.0.1.3" },
+  { "4*=.SPR[AV", 3928582722UL, 3928853792UL, "10.0.1.1" },
   { "-2F+^88P4U", 3023552752UL, 3025823613UL, "10.0.1.7" },
-  { "X;-F`(N?9D", 570465234UL, 572485994UL, "10.0.1.5" },
-  { "R=F_D-K2a]", 1287750228UL, 1290935562UL, "10.0.1.1" },
-  { "X*+2aaC.EG", 3200948713UL, 3201088518UL, "10.0.1.3" },
-  { "[1ZXONX2]a", 4108881567UL, 4109865744UL, "10.0.1.7" },
-  { "FL;\\GWacaV", 458449508UL, 467374054UL, "10.0.1.7" },
-  { "\\MQ_XNT7L-", 1259349383UL, 1259509450UL, "10.0.1.5" },
-  { "VD6D0]ba_\\", 3842502950UL, 3842588691UL, "10.0.1.7" },
+  { "X;-F`(N?9D", 570465234UL, 572485994UL, "10.0.1.7" },
+  { "R=F_D-K2a]", 1287750228UL, 1290935562UL, "10.0.1.7" },
+  { "X*+2aaC.EG", 3200948713UL, 3201088518UL, "10.0.1.5" },
+  { "[1ZXONX2]a", 4108881567UL, 4109865744UL, "10.0.1.4" },
+  { "FL;\\GWacaV", 458449508UL, 467374054UL, "10.0.1.4" },
+  { "\\MQ_XNT7L-", 1259349383UL, 1259509450UL, "10.0.1.7" },
+  { "VD6D0]ba_\\", 3842502950UL, 3842588691UL, "10.0.1.1" },
 };
+
+#include "ketama_test_cases_spy.h"
+
+#endif
diff --git a/tests/ketama_test_cases_spy.h b/tests/ketama_test_cases_spy.h
new file mode 100644 (file)
index 0000000..5e6a937
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef TESTS_KETAMA_TEST_CASES_SPY_H
+#define TESTS_KETAMA_TEST_CASES_SPY_H
+
+static struct {
+    const char *key;
+    unsigned long hash1;
+    unsigned long hash2;
+    const char *server;
+} ketama_test_cases_spy[99]= {
+  { "SVa_]_V41)", 443691461UL, 445379617UL, "10.0.1.2" },
+  { "*/Z;?V(.\\8", 1422915503UL, 1428303028UL, "10.0.1.4" },
+  { "30C1*Z*S/_", 1473165754UL, 1480075959UL, "10.0.1.2" },
+  { "ERR:EC58G>", 2148406511UL, 2168579133UL, "10.0.1.7" },
+  { "1I=cTMNTKF", 2882686667UL, 2885206587UL, "10.0.1.4" },
+  { "]VG<`I*Z8)", 1103544263UL, 1104827657UL, "10.0.1.5" },
+  { "UUTC`-V159", 3716288206UL, 3727224240UL, "10.0.1.7" },
+  { "@7RU6C6T+Z", 3862737685UL, 3871917949UL, "10.0.1.6" },
+  { "/XLN0@+36;", 1623269830UL, 1627683651UL, "10.0.1.7" },
+  { "4(`X;\\V.^c", 373546328UL, 383925769UL, "10.0.1.6" },
+  { "726bW=9*a4", 4213440020UL, 4213950705UL, "10.0.1.3" },
+  { "\\`)<B)UE,c", 951096736UL, 955226069UL, "10.0.1.8" },
+  { "P1[Ma3=K1/", 1989324036UL, 1994028240UL, "10.0.1.1" },
+  { "C89I.-V?cT", 1604239957UL, 1606398093UL, "10.0.1.5" },
+  { "D[HE+cFXDK", 2117036136UL, 2117124014UL, "10.0.1.5" },
+  { "P1L?NAB[)K", 2129972569UL, 2132542634UL, "10.0.1.7" },
+  { "cDT0)Z5P6,", 176485284UL, 178675413UL, "10.0.1.1" },
+  { "@JW`+[WAO8", 2720940826UL, 2743975456UL, "10.0.1.2" },
+  { "\\39DKW^)N_", 3548879868UL, 3550704865UL, "10.0.1.6" },
+  { "EM75N0+[X1", 1558531507UL, 1559308507UL, "10.0.1.5" },
+  { "`,SS]NBP,b", 1883545960UL, 1884847278UL, "10.0.1.1" },
+  { "XX1a9LT+F?", 653487707UL, 656410408UL, "10.0.1.6" },
+  { "Zc\\-,F-c6V", 1160802451UL, 1171575728UL, "10.0.1.6" },
+  { "1*RTMC7,03", 1602398012UL, 1606398093UL, "10.0.1.5" },
+  { "*Xc+V0P>32", 536016577UL, 539988520UL, "10.0.1.7" },
+  { "U))Fb-(`,.", 4128682289UL, 4136854163UL, "10.0.1.7" },
+  { "R-08RNTaRT", 3718170086UL, 3727224240UL, "10.0.1.5" },
+  { "(LHcO203I3", 1007779411UL, 1014643570UL, "10.0.1.1" },
+  { "=256P+;Qc8", 3976201210UL, 3976304873UL, "10.0.1.3" },
+  { "OI5XZ_BBT(", 2155922164UL, 2168579133UL, "10.0.1.5" },
+  { "2TLRL/UL;:", 1086800909UL, 1095659802UL, "10.0.1.2" },
+  { "WHD\\O1`ZRW", 3087923411UL, 3095471560UL, "10.0.1.1" },
+  { ".=54)_c;=T", 2497691631UL, 2502731301UL, "10.0.1.6" },
+  { ";G<W-XWZ@b", 2888169733UL, 2888728739UL, "10.0.1.7" },
+  { "(,>E`)FT\\4", 580747448UL, 581063326UL, "10.0.1.5" },
+  { "HZAU*;P*N]", 2564670474UL, 2565697267UL, "10.0.1.1" },
+  { "NZ@ZE=O84_", 533335275UL, 539988520UL, "10.0.1.7" },
+  { "6,cEI`F_P>", 3972869246UL, 3974773167UL, "10.0.1.3" },
+  { "c,5AQ/T5)6", 2835605783UL, 2847870057UL, "10.0.1.7" },
+  { ".O,>>BT)RX", 3857978174UL, 3871917949UL, "10.0.1.7" },
+  { "XY\\X::LX50", 1749241099UL, 1752196488UL, "10.0.1.7" },
+  { "+550F^/.01", 3781824099UL, 3783248219UL, "10.0.1.2" },
+  { "<.X9E2S5+9", 3232479481UL, 3234387706UL, "10.0.1.7" },
+  { "]\\.UH8_0a1", 2419699252UL, 2423002920UL, "10.0.1.6" },
+  { "8(6=(T0/Z0", 728266737UL, 729026070UL, "10.0.1.6" },
+  { "8*6a;Sc*X+", 4223431086UL, 4230156966UL, "10.0.1.5" },
+  { "<QW:;3K6;H", 2731158143UL, 2743975456UL, "10.0.1.7" },
+  { "7C@EY@-Y?_", 760770733UL, 761576669UL, "10.0.1.5" },
+  { "aPb3E1WD4K", 2500489218UL, 2502731301UL, "10.0.1.2" },
+  { "?@12R<=1BH", 1494795329UL, 1502505505UL, "10.0.1.1" },
+  { "QR(a+Q=1FU", 3238535074UL, 3238996435UL, "10.0.1.5" },
+  { "`C9^FV,960", 2628553463UL, 2628733766UL, "10.0.1.3" },
+  { "UNHVP..^8H", 977096483UL, 977319837UL, "10.0.1.6" },
+  { ":Y.2W2[(35", 2777083668UL, 2784182515UL, "10.0.1.6" },
+  { "M/HV^_HZ4O", 3623390946UL, 3624445007UL, "10.0.1.4" },
+  { "ZY16KQ<ICD", 1831153193UL, 1838563516UL, "10.0.1.7" },
+  { "bV2,`a.PY9", 1962228869UL, 1962648654UL, "10.0.1.7" },
+  { "U;9:-+5N]9", 269504649UL, 277560877UL, "10.0.1.2" },
+  { "1S/:aJ[1(;", 578069729UL, 581063326UL, "10.0.1.5" },
+  { "Nb-X^]M)I:", 330865696UL, 331009896UL, "10.0.1.3" },
+  { "2;M;ES>J5/", 2776949824UL, 2784182515UL, "10.0.1.6" },
+  { "[>RZHG97Q9", 71954686UL, 72034069UL, "10.0.1.4" },
+  { "J3/G[)9<^Z", 2799896459UL, 2805183696UL, "10.0.1.6" },
+  { "N-)88>[O`,", 50404102UL, 51792557UL, "10.0.1.2" },
+  { "NP:=FR\\OaA", 3837333776UL, 3837792034UL, "10.0.1.7" },
+  { "`@L+W;a,O[", 1512157148UL, 1522285852UL, "10.0.1.5" },
+  { "W2`P:-+1T[", 2945171975UL, 2946196424UL, "10.0.1.7" },
+  { "-6G7K^YDIN", 3168617340UL, 3170513015UL, "10.0.1.5" },
+  { "U>*>9ZI6V5", 668514946UL, 674097631UL, "10.0.1.5" },
+  { ".I?^6Ic9RK", 938419020UL, 942832691UL, "10.0.1.6" },
+  { "0OZH^9BKM[", 3682518606UL, 3686781297UL, "10.0.1.2" },
+  { "5?50UGZ:ML", 868610882UL, 869425986UL, "10.0.1.6" },
+  { "?K2NF@3=IU", 381218851UL, 383925769UL, "10.0.1.6" },
+  { "YI@G-2X?UB", 3688706179UL, 3693197681UL, "10.0.1.6" },
+  { "7cY</BSaL=", 3976870223UL, 3978903843UL, "10.0.1.7" },
+  { "A(`KF:[RH8", 3292979676UL, 3294849139UL, "10.0.1.6" },
+  { ";=ZT\\W^P+H", 1401102653UL, 1416290674UL, "10.0.1.6" },
+  { "b2?WFF56;R", 480494704UL, 486971192UL, "10.0.1.7" },
+  { "CTR74,J+N.", 137446045UL, 146633907UL, "10.0.1.7" },
+  { "<b;*R+QDST", 1304985302UL, 1308223778UL, "10.0.1.5" },
+  { "\\R^7=9UCG`", 126218373UL, 129199837UL, "10.0.1.6" },
+  { "1bQS5]WOXB", 1853470245UL, 1855329369UL, "10.0.1.7" },
+  { "M(@X^b[L:K", 3019630308UL, 3022260113UL, "10.0.1.4" },
+  { "431cBF8,YO", 1679726993UL, 1685224295UL, "10.0.1.1" },
+  { "(bEIQJ:E./", 2922607787UL, 2925521819UL, "10.0.1.7" },
+  { "WS/3H*)7F;", 419488232UL, 422140585UL, "10.0.1.3" },
+  { "ZJF[Ia6Q)+", 3960568056UL, 3962489998UL, "10.0.1.5" },
+  { "<]*QCK8U,>", 2590140172UL, 2598117636UL, "10.0.1.5" },
+  { "\\[a\\^=V_M0", 689410119UL, 698690782UL, "10.0.1.7" },
+  { "7;RM+8J9YC", 1530175299UL, 1531107082UL, "10.0.1.7" },
+  { "4*=.SPR[AV", 3928582722UL, 3928853792UL, "10.0.1.3" },
+  { "-2F+^88P4U", 3023552752UL, 3025823613UL, "10.0.1.7" },
+  { "X;-F`(N?9D", 570465234UL, 572485994UL, "10.0.1.5" },
+  { "R=F_D-K2a]", 1287750228UL, 1290935562UL, "10.0.1.1" },
+  { "X*+2aaC.EG", 3200948713UL, 3201088518UL, "10.0.1.3" },
+  { "[1ZXONX2]a", 4108881567UL, 4109865744UL, "10.0.1.7" },
+  { "FL;\\GWacaV", 458449508UL, 467374054UL, "10.0.1.7" },
+  { "\\MQ_XNT7L-", 1259349383UL, 1259509450UL, "10.0.1.5" },
+  { "VD6D0]ba_\\", 3842502950UL, 3842588691UL, "10.0.1.7" },
+};
+#endif