Merging changes from trunk
[m6w6/libmemcached] / tests / function.c
index 13daaa29f32909a5ddf1b0860425d2d07839848c..affa176ff80653c488b25eabdc230438a0c3770e 100644 (file)
@@ -1465,8 +1465,8 @@ static test_return_t mget_execute(memcached_st *memc)
   int max_keys= binary ? 20480 : 1;
   
 
-  char **keys= calloc(max_keys, sizeof(char*));
-  size_t *key_length=calloc(max_keys, sizeof(size_t));
+  char **keys= calloc((size_t)max_keys, sizeof(char*));
+  size_t *key_length=calloc((size_t)max_keys, sizeof(size_t));
 
   /* First add all of the items.. */
   char blob[1024] = {0};
@@ -1474,7 +1474,7 @@ static test_return_t mget_execute(memcached_st *memc)
   for (int x= 0; x < max_keys; ++x)
   {
     char k[251];
-    key_length[x]= snprintf(k, sizeof(k), "0200%u", x);
+    key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
     keys[x]= strdup(k);
     assert(keys[x] != NULL);
     rc= memcached_add(memc, keys[x], key_length[x], blob, sizeof(blob), 0, 0);
@@ -1484,9 +1484,8 @@ static test_return_t mget_execute(memcached_st *memc)
   /* Try to get all of them with a large multiget */
   unsigned int counter= 0;
   memcached_execute_function callbacks[1]= { [0]= &callback_counter };
-  rc= memcached_mget_execute(memc, NULL, 0,
-                             (const char**)keys, key_length,
-                             max_keys, callbacks, &counter, 1);
+  rc= memcached_mget_execute(memc, (const char**)keys, key_length,
+                             (size_t)max_keys, callbacks, &counter, 1);
 
   if (binary)
   {
@@ -1496,7 +1495,7 @@ static test_return_t mget_execute(memcached_st *memc)
     assert(rc == MEMCACHED_END);
 
     /* Verify that we got all of the items */
-    assert(counter == max_keys);
+    assert(counter == (unsigned int)max_keys);
   }
   else
   {
@@ -2757,6 +2756,57 @@ static test_return_t auto_eject_hosts(memcached_st *trash)
   return TEST_SUCCESS;
 }
 
+static test_return_t output_ketama_weighted_keys(memcached_st *trash)
+{
+  (void) trash;
+
+  memcached_return rc;
+  memcached_st *memc= memcached_create(NULL);
+  assert(memc);
+
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  uint64_t value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED);
+  assert(value == 1);
+
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH, MEMCACHED_HASH_MD5);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH);
+  assert(value == MEMCACHED_HASH_MD5);
+
+  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);
+
+  FILE *fp;
+  if ((fp = fopen("ketama_keys.txt", "w")))
+  {
+    // noop
+  } else {
+    printf("cannot write to file ketama_keys.txt");
+    return TEST_FAILURE;
+  }
+
+  for (int x= 0; x < 10000; x++)
+  {
+    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;
+    fprintf(fp, "key %s is on host /%s:%u\n", key, hostname, port);
+  }
+  fclose(fp);
+  memcached_server_list_free(server_pool);
+  memcached_free(memc);
+
+  return TEST_SUCCESS;
+}
+
+
 static test_return_t  result_static(memcached_st *memc)
 {
   memcached_result_st result;
@@ -4797,6 +4847,114 @@ static test_return_t regression_bug_442914(memcached_st *memc)
   return TEST_SUCCESS;
 }
 
