+/* 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>
+#include <libtest/test.hpp>
+
+#include <libmemcached/memcached.h>
+#include <libmemcached/server_instance.h>
+#include <libmemcached/continuum.hpp>
+
+#include <tests/ketama.h>
+#include <tests/ketama_test_cases.h>
+
+test_return_t ketama_compatibility_libmemcached(memcached_st *)
+{
+ memcached_st *memc= memcached_create(NULL);
+ test_true(memc);
+
+ test_compare(MEMCACHED_SUCCESS,
+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1));
+
+ test_compare(uint64_t(1), memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED));
+
+ test_compare(MEMCACHED_SUCCESS, memcached_behavior_set_distribution(memc, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA));
+ test_compare(MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA, memcached_behavior_get_distribution(memc));
+
+ memcached_server_st *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. */
+ test_compare(8U, memcached_server_count(memc));
+ test_strcmp(server_pool[0].hostname, "10.0.1.1");
+ test_compare(in_port_t(11211), server_pool[0].port);
+ test_compare(600U, server_pool[0].weight);
+ test_strcmp(server_pool[2].hostname, "10.0.1.3");
+ test_compare(in_port_t(11211), server_pool[2].port);
+ test_compare(200U, server_pool[2].weight);
+ test_strcmp(server_pool[7].hostname, "10.0.1.8");
+ test_compare(in_port_t(11211), server_pool[7].port);
+ test_compare(100U, server_pool[7].weight);
+
+ /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
+ * us test the boundary wraparound.
+ */
+ test_true(memcached_generate_hash(memc, (char *)"VDEAAAAA", 8) == memc->ketama.continuum[0].index);
+
+ /* verify the standard ketama set. */
+ for (uint32_t x= 0; x < 99; x++)
+ {
+ uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
+ memcached_server_instance_st instance=
+ memcached_server_instance_by_position(memc, server_idx);
+ const char *hostname = memcached_server_name(instance);
+
+ test_strcmp(hostname, ketama_test_cases[x].server);
+ }
+
+ memcached_server_list_free(server_pool);
+ memcached_free(memc);
+
+ return TEST_SUCCESS;
+}
+
+test_return_t user_supplied_bug18(memcached_st *trash)
+{
+ memcached_return_t rc;
+ uint64_t value;
+ int x;
+ memcached_st *memc;
+
+ (void)trash;
+
+ memc= memcached_create(NULL);
+ test_true(memc);
+
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1);
+ test_compare(MEMCACHED_SUCCESS, rc);
+
+ value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED);
+ test_true(value == 1);
+
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH, MEMCACHED_HASH_MD5);
+ test_compare(MEMCACHED_SUCCESS, rc);
+
+ value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH);
+ test_true(value == MEMCACHED_HASH_MD5);
+
+ memcached_server_st *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. */
+ test_true(memcached_server_count(memc) == 8);
+ test_strcmp(server_pool[0].hostname, "10.0.1.1");
+ test_true(server_pool[0].port == 11211);
+ test_true(server_pool[0].weight == 600);
+ test_strcmp(server_pool[2].hostname, "10.0.1.3");
+ test_true(server_pool[2].port == 11211);
+ test_true(server_pool[2].weight == 200);
+ test_strcmp(server_pool[7].hostname, "10.0.1.8");
+ test_true(server_pool[7].port == 11211);
+ test_true(server_pool[7].weight == 100);
+
+ /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
+ * us test the boundary wraparound.
+ */
+ test_true(memcached_generate_hash(memc, (char *)"VDEAAAAA", 8) == memc->ketama.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));
+
+ memcached_server_instance_st instance=
+ memcached_server_instance_by_position(memc, server_idx);
+
+ const char *hostname = memcached_server_name(instance);
+ test_strcmp(hostname, ketama_test_cases[x].server);
+ }
+
+ memcached_server_list_free(server_pool);
+ memcached_free(memc);
+
+ return TEST_SUCCESS;
+}
+
+test_return_t auto_eject_hosts(memcached_st *trash)
+{
+ (void) trash;
+ memcached_server_instance_st instance;
+
+ memcached_return_t rc;
+ memcached_st *memc= memcached_create(NULL);
+ test_true(memc);
+
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1);
+ test_compare(MEMCACHED_SUCCESS, rc);
+
+ uint64_t value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED);
+ test_true(value == 1);
+
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH, MEMCACHED_HASH_MD5);
+ test_compare(MEMCACHED_SUCCESS, rc);
+
+ value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH);
+ test_true(value == MEMCACHED_HASH_MD5);
+
+ /* server should be removed when in delay */
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS, 1);
+ test_compare(MEMCACHED_SUCCESS, rc);
+
+ value= memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS);
+ test_true(value == 1);
+
+ memcached_server_st *server_pool;
+ 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. */
+ test_true(memcached_server_count(memc) == 8);
+ test_strcmp(server_pool[0].hostname, "10.0.1.1");
+ test_true(server_pool[0].port == 11211);
+ test_true(server_pool[0].weight == 600);
+ test_strcmp(server_pool[2].hostname, "10.0.1.3");
+ test_true(server_pool[2].port == 11211);
+ test_true(server_pool[2].weight == 200);
+ test_strcmp(server_pool[7].hostname, "10.0.1.8");
+ test_true(server_pool[7].port == 11211);
+ test_true(server_pool[7].weight == 100);
+
+ instance= memcached_server_instance_by_position(memc, 2);
+ ((memcached_server_write_instance_st)instance)->next_retry = time(NULL) + 15;
+ memc->ketama.next_distribution_rebuild= time(NULL) - 1;
+
+ /*
+ This would not work if there were only two hosts.
+ */
+ for (size_t x= 0; x < 99; x++)
+ {
+ memcached_autoeject(memc);
+ uint32_t server_idx= memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
+ test_true(server_idx != 2);
+ }
+
+ /* and re-added when it's back. */
+ ((memcached_server_write_instance_st)instance)->next_retry = time(NULL) - 1;
+ memc->ketama.next_distribution_rebuild= time(NULL) - 1;
+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_DISTRIBUTION,
+ memc->distribution);
+ for (size_t x= 0; x < 99; x++)
+ {
+ uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
+ // We re-use instance from above.
+ instance=
+ memcached_server_instance_by_position(memc, server_idx);
+ const char *hostname = memcached_server_name(instance);
+ test_strcmp(hostname, ketama_test_cases[x].server);
+ }
+
+ memcached_server_list_free(server_pool);
+ memcached_free(memc);
+
+ return TEST_SUCCESS;
+}
+
+test_return_t ketama_compatibility_spymemcached(memcached_st *)
+{
+ memcached_st *memc= memcached_create(NULL);
+ test_true(memc);
+
+ test_compare(MEMCACHED_SUCCESS,
+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1));
+
+ test_compare(UINT64_C(1), memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED));
+
+ test_compare(MEMCACHED_SUCCESS, memcached_behavior_set_distribution(memc, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY));
+ test_compare(MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY, memcached_behavior_get_distribution(memc));
+
+ memcached_server_st *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");
+ test_true(server_pool);
+ memcached_server_push(memc, server_pool);
+
+ /* verify that the server list was parsed okay. */
+ test_compare(8U, memcached_server_count(memc));
+ test_strcmp(server_pool[0].hostname, "10.0.1.1");
+ test_compare(in_port_t(11211), server_pool[0].port);
+ test_compare(600U, server_pool[0].weight);
+ test_strcmp(server_pool[2].hostname, "10.0.1.3");
+ test_compare(in_port_t(11211), server_pool[2].port);
+ test_compare(200U, server_pool[2].weight);
+ test_strcmp(server_pool[7].hostname, "10.0.1.8");
+ test_compare(in_port_t(11211), server_pool[7].port);
+ test_compare(100U, server_pool[7].weight);
+
+ /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
+ * us test the boundary wraparound.
+ */
+ test_true(memcached_generate_hash(memc, (char *)"VDEAAAAA", 8) == memc->ketama.continuum[0].index);
+
+ /* verify the standard ketama set. */
+ for (uint32_t 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));
+
+ memcached_server_instance_st instance=
+ memcached_server_instance_by_position(memc, server_idx);
+
+ const char *hostname= memcached_server_name(instance);
+
+ test_strcmp(hostname, ketama_test_cases_spy[x].server);
+ }
+
+ memcached_server_list_free(server_pool);
+ memcached_free(memc);
+
+ return TEST_SUCCESS;
+}