From d01f59eec297365fdbfeba38248245bf4e5d827a Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 5 Oct 2020 16:59:26 +0200 Subject: [PATCH] testing: replication --- test/lib/MemcachedCluster.cpp | 8 +- test/lib/MemcachedCluster.hpp | 2 + test/lib/common.hpp | 4 +- test/lib/random.cpp | 18 +++++ test/lib/random.hpp | 2 + test/tests/memcached/replication.cpp | 112 +++++++++++++++++++++++++++ 6 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 test/tests/memcached/replication.cpp diff --git a/test/lib/MemcachedCluster.cpp b/test/lib/MemcachedCluster.cpp index 573dddf3..bb164e5d 100644 --- a/test/lib/MemcachedCluster.cpp +++ b/test/lib/MemcachedCluster.cpp @@ -134,10 +134,16 @@ void MemcachedCluster::enableBuffering(bool enable) { void MemcachedCluster::enableReplication() { REQUIRE(MEMCACHED_SUCCESS == memcached_behavior_set(&memc, - MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, memcached_server_count(&memc))); + MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, memcached_server_count(&memc) - 1)); } void MemcachedCluster::enableUdp(bool enable) { REQUIRE(MEMCACHED_SUCCESS == memcached_behavior_set(&memc, MEMCACHED_BEHAVIOR_USE_UDP, enable)); } + +void MemcachedCluster::killOneServer() { + const auto &servers = cluster.getServers(); + const auto &victim = servers[random_num(0UL, servers.size() - 1)]; + ::kill(victim.getPid(), SIGKILL); +} diff --git a/test/lib/MemcachedCluster.hpp b/test/lib/MemcachedCluster.hpp index 8e438580..5dbe5c2c 100644 --- a/test/lib/MemcachedCluster.hpp +++ b/test/lib/MemcachedCluster.hpp @@ -37,6 +37,8 @@ public: static MemcachedCluster sasl(); #endif + void killOneServer(); + private: static const memcached_st empty_memc; diff --git a/test/lib/common.hpp b/test/lib/common.hpp index ecfa07d4..517b4263 100644 --- a/test/lib/common.hpp +++ b/test/lib/common.hpp @@ -105,7 +105,7 @@ public: } }; -template +template class Malloced { T *ptr; public: @@ -115,7 +115,7 @@ public: {} ~Malloced() { if(ptr) - F(ptr); + free(ptr); } auto operator *() { return ptr; diff --git a/test/lib/random.cpp b/test/lib/random.cpp index 359c1f34..50cc53db 100644 --- a/test/lib/random.cpp +++ b/test/lib/random.cpp @@ -62,3 +62,21 @@ pair random_ascii_pair(size_t minlen, size_t maxlen) { random_ascii_string(random_num(minlen, maxlen)) }; } + +#include + +char random_binary() { + return random_num(CHAR_MIN, CHAR_MAX); +} + +string random_binary_string(size_t len) { + string s; + s.reserve(len + 1); + + for (size_t rem = 0; rem < len; ++rem) { + s += random_binary(); + } + s[len] = 0; + + return s; +} diff --git a/test/lib/random.hpp b/test/lib/random.hpp index 19ff4f8b..18e518c3 100644 --- a/test/lib/random.hpp +++ b/test/lib/random.hpp @@ -26,6 +26,8 @@ enable_if_t, T> random_num(T min, T max) { unsigned random_port(); string random_port_string(const string &); +char random_binary(); +string random_binary_string(size_t len); char random_ascii(char min = '!', char max = '~'); string random_ascii_string(size_t len, char min = '!', char max = '~'); kv_pair random_ascii_pair(size_t minlen = 1<<2, size_t maxlen = 1<<10); diff --git a/test/tests/memcached/replication.cpp b/test/tests/memcached/replication.cpp new file mode 100644 index 00000000..3444d4b1 --- /dev/null +++ b/test/tests/memcached/replication.cpp @@ -0,0 +1,112 @@ +#include "test/lib/common.hpp" +#include "test/lib/MemcachedCluster.hpp" + +#include "libmemcached/instance.hpp" + +TEST_CASE("memcached_replication") { + MemcachedCluster test = MemcachedCluster::network(); + auto memc = &test.memc; + + test.enableBinaryProto(); + test.enableReplication(); + + SECTION("replicates one key to all servers") { + REQUIRE_SUCCESS(memcached_set(memc, S(__func__), S(__func__), 0, 0)); + memcached_quit(memc); + + for (const auto &server : test.cluster.getServers()) { + MemcachedPtr check; + + REQUIRE_SUCCESS(memcached_server_add(*check, "localhost", get(server.getSocketOrPort()))); + + memcached_return_t rc; + Malloced val(memcached_get(*check, S(__func__), nullptr, nullptr, &rc)); + REQUIRE_SUCCESS(rc); + REQUIRE(string(__func__) == *val); + } + } + +#define NUM_KEYS 100 + + SECTION("replicates many keys to all servers") { + array str; + array chr; + array len; + + for (auto i = 0U; i < NUM_KEYS; ++i) { + str[i] = random_binary_string(12) + to_string(i); + chr[i] = str[i].data(); + len[i] = str[i].length(); + + REQUIRE_SUCCESS(memcached_set(memc, chr[i], len[i], chr[i], len[i], 0, 0)); + } + memcached_quit(memc); + + for (auto i = 0U; i < memcached_server_count(memc); ++i) { + auto pos = i % memcached_server_count(memc); + auto ins = memcached_server_instance_by_position(memc, pos); + MemcachedPtr single; + memcached_result_st r; + memcached_return_t rc; + + REQUIRE(memcached_result_create(*single, &r)); + REQUIRE_SUCCESS(memcached_server_add(*single, "localhost", ins->port())); + REQUIRE_SUCCESS(memcached_behavior_set(*single, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1)); + REQUIRE_SUCCESS(memcached_mget(*single, chr.data(), len.data(), NUM_KEYS)); + + size_t n = 0; + while (memcached_fetch_result(*single, &r, &rc)) { + ++n; + REQUIRE_SUCCESS(rc); + } + CHECK(n == NUM_KEYS); + REQUIRE_RC(MEMCACHED_END, rc); + memcached_result_free(&r); + } + + SECTION("randomize reads") { + REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ, 1)); + for (auto i = 0; i < NUM_KEYS; ++i) { + memcached_return_t rc; + auto result = memcached_result_create(memc, nullptr); + + REQUIRE(result); + REQUIRE_SUCCESS(memcached_mget_by_key(memc, chr[i], len[i], chr.data(), len.data(), NUM_KEYS)); + + size_t n = 0; + while (memcached_fetch_result(memc, result, &rc)) { + ++n; + REQUIRE_SUCCESS(rc); + } + CHECK(n == NUM_KEYS); + REQUIRE_RC(MEMCACHED_END, rc); + } + } + +#if 0 +# warning I think the old test is bogus and the functionality does not exist as advertised + + SECTION("deleted from primary") { + for (auto i = 0; i < NUM_KEYS; ++i) { + memcached_return_t rc; + + CHECK(MEMCACHED_SUCCESS == memcached_delete(memc, chr[i], len[i], 0)); + Malloced val(memcached_get(memc, chr[i], len[i], nullptr, nullptr, &rc)); + CHECK(MEMCACHED_SUCCESS == rc); + CHECK(*val); + } + } + SECTION("dead replica") { + test.killOneServer(); + + for (auto i = 0; i < NUM_KEYS; ++i) { + memcached_return_t rc; + + Malloced val(memcached_get(memc, chr[i], len[i], nullptr, nullptr, &rc)); + CHECK(MEMCACHED_SUCCESS == rc); + CHECK(*val); + } + } +#endif + } +} -- 2.30.2