+static test_return_t regression_bug_447342(memcached_st *memc)
+{
+  if (memc->number_of_hosts < 3 || pre_replication(memc) != MEMCACHED_SUCCESS)
+    return TEST_SKIPPED;
+
+  memcached_return rc;
+
+  rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 2);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  const size_t max_keys= 100;
+  char **keys= calloc(max_keys, sizeof(char*));
+  size_t *key_length=calloc(max_keys, sizeof(size_t));
+
+  for (int x= 0; x < (int)max_keys; ++x)
+  {
+    char k[251];
+    key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
+    keys[x]= strdup(k);
+    assert(keys[x] != NULL);
+    rc= memcached_set(memc, k, key_length[x], k, key_length[x], 0, 0);
+    assert(rc == MEMCACHED_SUCCESS);
+  }
+
+  /*
+  ** We are using the quiet commands to store the replicas, so we need
+  ** to ensure that all of them are processed before we can continue.
+  ** In the test we go directly from storing the object to trying to
+  ** receive the object from all of the different servers, so we
+  ** could end up in a race condition (the memcached server hasn't yet
+  ** processed the quiet command from the replication set when it process
+  ** the request from the other client (created by the clone)). As a
+  ** workaround for that we call memcached_quit to send the quit command
+  ** to the server and wait for the response ;-) If you use the test code
+  ** as an example for your own code, please note that you shouldn't need
+  ** to do this ;-)
+  */
+  memcached_quit(memc);
+  
+  /* Verify that all messages are stored, and we didn't stuff too much 
+   * into the servers
+   */
+  rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  unsigned int counter= 0;
+  memcached_execute_function callbacks[1]= { [0]= &callback_counter };
+  rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+  /* Verify that we received all of the key/value pairs */
+  assert(counter == (unsigned int)max_keys);
+
+  memcached_quit(memc);
+  /*
+   * Don't do the following in your code. I am abusing the internal details
+   * within the library, and this is not a supported interface.
+   * This is to verify correct behavior in the library. Fake that two servers
+   * are dead..
+   */
+  unsigned int port0= memc->hosts[0].port;
+  unsigned int port2= memc->hosts[2].port;
+  memc->hosts[0].port= 0;
+  memc->hosts[2].port= 0;
+
+  rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  counter= 0;
+  rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+  assert(counter == (unsigned int)max_keys);
+
+  /* restore the memc handle */
+  memc->hosts[0].port= port0;
+  memc->hosts[2].port= port2;
+
+  memcached_quit(memc);
+
+  /* Remove half of the objects */
+  for (int x= 0; x < (int)max_keys; ++x)
+    if (x & 1)
+    {
+      rc= memcached_delete(memc, keys[x], key_length[x], 0);
+      assert(rc == MEMCACHED_SUCCESS);
+    }
+
+  memcached_quit(memc);
+  memc->hosts[0].port= 0;
+  memc->hosts[2].port= 0;
+
+  /* now retry the command, this time we should have cache misses */
+  rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+  assert(rc == MEMCACHED_SUCCESS);
+
+  counter= 0;
+  rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+  assert(counter == (unsigned int)(max_keys >> 1));
+
+  /* Release allocated resources */
+  for (size_t x= 0; x < max_keys; ++x)
+    free(keys[x]);
+  free(keys);
+  free(key_length);
+
+  /* restore the memc handle */
+  memc->hosts[0].port= port0;
+  memc->hosts[2].port= port2;
+  return TEST_SUCCESS;
+}
+
 /* Test memcached_server_get_last_disconnect
  * For a working server set, shall be NULL
  * For a set of non existing server, shall not be NULL
@@ -5064,6 +5222,7 @@ test_st regression_tests[]= {
   {"lp:434843 buffered", 1, regression_bug_434843_buffered },
   {"lp:421108", 1, regression_bug_421108 },
   {"lp:442914", 1, regression_bug_442914 },
+  {"lp:447342", 1, regression_bug_447342 },
   {0, 0, 0}
 };
 
@@ -5109,6 +5268,7 @@ test_st hsieh_availability[] ={
 
 test_st ketama_auto_eject_hosts[] ={
   {"auto_eject_hosts", 1, auto_eject_hosts },
+  {"output_ketama_weighted_keys", 1, output_ketama_weighted_keys },
   {0, 0, 0}
 };