testing: replication
authorMichael Wallner <mike@php.net>
Mon, 5 Oct 2020 14:59:26 +0000 (16:59 +0200)
committerMichael Wallner <mike@php.net>
Mon, 5 Oct 2020 16:57:37 +0000 (18:57 +0200)
test/lib/MemcachedCluster.cpp
test/lib/MemcachedCluster.hpp
test/lib/common.hpp
test/lib/random.cpp
test/lib/random.hpp
test/tests/memcached/replication.cpp [new file with mode: 0644]

index 573dddf384edd850b9c07723b4fa857b3ca5a84a..bb164e5d1982ea707e0375db807f3d7bcefa9724 100644 (file)
@@ -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);
+}
index 8e4385808a766ac32533650a0fdf7fe077127217..5dbe5c2c8848b8bd3175e55e6811ff2c1758421d 100644 (file)
@@ -37,6 +37,8 @@ public:
   static MemcachedCluster sasl();
 #endif
 
+  void killOneServer();
+
 private:
   static const memcached_st empty_memc;
 
index ecfa07d413535b8aab9ed83650729320d7d3670b..517b426342e7ea596af1f6098333df51557ed1e7 100644 (file)
@@ -105,7 +105,7 @@ public:
   }
 };
 
-template<class T, void (*F)(void*) = free>
+template<class T>
 class Malloced {
   T *ptr;
 public:
@@ -115,7 +115,7 @@ public:
   {}
   ~Malloced() {
     if(ptr)
-      F(ptr);
+      free(ptr);
   }
   auto operator *() {
     return ptr;
index 359c1f3487ad5a2e97c78b3ed8d685d1b7a7e7ea..50cc53db508ed219da16f00d9538554f774f6cf0 100644 (file)
@@ -62,3 +62,21 @@ pair<string, string> random_ascii_pair(size_t minlen, size_t maxlen) {
       random_ascii_string(random_num(minlen, maxlen))
   };
 }
+
+#include <climits>
+
+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;
+}
index 19ff4f8b32700a24c125ff4bcc8912d6a84aeaf4..18e518c34866073d9da897459e2112a8b7e145b7 100644 (file)
@@ -26,6 +26,8 @@ enable_if_t<is_integral_v<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 (file)
index 0000000..3444d4b
--- /dev/null
@@ -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<int>(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<string, NUM_KEYS> str;
+    array<char *, NUM_KEYS> chr;
+    array<size_t, NUM_KEYS> 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
+  }
+}