testing: flush tests
authorMichael Wallner <mike@php.net>
Tue, 15 Sep 2020 15:09:28 +0000 (17:09 +0200)
committerMichael Wallner <mike@php.net>
Tue, 15 Sep 2020 15:09:28 +0000 (17:09 +0200)
testing/lib/MemcachedCluster.cpp
testing/lib/MemcachedCluster.hpp
testing/tests/memcached/regression/lp434484.cpp [new file with mode: 0644]
testing/tests/memcached/regression/lp434843.cpp [new file with mode: 0644]
testing/tests/memcached/regression/lp442914.cpp [new file with mode: 0644]
testing/tests/memcached/regression/lp447342.cpp [new file with mode: 0644]
testing/tests/memcached/regression/lp490486.cpp [new file with mode: 0644]
testing/tests/memcached/regression/lp996813.cpp [new file with mode: 0644]

index efb1a2de60d74e5b13d1c6ee73637083e5d83f6c..910aca3ce2ea3dab2a547ca5bd14d33ab7c89ab1 100644 (file)
@@ -47,6 +47,19 @@ MemcachedCluster::MemcachedCluster(Cluster &&cluster_)
   init();
 }
 
+MemcachedCluster::MemcachedCluster(MemcachedCluster &&mc)
+    : cluster{Server{}}
+{
+  *this = move(mc);
+}
+
+MemcachedCluster &MemcachedCluster::operator=(MemcachedCluster &&mc) {
+  cluster = move(mc.cluster);
+  memcached_clone(&memc, &mc.memc);
+  returns = ReturnMatcher{&memc};
+  return *this;
+}
+
 MemcachedCluster MemcachedCluster::mixed() {
   return MemcachedCluster{};
 }
@@ -70,15 +83,13 @@ void MemcachedCluster::enableBinaryProto(bool enable) {
       MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, enable));
 }
 
