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{};
}
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)));
}
+
MemcachedCluster &operator=(MemcachedCluster &&mc);
void enableBinaryProto(bool enable = true);
+ void enableBuffering(bool enable = true);
+ void enableReplication();
void flush();
static MemcachedCluster mixed();
--- /dev/null
+#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));
+
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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));
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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_")));
+}