Brian Aker, brian@tangent.org -- Client Library, Tools
Mark Atwood, -- Tools, Docs
Patrick Galbraith, -- C++ Interface
+Padraig O'Sullivan, -- C++ Interface (current one)
Tim Bunce, -- Docs
Trond Norbye, trond.norbye@sun.com -- Binary protocol, Misc
Yin Chen, -- Ketama Work
Toru Maesaka, dev@torum.net -- Stats analysis
+Eric Lambert, -- UDP work
+Monty Taylor, -- Build Releated (Pandora)
+0.34 Tue Oct 13 08:39:51 PDT 2009
+ * Added support for setting behavior flags on a connection pool.
+ * Don't increment server_failure_counter on normal disconnects.
+ * Added prototype for a callback based protocol parser (server side)
+ with examples so that you could let your own application speak the
+ memcached protocol
+ * Updated memcapable to test ASCII protocol.
+ * Changed behavior so that server can be removed at first sign of failure.
+ * Added memcached_server_get_last_disconnect() call
+
0.33 Wed Sep 23 10:11:58 PDT 2009
* Added memcapable to test servers for binary compatibility.
* Updated C++ interface. Added basic support for C++ exceptions. Added
#include <stdbool.h>
#include <unistd.h>
#include <poll.h>
+#include <ctype.h>
#include <libmemcached/memcached/protocol_binary.h>
#include <libmemcached/byteorder.h>
ret= read(fd, buf, len);
if (ret == -1 && errno == EWOULDBLOCK) {
- struct pollfd fds = {
+ struct pollfd fds= {
.events= direction,
.fd= fd
};
}
else if (err == 0)
{
- errno = ETIMEDOUT;
+ errno= ETIMEDOUT;
}
else
{
if (!val)
{
if (verbose)
- fprintf(stderr, "%s:%u: %s\n", file, line, expression);
+ fprintf(stderr, "\n%s:%u: %s", file, line, expression);
if (do_core)
abort();
cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SET;
execute(resend_packet(&cmd));
execute(recv_packet(&rsp));
- verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
PROTOCOL_BINARY_RESPONSE_SUCCESS));
cmd.set.message.header.request.opcode= PROTOCOL_BINARY_CMD_SETQ;
}
return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
}
-static enum test_return set_item(const char *key, const char *value)
+static enum test_return binary_set_item(const char *key, const char *value)
{
command cmd;
response rsp;
verify(validate_response_header(&rsp, cc, expected_result));
if (ii == 0)
- execute(set_item(key, key));
+ execute(binary_set_item(key, key));
}
else
execute(test_binary_noop());
execute(send_packet(&cmd));
execute(recv_packet(&rsp));
verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
- execute(set_item(key, key));
+ execute(binary_set_item(key, key));
/* The item should be present now, resend*/
execute(resend_packet(&cmd));
else
execute(test_binary_noop());
- execute(set_item(key, key));
+ execute(binary_set_item(key, key));
execute(resend_packet(&cmd));
execute(recv_packet(&rsp));
verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
for (int ii= 0; ii < 2; ++ii)
{
- execute(set_item(key, key));
+ execute(binary_set_item(key, key));
flush_command(&cmd, cc, 0, ii == 0);
execute(send_packet(&cmd));
else
value=" world";
- execute(set_item(key, value));
+ execute(binary_set_item(key, value));
if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
value=" world";
++cc;
}
+ return TEST_PASS_RECONNECT;
+}
+
+static enum test_return send_string(const char *cmd)
+{
+ execute(retry_write(cmd, strlen(cmd)));
+ return TEST_PASS;
+}
+
+static enum test_return receive_line(char *buffer, size_t size)
+{
+ size_t offset= 0;
+ while (offset < size)
+ {
+ execute(retry_read(buffer + offset, 1));
+ if (buffer[offset] == '\n')
+ {
+ if (offset + 1 < size)
+ {
+ buffer[offset + 1]= '\0';
+ return TEST_PASS;
+ }
+ else
+ return TEST_FAIL;
+ }
+ ++offset;
+ }
+
+ return TEST_FAIL;
+}
+
+static enum test_return receive_response(const char *msg) {
+ char buffer[80];
+ execute(receive_line(buffer, sizeof(buffer)));
+ verify(strcmp(msg, buffer) == 0);
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_quit(void)
+{
+ /* Verify that quit handles unknown options */
+ execute(send_string("quit foo bar\r\n"));
+ execute(receive_response("ERROR\r\n"));
+
+ /* quit doesn't support noreply */
+ execute(send_string("quit noreply\r\n"));
+ execute(receive_response("ERROR\r\n"));
+
+ /* Verify that quit works */
+ execute(send_string("quit\r\n"));
+
+ /* Socket should be closed now, read should return 0 */
+ char buffer[80];
+ verify(timeout_io_op(sock, POLLIN, buffer, sizeof(buffer)) == 0);
+ return TEST_PASS_RECONNECT;
+
+}
+
+static enum test_return test_ascii_version(void)
+{
+ /* Verify that version command handles unknown options */
+ execute(send_string("version foo bar\r\n"));
+ execute(receive_response("ERROR\r\n"));
+
+ /* version doesn't support noreply */
+ execute(send_string("version noreply\r\n"));
+ execute(receive_response("ERROR\r\n"));
+
+ /* Verify that verify works */
+ execute(send_string("version\r\n"));
+ char buffer[256];
+ execute(receive_line(buffer, sizeof(buffer)));
+ verify(strncmp(buffer, "VERSION ", 8) == 0);
+
return TEST_PASS;
}
+static enum test_return test_ascii_verbosity(void)
+{
+ /* This command does not adhere to the spec! */
+ execute(send_string("verbosity foo bar my\r\n"));
+ execute(receive_response("ERROR\r\n"));
+
+ execute(send_string("verbosity noreply\r\n"));
+ execute(test_ascii_version());
+
+ execute(send_string("verbosity 0 noreply\r\n"));
+ execute(test_ascii_version());
+
+ execute(send_string("verbosity\r\n"));
+ execute(receive_response("ERROR\r\n"));
+
+ execute(send_string("verbosity 1\r\n"));
+ execute(receive_response("OK\r\n"));
+
+ execute(send_string("verbosity 0\r\n"));
+ execute(receive_response("OK\r\n"));
+
+ return TEST_PASS;
+}
+
+
+
+static enum test_return test_ascii_set_impl(const char* key, bool noreply)
+{
+ /* @todo add tests for bogus format! */
+ char buffer[1024];
+ sprintf(buffer, "set %s 0 0 5%s\r\nvalue\r\n", key,
+ noreply ? " noreply" : "");
+ execute(send_string(buffer));
+
+ if (!noreply)
+ execute(receive_response("STORED\r\n"));
+
+ return test_ascii_version();
+}
+
+static enum test_return test_ascii_set(void)
+{
+ return test_ascii_set_impl("test_ascii_set", false);
+}
+
+static enum test_return test_ascii_set_noreply(void)
+{
+ return test_ascii_set_impl("test_ascii_set_noreply", true);
+}
+
+static enum test_return test_ascii_add_impl(const char* key, bool noreply)
+{
+ /* @todo add tests for bogus format! */
+ char buffer[1024];
+ sprintf(buffer, "add %s 0 0 5%s\r\nvalue\r\n", key,
+ noreply ? " noreply" : "");
+ execute(send_string(buffer));
+
+ if (!noreply)
+ execute(receive_response("STORED\r\n"));
+
+ execute(send_string(buffer));
+
+ if (!noreply)
+ execute(receive_response("NOT_STORED\r\n"));
+
+ return test_ascii_version();
+}
+
+static enum test_return test_ascii_add(void)
+{
+ return test_ascii_add_impl("test_ascii_add", false);
+}
+
+static enum test_return test_ascii_add_noreply(void)
+{
+ return test_ascii_add_impl("test_ascii_add_noreply", true);
+}
+
+static enum test_return ascii_get_value(const char *key, const char *value)
+{
+
+ char buffer[1024];
+ size_t datasize= strlen(value);
+
+ verify(datasize < sizeof(buffer));
+ execute(receive_line(buffer, sizeof(buffer)));
+ verify(strncmp(buffer, "VALUE ", 6) == 0);
+ verify(strncmp(buffer + 6, key, strlen(key)) == 0);
+ char *ptr= buffer + 6 + strlen(key) + 1;
+ char *end;
+
+ unsigned long val= strtoul(ptr, &end, 10); /* flags */
+ verify(ptr != end);
+ verify(val == 0);
+ verify(end != NULL);
+ val= strtoul(end, &end, 10); /* size */
+ verify(ptr != end);
+ verify(val == datasize);
+ verify(end != NULL);
+ while (*end != '\n' && isspace(*end))
+ ++end;
+ verify(*end == '\n');
+
+ execute(retry_read(buffer, datasize));
+ verify(memcmp(buffer, value, datasize) == 0);
+
+ execute(retry_read(buffer, 2));
+ verify(memcmp(buffer, "\r\n", 2) == 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return ascii_get_item(const char *key, const char *value,
+ bool exist)
+{
+ char buffer[1024];
+ size_t datasize= 0;
+ if (value != NULL)
+ datasize= strlen(value);
+
+ verify(datasize < sizeof(buffer));
+ sprintf(buffer, "get %s\r\n", key);
+ execute(send_string(buffer));
+
+ if (exist)
+ execute(ascii_get_value(key, value));
+
+ execute(retry_read(buffer, 5));
+ verify(memcmp(buffer, "END\r\n", 5) == 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return ascii_gets_value(const char *key, const char *value,
+ unsigned long *cas)
+{
+
+ char buffer[1024];
+ size_t datasize= strlen(value);
+
+ verify(datasize < sizeof(buffer));
+ execute(receive_line(buffer, sizeof(buffer)));
+ verify(strncmp(buffer, "VALUE ", 6) == 0);
+ verify(strncmp(buffer + 6, key, strlen(key)) == 0);
+ char *ptr= buffer + 6 + strlen(key) + 1;
+ char *end;
+
+ unsigned long val= strtoul(ptr, &end, 10); /* flags */
+ verify(ptr != end);
+ verify(val == 0);
+ verify(end != NULL);
+ val= strtoul(end, &end, 10); /* size */
+ verify(ptr != end);
+ verify(val == datasize);
+ verify(end != NULL);
+ *cas= strtoul(end, &end, 10); /* cas */
+ verify(ptr != end);
+ verify(val == datasize);
+ verify(end != NULL);
+
+ while (*end != '\n' && isspace(*end))
+ ++end;
+ verify(*end == '\n');
+
+ execute(retry_read(buffer, datasize));
+ verify(memcmp(buffer, value, datasize) == 0);
+
+ execute(retry_read(buffer, 2));
+ verify(memcmp(buffer, "\r\n", 2) == 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return ascii_gets_item(const char *key, const char *value,
+ bool exist, unsigned long *cas)
+{
+ char buffer[1024];
+ size_t datasize= 0;
+ if (value != NULL)
+ datasize= strlen(value);
+
+ verify(datasize < sizeof(buffer));
+ sprintf(buffer, "gets %s\r\n", key);
+ execute(send_string(buffer));
+
+ if (exist)
+ execute(ascii_gets_value(key, value, cas));
+
+ execute(retry_read(buffer, 5));
+ verify(memcmp(buffer, "END\r\n", 5) == 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return ascii_set_item(const char *key, const char *value)
+{
+ char buffer[300];
+ size_t len= strlen(value);
+ sprintf(buffer, "set %s 0 0 %u\r\n", key, (unsigned int)len);
+ execute(send_string(buffer));
+ execute(retry_write(value, len));
+ execute(send_string("\r\n"));
+ execute(receive_response("STORED\r\n"));
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_replace_impl(const char* key, bool noreply)
+{
+ char buffer[1024];
+ sprintf(buffer, "replace %s 0 0 5%s\r\nvalue\r\n", key,
+ noreply ? " noreply" : "");
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("NOT_STORED\r\n"));
+
+ execute(ascii_set_item(key, "value"));
+ execute(ascii_get_item(key, "value", true));
+
+
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("STORED\r\n"));
+
+ return test_ascii_version();
+}
+
+static enum test_return test_ascii_replace(void)
+{
+ return test_ascii_replace_impl("test_ascii_replace", false);
+}
+
+static enum test_return test_ascii_replace_noreply(void)
+{
+ return test_ascii_replace_impl("test_ascii_replace_noreply", true);
+}
+
+static enum test_return test_ascii_cas_impl(const char* key, bool noreply)
+{
+ char buffer[1024];
+ unsigned long cas;
+
+ execute(ascii_set_item(key, "value"));
+ execute(ascii_gets_item(key, "value", true, &cas));
+
+ sprintf(buffer, "cas %s 0 0 6 %lu%s\r\nvalue2\r\n", key, cas,
+ noreply ? " noreply" : "");
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("STORED\r\n"));
+
+ /* reexecute the same command should fail due to illegal cas */
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("EXISTS\r\n"));
+
+ return test_ascii_version();
+}
+
+static enum test_return test_ascii_cas(void)
+{
+ return test_ascii_cas_impl("test_ascii_cas", false);
+}
+
+static enum test_return test_ascii_cas_noreply(void)
+{
+ return test_ascii_cas_impl("test_ascii_cas_noreply", true);
+}
+
+static enum test_return test_ascii_delete_impl(const char *key, bool noreply)
+{
+ execute(ascii_set_item(key, "value"));
+
+ execute(send_string("delete\r\n"));
+ execute(receive_response("ERROR\r\n"));
+ /* BUG: the server accepts delete a b */
+ execute(send_string("delete a b c d e\r\n"));
+ execute(receive_response("ERROR\r\n"));
+
+ char buffer[1024];
+ sprintf(buffer, "delete %s%s\r\n", key, noreply ? " noreply" : "");
+ execute(send_string(buffer));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("DELETED\r\n"));
+
+ execute(ascii_get_item(key, "value", false));
+ execute(send_string(buffer));
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("NOT_FOUND\r\n"));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_delete(void)
+{
+ return test_ascii_delete_impl("test_ascii_delete", false);
+}
+
+static enum test_return test_ascii_delete_noreply(void)
+{
+ return test_ascii_delete_impl("test_ascii_delete_noreply", true);
+}
+
+static enum test_return test_ascii_get(void)
+{
+ execute(ascii_set_item("test_ascii_get", "value"));
+
+ execute(send_string("get\r\n"));
+ execute(receive_response("ERROR\r\n"));
+ execute(ascii_get_item("test_ascii_get", "value", true));
+ execute(ascii_get_item("test_ascii_get_notfound", "value", false));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_gets(void)
+{
+ execute(ascii_set_item("test_ascii_gets", "value"));
+
+ execute(send_string("gets\r\n"));
+ execute(receive_response("ERROR\r\n"));
+ unsigned long cas;
+ execute(ascii_gets_item("test_ascii_gets", "value", true, &cas));
+ execute(ascii_gets_item("test_ascii_gets_notfound", "value", false, &cas));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_mget(void)
+{
+ execute(ascii_set_item("test_ascii_mget1", "value"));
+ execute(ascii_set_item("test_ascii_mget2", "value"));
+ execute(ascii_set_item("test_ascii_mget3", "value"));
+ execute(ascii_set_item("test_ascii_mget4", "value"));
+ execute(ascii_set_item("test_ascii_mget5", "value"));
+
+ execute(send_string("get test_ascii_mget1 test_ascii_mget2 test_ascii_mget3 "
+ "test_ascii_mget4 test_ascii_mget5 "
+ "test_ascii_mget6\r\n"));
+ execute(ascii_get_value("test_ascii_mget1", "value"));
+ execute(ascii_get_value("test_ascii_mget2", "value"));
+ execute(ascii_get_value("test_ascii_mget3", "value"));
+ execute(ascii_get_value("test_ascii_mget4", "value"));
+ execute(ascii_get_value("test_ascii_mget5", "value"));
+
+ char buffer[5];
+ execute(retry_read(buffer, 5));
+ verify(memcmp(buffer, "END\r\n", 5) == 0);
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_incr_impl(const char* key, bool noreply)
+{
+ char cmd[300];
+ sprintf(cmd, "incr %s 1%s\r\n", key, noreply ? " noreply" : "");
+
+ execute(ascii_set_item(key, "0"));
+ for (int x= 1; x < 11; ++x)
+ {
+ execute(send_string(cmd));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ {
+ char buffer[80];
+ execute(receive_line(buffer, sizeof(buffer)));
+ int val= atoi(buffer);
+ verify(val == x);
+ }
+ }
+
+ execute(ascii_get_item(key, "10", true));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_incr(void)
+{
+ return test_ascii_incr_impl("test_ascii_incr", false);
+}
+
+static enum test_return test_ascii_incr_noreply(void)
+{
+ return test_ascii_incr_impl("test_ascii_incr_noreply", true);
+}
+
+static enum test_return test_ascii_decr_impl(const char* key, bool noreply)
+{
+ char cmd[300];
+ sprintf(cmd, "decr %s 1%s\r\n", key, noreply ? " noreply" : "");
+
+ execute(ascii_set_item(key, "9"));
+ for (int x= 8; x > -1; --x)
+ {
+ execute(send_string(cmd));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ {
+ char buffer[80];
+ execute(receive_line(buffer, sizeof(buffer)));
+ int val= atoi(buffer);
+ verify(val == x);
+ }
+ }
+
+ execute(ascii_get_item(key, "0", true));
+
+ /* verify that it doesn't wrap */
+ execute(send_string(cmd));
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ {
+ char buffer[80];
+ execute(receive_line(buffer, sizeof(buffer)));
+ }
+ execute(ascii_get_item(key, "0", true));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_decr(void)
+{
+ return test_ascii_decr_impl("test_ascii_decr", false);
+}
+
+static enum test_return test_ascii_decr_noreply(void)
+{
+ return test_ascii_decr_impl("test_ascii_decr_noreply", true);
+}
+
+
+static enum test_return test_ascii_flush_impl(const char *key, bool noreply)
+{
+#if 0
+ /* Verify that the flush_all command handles unknown options */
+ /* Bug in the current memcached server! */
+ execute(send_string("flush_all foo bar\r\n"));
+ execute(receive_response("ERROR\r\n"));
+#endif
+
+ execute(ascii_set_item(key, key));
+ execute(ascii_get_item(key, key, true));
+
+ if (noreply)
+ {
+ execute(send_string("flush_all noreply\r\n"));
+ execute(test_ascii_version());
+ }
+ else
+ {
+ execute(send_string("flush_all\r\n"));
+ execute(receive_response("OK\r\n"));
+ }
+
+ execute(ascii_get_item(key, key, false));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_flush(void)
+{
+ return test_ascii_flush_impl("test_ascii_flush", false);
+}
+
+static enum test_return test_ascii_flush_noreply(void)
+{
+ return test_ascii_flush_impl("test_ascii_flush_noreply", true);
+}
+
+static enum test_return test_ascii_concat_impl(const char *key,
+ bool append,
+ bool noreply)
+{
+ const char *value;
+
+ if (append)
+ value="hello";
+ else
+ value=" world";
+
+ execute(ascii_set_item(key, value));
+
+ if (append)
+ value=" world";
+ else
+ value="hello";
+
+ char cmd[400];
+ sprintf(cmd, "%s %s 0 0 %u%s\r\n%s\r\n",
+ append ? "append" : "prepend",
+ key, (unsigned int)strlen(value), noreply ? " noreply" : "",
+ value);
+ execute(send_string(cmd));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("STORED\r\n"));
+
+ execute(ascii_get_item(key, "hello world", true));
+
+ sprintf(cmd, "%s %s_notfound 0 0 %u%s\r\n%s\r\n",
+ append ? "append" : "prepend",
+ key, (unsigned int)strlen(value), noreply ? " noreply" : "",
+ value);
+ execute(send_string(cmd));
+
+ if (noreply)
+ execute(test_ascii_version());
+ else
+ execute(receive_response("NOT_STORED\r\n"));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_ascii_append(void)
+{
+ return test_ascii_concat_impl("test_ascii_append", true, false);
+}
+
+static enum test_return test_ascii_prepend(void)
+{
+ return test_ascii_concat_impl("test_ascii_prepend", false, false);
+}
+
+static enum test_return test_ascii_append_noreply(void)
+{
+ return test_ascii_concat_impl("test_ascii_append_noreply", true, true);
+}
+
+static enum test_return test_ascii_prepend_noreply(void)
+{
+ return test_ascii_concat_impl("test_ascii_prepend_noreply", false, true);
+}
+
+static enum test_return test_ascii_stat(void)
+{
+ execute(send_string("stats noreply\r\n"));
+ execute(receive_response("ERROR\r\n"));
+ execute(send_string("stats\r\n"));
+ char buffer[1024];
+ do {
+ execute(receive_line(buffer, sizeof(buffer)));
+ } while (strcmp(buffer, "END\r\n") != 0);
+
+ return TEST_PASS_RECONNECT;
+}
+
typedef enum test_return(*TEST_FUNC)(void);
struct testcase
};
struct testcase testcases[]= {
- { "noop", test_binary_noop},
- { "quit", test_binary_quit},
- { "quitq", test_binary_quitq},
- { "set", test_binary_set},
- { "setq", test_binary_setq},
- { "flush", test_binary_flush},
- { "flushq", test_binary_flushq},
- { "add", test_binary_add},
- { "addq", test_binary_addq},
- { "replace", test_binary_replace},
- { "replaceq", test_binary_replaceq},
- { "delete", test_binary_delete},
- { "deleteq", test_binary_deleteq},
- { "get", test_binary_get},
- { "getq", test_binary_getq},
- { "getk", test_binary_getk},
- { "getkq", test_binary_getkq},
- { "incr", test_binary_incr},
- { "incrq", test_binary_incrq},
- { "decr", test_binary_decr},
- { "decrq", test_binary_decrq},
- { "version", test_binary_version},
- { "append", test_binary_append},
- { "appendq", test_binary_appendq},
- { "prepend", test_binary_prepend},
- { "prependq", test_binary_prependq},
- { "stat", test_binary_stat},
- { "illegal", test_binary_illegal},
+ { "ascii quit", test_ascii_quit },
+ { "ascii version", test_ascii_version },
+ { "ascii verbosity", test_ascii_verbosity },
+ { "ascii set", test_ascii_set },
+ { "ascii set noreply", test_ascii_set_noreply },
+ { "ascii get", test_ascii_get },
+ { "ascii gets", test_ascii_gets },
+ { "ascii mget", test_ascii_mget },
+ { "ascii flush", test_ascii_flush },
+ { "ascii flush noreply", test_ascii_flush_noreply },
+ { "ascii add", test_ascii_add },
+ { "ascii add noreply", test_ascii_add_noreply },
+ { "ascii replace", test_ascii_replace },
+ { "ascii replace noreply", test_ascii_replace_noreply },
+ { "ascii cas", test_ascii_cas },
+ { "ascii cas noreply", test_ascii_cas_noreply },
+ { "ascii delete", test_ascii_delete },
+ { "ascii delete noreply", test_ascii_delete_noreply },
+ { "ascii incr", test_ascii_incr },
+ { "ascii incr noreply", test_ascii_incr_noreply },
+ { "ascii decr", test_ascii_decr },
+ { "ascii decr noreply", test_ascii_decr_noreply },
+ { "ascii append", test_ascii_append },
+ { "ascii append noreply", test_ascii_append_noreply },
+ { "ascii prepend", test_ascii_prepend },
+ { "ascii prepend noreply", test_ascii_prepend_noreply },
+ { "ascii stat", test_ascii_stat },
+ { "binary noop", test_binary_noop },
+ { "binary quit", test_binary_quit },
+ { "binary quitq", test_binary_quitq },
+ { "binary set", test_binary_set },
+ { "binary setq", test_binary_setq },
+ { "binary flush", test_binary_flush },
+ { "binary flushq", test_binary_flushq },
+ { "binary add", test_binary_add },
+ { "binary addq", test_binary_addq },
+ { "binary replace", test_binary_replace },
+ { "binary replaceq", test_binary_replaceq },
+ { "binary delete", test_binary_delete },
+ { "binary deleteq", test_binary_deleteq },
+ { "binary get", test_binary_get },
+ { "binary getq", test_binary_getq },
+ { "binary getk", test_binary_getk },
+ { "binary getkq", test_binary_getkq },
+ { "binary incr", test_binary_incr },
+ { "binary incrq", test_binary_incrq },
+ { "binary decr", test_binary_decr },
+ { "binary decrq", test_binary_decrq },
+ { "binary version", test_binary_version },
+ { "binary append", test_binary_append },
+ { "binary appendq", test_binary_appendq },
+ { "binary prepend", test_binary_prepend },
+ { "binary prependq", test_binary_prependq },
+ { "binary stat", test_binary_stat },
+ { "binary illegal", test_binary_illegal },
{ NULL, NULL}
};
for (int ii= 0; testcases[ii].description != NULL; ++ii)
{
++total;
- fprintf(stdout, "%s\t\t", testcases[ii].description);
+ fprintf(stdout, "%-40s", testcases[ii].description);
fflush(stdout);
bool reconnect= false;
enum test_return ret= testcases[ii].function();
- fprintf(stderr, "%s\n", status_msg[ret]);
if (ret == TEST_FAIL)
{
reconnect= true;
++failed;
+ if (verbose)
+ fprintf(stderr, "\n");
}
else if (ret == TEST_PASS_RECONNECT)
reconnect= true;
+ fprintf(stderr, "%s\n", status_msg[ret]);
if (reconnect)
{
(void) close(sock);
# the COPYING file in this directory for full text.
AC_PREREQ(2.59)
-AC_INIT([libmemcached],[0.33],[http://tangent.org/552/libmemcached.html])
+AC_INIT([libmemcached],[0.34],[http://tangent.org/552/libmemcached.html])
AC_CONFIG_SRCDIR([libmemcached/memcached.c])
AC_CONFIG_AUX_DIR(config)
AM_CONFIG_HEADER([config.h])
memcached_increment_with_initial.3\
memcached_mget.3\
memcached_mget_by_key.3\
+ memcached_mget_execute.3 \
+ memcached_mget_execute_by_key.3 \
memcached_prepend.3\
memcached_prepend_by_key.3\
memcached_replace.3\
memcached_mget_by_key.3: memcached_get.pod
${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_get.pod > memcached_mget_by_key.3
+memcached_mget_execute.3: memcached_get.pod
+ ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_get.pod > memcached_mget_execute.3
+
+memcached_mget_execute_by_key.3: memcached_get.pod
+ ${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_get.pod > memcached_mget_execute_by_key.3
+
memcached_fetch.3: memcached_get.pod
${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_get.pod > memcached_fetch.3
=head1 NAME
-memcached_get, memcached_mget, memcached_fetch - Get a value
+memcached_get, memcached_mget, memcached_fetch, memcached_mget_execute,
+memcached_mget_execute_by_key - Get a value
=head1 LIBRARY
memcached_return
memcached_mget (memcached_st *ptr,
- char **keys, size_t *key_length,
+ const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys);
char *
memcached_get_by_key(memcached_st *ptr,
memcached_return
memcached_mget_by_key(memcached_st *ptr,
const char *master_key, size_t master_key_length,
- char **keys, size_t *key_length,
+ const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys);
char *memcached_fetch (memcached_st *ptr,
size_t *value_length,
uint32_t *flags,
memcached_return *error);
+
memcached_return
memcached_fetch_execute(memcached_st *ptr,
memcached_return (*callback[])(memcached_st *ptr, memcached_result_st *result, void *context),
void *context,
unsigned int number_of_callbacks);
+
+ memcached_return
+ memcached_mget_execute(memcached_st *ptr,
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ memcached_execute_function *callback,
+ void *context,
+ unsigned int number_of_callbacks);
+
+ memcached_return
+ memcached_mget_execute_by_key(memcached_st *ptr,
+ const char *master_key,
+ size_t master_key_length,
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ memcached_execute_function *callback,
+ void *context,
+ unsigned int number_of_callbacks);
+
+
=head1 DESCRIPTION
memcached_get() is used to fetch an individual value from the server. You
to each function call. In the future there will be an option to allow this
to be an array.
+memcached_mget_execute() and memcached_mget_execute_by_key() is
+similar to memcached_mget(), but it may trigger the supplied callbacks
+with result sets while sending out the queries. If you try to perform
+a really large multiget with memcached_mget() you may encounter a
+deadlock in the OS kernel (we fail to write data to the socket because
+the input buffer is full). memcached_mget_execute() solves this
+problem by processing some of the results before continuing sending
+out requests. Please note that this function is only available in the
+binary protocol.
+
memcached_get_by_key() and memcached_mget_by_key() behave in a similar nature
as memcached_get() and memcached_mget(). The difference is that they take
a master key that is used for determining which server an object was stored
=head1 NAME
-memcached_server_count, memcached_server_list, memcached_server_add, memcached_server_push - Manage server list
+memcached_server_count, memcached_server_list, memcached_server_add, memcached_server_push, memcached_server_get_last_disconnect - Manage server list
=head1 LIBRARY
memcached_server_by_key (memcached_st *ptr,
const char *key, size_t key_length,
memcached_return *error);
+ memcached_server_st *
+ memcached_server_get_last_disconnect (memcached_st *ptr)
+
=head1 DESCRIPTION
should consult *error. The returning structure should be freed with
memcached_server_free().
+memcached_server_get_last_disconnect() returns a pointer to the last server
+for which there was a connection problem. It does not mean this particular
+server is currently dead but if the library is reporting a server is,
+the returned server is a very good candidate.
+
=head1 RETURN
Varies, see particular functions.
#endif
};
-void initialize_iterface_v0_handler(void)
+void initialize_interface_v0_handler(void)
{
interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GET]= get_command_handler;
interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_SET]= set_command_handler;
* We need to initialize the handlers manually due to a bug in the
* warnings generated by struct initialization in gcc (all the way up to 4.4)
*/
- initialize_iterface_v0_handler();
+ initialize_interface_v0_handler();
while ((cmd= getopt(argc, argv, "v1p:?")) != EOF)
{
#ifndef MEMCACHED_LIGHT_H
#define MEMCACHED_LIGHT_H
-extern void initialize_iterface_v0_handler(void);
+extern void initialize_interface_v0_handler(void);
#endif
void server_list_free(memcached_st *ptr, memcached_server_st *servers);
LIBMEMCACHED_LOCAL
-memcached_return memcached_key_test(const char **keys, size_t *key_length,
+memcached_return memcached_key_test(const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys);
#endif
#define MEMCACHED_VERSION_STRING_LENGTH 24
-#define LIBMEMCACHED_VERSION_STRING "0.33"
+#define LIBMEMCACHED_VERSION_STRING "0.34"
struct memcached_analysis_st {
uint32_t average_item_size;
uint8_t hash;
uint32_t continuum_points_counter;
memcached_server_st *hosts;
+ memcached_server_st *last_disconnected_server;
int32_t snd_timeout;
int32_t rcv_timeout;
uint32_t server_failure_limit;
memcached_trigger_delete_key delete_trigger;
char prefix_key[MEMCACHED_PREFIX_KEY_MAX_SIZE];
uint32_t number_of_replicas;
+ memcached_callback_st *callbacks;
};
LIBMEMCACHED_API
memcached_return memcached_delete(memcached_st *ptr, const char *key, size_t key_length,
time_t expiration);
LIBMEMCACHED_API
-memcached_return memcached_increment(memcached_st *ptr,
+memcached_return memcached_increment(memcached_st *ptr,
const char *key, size_t key_length,
uint32_t offset,
uint64_t *value);
LIBMEMCACHED_API
-memcached_return memcached_decrement(memcached_st *ptr,
+memcached_return memcached_decrement(memcached_st *ptr,
const char *key, size_t key_length,
uint32_t offset,
uint64_t *value);
LIBMEMCACHED_API
memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_return *error);
LIBMEMCACHED_API
-memcached_return memcached_stat_servername(memcached_stat_st *memc_stat, char *args,
+memcached_return memcached_stat_servername(memcached_stat_st *memc_stat, char *args,
char *hostname, unsigned int port);
LIBMEMCACHED_API
memcached_return memcached_flush(memcached_st *ptr, time_t expiration);
/* Server Public functions */
LIBMEMCACHED_API
-memcached_return memcached_server_add_udp(memcached_st *ptr,
+memcached_return memcached_server_add_udp(memcached_st *ptr,
const char *hostname,
unsigned int port);
LIBMEMCACHED_API
-memcached_return memcached_server_add_unix_socket(memcached_st *ptr,
+memcached_return memcached_server_add_unix_socket(memcached_st *ptr,
const char *filename);
LIBMEMCACHED_API
-memcached_return memcached_server_add(memcached_st *ptr, const char *hostname,
+memcached_return memcached_server_add(memcached_st *ptr, const char *hostname,
unsigned int port);
LIBMEMCACHED_API
-memcached_return memcached_server_add_udp_with_weight(memcached_st *ptr,
+memcached_return memcached_server_add_udp_with_weight(memcached_st *ptr,
const char *hostname,
unsigned int port,
uint32_t weight);
LIBMEMCACHED_API
-memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr,
+memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr,
const char *filename,
uint32_t weight);
LIBMEMCACHED_API
-memcached_return memcached_server_add_with_weight(memcached_st *ptr, const char *hostname,
+memcached_return memcached_server_add_with_weight(memcached_st *ptr, const char *hostname,
unsigned int port,
uint32_t weight);
LIBMEMCACHED_API
memcached_return memcached_server_push(memcached_st *ptr, memcached_server_st *list);
LIBMEMCACHED_API
-memcached_server_st *memcached_server_list_append(memcached_server_st *ptr,
- const char *hostname,
- unsigned int port,
+memcached_server_st *memcached_server_list_append(memcached_server_st *ptr,
+ const char *hostname,
+ unsigned int port,
memcached_return *error);
LIBMEMCACHED_API
-memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr,
- const char *hostname,
- unsigned int port,
+memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr,
+ const char *hostname,
+ unsigned int port,
uint32_t weight,
memcached_return *error);
LIBMEMCACHED_API
memcached_server_st *memcached_servers_parse(const char *server_strings);
LIBMEMCACHED_API
-char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *memc_stat,
+char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *memc_stat,
const char *key, memcached_return *error);
LIBMEMCACHED_API
-char ** memcached_stat_get_keys(memcached_st *ptr, memcached_stat_st *memc_stat,
+char ** memcached_stat_get_keys(memcached_st *ptr, memcached_stat_st *memc_stat,
memcached_return *error);
LIBMEMCACHED_API
-memcached_return memcached_delete_by_key(memcached_st *ptr,
+memcached_return memcached_delete_by_key(memcached_st *ptr,
const char *master_key, size_t master_key_length,
const char *key, size_t key_length,
time_t expiration);
LIBMEMCACHED_API
-memcached_return memcached_fetch_execute(memcached_st *ptr,
+memcached_return memcached_fetch_execute(memcached_st *ptr,
memcached_execute_function *callback,
void *context,
unsigned int number_of_callbacks);
LIBMEMCACHED_API
-memcached_return memcached_callback_set(memcached_st *ptr,
- memcached_callback flag,
+memcached_return memcached_callback_set(memcached_st *ptr,
+ memcached_callback flag,
void *data);
LIBMEMCACHED_API
-void *memcached_callback_get(memcached_st *ptr,
+void *memcached_callback_get(memcached_st *ptr,
memcached_callback flag,
memcached_return *error);
waittime.tv_sec= 0;
waittime.tv_usec= ptr->root->snd_timeout;
- error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO,
+ error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO,
&waittime, (socklen_t)sizeof(struct timeval));
WATCHPOINT_ASSERT(error == 0);
}
waittime.tv_sec= 0;
waittime.tv_usec= ptr->root->rcv_timeout;
- error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO,
+ error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO,
&waittime, (socklen_t)sizeof(struct timeval));
WATCHPOINT_ASSERT(error == 0);
}
int error;
struct linger linger;
- linger.l_onoff= 1;
- linger.l_linger= 0; /* By default on close() just drop the socket */
- error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER,
+ linger.l_onoff= 1;
+ linger.l_linger= 0; /* By default on close() just drop the socket */
+ error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER,
&linger, (socklen_t)sizeof(struct linger));
WATCHPOINT_ASSERT(error == 0);
}
int flag= 1;
int error;
- error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY,
+ error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY,
&flag, (socklen_t)sizeof(int));
WATCHPOINT_ASSERT(error == 0);
}
{
int error;
- error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF,
+ error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF,
&ptr->root->send_size, (socklen_t)sizeof(int));
WATCHPOINT_ASSERT(error == 0);
}
{
int error;
- error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF,
+ error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF,
&ptr->root->recv_size, (socklen_t)sizeof(int));
WATCHPOINT_ASSERT(error == 0);
}
- /* For the moment, not getting a nonblocking mode will not be fatal */
- if ((ptr->root->flags & MEM_NO_BLOCK) || ptr->root->connect_timeout)
- {
- int flags;
+ /* libmemcached will always use nonblocking IO to avoid write deadlocks */
+ int flags;
+ do
flags= fcntl(ptr->fd, F_GETFL, 0);
- unlikely (flags != -1)
- {
- (void)fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
- }
+ while (flags == -1 && (errno == EINTR || errno == EAGAIN));
+
+ unlikely (flags == -1)
+ return MEMCACHED_CONNECTION_FAILURE;
+ else if ((flags & O_NONBLOCK) == 0)
+ {
+ int rval;
+
+ do
+ rval= fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
+ while (rval == -1 && (errno == EINTR || errno == EAGAIN));
+
+ unlikely (rval == -1)
+ return MEMCACHED_CONNECTION_FAILURE;
}
return MEMCACHED_SUCCESS;
addrlen= (socklen_t) (strlen(servAddr.sun_path) + sizeof(servAddr.sun_family));
test_connect:
- if (connect(ptr->fd,
+ if (connect(ptr->fd,
(struct sockaddr *)&servAddr,
sizeof(servAddr)) < 0)
{
continue;
}
- if ((ptr->fd= socket(use->ai_family,
- use->ai_socktype,
+ if ((ptr->fd= socket(use->ai_family,
+ use->ai_socktype,
use->ai_protocol)) < 0)
{
ptr->cached_errno= errno;
(void)set_socket_options(ptr);
- int flags= 0;
- if (ptr->root->connect_timeout)
- {
- flags= fcntl(ptr->fd, F_GETFL, 0);
- if (flags != -1 && !(flags & O_NONBLOCK))
- (void)fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
- }
-
/* connect to server */
- while (ptr->fd != -1 &&
+ while (ptr->fd != -1 &&
connect(ptr->fd, use->ai_addr, use->ai_addrlen) < 0)
{
ptr->cached_errno= errno;
(void)close(ptr->fd);
ptr->fd= -1;
}
- }
+ }
else if (errno == EISCONN) /* we are connected :-) */
{
break;
- }
+ }
else if (errno != EINTR)
{
(void)close(ptr->fd);
ptr->fd= -1;
break;
- }
+ }
}
if (ptr->fd != -1)
{
- /* restore flags */
- if (ptr->root->connect_timeout && (ptr->root->flags & MEM_NO_BLOCK) == 0)
- (void)fcntl(ptr->fd, F_SETFL, flags & ~O_NONBLOCK);
-
WATCHPOINT_ASSERT(ptr->cursor_active == 0);
ptr->server_failure_counter= 0;
return MEMCACHED_SUCCESS;
if (gettimeofday(&next_time, NULL) == 0)
ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
}
- ptr->server_failure_counter+= 1;
+ ptr->server_failure_counter++;
if (ptr->cached_errno == 0)
return MEMCACHED_TIMEOUT;
+
return MEMCACHED_ERRNO; /* The last error should be from connect() */
}
WATCHPOINT_ASSERT(ptr->root);
if (ptr->root->retry_timeout && ptr->root->server_failure_limit)
{
- struct timeval next_time;
+ struct timeval curr_time;
- gettimeofday(&next_time, NULL);
+ gettimeofday(&curr_time, NULL);
/* if we've had too many consecutive errors on this server, mark it dead. */
- if (ptr->server_failure_counter > ptr->root->server_failure_limit)
+ if (ptr->server_failure_counter >= ptr->root->server_failure_limit)
{
- ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
+ ptr->next_retry= curr_time.tv_sec + ptr->root->retry_timeout;
ptr->server_failure_counter= 0;
}
- if (next_time.tv_sec < ptr->next_retry)
+ if (curr_time.tv_sec < ptr->next_retry)
{
if (memcached_behavior_get(ptr->root, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS))
run_distribution(ptr->root);
+ ptr->root->last_disconnected_server = ptr;
return MEMCACHED_SERVER_MARKED_DEAD;
}
}
WATCHPOINT_ASSERT(0);
}
+ unlikely ( rc != MEMCACHED_SUCCESS) ptr->root->last_disconnected_server = ptr;
+
LIBMEMCACHED_MEMCACHED_CONNECT_END();
return rc;
return MEMCACHED_NO_SERVERS;
server_key= memcached_generate_hash(ptr, master_key, master_key_length);
- to_write= (uint8_t) (ptr->flags & MEM_BUFFER_REQUESTS) ? 0 : 1;
+ to_write= (uint8_t)((ptr->flags & MEM_BUFFER_REQUESTS) ? 0 : 1);
bool no_reply= (ptr->flags & MEM_NOREPLY);
if (ptr->flags & MEM_BINARY_PROTOCOL)
return result;
else if (*error == MEMCACHED_END)
memcached_server_response_reset(server);
- else
+ else if (*error != MEMCACHED_NOTFOUND)
break;
}
}
static memcached_return memcached_mget_by_key_real(memcached_st *ptr,
- const char *master_key,
- size_t master_key_length,
- const char **keys,
- size_t *key_length,
- size_t number_of_keys,
- bool mget_mode);
+ const char *master_key,
+ size_t master_key_length,
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ bool mget_mode);
char *memcached_get_by_key(memcached_st *ptr,
const char *master_key,
}
/* Request the key */
- *error= memcached_mget_by_key_real(ptr,
- master_key,
- master_key_length,
- (const char **)&key, &key_length, 1, false);
+ *error= memcached_mget_by_key_real(ptr, master_key, master_key_length,
+ (const char * const *)&key,
+ &key_length, 1, false);
value= memcached_fetch(ptr, NULL, NULL,
value_length, flags, error);
}
memcached_return memcached_mget(memcached_st *ptr,
- const char **keys, size_t *key_length,
+ const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys)
{
return memcached_mget_by_key(ptr, NULL, 0, keys, key_length, number_of_keys);
static memcached_return binary_mget_by_key(memcached_st *ptr,
unsigned int master_server_key,
bool is_master_key_set,
- const char **keys, size_t *key_length,
+ const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys,
bool mget_mode);
static memcached_return memcached_mget_by_key_real(memcached_st *ptr,
- const char *master_key,
- size_t master_key_length,
- const char **keys,
- size_t *key_length,
- size_t number_of_keys,
- bool mget_mode)
+ const char *master_key,
+ size_t master_key_length,
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ bool mget_mode)
{
unsigned int x;
memcached_return rc= MEMCACHED_NOTFOUND;
if (master_key && master_key_length)
{
- if ((ptr->flags & MEM_VERIFY_KEY) && (memcached_key_test((const char **)&master_key, &master_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED))
+ if ((ptr->flags & MEM_VERIFY_KEY) && (memcached_key_test((const char * const *)&master_key, &master_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED))
return MEMCACHED_BAD_KEY_PROVIDED;
master_server_key= memcached_generate_hash(ptr, master_key, master_key_length);
is_master_key_set= true;
memcached_return memcached_mget_by_key(memcached_st *ptr,
const char *master_key,
size_t master_key_length,
- const char **keys,
- size_t *key_length,
+ const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys)
{
return memcached_mget_by_key_real(ptr, master_key, master_key_length, keys,
key_length, number_of_keys, true);
}
+memcached_return memcached_mget_execute(memcached_st *ptr,
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ memcached_execute_function *callback,
+ void *context,
+ unsigned int number_of_callbacks)
+{
+ return memcached_mget_execute_by_key(ptr, NULL, 0, keys, key_length,
+ number_of_keys, callback,
+ context, number_of_callbacks);
+}
+
+memcached_return memcached_mget_execute_by_key(memcached_st *ptr,
+ const char *master_key,
+ size_t master_key_length,
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ memcached_execute_function *callback,
+ void *context,
+ unsigned int number_of_callbacks)
+{
+ if ((ptr->flags & MEM_BINARY_PROTOCOL) == 0)
+ return MEMCACHED_NOT_SUPPORTED;
+
+ memcached_return rc;
+ memcached_callback_st *original_callbacks= ptr->callbacks;
+ memcached_callback_st cb= {
+ .callback= callback,
+ .context= context,
+ .number_of_callback= number_of_callbacks
+ };
+
+ ptr->callbacks= &cb;
+ rc= memcached_mget_by_key(ptr, master_key, master_key_length, keys,
+ key_length, number_of_keys);
+ ptr->callbacks= original_callbacks;
+ return rc;
+}
+
static memcached_return simple_binary_mget(memcached_st *ptr,
unsigned int master_server_key,
bool is_master_key_set,
- const char **keys, size_t *key_length,
+ const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys, bool mget_mode)
{
memcached_return rc= MEMCACHED_NOTFOUND;
rc= MEMCACHED_SOME_ERRORS;
continue;
}
+
+ /* We just want one pending response per server */
+ memcached_server_response_reset(&ptr->hosts[server_key]);
memcached_server_response_increment(&ptr->hosts[server_key]);
if ((x > 0 && x == ptr->io_key_prefetch) &&
memcached_flush_buffers(ptr) != MEMCACHED_SUCCESS)
memcached_io_reset(&ptr->hosts[x]);
rc= MEMCACHED_SOME_ERRORS;
}
- memcached_server_response_increment(&ptr->hosts[x]);
}
}
}
static memcached_return replication_binary_mget(memcached_st *ptr,
- uint32_t* hash, bool* dead_servers,
- const char **keys, size_t *key_length,
- size_t number_of_keys, bool mget_mode)
+ uint32_t* hash,
+ bool* dead_servers,
+ const char *const *keys,
+ const size_t *key_length,
+ size_t number_of_keys)
{
memcached_return rc= MEMCACHED_NOTFOUND;
uint32_t x;
- int flush= number_of_keys == 1;
-
for (uint32_t replica= 0; replica <= ptr->number_of_replicas; ++replica)
{
bool success= true;
-
+
for (x= 0; x < number_of_keys; ++x)
{
if (hash[x] == ptr->number_of_hosts)
}
}
- protocol_binary_request_getk request= {.bytes= {0}};
- request.message.header.request.magic= PROTOCOL_BINARY_REQ;
- if (mget_mode)
- request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ;
- else
- request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
-
- request.message.header.request.keylen= htons((uint16_t)key_length[x]);
- request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
- request.message.header.request.bodylen= htonl((uint32_t) key_length[x]);
+ protocol_binary_request_getk request= {
+ .message.header.request= {
+ .magic= PROTOCOL_BINARY_REQ,
+ .opcode= PROTOCOL_BINARY_CMD_GETK,
+ .keylen= htons((uint16_t)key_length[x]),
+ .datatype= PROTOCOL_BINARY_RAW_BYTES,
+ .bodylen= htonl((uint32_t)key_length[x])
+ }
+ };
+ /*
+ * We need to disable buffering to actually know that the request was
+ * successfully sent to the server (so that we should expect a result
+ * back). It would be nice to do this in buffered mode, but then it
+ * would be complex to handle all error situations if we got to send
+ * some of the messages, and then we failed on writing out some others
+ * and we used the callback interface from memcached_mget_execute so
+ * that we might have processed some of the responses etc. For now,
+ * just make sure we work _correctly_
+ */
if ((memcached_io_write(&ptr->hosts[server], request.bytes,
sizeof(request.bytes), 0) == -1) ||
(memcached_io_write(&ptr->hosts[server], keys[x],
- key_length[x], (char) flush) == -1))
+ key_length[x], 1) == -1))
{
memcached_io_reset(&ptr->hosts[server]);
dead_servers[server]= true;
success= false;
continue;
}
- memcached_server_response_increment(&ptr->hosts[server]);
- }
- if (mget_mode)
- {
- /*
- * Send a noop command to flush the buffers
- */
- protocol_binary_request_noop request= {.bytes= {0}};
- request.message.header.request.magic= PROTOCOL_BINARY_REQ;
- request.message.header.request.opcode= PROTOCOL_BINARY_CMD_NOOP;
- request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
-
- for (x= 0; x < ptr->number_of_hosts; x++)
- if (memcached_server_response_count(&ptr->hosts[x]))
- {
- if (memcached_io_write(&ptr->hosts[x], request.bytes,
- sizeof(request.bytes), 1) == -1)
- {
- memcached_io_reset(&ptr->hosts[x]);
- dead_servers[x]= true;
- success= false;
- }
- memcached_server_response_increment(&ptr->hosts[x]);
-
- /* mark all of the messages bound for this server as sent! */
- for (x= 0; x < number_of_keys; ++x)
- if (hash[x] == x)
- hash[x]= ptr->number_of_hosts;
- }
+ memcached_server_response_increment(&ptr->hosts[server]);
+ hash[x]= ptr->number_of_hosts;
}
if (success)
static memcached_return binary_mget_by_key(memcached_st *ptr,
unsigned int master_server_key,
bool is_master_key_set,
- const char **keys, size_t *key_length,
- size_t number_of_keys, bool mget_mode)
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ bool mget_mode)
{
memcached_return rc;
hash[x]= memcached_generate_hash(ptr, keys[x], key_length[x]);
rc= replication_binary_mget(ptr, hash, dead_servers, keys,
- key_length, number_of_keys, mget_mode);
+ key_length, number_of_keys);
ptr->call_free(ptr, hash);
ptr->call_free(ptr, dead_servers);
* Author: Brian Aker
*/
-#ifndef __MEMCACHED_GET_H__
-#define __MEMCACHED_GET_H__
+#ifndef LIBMEMCACHED_MEMCACHED_GET_H
+#define LIBMEMCACHED_MEMCACHED_GET_H
#ifdef __cplusplus
extern "C" {
LIBMEMCACHED_API
memcached_return memcached_mget(memcached_st *ptr,
- const char **keys, size_t *key_length,
+ const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys);
LIBMEMCACHED_API
memcached_return memcached_mget_by_key(memcached_st *ptr,
const char *master_key, size_t
master_key_length,
- const char **keys,
- size_t *key_length,
+ const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys);
LIBMEMCACHED_API
memcached_result_st *result,
memcached_return *error);
+LIBMEMCACHED_API
+memcached_return memcached_mget_execute(memcached_st *ptr,
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ memcached_execute_function *callback,
+ void *context,
+ unsigned int number_of_callbacks);
+LIBMEMCACHED_API
+memcached_return memcached_mget_execute_by_key(memcached_st *ptr,
+ const char *master_key,
+ size_t master_key_length,
+ const char * const *keys,
+ const size_t *key_length,
+ size_t number_of_keys,
+ memcached_execute_function *callback,
+ void *context,
+ unsigned int number_of_callbacks);
#ifdef __cplusplus
}
#endif
-#endif /* __MEMCACHED_GET_H__ */
+#endif /* LIBMEMCACHED_MEMCACHED_GET_H */
#include <math.h>
/* Protoypes (static) */
-static memcached_return server_add(memcached_st *ptr, const char *hostname,
+static memcached_return server_add(memcached_st *ptr, const char *hostname,
unsigned int port,
uint32_t weight,
memcached_connection type);
memcached_return run_distribution(memcached_st *ptr)
{
- switch (ptr->distribution)
+ switch (ptr->distribution)
{
case MEMCACHED_DISTRIBUTION_CONSISTENT:
case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA:
WATCHPOINT_ASSERT(0); /* We have added a distribution without extending the logic */
}
+ ptr->last_disconnected_server = NULL;
+
return MEMCACHED_SUCCESS;
}
static uint32_t ketama_server_hash(const char *key, unsigned int key_length, int alignment)
{
unsigned char results[16];
-
+
md5_signature((unsigned char*)key, key_length, results);
return ((uint32_t) (results[3 + alignment * 4] & 0xFF) << 24)
| ((uint32_t) (results[2 + alignment * 4] & 0xFF) << 16)
{
memcached_continuum_item_st *new_ptr;
- new_ptr= ptr->call_realloc(ptr, ptr->continuum,
+ new_ptr= ptr->call_realloc(ptr, ptr->continuum,
sizeof(memcached_continuum_item_st) * (live_servers + MEMCACHED_CONTINUUM_ADDITION) * points_per_server);
if (new_ptr == 0)
ptr->continuum_count= live_servers + MEMCACHED_CONTINUUM_ADDITION;
}
- if (is_ketama_weighted)
+ if (is_ketama_weighted)
{
- for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index)
+ for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index)
{
if (list[host_index].weight == 0)
{
}
}
- for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index)
+ for (host_index = 0; host_index < ptr->number_of_hosts; ++host_index)
{
if (is_auto_ejecting && list[host_index].next_retry > now.tv_sec)
continue;
- if (is_ketama_weighted)
+ if (is_ketama_weighted)
{
float pct = (float)list[host_index].weight / (float)total_weight;
pointer_per_server= (uint32_t) ((floorf((float) (pct * MEMCACHED_POINTS_PER_SERVER_KETAMA / 4 * (float)live_servers + 0.0000000001))) * 4);
pointer_per_hash= 4;
#ifdef DEBUG
- printf("ketama_weighted:%s|%d|%llu|%u\n",
- list[host_index].hostname,
- list[host_index].port,
- (unsigned long long)list[host_index].weight,
+ printf("ketama_weighted:%s|%d|%llu|%u\n",
+ list[host_index].hostname,
+ list[host_index].port,
+ (unsigned long long)list[host_index].weight,
pointer_per_server);
#endif
}
for (pointer_index= 1;
pointer_index <= pointer_per_server / pointer_per_hash;
- ++pointer_index)
+ ++pointer_index)
{
char sort_host[MEMCACHED_MAX_HOST_SORT_LENGTH]= "";
size_t sort_host_length;
else
{
sort_host_length= (size_t) snprintf(sort_host, MEMCACHED_MAX_HOST_SORT_LENGTH,
- "%s:%d-%d",
+ "%s:%d-%d",
list[host_index].hostname,
list[host_index].port, pointer_index - 1);
}
qsort(ptr->continuum, ptr->continuum_points_counter, sizeof(memcached_continuum_item_st), continuum_item_cmp);
#ifdef DEBUG
- for (pointer_index= 0; ptr->number_of_hosts && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++)
+ for (pointer_index= 0; ptr->number_of_hosts && pointer_index < ((live_servers * MEMCACHED_POINTS_PER_SERVER) - 1); pointer_index++)
{
WATCHPOINT_ASSERT(ptr->continuum[pointer_index].value <= ptr->continuum[pointer_index + 1].value);
}
return MEMCACHED_SUCCESS;
count= list[0].count;
- new_host_list= ptr->call_realloc(ptr, ptr->hosts,
+ new_host_list= ptr->call_realloc(ptr, ptr->hosts,
sizeof(memcached_server_st) * (count + ptr->number_of_hosts));
if (!new_host_list)
WATCHPOINT_ASSERT(list[x].hostname[0] != 0);
memcached_server_create(ptr, &ptr->hosts[ptr->number_of_hosts]);
/* TODO check return type */
- (void)memcached_server_create_with(ptr, &ptr->hosts[ptr->number_of_hosts], list[x].hostname,
+ (void)memcached_server_create_with(ptr, &ptr->hosts[ptr->number_of_hosts], list[x].hostname,
list[x].port, list[x].weight, list[x].type);
ptr->number_of_hosts++;
}
return run_distribution(ptr);
}
-memcached_return memcached_server_add_unix_socket(memcached_st *ptr,
+memcached_return memcached_server_add_unix_socket(memcached_st *ptr,
const char *filename)
{
return memcached_server_add_unix_socket_with_weight(ptr, filename, 0);
}
-memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr,
- const char *filename,
+memcached_return memcached_server_add_unix_socket_with_weight(memcached_st *ptr,
+ const char *filename,
uint32_t weight)
{
if (!filename)
return server_add(ptr, filename, 0, weight, MEMCACHED_CONNECTION_UNIX_SOCKET);
}
-memcached_return memcached_server_add_udp(memcached_st *ptr,
+memcached_return memcached_server_add_udp(memcached_st *ptr,
const char *hostname,
unsigned int port)
{
return memcached_server_add_udp_with_weight(ptr, hostname, port, 0);
}
-memcached_return memcached_server_add_udp_with_weight(memcached_st *ptr,
+memcached_return memcached_server_add_udp_with_weight(memcached_st *ptr,
const char *hostname,
unsigned int port,
uint32_t weight)
{
if (!port)
- port= MEMCACHED_DEFAULT_PORT;
+ port= MEMCACHED_DEFAULT_PORT;
if (!hostname)
- hostname= "localhost";
+ hostname= "localhost";
return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_UDP);
}
-memcached_return memcached_server_add(memcached_st *ptr,
- const char *hostname,
+memcached_return memcached_server_add(memcached_st *ptr,
+ const char *hostname,
unsigned int port)
{
return memcached_server_add_with_weight(ptr, hostname, port, 0);
}
-memcached_return memcached_server_add_with_weight(memcached_st *ptr,
- const char *hostname,
+memcached_return memcached_server_add_with_weight(memcached_st *ptr,
+ const char *hostname,
unsigned int port,
uint32_t weight)
{
if (!port)
- port= MEMCACHED_DEFAULT_PORT;
+ port= MEMCACHED_DEFAULT_PORT;
if (!hostname)
hostname= "localhost";
return server_add(ptr, hostname, port, weight, MEMCACHED_CONNECTION_TCP);
}
-static memcached_return server_add(memcached_st *ptr, const char *hostname,
+static memcached_return server_add(memcached_st *ptr, const char *hostname,
unsigned int port,
uint32_t weight,
memcached_connection type)
if ( (ptr->flags & MEM_USE_UDP && type != MEMCACHED_CONNECTION_UDP)
|| ( (type == MEMCACHED_CONNECTION_UDP) && !(ptr->flags & MEM_USE_UDP) ) )
return MEMCACHED_INVALID_HOST_PROTOCOL;
-
- new_host_list= ptr->call_realloc(ptr, ptr->hosts,
+
+ new_host_list= ptr->call_realloc(ptr, ptr->hosts,
sizeof(memcached_server_st) * (ptr->number_of_hosts+1));
if (new_host_list == NULL)
memcached_st *ptr= st_ptr->root;
memcached_server_st *list= ptr->hosts;
- for (x= 0, host_index= 0; x < ptr->number_of_hosts; x++)
+ for (x= 0, host_index= 0; x < ptr->number_of_hosts; x++)
{
- if (strncmp(list[x].hostname, st_ptr->hostname, MEMCACHED_MAX_HOST_LENGTH) != 0 || list[x].port != st_ptr->port)
+ if (strncmp(list[x].hostname, st_ptr->hostname, MEMCACHED_MAX_HOST_LENGTH) != 0 || list[x].port != st_ptr->port)
{
if (host_index != x)
memcpy(list+host_index, list+x, sizeof(memcached_server_st));
host_index++;
- }
+ }
}
ptr->number_of_hosts= host_index;
- if (st_ptr->address_info)
+ if (st_ptr->address_info)
{
freeaddrinfo(st_ptr->address_info);
st_ptr->address_info= NULL;
return MEMCACHED_SUCCESS;
}
-memcached_server_st *memcached_server_list_append(memcached_server_st *ptr,
+memcached_server_st *memcached_server_list_append(memcached_server_st *ptr,
const char *hostname, unsigned int port,
memcached_return *error)
{
return memcached_server_list_append_with_weight(ptr, hostname, port, 0, error);
}
-memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr,
+memcached_server_st *memcached_server_list_append_with_weight(memcached_server_st *ptr,
const char *hostname, unsigned int port,
- uint32_t weight,
+ uint32_t weight,
memcached_return *error)
{
unsigned int count;
return NULL;
if (!port)
- port= MEMCACHED_DEFAULT_PORT;
+ port= MEMCACHED_DEFAULT_PORT;
/* Increment count for hosts */
count= 1;
if (ptr != NULL)
{
count+= ptr[0].count;
- }
+ }
new_host_list= (memcached_server_st *)realloc(ptr, sizeof(memcached_server_st) * count);
if (!new_host_list)
typedef enum {
MEM_READ,
- MEM_WRITE,
+ MEM_WRITE
} memc_read_or_write;
static ssize_t io_flush(memcached_server_st *ptr, memcached_return *error);
static memcached_return io_wait(memcached_server_st *ptr,
memc_read_or_write read_or_write)
{
- struct pollfd fds[1];
- short flags= 0;
+ struct pollfd fds= {
+ .fd= ptr->fd,
+ .events = POLLIN
+ };
int error;
- if (read_or_write == MEM_WRITE) /* write */
- flags= POLLOUT;
- else
- flags= POLLIN;
-
- memset(&fds, 0, sizeof(struct pollfd));
- fds[0].fd= ptr->fd;
- fds[0].events= flags;
+ unlikely (read_or_write == MEM_WRITE) /* write */
+ fds.events= POLLOUT;
/*
** We are going to block on write, but at least on Solaris we might block
*/
if (read_or_write == MEM_WRITE)
{
- memcached_return rc=memcached_purge(ptr);
+ memcached_return rc= memcached_purge(ptr);
if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_STORED)
- return MEMCACHED_FAILURE;
+ return MEMCACHED_FAILURE;
}
- error= poll(fds, 1, ptr->root->poll_timeout);
+ int timeout= ptr->root->poll_timeout;
+ if ((ptr->root->flags & MEM_NO_BLOCK) == 0)
+ timeout= -1;
+
+ error= poll(&fds, 1, timeout);
if (error == 1)
return MEMCACHED_SUCCESS;
else if (error == 0)
- {
return MEMCACHED_TIMEOUT;
- }
/* Imposssible for anything other then -1 */
WATCHPOINT_ASSERT(error == -1);
memcached_quit_server(ptr, 1);
return MEMCACHED_FAILURE;
+}
+
+/**
+ * Try to fill the input buffer for a server with as much
+ * data as possible.
+ *
+ * @param ptr the server to pack
+ */
+static bool repack_input_buffer(memcached_server_st *ptr)
+{
+ if (ptr->read_ptr != ptr->read_buffer)
+ {
+ /* Move all of the data to the beginning of the buffer so
+ ** that we can fit more data into the buffer...
+ */
+ memmove(ptr->read_buffer, ptr->read_ptr, ptr->read_buffer_length);
+ ptr->read_ptr= ptr->read_buffer;
+ ptr->read_data_length= ptr->read_buffer_length;
+ }
+
+ /* There is room in the buffer, try to fill it! */
+ if (ptr->read_buffer_length != MEMCACHED_MAX_BUFFER)
+ {
+ /* Just try a single read to grab what's available */
+ ssize_t nr= read(ptr->fd,
+ ptr->read_ptr + ptr->read_data_length,
+ MEMCACHED_MAX_BUFFER - ptr->read_data_length);
+ if (nr > 0)
+ {
+ ptr->read_data_length+= (size_t)nr;
+ ptr->read_buffer_length+= (size_t)nr;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * If the we have callbacks connected to this server structure
+ * we may start process the input queue and fire the callbacks
+ * for the incomming messages. This function is _only_ called
+ * when the input buffer is full, so that we _know_ that we have
+ * at least _one_ message to process.
+ *
+ * @param ptr the server to star processing iput messages for
+ * @return true if we processed anything, false otherwise
+ */
+static bool process_input_buffer(memcached_server_st *ptr)
+{
+ /*
+ ** We might be able to process some of the response messages if we
+ ** have a callback set up
+ */
+ if (ptr->root->callbacks != NULL && (ptr->root->flags & MEM_USE_UDP) == 0)
+ {
+ /*
+ * We might have responses... try to read them out and fire
+ * callbacks
+ */
+ memcached_callback_st cb= *ptr->root->callbacks;
+
+ char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
+ memcached_return error;
+ error= memcached_response(ptr, buffer, sizeof(buffer),
+ &ptr->root->result);
+ if (error == MEMCACHED_SUCCESS)
+ {
+ for (unsigned int x= 0; x < cb.number_of_callback; x++)
+ {
+ error= (*cb.callback[x])(ptr->root, &ptr->root->result, cb.context);
+ if (error != MEMCACHED_SUCCESS)
+ break;
+ }
+
+ /* @todo what should I do with the error message??? */
+ }
+ /* @todo what should I do with other error messages?? */
+ return true;
+ }
+
+ return false;
}
#ifdef UNUSED
continue;
case EAGAIN:
{
+ /*
+ * We may be blocked on write because the input buffer
+ * is full. Let's check if we have room in our input
+ * buffer for more data and retry the write before
+ * waiting..
+ */
+ if (repack_input_buffer(ptr) ||
+ process_input_buffer(ptr))
+ continue;
+
memcached_return rc;
rc= io_wait(ptr, MEM_WRITE);
return (ssize_t) return_length;
}
-/*
+/*
Eventually we will just kill off the server with the problem.
*/
void memcached_io_reset(memcached_server_st *ptr)
/**
* Read a given number of bytes from the server and place it into a specific
- * buffer. Reset the IO channel on this server if an error occurs.
+ * buffer. Reset the IO channel on this server if an error occurs.
*/
memcached_return memcached_safe_read(memcached_server_st *ptr,
void *dta,
uint16_t cur_req= get_udp_datagram_request_id(header);
int msg_num= get_msg_num_from_request_id(cur_req);
int thread_id= get_thread_id_from_request_id(cur_req);
-
+
if (((++msg_num) & UDP_REQUEST_ID_THREAD_MASK) != 0)
msg_num= 0;
#include "common.h"
-memcached_return memcached_key_test(const char **keys, size_t *key_length,
+memcached_return memcached_key_test(const char * const *keys,
+ const size_t *key_length,
size_t number_of_keys)
{
uint32_t x;
if (rc != MEMCACHED_SUCCESS)
return rc;
-
-
for (y= 0; y < *(key_length + x); y++)
{
if ((isgraph(keys[x][y])) == 0)
if (ptr->root->purging || /* already purging */
(memcached_server_response_count(ptr) < ptr->root->io_msg_watermark &&
ptr->io_bytes_sent < ptr->root->io_bytes_watermark) ||
- (ptr->io_bytes_sent > ptr->root->io_bytes_watermark &&
+ (ptr->io_bytes_sent >= ptr->root->io_bytes_watermark &&
memcached_server_response_count(ptr) < 2))
{
return MEMCACHED_SUCCESS;
/*
This closes all connections (forces flush of input as well).
-
- Maybe add a host specific, or key specific version?
-
- The reason we send "quit" is that in case we have buffered IO, this
+
+ Maybe add a host specific, or key specific version?
+
+ The reason we send "quit" is that in case we have buffered IO, this
will force data to be completed.
*/
memcached_return rc;
char buffer[MEMCACHED_MAX_BUFFER];
- if (ptr->root->flags & MEM_BINARY_PROTOCOL)
+ if (ptr->root->flags & MEM_BINARY_PROTOCOL)
{
protocol_binary_request_quit request = {.bytes= {0}};
request.message.header.request.magic = PROTOCOL_BINARY_REQ;
rc= memcached_do(ptr, "quit\r\n", 6, 1);
WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_FETCH_NOTFINISHED);
-
+
/* read until socket is closed, or there is an error
* closing the socket before all data is read
* results in server throwing away all data which is
ssize_t nread;
while (memcached_io_read(ptr, buffer, sizeof(buffer)/sizeof(*buffer),
&nread) == MEMCACHED_SUCCESS);
+
+ /*
+ * memcached_io_read may call memcached_quit_server with io_death if
+ * it encounters problems, but we don't care about those occurences.
+ * The intention of that loop is to drain the data sent from the
+ * server to ensure that the server processed all of the data we
+ * sent to the server.
+ */
+ ptr->server_failure_counter= 0;
}
memcached_io_close(ptr);
memcached_server_response_reset(ptr);
}
- ptr->server_failure_counter++;
+ if(io_death) ptr->server_failure_counter++;
}
void memcached_quit(memcached_st *ptr)
{
unsigned int x;
- if (ptr->hosts == NULL ||
+ if (ptr->hosts == NULL ||
ptr->number_of_hosts == 0)
return;
{
switch (header.response.opcode)
{
- case PROTOCOL_BINARY_CMD_GETK:
case PROTOCOL_BINARY_CMD_GETKQ:
+ /*
+ * We didn't increment the response counter for the GETKQ packet
+ * (only the final NOOP), so we need to increment the counter again.
+ */
+ memcached_server_response_increment(ptr);
+ /* FALLTHROUGH */
+ case PROTOCOL_BINARY_CMD_GETK:
{
uint16_t keylen= header.response.keylen;
memcached_result_reset(result);
}
else
memset(ptr, 0, sizeof(memcached_server_st));
-
+
ptr->root= memc;
return ptr;
}
-memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host,
- const char *hostname, unsigned int port,
+memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host,
+ const char *hostname, unsigned int port,
uint32_t weight, memcached_connection type)
{
host= memcached_server_create(memc, host);
if (ptr == NULL)
return NULL;
- rv = memcached_server_create_with(ptr->root, clone,
+ rv = memcached_server_create_with(ptr->root, clone,
ptr->hostname, ptr->port, ptr->weight,
ptr->type);
if (rv != NULL)
}
-memcached_return memcached_server_cursor(memcached_st *ptr,
+memcached_return memcached_server_cursor(memcached_st *ptr,
memcached_server_function *callback,
void *context,
unsigned int number_of_callbacks)
{
uint32_t server_key;
- *error= memcached_validate_key_length(key_length,
+ *error= memcached_validate_key_length(key_length,
ptr->flags & MEM_BINARY_PROTOCOL);
unlikely (*error != MEMCACHED_SUCCESS)
return NULL;
{
if (ptr)
return ptr->cached_server_error;
- else
+ else
return NULL;
}
{
ptr->cached_server_error[0]= 0;
}
+
+memcached_server_st *memcached_server_get_last_disconnect(memcached_st *ptr)
+{
+ return ptr->last_disconnected_server;
+}
#define memcached_server_response_count(A) (A)->cursor_active
LIBMEMCACHED_API
-memcached_return memcached_server_cursor(memcached_st *ptr,
+memcached_return memcached_server_cursor(memcached_st *ptr,
memcached_server_function *callback,
void *context,
unsigned int number_of_callbacks);
LIBMEMCACHED_API
-memcached_server_st *memcached_server_by_key(memcached_st *ptr, const char *key,
+memcached_server_st *memcached_server_by_key(memcached_st *ptr, const char *key,
size_t key_length, memcached_return *error);
LIBMEMCACHED_API
memcached_server_st *memcached_server_create(memcached_st *memc, memcached_server_st *ptr);
LIBMEMCACHED_API
-memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host,
- const char *hostname, unsigned int port,
+memcached_server_st *memcached_server_create_with(memcached_st *memc, memcached_server_st *host,
+ const char *hostname, unsigned int port,
uint32_t weight, memcached_connection type);
LIBMEMCACHED_API
LIBMEMCACHED_API
memcached_return memcached_server_remove(memcached_server_st *st_ptr);
+LIBMEMCACHED_API
+memcached_server_st *memcached_server_get_last_disconnect(memcached_st *ptr);
+
#ifdef __cplusplus
}
#endif
length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_items);
else if (!memcmp("total_items", key, strlen("total_items")))
length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->total_items);
- else if (!memcmp("bytes", key, strlen("bytes")))
- length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
else if (!memcmp("curr_connections", key, strlen("curr_connections")))
length= snprintf(buffer, SMALL_STRING_LEN,"%u", memc_stat->curr_connections);
else if (!memcmp("total_connections", key, strlen("total_connections")))
length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_read);
else if (!memcmp("bytes_written", key, strlen("bytes_written")))
length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes_written);
+ else if (!memcmp("bytes", key, strlen("bytes")))
+ length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->bytes);
else if (!memcmp("limit_maxbytes", key, strlen("limit_maxbytes")))
length= snprintf(buffer, SMALL_STRING_LEN,"%llu", (unsigned long long)memc_stat->limit_maxbytes);
else if (!memcmp("threads", key, strlen("threads")))
* Author: Brian Aker
*/
-#ifndef __MEMCACHED_TYPES_H__
-#define __MEMCACHED_TYPES_H__
+#ifndef LIBMEMCACHED_MEMCACHED_TYPES_H
+#define LIBMEMCACHED_MEMCACHED_TYPES_H
#ifdef __cplusplus
extern "C" {
typedef memcached_return (*memcached_dump_func)(memcached_st *ptr,
const char *key, size_t key_length, void *context);
+typedef struct {
+ memcached_execute_function *callback;
+ void *context;
+ unsigned int number_of_callback;
+} memcached_callback_st;
+
#ifdef __cplusplus
}
#endif
-#endif /* __MEMCACHED_TYPES_H__ */
+#endif /* LIBMEMCACHED_MEMCACHED_TYPES_H */
%exclude %{_libdir}/libmemcached.a
%exclude %{_libdir}/libmemcached.la
%exclude %{_libdir}/libmemcachedutil.a
+%exclude %{_libdir}/libmemcachedprotocol.a
%exclude %{_libdir}/libmemcachedutil.la
+%exclude %{_libdir}/libmemcachedprotocol.la
%{_libdir}/libmemcached.so.*
%{_libdir}/libmemcachedutil.so.*
+%{_libdir}/libmemcachedprotocol.so.*
%{_mandir}/man1/mem*
%{_includedir}/libmemcached
%{_libdir}/libmemcached.so
%{_libdir}/libmemcachedutil.so
+%{_libdir}/libmemcachedprotocol.so
%{_libdir}/pkgconfig/libmemcached.pc
%{_mandir}/man3/libmemcached*.3.gz
%{_mandir}/man3/memcached_*.3.gz
static char *global_keys[GLOBAL_COUNT];
static size_t global_keys_length[GLOBAL_COUNT];
-static test_return cleanup_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t cleanup_pairs(memcached_st *memc __attribute__((unused)))
{
pairs_free(global_pairs);
return 0;
}
-static test_return generate_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t generate_pairs(memcached_st *memc __attribute__((unused)))
{
unsigned long long x;
global_pairs= pairs_generate(GLOBAL_COUNT, 400);
return 0;
}
-static test_return drizzle(memcached_st *memc)
+static test_return_t drizzle(memcached_st *memc)
{
unsigned int x;
memcached_return rc;
Set the value, then quit to make sure it is flushed.
Come back in and test that add fails.
*/
-static test_return add_test(memcached_st *memc)
+static test_return_t add_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
* repeating add_tests many times
* may show a problem in timing
*/
-static test_return many_adds(memcached_st *memc)
+static test_return_t many_adds(memcached_st *memc)
{
unsigned int i;
for (i = 0; i < TEST_COUNTER; i++)
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <signal.h>
#include <unistd.h>
#include <time.h>
#include "server.h"
static const char *global_keys[GLOBAL_COUNT];
static size_t global_keys_length[GLOBAL_COUNT];
-static test_return init_test(memcached_st *not_used __attribute__((unused)))
+static test_return_t init_test(memcached_st *not_used __attribute__((unused)))
{
memcached_st memc;
return TEST_SUCCESS;
}
-static test_return server_list_null_test(memcached_st *ptr __attribute__((unused)))
+static test_return_t server_list_null_test(memcached_st *ptr __attribute__((unused)))
{
memcached_server_st *server_list;
memcached_return rc;
return MEMCACHED_SUCCESS;
}
-static test_return server_sort_test(memcached_st *ptr __attribute__((unused)))
+static test_return_t server_sort_test(memcached_st *ptr __attribute__((unused)))
{
uint32_t x;
uint32_t bigger= 0; /* Prime the value for the assert in server_display_function */
return TEST_SUCCESS;
}
-static test_return server_sort2_test(memcached_st *ptr __attribute__((unused)))
+static test_return_t server_sort2_test(memcached_st *ptr __attribute__((unused)))
{
uint32_t bigger= 0; /* Prime the value for the assert in server_display_function */
memcached_return rc;
return MEMCACHED_SUCCESS;
}
-static test_return server_unsort_test(memcached_st *ptr __attribute__((unused)))
+static test_return_t server_unsort_test(memcached_st *ptr __attribute__((unused)))
{
uint32_t x;
uint32_t counter= 0; /* Prime the value for the assert in server_display_function */
return TEST_SUCCESS;
}
-static test_return allocation_test(memcached_st *not_used __attribute__((unused)))
+static test_return_t allocation_test(memcached_st *not_used __attribute__((unused)))
{
memcached_st *memc;
memc= memcached_create(NULL);
return TEST_SUCCESS;
}
-static test_return clone_test(memcached_st *memc)
+static test_return_t clone_test(memcached_st *memc)
{
/* All null? */
{
return TEST_SUCCESS;
}
-static test_return userdata_test(memcached_st *memc)
+static test_return_t userdata_test(memcached_st *memc)
{
void* foo= NULL;
assert(memcached_set_user_data(memc, foo) == NULL);
return TEST_SUCCESS;
}
-static test_return connection_test(memcached_st *memc)
+static test_return_t connection_test(memcached_st *memc)
{
memcached_return rc;
return TEST_SUCCESS;
}
-static test_return error_test(memcached_st *memc)
+static test_return_t error_test(memcached_st *memc)
{
memcached_return rc;
uint32_t values[] = { 851992627U, 2337886783U, 3196981036U, 4001849190U,
return TEST_SUCCESS;
}
-static test_return set_test(memcached_st *memc)
+static test_return_t set_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return TEST_SUCCESS;
}
-static test_return append_test(memcached_st *memc)
+static test_return_t append_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "fig";
return TEST_SUCCESS;
}
-static test_return append_binary_test(memcached_st *memc)
+static test_return_t append_binary_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "numbers";
return TEST_SUCCESS;
}
-static test_return cas2_test(memcached_st *memc)
+static test_return_t cas2_test(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"fudge", "son", "food"};
return TEST_SUCCESS;
}
-static test_return cas_test(memcached_st *memc)
+static test_return_t cas_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "fun";
return TEST_SUCCESS;
}
-static test_return prepend_test(memcached_st *memc)
+static test_return_t prepend_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "fig";
Set the value, then quit to make sure it is flushed.
Come back in and test that add fails.
*/
-static test_return add_test(memcached_st *memc)
+static test_return_t add_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
** because the connects starts to time out (the test doesn't do much
** anyway, so just loop 10 iterations)
*/
-static test_return add_wrapper(memcached_st *memc)
+static test_return_t add_wrapper(memcached_st *memc)
{
unsigned int x;
unsigned int max= 10000;
return TEST_SUCCESS;
}
-static test_return replace_test(memcached_st *memc)
+static test_return_t replace_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return TEST_SUCCESS;
}
-static test_return delete_test(memcached_st *memc)
+static test_return_t delete_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return TEST_SUCCESS;
}
-static test_return flush_test(memcached_st *memc)
+static test_return_t flush_test(memcached_st *memc)
{
memcached_return rc;
return MEMCACHED_SUCCESS;
}
-static test_return memcached_server_cursor_test(memcached_st *memc)
+static test_return_t memcached_server_cursor_test(memcached_st *memc)
{
char context[8];
strcpy(context, "foo bad");
return TEST_SUCCESS;
}
-static test_return bad_key_test(memcached_st *memc)
+static test_return_t bad_key_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo bad";
return memcached_result_set_value(result, READ_THROUGH_VALUE, strlen(READ_THROUGH_VALUE));
}
-static test_return read_through(memcached_st *memc)
+static test_return_t read_through(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return MEMCACHED_SUCCESS;
}
-static test_return delete_through(memcached_st *memc)
+static test_return_t delete_through(memcached_st *memc)
{
memcached_trigger_delete_key callback;
memcached_return rc;
return TEST_SUCCESS;
}
-static test_return get_test(memcached_st *memc)
+static test_return_t get_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return TEST_SUCCESS;
}
-static test_return get_test2(memcached_st *memc)
+static test_return_t get_test2(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return TEST_SUCCESS;
}
-static test_return set_test2(memcached_st *memc)
+static test_return_t set_test2(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return TEST_SUCCESS;
}
-static test_return set_test3(memcached_st *memc)
+static test_return_t set_test3(memcached_st *memc)
{
memcached_return rc;
char *value;
return TEST_SUCCESS;
}
-static test_return get_test3(memcached_st *memc)
+static test_return_t get_test3(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return TEST_SUCCESS;
}
-static test_return get_test4(memcached_st *memc)
+static test_return_t get_test4(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
* dereference a NIL-pointer if you issue a multi-get and don't read out all
* responses before you execute a storage command.
*/
-static test_return get_test5(memcached_st *memc)
+static test_return_t get_test5(memcached_st *memc)
{
/*
** Request the same key twice, to ensure that we hash to the same server
return TEST_SUCCESS;
}
-static test_return mget_end(memcached_st *memc)
+static test_return_t mget_end(memcached_st *memc)
{
const char *keys[]= { "foo", "foo2" };
size_t lengths[]= { 3, 4 };
}
/* Do not copy the style of this code, I just access hosts to testthis function */
-static test_return stats_servername_test(memcached_st *memc)
+static test_return_t stats_servername_test(memcached_st *memc)
{
memcached_return rc;
memcached_stat_st memc_stat;
return TEST_SUCCESS;
}
-static test_return increment_test(memcached_st *memc)
+static test_return_t increment_test(memcached_st *memc)
{
uint64_t new_number;
memcached_return rc;
return TEST_SUCCESS;
}
-static test_return increment_with_initial_test(memcached_st *memc)
+static test_return_t increment_with_initial_test(memcached_st *memc)
{
if (memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) != 0)
{
return TEST_SUCCESS;
}
-static test_return decrement_test(memcached_st *memc)
+static test_return_t decrement_test(memcached_st *memc)
{
uint64_t new_number;
memcached_return rc;
return TEST_SUCCESS;
}
-static test_return decrement_with_initial_test(memcached_st *memc)
+static test_return_t decrement_with_initial_test(memcached_st *memc)
{
if (memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) != 0)
{
return TEST_SUCCESS;
}
-static test_return quit_test(memcached_st *memc)
+static test_return_t quit_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "fudge";
return TEST_SUCCESS;
}
-static test_return mget_result_test(memcached_st *memc)
+static test_return_t mget_result_test(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"fudge", "son", "food"};
return TEST_SUCCESS;
}
-static test_return mget_result_alloc_test(memcached_st *memc)
+static test_return_t mget_result_alloc_test(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"fudge", "son", "food"};
return MEMCACHED_SUCCESS;
}
-static test_return mget_result_function(memcached_st *memc)
+static test_return_t mget_result_function(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"fudge", "son", "food"};
return TEST_SUCCESS;
}
-static test_return mget_test(memcached_st *memc)
+static test_return_t mget_test(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"fudge", "son", "food"};
return TEST_SUCCESS;
}
-static test_return get_stats_keys(memcached_st *memc)
+static test_return_t mget_execute(memcached_st *memc)
+{
+ bool binary= false;
+ if (memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL) != 0)
+ binary= true;
+
+ /*
+ * I only want to hit _one_ server so I know the number of requests I'm
+ * sending in the pipeline.
+ */
+ uint32_t number_of_hosts= memc->number_of_hosts;
+ memc->number_of_hosts= 1;
+
+ int max_keys= binary ? 20480 : 1;
+
+
+ char **keys= calloc((size_t)max_keys, sizeof(char*));
+ size_t *key_length=calloc((size_t)max_keys, sizeof(size_t));
+
+ /* First add all of the items.. */
+ char blob[1024] = {0};
+ memcached_return rc;
+ for (int x= 0; x < max_keys; ++x)
+ {
+ char k[251];
+ key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
+ keys[x]= strdup(k);
+ assert(keys[x] != NULL);
+ rc= memcached_add(memc, keys[x], key_length[x], blob, sizeof(blob), 0, 0);
+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+ }
+
+ /* Try to get all of them with a large multiget */
+ unsigned int counter= 0;
+ memcached_execute_function callbacks[1]= { [0]= &callback_counter };
+ rc= memcached_mget_execute(memc, (const char**)keys, key_length,
+ (size_t)max_keys, callbacks, &counter, 1);
+
+ if (binary)
+ {
+ assert(rc == MEMCACHED_SUCCESS);
+
+ rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+ assert(rc == MEMCACHED_END);
+
+ /* Verify that we got all of the items */
+ assert(counter == (unsigned int)max_keys);
+ }
+ else
+ {
+ assert(rc == MEMCACHED_NOT_SUPPORTED);
+ assert(counter == 0);
+ }
+
+ /* Release all allocated resources */
+ for (int x= 0; x < max_keys; ++x)
+ free(keys[x]);
+ free(keys);
+ free(key_length);
+
+ memc->number_of_hosts= number_of_hosts;
+ return TEST_SUCCESS;
+}
+
+static test_return_t get_stats_keys(memcached_st *memc)
{
char **list;
char **ptr;
return TEST_SUCCESS;
}
-static test_return version_string_test(memcached_st *memc __attribute__((unused)))
+static test_return_t version_string_test(memcached_st *memc __attribute__((unused)))
{
const char *version_string;
return TEST_SUCCESS;
}
-static test_return get_stats(memcached_st *memc)
+static test_return_t get_stats(memcached_st *memc)
{
unsigned int x;
char **list;
return TEST_SUCCESS;
}
-static test_return add_host_test(memcached_st *memc)
+static test_return_t add_host_test(memcached_st *memc)
{
unsigned int x;
memcached_server_st *servers;
return MEMCACHED_SUCCESS;
}
-static test_return callback_test(memcached_st *memc)
+static test_return_t callback_test(memcached_st *memc)
{
/* Test User Data */
{
}
/* We don't test the behavior itself, we test the switches */
-static test_return behavior_test(memcached_st *memc)
+static test_return_t behavior_test(memcached_st *memc)
{
uint64_t value;
uint32_t set= 1;
return TEST_SUCCESS;
}
+static test_return_t fetch_all_results(memcached_st *memc)
+{
+ memcached_return rc= MEMCACHED_SUCCESS;
+ char return_key[MEMCACHED_MAX_KEY];
+ size_t return_key_length;
+ char *return_value;
+ size_t return_value_length;
+ uint32_t flags;
+
+ while ((return_value= memcached_fetch(memc, return_key, &return_key_length,
+ &return_value_length, &flags, &rc)))
+ {
+ assert(return_value);
+ assert(rc == MEMCACHED_SUCCESS);
+ free(return_value);
+ }
+
+ return ((rc == MEMCACHED_END) || (rc == MEMCACHED_SUCCESS)) ? TEST_SUCCESS : TEST_FAILURE;
+}
+
/* Test case provided by Cal Haldenbrand */
-static test_return user_supplied_bug1(memcached_st *memc)
+static test_return_t user_supplied_bug1(memcached_st *memc)
{
unsigned int setter= 1;
unsigned int x;
}
/* Test case provided by Cal Haldenbrand */
-static test_return user_supplied_bug2(memcached_st *memc)
+static test_return_t user_supplied_bug2(memcached_st *memc)
{
int errors;
unsigned int setter;
/* Do a large mget() over all the keys we think exist */
#define KEY_COUNT 3000 // * 1024576
-static test_return user_supplied_bug3(memcached_st *memc)
+static test_return_t user_supplied_bug3(memcached_st *memc)
{
memcached_return rc;
unsigned int setter;
getter = memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE);
#endif
- keys= (char **)malloc(sizeof(char *) * KEY_COUNT);
+ keys= calloc(KEY_COUNT, sizeof(char *));
assert(keys);
- memset(keys, 0, (sizeof(char *) * KEY_COUNT));
for (x= 0; x < KEY_COUNT; x++)
{
char buffer[30];
rc= memcached_mget(memc, (const char **)keys, key_lengths, KEY_COUNT);
assert(rc == MEMCACHED_SUCCESS);
- /* Turn this into a help function */
- {
- char return_key[MEMCACHED_MAX_KEY];
- size_t return_key_length;
- char *return_value;
- size_t return_value_length;
- uint32_t flags;
-
- while ((return_value= memcached_fetch(memc, return_key, &return_key_length,
- &return_value_length, &flags, &rc)))
- {
- assert(return_value);
- assert(rc == MEMCACHED_SUCCESS);
- free(return_value);
- }
- }
+ assert(fetch_all_results(memc) == TEST_SUCCESS);
for (x= 0; x < KEY_COUNT; x++)
free(keys[x]);
}
/* Make sure we behave properly if server list has no values */
-static test_return user_supplied_bug4(memcached_st *memc)
+static test_return_t user_supplied_bug4(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"fudge", "son", "food"};
}
#define VALUE_SIZE_BUG5 1048064
-static test_return user_supplied_bug5(memcached_st *memc)
+static test_return_t user_supplied_bug5(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"036790384900", "036790384902", "036790384904", "036790384906"};
return TEST_SUCCESS;
}
-static test_return user_supplied_bug6(memcached_st *memc)
+static test_return_t user_supplied_bug6(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"036790384900", "036790384902", "036790384904", "036790384906"};
return TEST_SUCCESS;
}
-static test_return user_supplied_bug8(memcached_st *memc __attribute__((unused)))
+static test_return_t user_supplied_bug8(memcached_st *memc __attribute__((unused)))
{
memcached_return rc;
memcached_st *mine;
}
/* Test flag store/retrieve */
-static test_return user_supplied_bug7(memcached_st *memc)
+static test_return_t user_supplied_bug7(memcached_st *memc)
{
memcached_return rc;
const char *keys= "036790384900";
return TEST_SUCCESS;
}
-static test_return user_supplied_bug9(memcached_st *memc)
+static test_return_t user_supplied_bug9(memcached_st *memc)
{
memcached_return rc;
const char *keys[]= {"UDATA:edevil@sapo.pt", "fudge&*@#", "for^#@&$not"};
}
/* We are testing with aggressive timeout to get failures */
-static test_return user_supplied_bug10(memcached_st *memc)
+static test_return_t user_supplied_bug10(memcached_st *memc)
{
const char *key= "foo";
char *value;
/*
We are looking failures in the async protocol
*/
-static test_return user_supplied_bug11(memcached_st *memc)
+static test_return_t user_supplied_bug11(memcached_st *memc)
{
const char *key= "foo";
char *value;
/*
Bug found where incr was not returning MEMCACHED_NOTFOUND when object did not exist.
*/
-static test_return user_supplied_bug12(memcached_st *memc)
+static test_return_t user_supplied_bug12(memcached_st *memc)
{
memcached_return rc;
uint32_t flags;
Bug found where command total one more than MEMCACHED_MAX_BUFFER
set key34567890 0 0 8169 \r\n is sent followed by buffer of size 8169, followed by 8169
*/
-static test_return user_supplied_bug13(memcached_st *memc)
+static test_return_t user_supplied_bug13(memcached_st *memc)
{
char key[] = "key34567890";
char *overflow;
set key34567890 0 0 8169 \r\n
is sent followed by buffer of size 8169, followed by 8169
*/
-static test_return user_supplied_bug14(memcached_st *memc)
+static test_return_t user_supplied_bug14(memcached_st *memc)
{
size_t setter= 1;
memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, setter);
/*
Look for zero length value problems
*/
-static test_return user_supplied_bug15(memcached_st *memc)
+static test_return_t user_supplied_bug15(memcached_st *memc)
{
uint32_t x;
memcached_return rc;
}
/* Check the return sizes on FLAGS to make sure it stores 32bit unsigned values correctly */
-static test_return user_supplied_bug16(memcached_st *memc)
+static test_return_t user_supplied_bug16(memcached_st *memc)
{
memcached_return rc;
const char *key= "mykey";
#ifndef __sun
/* Check the validity of chinese key*/
-static test_return user_supplied_bug17(memcached_st *memc)
+static test_return_t user_supplied_bug17(memcached_st *memc)
{
memcached_return rc;
const char *key= "豆瓣";
From Andrei on IRC
*/
-static test_return user_supplied_bug19(memcached_st *memc)
+static test_return_t user_supplied_bug19(memcached_st *memc)
{
memcached_st *m;
memcached_server_st *s;
}
/* CAS test from Andei */
-static test_return user_supplied_bug20(memcached_st *memc)
+static test_return_t user_supplied_bug20(memcached_st *memc)
{
memcached_return status;
memcached_result_st *result, result_obj;
}
#include "ketama_test_cases.h"
-static test_return user_supplied_bug18(memcached_st *trash)
+static test_return_t user_supplied_bug18(memcached_st *trash)
{
memcached_return rc;
uint64_t value;
return TEST_SUCCESS;
}
-static test_return auto_eject_hosts(memcached_st *trash)
+/* Large mget() of missing keys with binary proto
+ *
+ * If many binary quiet commands (such as getq's in an mget) fill the output
+ * buffer and the server chooses not to respond, memcached_flush hangs. See
+ * http://lists.tangent.org/pipermail/libmemcached/2009-August/000918.html
+ */
+
+/* sighandler_t function that always asserts false */
+static void fail(int unused __attribute__((unused)))
+{
+ assert(0);
+}
+
+
+static test_return_t _user_supplied_bug21(memcached_st* memc, size_t key_count)
+{
+ memcached_return rc;
+ unsigned int x;
+ char **keys;
+ size_t* key_lengths;
+ void (*oldalarm)(int);
+ memcached_st *memc_clone;
+
+ memc_clone= memcached_clone(NULL, memc);
+ assert(memc_clone);
+
+ /* only binproto uses getq for mget */
+ memcached_behavior_set(memc_clone, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1);
+
+ /* empty the cache to ensure misses (hence non-responses) */
+ rc= memcached_flush(memc_clone, 0);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ key_lengths= calloc(key_count, sizeof(size_t));
+ keys= calloc(key_count, sizeof(char *));
+ assert(keys);
+ for (x= 0; x < key_count; x++)
+ {
+ char buffer[30];
+
+ snprintf(buffer, 30, "%u", x);
+ keys[x]= strdup(buffer);
+ key_lengths[x]= strlen(keys[x]);
+ }
+
+ oldalarm= signal(SIGALRM, fail);
+ alarm(5);
+
+ rc= memcached_mget(memc_clone, (const char **)keys, key_lengths, key_count);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ alarm(0);
+ signal(SIGALRM, oldalarm);
+
+ assert(fetch_all_results(memc) == TEST_SUCCESS);
+
+ for (x= 0; x < key_count; x++)
+ free(keys[x]);
+ free(keys);
+ free(key_lengths);
+
+ memcached_free(memc_clone);
+
+ return TEST_SUCCESS;
+}
+
+static memcached_return pre_binary(memcached_st *memc);
+
+static test_return_t user_supplied_bug21(memcached_st *memc)
+{
+ if (pre_binary(memc) != MEMCACHED_SUCCESS)
+ return TEST_SKIPPED;
+
+ test_return_t rc;
+
+ /* should work as of r580 */
+ rc= _user_supplied_bug21(memc, 10);
+ assert(rc == TEST_SUCCESS);
+
+ /* should fail as of r580 */
+ rc= _user_supplied_bug21(memc, 1000);
+ assert(rc == TEST_SUCCESS);
+
+ return TEST_SUCCESS;
+}
+
+static test_return_t auto_eject_hosts(memcached_st *trash)
{
(void) trash;
return TEST_SUCCESS;
}
-static test_return result_static(memcached_st *memc)
+static test_return_t result_static(memcached_st *memc)
{
memcached_result_st result;
memcached_result_st *result_ptr;
return TEST_SUCCESS;
}
-static test_return result_alloc(memcached_st *memc)
+static test_return_t result_alloc(memcached_st *memc)
{
memcached_result_st *result;
return TEST_SUCCESS;
}
-static test_return string_static_null(memcached_st *memc)
+static test_return_t string_static_null(memcached_st *memc)
{
memcached_string_st string;
memcached_string_st *string_ptr;
return TEST_SUCCESS;
}
-static test_return string_alloc_null(memcached_st *memc)
+static test_return_t string_alloc_null(memcached_st *memc)
{
memcached_string_st *string;
return TEST_SUCCESS;
}
-static test_return string_alloc_with_size(memcached_st *memc)
+static test_return_t string_alloc_with_size(memcached_st *memc)
{
memcached_string_st *string;
return TEST_SUCCESS;
}
-static test_return string_alloc_with_size_toobig(memcached_st *memc)
+static test_return_t string_alloc_with_size_toobig(memcached_st *memc)
{
memcached_string_st *string;
return TEST_SUCCESS;
}
-static test_return string_alloc_append(memcached_st *memc)
+static test_return_t string_alloc_append(memcached_st *memc)
{
unsigned int x;
char buffer[SMALL_STRING_LEN];
return TEST_SUCCESS;
}
-static test_return string_alloc_append_toobig(memcached_st *memc)
+static test_return_t string_alloc_append_toobig(memcached_st *memc)
{
memcached_return rc;
unsigned int x;
return TEST_SUCCESS;
}
-static test_return cleanup_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t cleanup_pairs(memcached_st *memc __attribute__((unused)))
{
pairs_free(global_pairs);
return TEST_SUCCESS;
}
-static test_return generate_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t generate_pairs(memcached_st *memc __attribute__((unused)))
{
unsigned long long x;
global_pairs= pairs_generate(GLOBAL_COUNT, 400);
return TEST_SUCCESS;
}
-static test_return generate_large_pairs(memcached_st *memc __attribute__((unused)))
+static test_return_t generate_large_pairs(memcached_st *memc __attribute__((unused)))
{
unsigned long long x;
global_pairs= pairs_generate(GLOBAL2_COUNT, MEMCACHED_MAX_BUFFER+10);
return TEST_SUCCESS;
}
-static test_return generate_data(memcached_st *memc)
+static test_return_t generate_data(memcached_st *memc)
{
execute_set(memc, global_pairs, global_count);
return TEST_SUCCESS;
}
-static test_return generate_data_with_stats(memcached_st *memc)
+static test_return_t generate_data_with_stats(memcached_st *memc)
{
memcached_stat_st *stat_p;
memcached_return rc;
return TEST_SUCCESS;
}
-static test_return generate_buffer_data(memcached_st *memc)
+static test_return_t generate_buffer_data(memcached_st *memc)
{
size_t latch= 0;
return TEST_SUCCESS;
}
-static test_return get_read_count(memcached_st *memc)
+static test_return_t get_read_count(memcached_st *memc)
{
unsigned int x;
memcached_return rc;
free(return_value);
}
}
- fprintf(stderr, "\t%u -> %u", global_count, count);
}
memcached_free(memc_clone);
return TEST_SUCCESS;
}
-static test_return get_read(memcached_st *memc)
+static test_return_t get_read(memcached_st *memc)
{
unsigned int x;
memcached_return rc;
return TEST_SUCCESS;
}
-static test_return mget_read(memcached_st *memc)
+static test_return_t mget_read(memcached_st *memc)
{
memcached_return rc;
rc= memcached_mget(memc, global_keys, global_keys_length, global_count);
assert(rc == MEMCACHED_SUCCESS);
- /* Turn this into a help function */
- {
- char return_key[MEMCACHED_MAX_KEY];
- size_t return_key_length;
- char *return_value;
- size_t return_value_length;
- uint32_t flags;
-
- while ((return_value= memcached_fetch(memc, return_key, &return_key_length,
- &return_value_length, &flags, &rc)))
- {
- assert(return_value);
- assert(rc == MEMCACHED_SUCCESS);
- free(return_value);
- }
- }
+ assert(fetch_all_results(memc) == TEST_SUCCESS);
return TEST_SUCCESS;
}
-static test_return mget_read_result(memcached_st *memc)
+static test_return_t mget_read_result(memcached_st *memc)
{
memcached_return rc;
return TEST_SUCCESS;
}
-static test_return mget_read_function(memcached_st *memc)
+static test_return_t mget_read_function(memcached_st *memc)
{
memcached_return rc;
unsigned int counter;
return TEST_SUCCESS;
}
-static test_return delete_generate(memcached_st *memc)
+static test_return_t delete_generate(memcached_st *memc)
{
unsigned int x;
return TEST_SUCCESS;
}
-static test_return delete_buffer_generate(memcached_st *memc)
+static test_return_t delete_buffer_generate(memcached_st *memc)
{
size_t latch= 0;
unsigned int x;
return TEST_SUCCESS;
}
-static test_return add_host_test1(memcached_st *memc)
+static test_return_t add_host_test1(memcached_st *memc)
{
unsigned int x;
memcached_return rc;
}
memcached_free(memc_clone);
+
return rc;
}
static memcached_return pre_replication(memcached_st *memc)
{
- memcached_return rc= MEMCACHED_FAILURE;
- if (pre_binary(memc) == MEMCACHED_SUCCESS)
- {
- /*
- * Make sure that we store the item on all servers
- * (master + replicas == number of servers)
- */
- rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS,
- memc->number_of_hosts - 1);
- assert(rc == MEMCACHED_SUCCESS);
- assert(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS) == memc->number_of_hosts - 1);
- }
+ if (pre_binary(memc) != MEMCACHED_SUCCESS)
+ return MEMCACHED_FAILURE;
+
+ /*
+ * Make sure that we store the item on all servers
+ * (master + replicas == number of servers)
+ */
+ memcached_return rc;
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS,
+ memc->number_of_hosts - 1);
+ assert(rc == MEMCACHED_SUCCESS);
+ assert(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS) == memc->number_of_hosts - 1);
return rc;
}
return MEMCACHED_SUCCESS;
}
-static test_return noreply_test(memcached_st *memc)
+static test_return_t noreply_test(memcached_st *memc)
{
memcached_return ret;
ret= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1);
return TEST_SUCCESS;
}
-static test_return analyzer_test(memcached_st *memc)
+static test_return_t analyzer_test(memcached_st *memc)
{
memcached_return rc;
memcached_stat_st *memc_stat;
return MEMCACHED_SUCCESS;
}
-static test_return dump_test(memcached_st *memc)
+static test_return_t dump_test(memcached_st *memc)
{
memcached_return rc;
uint32_t counter= 0;
memcached_dump_func callbacks[1];
- test_return main_rc;
+ test_return_t main_rc;
callbacks[0]= &callback_dump_counter;
return arg;
}
-static test_return connection_pool_test(memcached_st *memc)
+static test_return_t connection_pool_test(memcached_st *memc)
{
memcached_pool_st* pool= memcached_pool_create(memc, 5, 10);
assert(pool != NULL);
}
#endif
-static test_return replication_set_test(memcached_st *memc)
+static test_return_t replication_set_test(memcached_st *memc)
{
memcached_return rc;
memcached_st *memc_clone= memcached_clone(NULL, memc);
return TEST_SUCCESS;
}
-static test_return replication_get_test(memcached_st *memc)
+static test_return_t replication_get_test(memcached_st *memc)
{
memcached_return rc;
return TEST_SUCCESS;
}
-static test_return replication_mget_test(memcached_st *memc)
+static test_return_t replication_mget_test(memcached_st *memc)
{
memcached_return rc;
memcached_st *memc_clone= memcached_clone(NULL, memc);
return TEST_SUCCESS;
}
-static test_return replication_delete_test(memcached_st *memc)
+static test_return_t replication_delete_test(memcached_st *memc)
{
memcached_return rc;
memcached_st *memc_clone= memcached_clone(NULL, memc);
return ids;
}
-static test_return post_udp_op_check(memcached_st *memc, uint16_t *expected_req_ids)
+static test_return_t post_udp_op_check(memcached_st *memc, uint16_t *expected_req_ids)
{
unsigned int x;
memcached_server_st *cur_server = memc->hosts;
}
/* Make sure that I cant add a tcp server to a udp client */
-static test_return add_tcp_server_udp_client_test(memcached_st *memc)
+static test_return_t add_tcp_server_udp_client_test(memcached_st *memc)
{
memcached_server_st server;
memcached_server_clone(&server, &memc->hosts[0]);
}
/* Make sure that I cant add a udp server to a tcp client */
-static test_return add_udp_server_tcp_client_test(memcached_st *memc)
+static test_return_t add_udp_server_tcp_client_test(memcached_st *memc)
{
memcached_server_st server;
memcached_server_clone(&server, &memc->hosts[0]);
return TEST_SUCCESS;
}
-static test_return set_udp_behavior_test(memcached_st *memc)
+static test_return_t set_udp_behavior_test(memcached_st *memc)
{
memcached_quit(memc);
return TEST_SUCCESS;
}
-static test_return udp_set_test(memcached_st *memc)
+static test_return_t udp_set_test(memcached_st *memc)
{
unsigned int x= 0;
unsigned int num_iters= 1025; //request id rolls over at 1024
return TEST_SUCCESS;
}
-static test_return udp_buffered_set_test(memcached_st *memc)
+static test_return_t udp_buffered_set_test(memcached_st *memc)
{
memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
return udp_set_test(memc);
}
-static test_return udp_set_too_big_test(memcached_st *memc)
+static test_return_t udp_set_too_big_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "bar";
return post_udp_op_check(memc,expected_ids);
}
-static test_return udp_delete_test(memcached_st *memc)
+static test_return_t udp_delete_test(memcached_st *memc)
{
unsigned int x= 0;
unsigned int num_iters= 1025; //request id rolls over at 1024
return TEST_SUCCESS;
}
-static test_return udp_buffered_delete_test(memcached_st *memc)
+static test_return_t udp_buffered_delete_test(memcached_st *memc)
{
memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
return udp_delete_test(memc);
}
-static test_return udp_verbosity_test(memcached_st *memc)
+static test_return_t udp_verbosity_test(memcached_st *memc)
{
memcached_return rc;
uint16_t *expected_ids= get_udp_request_ids(memc);
return post_udp_op_check(memc,expected_ids);
}
-static test_return udp_quit_test(memcached_st *memc)
+static test_return_t udp_quit_test(memcached_st *memc)
{
uint16_t *expected_ids= get_udp_request_ids(memc);
memcached_quit(memc);
return post_udp_op_check(memc, expected_ids);
}
-static test_return udp_flush_test(memcached_st *memc)
+static test_return_t udp_flush_test(memcached_st *memc)
{
memcached_return rc;
uint16_t *expected_ids= get_udp_request_ids(memc);
return post_udp_op_check(memc,expected_ids);
}
-static test_return udp_incr_test(memcached_st *memc)
+static test_return_t udp_incr_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "incr";
return post_udp_op_check(memc, expected_ids);
}
-static test_return udp_decr_test(memcached_st *memc)
+static test_return_t udp_decr_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "decr";
}
-static test_return udp_stat_test(memcached_st *memc)
+static test_return_t udp_stat_test(memcached_st *memc)
{
memcached_stat_st * rv= NULL;
memcached_return rc;
return post_udp_op_check(memc, expected_ids);
}
-static test_return udp_version_test(memcached_st *memc)
+static test_return_t udp_version_test(memcached_st *memc)
{
memcached_return rc;
uint16_t *expected_ids = get_udp_request_ids(memc);
return post_udp_op_check(memc, expected_ids);
}
-static test_return udp_get_test(memcached_st *memc)
+static test_return_t udp_get_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";
return post_udp_op_check(memc, expected_ids);
}
-static test_return udp_mixed_io_test(memcached_st *memc)
+static test_return_t udp_mixed_io_test(memcached_st *memc)
{
test_st current_op;
test_st mixed_io_ops [] ={
return TEST_SUCCESS;
}
-static test_return hsieh_avaibility_test (memcached_st *memc)
+static test_return_t hsieh_avaibility_test (memcached_st *memc)
{
memcached_return expected_rc= MEMCACHED_FAILURE;
#ifdef HAVE_HSIEH_HASH
NULL
};
-static test_return md5_run (memcached_st *memc __attribute__((unused)))
+static test_return_t md5_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return crc_run (memcached_st *memc __attribute__((unused)))
+static test_return_t crc_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return fnv1_64_run (memcached_st *memc __attribute__((unused)))
+static test_return_t fnv1_64_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return fnv1a_64_run (memcached_st *memc __attribute__((unused)))
+static test_return_t fnv1a_64_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return fnv1_32_run (memcached_st *memc __attribute__((unused)))
+static test_return_t fnv1_32_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return fnv1a_32_run (memcached_st *memc __attribute__((unused)))
+static test_return_t fnv1a_32_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return hsieh_run (memcached_st *memc __attribute__((unused)))
+static test_return_t hsieh_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return murmur_run (memcached_st *memc __attribute__((unused)))
+static test_return_t murmur_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return jenkins_run (memcached_st *memc __attribute__((unused)))
+static test_return_t jenkins_run (memcached_st *memc __attribute__((unused)))
{
uint32_t x;
const char **ptr;
return TEST_SUCCESS;
}
-static test_return regression_bug_434484(memcached_st *memc)
+static test_return_t regression_bug_434484(memcached_st *memc)
{
- if (pre_binary(memc) != TEST_SUCCESS)
- return TEST_SUCCESS;
+ if (pre_binary(memc) != MEMCACHED_SUCCESS)
+ return TEST_SKIPPED;
memcached_return ret;
const char *key= "regression_bug_434484";
assert(ret == MEMCACHED_NOTSTORED);
size_t size= 2048 * 1024;
- void *data= malloc(size);
+ void *data= calloc(1, size);
assert(data != NULL);
ret= memcached_set(memc, key, keylen, data, size, 0, 0);
assert(ret == MEMCACHED_E2BIG);
return TEST_SUCCESS;
}
+static test_return_t regression_bug_434843(memcached_st *memc)
+{
+ if (pre_binary(memc) != MEMCACHED_SUCCESS)
+ return TEST_SKIPPED;
+
+ memcached_return rc;
+ unsigned int counter= 0;
+ memcached_execute_function callbacks[1]= { [0]= &callback_counter };
+
+ /*
+ * I only want to hit only _one_ server so I know the number of requests I'm
+ * sending in the pipleine to the server. Let's try to do a multiget of
+ * 1024 (that should satisfy most users don't you think?). Future versions
+ * will include a mget_execute function call if you need a higher number.
+ */
+ uint32_t number_of_hosts= memc->number_of_hosts;
+ memc->number_of_hosts= 1;
+ const size_t max_keys= 1024;
+ char **keys= calloc(max_keys, sizeof(char*));
+ size_t *key_length=calloc(max_keys, sizeof(size_t));
+
+ for (int x= 0; x < (int)max_keys; ++x)
+ {
+ char k[251];
+ key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
+ keys[x]= strdup(k);
+ assert(keys[x] != NULL);
+ }
+
+ /*
+ * Run two times.. the first time we should have 100% cache miss,
+ * and the second time we should have 100% cache hits
+ */
+ for (int y= 0; y < 2; ++y)
+ {
+ rc= memcached_mget(memc, (const char**)keys, key_length, max_keys);
+ assert(rc == MEMCACHED_SUCCESS);
+ rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+ if (y == 0)
+ {
+ /* The first iteration should give me a 100% cache miss. verify that*/
+ assert(counter == 0);
+ char blob[1024]= { 0 };
+ for (int x= 0; x < (int)max_keys; ++x)
+ {
+ rc= memcached_add(memc, keys[x], key_length[x],
+ blob, sizeof(blob), 0, 0);
+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+ }
+ }
+ else
+ {
+ /* Verify that we received all of the key/value pairs */
+ assert(counter == (unsigned int)max_keys);
+ }
+ }
+
+ /* Release allocated resources */
+ for (size_t x= 0; x < max_keys; ++x)
+ free(keys[x]);
+ free(keys);
+ free(key_length);
+
+ memc->number_of_hosts= number_of_hosts;
+ return TEST_SUCCESS;
+}
+
+static test_return_t regression_bug_434843_buffered(memcached_st *memc)
+{
+ memcached_return rc;
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ return regression_bug_434843(memc);
+}
+
+static test_return_t regression_bug_421108(memcached_st *memc)
+{
+ memcached_return rc;
+ memcached_stat_st *memc_stat= memcached_stat(memc, NULL, &rc);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ char *bytes= memcached_stat_get_value(memc, memc_stat, "bytes", &rc);
+ assert(rc == MEMCACHED_SUCCESS);
+ assert(bytes != NULL);
+ char *bytes_read= memcached_stat_get_value(memc, memc_stat,
+ "bytes_read", &rc);
+ assert(rc == MEMCACHED_SUCCESS);
+ assert(bytes_read != NULL);
+
+ char *bytes_written= memcached_stat_get_value(memc, memc_stat,
+ "bytes_written", &rc);
+ assert(rc == MEMCACHED_SUCCESS);
+ assert(bytes_written != NULL);
+
+ assert(strcmp(bytes, bytes_read) != 0);
+ assert(strcmp(bytes, bytes_written) != 0);
+
+ /* Release allocated resources */
+ free(bytes);
+ free(bytes_read);
+ free(bytes_written);
+ memcached_stat_free(NULL, memc_stat);
+ return TEST_SUCCESS;
+}
+
+/*
+ * 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....
+ */
+static test_return_t regression_bug_442914(memcached_st *memc)
+{
+ memcached_return rc;
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 1);
+ assert(rc == MEMCACHED_SUCCESS);
+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1);
+
+ uint32_t number_of_hosts= memc->number_of_hosts;
+ memc->number_of_hosts= 1;
+
+ char k[250];
+ size_t len;
+
+ for (int x= 0; x < 250; ++x)
+ {
+ len= (size_t)snprintf(k, sizeof(k), "%0250u", x);
+ rc= memcached_delete(memc, k, len, 0);
+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+ }
+
+ len= (size_t)snprintf(k, sizeof(k), "%037u", 251);
+ rc= memcached_delete(memc, k, len, 0);
+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, 0);
+ assert(rc == MEMCACHED_SUCCESS);
+ rc= memcached_delete(memc, k, len, 0);
+ assert(rc == MEMCACHED_NOTFOUND);
+
+ memc->number_of_hosts= number_of_hosts;
+
+ return TEST_SUCCESS;
+}
+
+static test_return_t regression_bug_447342(memcached_st *memc)
+{
+ if (memc->number_of_hosts < 3 || pre_replication(memc) != MEMCACHED_SUCCESS)
+ return TEST_SKIPPED;
+
+ memcached_return rc;
+
+ rc= memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, 2);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ const size_t max_keys= 100;
+ char **keys= calloc(max_keys, sizeof(char*));
+ size_t *key_length=calloc(max_keys, sizeof(size_t));
+
+ for (int x= 0; x < (int)max_keys; ++x)
+ {
+ char k[251];
+ key_length[x]= (size_t)snprintf(k, sizeof(k), "0200%u", x);
+ keys[x]= strdup(k);
+ assert(keys[x] != NULL);
+ rc= memcached_set(memc, k, key_length[x], k, key_length[x], 0, 0);
+ assert(rc == MEMCACHED_SUCCESS);
+ }
+
+ /*
+ ** We are using the quiet commands 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);
+
+ /* Verify that all messages are stored, and we didn't stuff too much
+ * into the servers
+ */
+ rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ unsigned int counter= 0;
+ memcached_execute_function callbacks[1]= { [0]= &callback_counter };
+ rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+ /* Verify that we received all of the key/value pairs */
+ assert(counter == (unsigned int)max_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..
+ */
+ unsigned int port0= memc->hosts[0].port;
+ unsigned int port2= memc->hosts[2].port;
+ memc->hosts[0].port= 0;
+ memc->hosts[2].port= 0;
+
+ rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ counter= 0;
+ rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+ assert(counter == (unsigned int)max_keys);
+
+ /* restore the memc handle */
+ memc->hosts[0].port= port0;
+ memc->hosts[2].port= port2;
+
+ memcached_quit(memc);
+
+ /* Remove half of the objects */
+ for (int x= 0; x < (int)max_keys; ++x)
+ if (x & 1)
+ {
+ rc= memcached_delete(memc, keys[x], key_length[x], 0);
+ assert(rc == MEMCACHED_SUCCESS);
+ }
+
+ memcached_quit(memc);
+ memc->hosts[0].port= 0;
+ memc->hosts[2].port= 0;
+
+ /* now retry the command, this time we should have cache misses */
+ rc= memcached_mget(memc, (const char* const *)keys, key_length, max_keys);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ counter= 0;
+ rc= memcached_fetch_execute(memc, callbacks, (void *)&counter, 1);
+ assert(counter == (unsigned int)(max_keys >> 1));
+
+ /* Release allocated resources */
+ for (size_t x= 0; x < max_keys; ++x)
+ free(keys[x]);
+ free(keys);
+ free(key_length);
+
+ /* restore the memc handle */
+ memc->hosts[0].port= port0;
+ memc->hosts[2].port= port2;
+ return TEST_SUCCESS;
+}
+
+/* Test memcached_server_get_last_disconnect
+ * For a working server set, shall be NULL
+ * For a set of non existing server, shall not be NULL
+ */
+static test_return_t test_get_last_disconnect(memcached_st *memc)
+{
+ memcached_return rc;
+ memcached_server_st *disconnected_server;
+
+ /* With the working set of server */
+ const char *key= "marmotte";
+ const char *value= "milka";
+
+ rc= memcached_set(memc, key, strlen(key),
+ value, strlen(value),
+ (time_t)0, (uint32_t)0);
+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+
+ disconnected_server = memcached_server_get_last_disconnect(memc);
+ assert(disconnected_server == NULL);
+
+ /* With a non existing server */
+ memcached_st *mine;
+ memcached_server_st *servers;
+
+ const char *server_list= "localhost:9";
+
+ servers= memcached_servers_parse(server_list);
+ assert(servers);
+ mine= memcached_create(NULL);
+ rc= memcached_server_push(mine, servers);
+ assert(rc == MEMCACHED_SUCCESS);
+ memcached_server_list_free(servers);
+ assert(mine);
+
+ rc= memcached_set(mine, key, strlen(key),
+ value, strlen(value),
+ (time_t)0, (uint32_t)0);
+ assert(rc != MEMCACHED_SUCCESS);
+
+ disconnected_server = memcached_server_get_last_disconnect(mine);
+ assert(disconnected_server != NULL);
+ assert(disconnected_server->port == 9);
+ assert(strncmp(disconnected_server->hostname,"localhost",9) == 0);
+
+ memcached_quit(mine);
+ memcached_free(mine);
+
+ return TEST_SUCCESS;
+}
+
+/*
+ * This test ensures that the failure counter isn't incremented during
+ * normal termination of the memcached instance.
+ */
+static test_return_t wrong_failure_counter_test(memcached_st *memc)
+{
+ memcached_return rc;
+
+ /* Set value to force connection to the server */
+ const char *key= "marmotte";
+ const char *value= "milka";
+
+ /*
+ * Please note that I'm abusing the internal structures in libmemcached
+ * in a non-portable way and you shouldn't be doing this. I'm only
+ * doing this in order to verify that the library works the way it should
+ */
+ uint32_t number_of_hosts= memc->number_of_hosts;
+ memc->number_of_hosts= 1;
+
+ /* Ensure that we are connected to the server by setting a value */
+ rc= memcached_set(memc, key, strlen(key),
+ value, strlen(value),
+ (time_t)0, (uint32_t)0);
+ assert(rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED);
+
+
+ /* The test is to see that the memcached_quit doesn't increase the
+ * the server failure conter, so let's ensure that it is zero
+ * before sending quit
+ */
+ memc->hosts[0].server_failure_counter= 0;
+
+ memcached_quit(memc);
+
+ /* Verify that it memcached_quit didn't increment the failure counter
+ * Please note that this isn't bullet proof, because an error could
+ * occur...
+ */
+ assert(memc->hosts[0].server_failure_counter == 0);
+
+ /* restore the instance */
+ memc->number_of_hosts= number_of_hosts;
+
+ return TEST_SUCCESS;
+}
+
test_st udp_setup_server_tests[] ={
{"set_udp_behavior_test", 0, set_udp_behavior_test},
{"add_tcp_server_udp_client_test", 0, add_tcp_server_udp_client_test},
{"mget_result", 1, mget_result_test },
{"mget_result_alloc", 1, mget_result_alloc_test },
{"mget_result_function", 1, mget_result_function },
+ {"mget_execute", 1, mget_execute },
{"mget_end", 0, mget_end },
{"get_stats", 0, get_stats },
{"add_host_test", 0, add_host_test },
#ifdef HAVE_LIBMEMCACHEDUTIL
{"connectionpool", 1, connection_pool_test },
#endif
+ {"test_get_last_disconnect", 1, test_get_last_disconnect},
{0, 0, 0}
};
{"user_supplied_bug18", 1, user_supplied_bug18 },
{"user_supplied_bug19", 1, user_supplied_bug19 },
{"user_supplied_bug20", 1, user_supplied_bug20 },
+ {"user_supplied_bug21", 1, user_supplied_bug21 },
+ {"wrong_failure_counter_test", 1, wrong_failure_counter_test},
{0, 0, 0}
};
*/
test_st regression_tests[]= {
{"lp:434484", 1, regression_bug_434484 },
+ {"lp:434843", 1, regression_bug_434843 },
+ {"lp:434843 buffered", 1, regression_bug_434843_buffered },
+ {"lp:421108", 1, regression_bug_421108 },
+ {"lp:442914", 1, regression_bug_442914 },
+ {"lp:447342", 1, regression_bug_447342 },
{0, 0, 0}
};
{
server_startup_st *construct;
- construct= (server_startup_st *)malloc(sizeof(server_startup_st));
- memset(construct, 0, sizeof(server_startup_st));
+ construct= calloc(sizeof(server_startup_st), 1);
construct->count= SERVERS_TO_CREATE;
construct->udp= 0;
server_startup(construct);
using namespace memcache;
extern "C" {
- test_return basic_test(memcached_st *memc);
- test_return increment_test(memcached_st *memc);
- test_return basic_master_key_test(memcached_st *memc);
- test_return mget_result_function(memcached_st *memc);
- test_return mget_test(memcached_st *memc);
+ test_return_t basic_test(memcached_st *memc);
+ test_return_t increment_test(memcached_st *memc);
+ test_return_t basic_master_key_test(memcached_st *memc);
+ test_return_t mget_result_function(memcached_st *memc);
+ test_return_t mget_test(memcached_st *memc);
memcached_return callback_counter(memcached_st *,
memcached_result_st *,
void *context);
}
}
-test_return basic_test(memcached_st *memc)
+test_return_t basic_test(memcached_st *memc)
{
Memcache foo(memc);
const string value_set("This is some data");
return TEST_FAILURE;
}
-test_return increment_test(memcached_st *memc)
+test_return_t increment_test(memcached_st *memc)
{
Memcache mcach(memc);
bool rc;
return TEST_SUCCESS;
}
-test_return basic_master_key_test(memcached_st *memc)
+test_return_t basic_master_key_test(memcached_st *memc)
{
Memcache foo(memc);
const string value_set("Data for server A");
return MEMCACHED_SUCCESS;
}
-test_return mget_result_function(memcached_st *memc)
+test_return_t mget_result_function(memcached_st *memc)
{
Memcache mc(memc);
bool rc;
return TEST_SUCCESS;
}
-test_return mget_test(memcached_st *memc)
+test_return_t mget_test(memcached_st *memc)
{
Memcache mc(memc);
bool rc;
return s + us;
}
+static const char *test_strerror(test_return_t code)
+{
+ switch (code) {
+ case TEST_SUCCESS:
+ return "ok";
+ case TEST_FAILURE:
+ return "failed";
+ case TEST_MEMORY_ALLOCATION_FAILURE:
+ return "memory allocation";
+ case TEST_SKIPPED:
+ return "skipped";
+ case TEST_MAXIMUM_RETURN:
+ default:
+ fprintf(stderr, "Unknown return value\n");
+ abort();
+ }
+
+}
+
int main(int argc, char *argv[])
{
+ test_return_t failed;
unsigned int x;
char *collection_to_run= NULL;
char *wildcard= NULL;
world_st world;
collection_st *collection;
collection_st *next;
- uint8_t failed;
void *world_ptr;
memset(&world, 0, sizeof(world_st));
if (world.create)
world_ptr= world.create();
- else
+ else
world_ptr= NULL;
startup_ptr= (server_startup_st *)world_ptr;
failed= run->function(memc);
gettimeofday(&end_time, NULL);
load_time= timedif(end_time, start_time);
- if (failed)
- fprintf(stderr, "\t\t\t\t\t %ld.%03ld [ failed ]\n", load_time / 1000,
- load_time % 1000);
- else
- fprintf(stderr, "\t\t\t\t\t %ld.%03ld [ ok ]\n", load_time / 1000,
- load_time % 1000);
+
+ fprintf(stderr, "\t\t\t\t\t %ld.%03ld [ %s ]\n", load_time / 1000,
+ load_time % 1000, test_strerror(failed));
if (next->post)
(void)next->post(memc);
TEST_SUCCESS= 0, /* Backwards compatibility */
TEST_FAILURE,
TEST_MEMORY_ALLOCATION_FAILURE,
+ TEST_SKIPPED,
TEST_MAXIMUM_RETURN /* Always add new error code before */
-} test_return;
+} test_return_t;
struct test_st {
const char *name;
unsigned int requires_flush;
- test_return (*function)(memcached_st *memc);
+ test_return_t (*function)(memcached_st *memc);
};
struct collection_st {
#include "server.h"
/* Prototypes */
-test_return set_test(memcached_st *memc);
+test_return_t set_test(memcached_st *memc);
void *world_create(void);
void world_destroy(void *p);
-test_return set_test(memcached_st *memc)
+test_return_t set_test(memcached_st *memc)
{
memcached_return rc;
const char *key= "foo";