-MemcachedCluster::MemcachedCluster(MemcachedCluster &&mc)
-    : cluster{Server{}}
-{
-  *this = move(mc);
+void MemcachedCluster::enableBuffering(bool enable) {
+  REQUIRE(MEMCACHED_SUCCESS == memcached_behavior_set(&memc,
+      MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, enable));
 }
 
-MemcachedCluster &MemcachedCluster::operator=(MemcachedCluster &&mc) {
-  cluster = move(mc.cluster);
-  memcached_clone(&memc, &mc.memc);
-  returns = ReturnMatcher{&memc};
-  return *this;
+void MemcachedCluster::enableReplication() {
+  REQUIRE(MEMCACHED_SUCCESS == memcached_behavior_set(&memc,
+      MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, memcached_server_count(&memc)));
 }
+
index ea6aa3467f650af197a6085fa949765856426dbe..ee2dfdd2fa91f4ffbd69c1e74542c40fe9a93f7f 100644 (file)
@@ -23,6 +23,8 @@ public:
   MemcachedCluster &operator=(MemcachedCluster &&mc);
 
   void enableBinaryProto(bool enable = true);
+  void enableBuffering(bool enable = true);
+  void enableReplication();
   void flush();
 
   static MemcachedCluster mixed();
diff --git a/testing/tests/memcached/regression/lp434484.cpp b/testing/tests/memcached/regression/lp434484.cpp
new file mode 100644 (file)
index 0000000..48fd1af
--- /dev/null
@@ -0,0 +1,16 @@
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+TEST_CASE("memcached_regression_lp434484") {
+  MemcachedCluster test;
+  auto memc = &test.memc;
+
+  test.enableBinaryProto();
+
+  REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_append(memc, S(__func__), S(__func__), 0, 0));
+
+  vector<char> blob;
+  blob.resize(2048 * 1024);
+  REQUIRE_RC(MEMCACHED_E2BIG, memcached_set(memc, S(__func__), blob.data(), blob.size(), 0, 0));
+
+}
diff --git a/testing/tests/memcached/regression/lp434843.cpp b/testing/tests/memcached/regression/lp434843.cpp
new file mode 100644 (file)
index 0000000..87f8539
--- /dev/null
@@ -0,0 +1,48 @@
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+static memcached_return_t callback_counter(const memcached_st *, memcached_result_st *, void *context) {
+  auto *counter= reinterpret_cast<size_t *>(context);
+  *counter = *counter + 1;
+
+  return MEMCACHED_SUCCESS;
+}
+
+#define NUM_KEYS 1024
+
+TEST_CASE("memcached_regression_lp434843") {
+  MemcachedCluster test{Cluster{Server{MEMCACHED_BINARY, {"-p", random_port_string}}, 1}};
+  auto memc = &test.memc;
+  auto buffering = GENERATE(0, 1);
+
+  test.enableBinaryProto();
+  test.enableBuffering(buffering);
+
+  INFO("buffering: " << buffering);
+
+  size_t counter = 0;
+  memcached_execute_fn cb[] = {&callback_counter};
+
+  array<string, NUM_KEYS> str;
+  array<char *, NUM_KEYS> chr;
+  array<size_t, NUM_KEYS> len;
+  
+  for (auto i = 0; i < NUM_KEYS; ++i) {
+    str[i] = random_ascii_string(random_num<size_t>(24, 32));
+    chr[i] = str[i].data();
+    len[i] = str[i].length();
+  }
+
+  REQUIRE_SUCCESS(memcached_mget(memc, chr.data(), len.data(), NUM_KEYS));
+  REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_fetch_execute(memc, cb, &counter, 1));
+  REQUIRE(counter == 0);
+
+  for (auto i = 0; i < NUM_KEYS; ++i) {
+    char data[1024];
+    REQUIRE_SUCCESS(memcached_add(memc, chr[i], len[i], data, sizeof(data), 0, 0));
+  }
+
+  REQUIRE_SUCCESS(memcached_mget(memc, chr.data(), len.data(), NUM_KEYS));
+  REQUIRE_SUCCESS( memcached_fetch_execute(memc, cb, &counter, 1));
+  REQUIRE(counter == NUM_KEYS);
+}
diff --git a/testing/tests/memcached/regression/lp442914.cpp b/testing/tests/memcached/regression/lp442914.cpp
new file mode 100644 (file)
index 0000000..e833f31
--- /dev/null
@@ -0,0 +1,39 @@
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+#include <iomanip>
+
+/* Original Comment:
+ * The test case isn't obvious so I should probably document why
+ * it works the way it does. Bug 442914 was caused by a bug
+ * in the logic in memcached_purge (it did not handle the case
+ * where the number of bytes sent was equal to the watermark).
+ * In this test case, create messages so that we hit that case
+ * and then disable noreply mode and issue a new command to
+ * verify that it isn't stuck. If we change the format for the
+ * delete command or the watermarks, we need to update this
+ * test....
+ */
+
+TEST_CASE("memcached_regression_lp442914") {
+  MemcachedCluster test{Cluster{Server{MEMCACHED_BINARY, {"-p", random_port_string}}, 1}};
+  auto memc = &test.memc;
+
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1));
+
+
+  for (auto x = 0; x < 250; ++x) {
+    stringstream ss;
+    ss << setfill('0') << setw(250) << x;
+    REQUIRE_SUCCESS(memcached_delete(memc, ss.str().c_str(), ss.str().length(), 0));
+  }
+
+  stringstream key;
+  key << setfill('0') << setw(37) << 251;
+  REQUIRE_SUCCESS(memcached_delete(memc, key.str().c_str(), key.str().length(), 0));
+
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 0));
+
+  REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_delete(memc, key.str().c_str(), key.str().length(), 0));
+}
diff --git a/testing/tests/memcached/regression/lp447342.cpp b/testing/tests/memcached/regression/lp447342.cpp
new file mode 100644 (file)
index 0000000..1ff33fe
--- /dev/null
@@ -0,0 +1,100 @@
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+#include "libmemcached/instance.hpp"
+
+static memcached_return_t callback_counter(const memcached_st *, memcached_result_st *, void *context) {
+  auto *counter= reinterpret_cast<size_t *>(context);
+  *counter = *counter + 1;
+
+  return MEMCACHED_SUCCESS;
+}
+
+#define NUM_KEYS 100U
+
+TEST_CASE("memcached_regression_lp447342") {
+  auto test = MemcachedCluster::mixed();
+  auto memc = &test.memc;
+  
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 2));
+  
+  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_ascii_string(random_num(24, 32));
+    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));
+  }
+
+ /*
+ ** We are using the qiet command 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);
+ REQUIRE_SUCCESS(memcached_mget(memc, chr.data(), len.data(), NUM_KEYS));
+
+ size_t counter = 0;
+ memcached_execute_fn cb[] = {&callback_counter};
+ REQUIRE_SUCCESS(memcached_fetch_execute(memc, cb, &counter, 1));
+ REQUIRE(counter == NUM_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..
+  */
+  auto instance_one= memcached_server_instance_by_position(memc, 0);
+  auto instance_two= memcached_server_instance_by_position(memc, 2);
+  in_port_t port0= instance_one->port();
+  in_port_t port2= instance_two->port();
+
+  const_cast<memcached_instance_st*>(instance_one)->port(0);
+  const_cast<memcached_instance_st*>(instance_two)->port(0);
+
+  REQUIRE_SUCCESS(memcached_mget(memc, chr.data(), len.data(), NUM_KEYS));
+
+  counter = 0;
+  REQUIRE_SUCCESS(memcached_fetch_execute(memc, cb, &counter, 1));
+  REQUIRE(counter == NUM_KEYS);
+
+  /* restore the memc handle */
+  const_cast<memcached_instance_st*>(instance_one)->port(port0);
+  const_cast<memcached_instance_st*>(instance_two)->port(port2);
+
+  memcached_quit(memc);
+
+  for (auto i = 0U; i < NUM_KEYS; ++i) {
+    if (i & 1U) {
+      REQUIRE_SUCCESS(memcached_delete(memc, chr[i], len[i], 0));
+    }
+  }
+
+  memcached_quit(memc);
+
+  const_cast<memcached_instance_st*>(instance_one)->port(0);
+  const_cast<memcached_instance_st*>(instance_two)->port(0);
+
+  /* now retry the command, this time we should have cache misses */
+  REQUIRE_SUCCESS(memcached_mget(memc, chr.data(), len.data(), NUM_KEYS));
+
+  counter = 0;
+  REQUIRE_SUCCESS(memcached_fetch_execute(memc, cb, &counter, 1));
+  REQUIRE(counter == NUM_KEYS>>1U);
+}
diff --git a/testing/tests/memcached/regression/lp490486.cpp b/testing/tests/memcached/regression/lp490486.cpp
new file mode 100644 (file)
index 0000000..cc7dd9a
--- /dev/null
@@ -0,0 +1,52 @@
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+#define NUM_KEYS 20480U
+
+static memcached_return_t callback_counter(const memcached_st *, memcached_result_st *, void *context) {
+  auto *counter= reinterpret_cast<size_t *>(context);
+  *counter = *counter + 1;
+
+  return MEMCACHED_SUCCESS;
+}
+
+TEST_CASE("memcached_regression_lp490486") {
+  MemcachedCluster test{Cluster{Server{MEMCACHED_BINARY, {"-p", random_port_string}}, 1}};
+  auto memc = &test.memc;
+
+  test.enableBinaryProto();
+
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, 1000));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS, 1));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, 3600));
+
+  array<string, NUM_KEYS> str;
+  array<char *, NUM_KEYS> chr;
+  array<size_t, NUM_KEYS> len;
+
+  for (auto i = 0U; i < NUM_KEYS; ++i) {
+    char blob[1024];
+
+    str[i] = random_ascii_string(36);
+    chr[i] = str[i].data();
+    len[i] = str[i].length();
+
+    REQUIRE_SUCCESS(memcached_set(memc, chr[i], len[i], blob, sizeof(blob), 0, 0));
+  }
+
+  size_t counter = 0;
+  memcached_return_t rc;
+  memcached_execute_fn cb[] = {&callback_counter};
+  REQUIRE_SUCCESS(memcached_mget_execute(memc, chr.data(), len.data(), NUM_KEYS, cb, &counter, 1));
+
+  do {
+    char key_buf[MEMCACHED_MAX_KEY];
+    size_t key_len;
+
+    Malloced value(memcached_fetch(memc, key_buf, &key_len, nullptr, nullptr, &rc));
+    counter += !!*value;
+  } while(rc == MEMCACHED_SUCCESS);
+
+  REQUIRE_RC(MEMCACHED_END, rc);
+  REQUIRE(counter == NUM_KEYS);
+}
diff --git a/testing/tests/memcached/regression/lp996813.cpp b/testing/tests/memcached/regression/lp996813.cpp
new file mode 100644 (file)
index 0000000..540cb04
--- /dev/null
@@ -0,0 +1,27 @@
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+TEST_CASE("memcached_regression_lp996813") {
+  MemcachedPtr memc_ptr;
+  auto memc = *memc_ptr;
+  LoneReturnMatcher test{memc};
+
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, 1));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, 300));
+  REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, 30));
+
+  // We will never connect to these servers
+  in_port_t base_port = 11211;
+  for (size_t x = 0; x < 17; x++) {
+    REQUIRE_SUCCESS(memcached_server_add(memc, "10.2.3.4", base_port + x));
+  }
+
+  REQUIRE(6U == memcached_generate_hash(memc, S("SZ6hu0SHweFmpwpc0w2R")));
+  REQUIRE(1U == memcached_generate_hash(memc, S("SQCK9eiCf53YxHWnYA.o")));
+  REQUIRE(9U == memcached_generate_hash(memc, S("SUSDkGXuuZC9t9VhMwa.")));
+  REQUIRE(0U == memcached_generate_hash(memc, S("SnnqnJARfaCNT679iAF_")));
+}