TAGS
aclocal.m4
autom4te.cache
+clients/memcapable
clients/memcat
clients/memcp
clients/memdump
- 0.32 Tue Sep 15 15:49:09 PDT 2009
+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
+ multiple constructors the memcached client object. The C++ interface
+ now takes parameters which are C++ types (such as std::string).
+ * Several bug fixes for binary protocol support.
+ * Fixed crashing issue with dumping from memcachd server (server internals
+ were changed without documenting change).
+
+0.32 Tue Sep 15 15:49:09 PDT 2009
* Change of behavior where linger is only modified for no-block and then
it is set to zero.
* Added Twitter's memcached_server_error() functions.
* Fix for OSX compiles in development builds.
- * Updated C++ interface. Added basic support for C++ exceptions. Added
- multiple constructors the memcached client object. The C++ interface
- now takes parameters which are C++ types (such as std::string).
+ * Updated C++ interface.
* Updated memcached_mget and memcached_mget_by_key to take a size_t
as a parameter instead of an unsigned int for number_of_keys.
LDADDS = libutilities.la $(top_builddir)/libmemcached/libmemcached.la
-bin_PROGRAMS = memcat memdump memcp memstat memrm memflush memslap memerror
+bin_PROGRAMS = memcat memdump memcp memstat memrm memflush memslap memerror memcapable
noinst_HEADERS = client_options.h \
utilities.h \
generator.h \
- execute.h
+ execute.h
noinst_LTLIBRARIES= libutilities.la libgenexec.la
memslap_SOURCES = memslap.c
memslap_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS)
-memslap_LDADD = $(PTHREAD_LIBS) libgenexec.la $(LDADDS)
+memslap_LDADD = $(PTHREAD_LIBS) libgenexec.la $(LDADDS)
+
+memcapable_SOURCES = memcapable.c
+if BUILD_BYTEORDER
+memcapable_LDADD=$(top_builddir)/libmemcached/libbyteorder.la
+endif
test-start-server:
memflush --servers=localhost
uint32_t flags;
unsigned int fetch_key;
- fetch_key= (unsigned int)random() % number_of;
+ fetch_key= (unsigned int)((unsigned int)random() % number_of);
value= memcached_get(memc, pairs[fetch_key].key, pairs[fetch_key].key_length,
&value_length, &flags, &rc);
pairs_st *pairs_generate(uint64_t number_of, size_t value_length)
{
unsigned int x;
- pairs_st *pairs= calloc((size_t)(number_of + 1), sizeof(pairs_st));
+ pairs_st *pairs;
+
+ pairs= (pairs_st*)calloc((size_t)number_of + 1, sizeof(pairs_st));
if (!pairs)
goto error;
--- /dev/null
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#undef NDEBUG
+#include "config.h"
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <libmemcached/memcached/protocol_binary.h>
+#include <libmemcached/byteorder.h>
+
+#ifdef linux
+/* /usr/include/netinet/in.h defines macros from ntohs() to _bswap_nn to
+ * optimize the conversion functions, but the prototypes generate warnings
+ * from gcc. The conversion methods isn't the bottleneck for my app, so
+ * just remove the warnings by undef'ing the optimization ..
+ */
+#undef ntohs
+#undef ntohl
+#endif
+
+/* Should we generate coredumps when we enounter an error (-c) */
+static bool do_core= false;
+/* connection to the server */
+static int sock;
+/* Should the output from test failures be verbose or quiet? */
+static bool verbose= false;
+
+/* The number of seconds to wait for an IO-operation */
+static int timeout= 2;
+
+/*
+ * Instead of having to cast between the different datatypes we create
+ * a union of all of the different types of pacages we want to send.
+ * A lot of the different commands use the same packet layout, so I'll
+ * just define the different types I need. The typedefs only contain
+ * the header of the message, so we need some space for keys and body
+ * To avoid to have to do multiple writes, lets add a chunk of memory
+ * to use. 1k should be more than enough for header, key and body.
+ */
+typedef union
+{
+ protocol_binary_request_no_extras plain;
+ protocol_binary_request_flush flush;
+ protocol_binary_request_incr incr;
+ protocol_binary_request_set set;
+ char bytes[1024];
+} command;
+
+typedef union
+{
+ protocol_binary_response_no_extras plain;
+ protocol_binary_response_incr incr;
+ protocol_binary_response_decr decr;
+ char bytes[1024];
+} response;
+
+enum test_return
+{
+ TEST_SKIP, TEST_PASS, TEST_PASS_RECONNECT, TEST_FAIL
+};
+
+/**
+ * Try to get an addrinfo struct for a given port on a given host
+ */
+static struct addrinfo *lookuphost(const char *hostname, const char *port)
+{
+ struct addrinfo *ai= 0;
+ struct addrinfo hints= {.ai_family=AF_UNSPEC,
+ .ai_protocol=IPPROTO_TCP,
+ .ai_socktype=SOCK_STREAM};
+ int error= getaddrinfo(hostname, port, &hints, &ai);
+
+ if (error != 0)
+ {
+ if (error != EAI_SYSTEM)
+ fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
+ else
+ perror("getaddrinfo()");
+ }
+
+ return ai;
+}
+
+/**
+ * Set the socket in nonblocking mode
+ * @return -1 if failure, the socket otherwise
+ */
+static int set_noblock(void)
+{
+ int flags= fcntl(sock, F_GETFL, 0);
+ if (flags == -1)
+ {
+ perror("Failed to get socket flags");
+ close(sock);
+ return -1;
+ }
+
+ if ((flags & O_NONBLOCK) != O_NONBLOCK)
+ {
+ if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
+ {
+ perror("Failed to set socket to nonblocking mode");
+ close(sock);
+ return -1;
+ }
+ }
+
+ return sock;
+}
+
+/**
+ * Try to open a connection to the server
+ * @param hostname the name of the server to connect to
+ * @param port the port number (or service) to connect to
+ * @return positive integer if success, -1 otherwise
+ */
+static int connect_server(const char *hostname, const char *port)
+{
+ struct addrinfo *ai= lookuphost(hostname, port);
+ sock= -1;
+ if (ai != NULL)
+ {
+ if ((sock=socket(ai->ai_family, ai->ai_socktype,
+ ai->ai_protocol)) != -1)
+ {
+ if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1)
+ {
+ fprintf(stderr, "Failed to connect socket: %s\n",
+ strerror(errno));
+ close(sock);
+ sock= -1;
+ }
+ else
+ {
+ sock= set_noblock();
+ }
+ } else
+ fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
+
+ freeaddrinfo(ai);
+ }
+
+ return sock;
+}
+
+static ssize_t timeout_io_op(int fd, short direction, void *buf, size_t len)
+{
+ ssize_t ret;
+
+ if (direction == POLLOUT)
+ ret= write(fd, buf, len);
+ else
+ ret= read(fd, buf, len);
+
+ if (ret == -1 && errno == EWOULDBLOCK) {
+ struct pollfd fds = {
+ .events= direction,
+ .fd= fd
+ };
+ int err= poll(&fds, 1, timeout * 1000);
+
+ if (err == 1)
+ {
+ if (direction == POLLOUT)
+ ret= write(fd, buf, len);
+ else
+ ret= read(fd, buf, len);
+ }
+ else if (err == 0)
+ {
+ errno = ETIMEDOUT;
+ }
+ else
+ {
+ perror("Failed to poll");
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Ensure that an expression is true. If it isn't print out a message similar
+ * to assert() and create a coredump if the user wants that. If not an error
+ * message is returned.
+ *
+ */
+static enum test_return ensure(bool val, const char *expression, const char *file, int line)
+{
+ if (!val)
+ {
+ if (verbose)
+ fprintf(stderr, "%s:%u: %s\n", file, line, expression);
+
+ if (do_core)
+ abort();
+
+ return TEST_FAIL;
+ }
+
+ return TEST_PASS;
+}
+
+#define verify(expression) do { if (ensure(expression, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0)
+#define execute(expression) do { if (ensure(expression == TEST_PASS, #expression, __FILE__, __LINE__) == TEST_FAIL) return TEST_FAIL; } while (0)
+
+/**
+ * Send a chunk of memory over the socket (retry if the call is iterrupted
+ */
+static enum test_return retry_write(const void* buf, size_t len)
+{
+ size_t offset= 0;
+ const char* ptr= buf;
+
+ do
+ {
+ size_t num_bytes= len - offset;
+ ssize_t nw= timeout_io_op(sock, POLLOUT, (void*)(ptr + offset), num_bytes);
+ if (nw == -1)
+ verify(errno == EINTR || errno == EAGAIN);
+ else
+ offset+= (size_t)nw;
+ } while (offset < len);
+
+ return TEST_PASS;
+}
+
+/**
+ * Resend a packet to the server (All fields in the command header should
+ * be in network byte order)
+ */
+static enum test_return resend_packet(command *cmd)
+{
+ size_t length= sizeof (protocol_binary_request_no_extras) +
+ ntohl(cmd->plain.message.header.request.bodylen);
+
+ execute(retry_write(cmd, length));
+ return TEST_PASS;
+}
+
+/**
+ * Send a command to the server. The command header needs to be updated
+ * to network byte order
+ */
+static enum test_return send_packet(command *cmd)
+{
+ /* Fix the byteorder of the header */
+ cmd->plain.message.header.request.keylen=
+ ntohs(cmd->plain.message.header.request.keylen);
+ cmd->plain.message.header.request.bodylen=
+ ntohl(cmd->plain.message.header.request.bodylen);
+ cmd->plain.message.header.request.cas=
+ ntohll(cmd->plain.message.header.request.cas);
+
+ execute(resend_packet(cmd));
+ return TEST_PASS;
+}
+
+/**
+ * Read a fixed length chunk of data from the server
+ */
+static enum test_return retry_read(void *buf, size_t len)
+{
+ size_t offset= 0;
+ do
+ {
+ ssize_t nr= timeout_io_op(sock, POLLIN, ((char*) buf) + offset, len - offset);
+ switch (nr) {
+ case -1 :
+ verify(errno == EINTR || errno == EAGAIN);
+ break;
+ case 0:
+ return TEST_FAIL;
+ default:
+ offset+= (size_t)nr;
+ }
+ } while (offset < len);
+
+ return TEST_PASS;
+}
+
+/**
+ * Receive a response from the server and conver the fields in the header
+ * to local byte order
+ */
+static enum test_return recv_packet(response *rsp)
+{
+ execute(retry_read(rsp, sizeof (protocol_binary_response_no_extras)));
+
+ /* Fix the byte order in the packet header */
+ rsp->plain.message.header.response.keylen=
+ ntohs(rsp->plain.message.header.response.keylen);
+ rsp->plain.message.header.response.status=
+ ntohs(rsp->plain.message.header.response.status);
+ rsp->plain.message.header.response.bodylen=
+ ntohl(rsp->plain.message.header.response.bodylen);
+ rsp->plain.message.header.response.cas=
+ ntohll(rsp->plain.message.header.response.cas);
+
+ size_t bodysz= rsp->plain.message.header.response.bodylen;
+ if (bodysz > 0)
+ execute(retry_read(rsp->bytes + sizeof (protocol_binary_response_no_extras), bodysz));
+
+ return TEST_PASS;
+}
+
+/**
+ * Create a storage command (add, set, replace etc)
+ *
+ * @param cmd destination buffer
+ * @param cc the storage command to create
+ * @param key the key to store
+ * @param keylen the length of the key
+ * @param dta the data to store with the key
+ * @param dtalen the length of the data to store with the key
+ * @param flags the flags to store along with the key
+ * @param exp the expiry time for the key
+ */
+static void storage_command(command *cmd,
+ uint8_t cc,
+ const void* key,
+ size_t keylen,
+ const void* dta,
+ size_t dtalen,
+ uint32_t flags,
+ uint32_t exp)
+{
+ /* all of the storage commands use the same command layout */
+ protocol_binary_request_set *request= &cmd->set;
+
+ memset(request, 0, sizeof (*request));
+ request->message.header.request.magic= PROTOCOL_BINARY_REQ;
+ request->message.header.request.opcode= cc;
+ request->message.header.request.keylen= (uint16_t)keylen;
+ request->message.header.request.extlen= 8;
+ request->message.header.request.bodylen= (uint32_t)(keylen + 8 + dtalen);
+ request->message.header.request.opaque= 0xdeadbeef;
+ request->message.body.flags= flags;
+ request->message.body.expiration= exp;
+
+ off_t key_offset= sizeof (protocol_binary_request_no_extras) + 8;
+ memcpy(cmd->bytes + key_offset, key, keylen);
+ if (dta != NULL)
+ memcpy(cmd->bytes + key_offset + keylen, dta, dtalen);
+}
+
+/**
+ * Create a basic command to send to the server
+ * @param cmd destination buffer
+ * @param cc the command to create
+ * @param key the key to store
+ * @param keylen the length of the key
+ * @param dta the data to store with the key
+ * @param dtalen the length of the data to store with the key
+ */
+static void raw_command(command *cmd,
+ uint8_t cc,
+ const void* key,
+ size_t keylen,
+ const void* dta,
+ size_t dtalen)
+{
+ /* all of the storage commands use the same command layout */
+ memset(cmd, 0, sizeof (*cmd));
+ cmd->plain.message.header.request.magic= PROTOCOL_BINARY_REQ;
+ cmd->plain.message.header.request.opcode= cc;
+ cmd->plain.message.header.request.keylen= (uint16_t)keylen;
+ cmd->plain.message.header.request.bodylen= (uint32_t)(keylen + dtalen);
+ cmd->plain.message.header.request.opaque= 0xdeadbeef;
+
+ off_t key_offset= sizeof (protocol_binary_request_no_extras);
+
+ if (key != NULL)
+ memcpy(cmd->bytes + key_offset, key, keylen);
+
+ if (dta != NULL)
+ memcpy(cmd->bytes + key_offset + keylen, dta, dtalen);
+}
+
+/**
+ * Create the flush command
+ * @param cmd destination buffer
+ * @param cc the command to create (FLUSH/FLUSHQ)
+ * @param exptime when to flush
+ * @param use_extra to force using of the extra field?
+ */
+static void flush_command(command *cmd,
+ uint8_t cc, uint32_t exptime, bool use_extra)
+{
+ memset(cmd, 0, sizeof (cmd->flush));
+ cmd->flush.message.header.request.magic= PROTOCOL_BINARY_REQ;
+ cmd->flush.message.header.request.opcode= cc;
+ cmd->flush.message.header.request.opaque= 0xdeadbeef;
+
+ if (exptime != 0 || use_extra)
+ {
+ cmd->flush.message.header.request.extlen= 4;
+ cmd->flush.message.body.expiration= htonl(exptime);
+ cmd->flush.message.header.request.bodylen= 4;
+ }
+}
+
+/**
+ * Create a incr/decr command
+ * @param cc the cmd to create (FLUSH/FLUSHQ)
+ * @param key the key to operate on
+ * @param keylen the number of bytes in the key
+ * @param delta the number to add/subtract
+ * @param initial the initial value if the key doesn't exist
+ * @param exp when the key should expire if it isn't set
+ */
+static void arithmetic_command(command *cmd,
+ uint8_t cc,
+ const void* key,
+ size_t keylen,
+ uint64_t delta,
+ uint64_t initial,
+ uint32_t exp)
+{
+ memset(cmd, 0, sizeof (cmd->incr));
+ cmd->incr.message.header.request.magic= PROTOCOL_BINARY_REQ;
+ cmd->incr.message.header.request.opcode= cc;
+ cmd->incr.message.header.request.keylen= (uint16_t)keylen;
+ cmd->incr.message.header.request.extlen= 20;
+ cmd->incr.message.header.request.bodylen= (uint32_t)(keylen + 20);
+ cmd->incr.message.header.request.opaque= 0xdeadbeef;
+ cmd->incr.message.body.delta= htonll(delta);
+ cmd->incr.message.body.initial= htonll(initial);
+ cmd->incr.message.body.expiration= htonl(exp);
+
+ off_t key_offset= sizeof (protocol_binary_request_no_extras) + 20;
+ memcpy(cmd->bytes + key_offset, key, keylen);
+}
+
+/**
+ * Validate the response header from the server
+ * @param rsp the response to check
+ * @param cc the expected command
+ * @param status the expected status
+ */
+static enum test_return validate_response_header(response *rsp,
+ uint8_t cc, uint16_t status)
+{
+ verify(rsp->plain.message.header.response.magic == PROTOCOL_BINARY_RES);
+ verify(rsp->plain.message.header.response.opcode == cc);
+ verify(rsp->plain.message.header.response.datatype == PROTOCOL_BINARY_RAW_BYTES);
+ verify(rsp->plain.message.header.response.status == status);
+ verify(rsp->plain.message.header.response.opaque == 0xdeadbeef);
+
+ if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS)
+ {
+ switch (cc) {
+ case PROTOCOL_BINARY_CMD_ADDQ:
+ case PROTOCOL_BINARY_CMD_APPENDQ:
+ case PROTOCOL_BINARY_CMD_DECREMENTQ:
+ case PROTOCOL_BINARY_CMD_DELETEQ:
+ case PROTOCOL_BINARY_CMD_FLUSHQ:
+ case PROTOCOL_BINARY_CMD_INCREMENTQ:
+ case PROTOCOL_BINARY_CMD_PREPENDQ:
+ case PROTOCOL_BINARY_CMD_QUITQ:
+ case PROTOCOL_BINARY_CMD_REPLACEQ:
+ case PROTOCOL_BINARY_CMD_SETQ:
+ verify("Quiet command shouldn't return on success" == NULL);
+ default:
+ break;
+ }
+
+ switch (cc) {
+ case PROTOCOL_BINARY_CMD_ADD:
+ case PROTOCOL_BINARY_CMD_REPLACE:
+ case PROTOCOL_BINARY_CMD_SET:
+ case PROTOCOL_BINARY_CMD_APPEND:
+ case PROTOCOL_BINARY_CMD_PREPEND:
+ verify(rsp->plain.message.header.response.keylen == 0);
+ verify(rsp->plain.message.header.response.extlen == 0);
+ verify(rsp->plain.message.header.response.bodylen == 0);
+ verify(rsp->plain.message.header.response.cas != 0);
+ break;
+ case PROTOCOL_BINARY_CMD_FLUSH:
+ case PROTOCOL_BINARY_CMD_NOOP:
+ case PROTOCOL_BINARY_CMD_QUIT:
+ case PROTOCOL_BINARY_CMD_DELETE:
+ verify(rsp->plain.message.header.response.keylen == 0);
+ verify(rsp->plain.message.header.response.extlen == 0);
+ verify(rsp->plain.message.header.response.bodylen == 0);
+ verify(rsp->plain.message.header.response.cas == 0);
+ break;
+
+ case PROTOCOL_BINARY_CMD_DECREMENT:
+ case PROTOCOL_BINARY_CMD_INCREMENT:
+ verify(rsp->plain.message.header.response.keylen == 0);
+ verify(rsp->plain.message.header.response.extlen == 0);
+ verify(rsp->plain.message.header.response.bodylen == 8);
+ verify(rsp->plain.message.header.response.cas != 0);
+ break;
+
+ case PROTOCOL_BINARY_CMD_STAT:
+ verify(rsp->plain.message.header.response.extlen == 0);
+ /* key and value exists in all packets except in the terminating */
+ verify(rsp->plain.message.header.response.cas == 0);
+ break;
+
+ case PROTOCOL_BINARY_CMD_VERSION:
+ verify(rsp->plain.message.header.response.keylen == 0);
+ verify(rsp->plain.message.header.response.extlen == 0);
+ verify(rsp->plain.message.header.response.bodylen != 0);
+ verify(rsp->plain.message.header.response.cas == 0);
+ break;
+
+ case PROTOCOL_BINARY_CMD_GET:
+ case PROTOCOL_BINARY_CMD_GETQ:
+ verify(rsp->plain.message.header.response.keylen == 0);
+ verify(rsp->plain.message.header.response.extlen == 4);
+ verify(rsp->plain.message.header.response.cas != 0);
+ break;
+
+ case PROTOCOL_BINARY_CMD_GETK:
+ case PROTOCOL_BINARY_CMD_GETKQ:
+ verify(rsp->plain.message.header.response.keylen != 0);
+ verify(rsp->plain.message.header.response.extlen == 4);
+ verify(rsp->plain.message.header.response.cas != 0);
+ break;
+
+ default:
+ /* Undefined command code */
+ break;
+ }
+ }
+ else
+ {
+ verify(rsp->plain.message.header.response.cas == 0);
+ verify(rsp->plain.message.header.response.extlen == 0);
+ if (cc != PROTOCOL_BINARY_CMD_GETK)
+ {
+ verify(rsp->plain.message.header.response.keylen == 0);
+ }
+ }
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_noop(void)
+{
+ command cmd;
+ response rsp;
+ raw_command(&cmd, PROTOCOL_BINARY_CMD_NOOP, NULL, 0, NULL, 0);
+ execute(send_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_NOOP,
+ PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_quit_impl(uint8_t cc)
+{
+ command cmd;
+ response rsp;
+ raw_command(&cmd, cc, NULL, 0, NULL, 0);
+
+ execute(send_packet(&cmd));
+ if (cc == PROTOCOL_BINARY_CMD_QUIT)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_QUIT,
+ PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ }
+
+ /* Socket should be closed now, read should return 0 */
+ verify(timeout_io_op(sock, POLLIN, rsp.bytes, sizeof(rsp.bytes)) == 0);
+
+ return TEST_PASS_RECONNECT;
+}
+
+static enum test_return test_binary_quit(void)
+{
+ return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUIT);
+}
+
+static enum test_return test_binary_quitq(void)
+{
+ return test_binary_quit_impl(PROTOCOL_BINARY_CMD_QUITQ);
+}
+
+static enum test_return test_binary_set_impl(const char* key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+
+ uint64_t value= 0xdeadbeefdeadcafe;
+ storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
+
+ /* set should always work */
+ for (int ii= 0; ii < 10; ii++)
+ {
+ if (ii == 0)
+ execute(send_packet(&cmd));
+ else
+ execute(resend_packet(&cmd));
+
+ if (cc == PROTOCOL_BINARY_CMD_SET)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ }
+ else
+ execute(test_binary_noop());
+ }
+
+ /* try to set with the correct CAS value */
+ cmd.plain.message.header.request.cas=
+ htonll(rsp.plain.message.header.response.cas);
+ execute(resend_packet(&cmd));
+ if (cc == PROTOCOL_BINARY_CMD_SET)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ }
+ else
+ execute(test_binary_noop());
+
+ /* try to set with an incorrect CAS value */
+ cmd.plain.message.header.request.cas=
+ htonll(rsp.plain.message.header.response.cas - 1);
+ execute(resend_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS));
+
+ return test_binary_noop();
+}
+
+static enum test_return test_binary_set(void)
+{
+ return test_binary_set_impl("test_binary_set", PROTOCOL_BINARY_CMD_SET);
+}
+
+static enum test_return test_binary_setq(void)
+{
+ return test_binary_set_impl("test_binary_setq", PROTOCOL_BINARY_CMD_SETQ);
+}
+
+static enum test_return test_binary_add_impl(const char* key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+ uint64_t value= 0xdeadbeefdeadcafe;
+ storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
+
+ /* first add should work, rest of them should fail (even with cas
+ as wildcard */
+ for (int ii=0; ii < 10; ii++)
+ {
+ if (ii == 0)
+ execute(send_packet(&cmd));
+ else
+ execute(resend_packet(&cmd));
+
+ if (cc == PROTOCOL_BINARY_CMD_ADD || ii > 0)
+ {
+ uint16_t expected_result;
+ if (ii == 0)
+ expected_result= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+ else
+ expected_result= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, expected_result));
+ }
+ else
+ execute(test_binary_noop());
+ }
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_add(void)
+{
+ return test_binary_add_impl("test_binary_add", PROTOCOL_BINARY_CMD_ADD);
+}
+
+static enum test_return test_binary_addq(void)
+{
+ return test_binary_add_impl("test_binary_addq", PROTOCOL_BINARY_CMD_ADDQ);
+}
+
+static enum test_return set_item(const char *key, const char *value)
+{
+ command cmd;
+ response rsp;
+ storage_command(&cmd, PROTOCOL_BINARY_CMD_SET, key, strlen(key),
+ value, strlen(value), 0, 0);
+ execute(send_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_SET,
+ PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_replace_impl(const char* key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+ uint64_t value= 0xdeadbeefdeadcafe;
+ storage_command(&cmd, cc, key, strlen(key), &value, sizeof (value), 0, 0);
+
+ /* first replace should fail, successive should succeed (when the
+ item is added! */
+ for (int ii= 0; ii < 10; ii++)
+ {
+ if (ii == 0)
+ execute(send_packet(&cmd));
+ else
+ execute(resend_packet(&cmd));
+
+ if (cc == PROTOCOL_BINARY_CMD_REPLACE || ii == 0)
+ {
+ uint16_t expected_result;
+ if (ii == 0)
+ expected_result=PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
+ else
+ expected_result=PROTOCOL_BINARY_RESPONSE_SUCCESS;
+
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, expected_result));
+
+ if (ii == 0)
+ execute(set_item(key, key));
+ }
+ else
+ execute(test_binary_noop());
+ }
+
+ /* verify that replace with CAS value works! */
+ cmd.plain.message.header.request.cas=
+ htonll(rsp.plain.message.header.response.cas);
+ execute(resend_packet(&cmd));
+
+ if (cc == PROTOCOL_BINARY_CMD_REPLACE)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ }
+ else
+ execute(test_binary_noop());
+
+ /* try to set with an incorrect CAS value */
+ cmd.plain.message.header.request.cas=
+ htonll(rsp.plain.message.header.response.cas - 1);
+ execute(resend_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_replace(void)
+{
+ return test_binary_replace_impl("test_binary_replace", PROTOCOL_BINARY_CMD_REPLACE);
+}
+
+static enum test_return test_binary_replaceq(void)
+{
+ return test_binary_replace_impl("test_binary_replaceq", PROTOCOL_BINARY_CMD_REPLACEQ);
+}
+
+static enum test_return test_binary_delete_impl(const char *key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+ raw_command(&cmd, cc, key, strlen(key), NULL, 0);
+
+ /* The delete shouldn't work the first time, because the item isn't there */
+ execute(send_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
+ execute(set_item(key, key));
+
+ /* The item should be present now, resend*/
+ execute(resend_packet(&cmd));
+ if (cc == PROTOCOL_BINARY_CMD_DELETE)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ }
+
+ execute(test_binary_noop());
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_delete(void)
+{
+ return test_binary_delete_impl("test_binary_delete", PROTOCOL_BINARY_CMD_DELETE);
+}
+
+static enum test_return test_binary_deleteq(void)
+{
+ return test_binary_delete_impl("test_binary_deleteq", PROTOCOL_BINARY_CMD_DELETEQ);
+}
+
+static enum test_return test_binary_get_impl(const char *key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+
+ raw_command(&cmd, cc, key, strlen(key), NULL, 0);
+ execute(send_packet(&cmd));
+
+ if (cc == PROTOCOL_BINARY_CMD_GET || cc == PROTOCOL_BINARY_CMD_GETK)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
+ }
+ else
+ execute(test_binary_noop());
+
+ execute(set_item(key, key));
+ execute(resend_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_get(void)
+{
+ return test_binary_get_impl("test_binary_get", PROTOCOL_BINARY_CMD_GET);
+}
+
+static enum test_return test_binary_getk(void)
+{
+ return test_binary_get_impl("test_binary_getk", PROTOCOL_BINARY_CMD_GETK);
+}
+
+static enum test_return test_binary_getq(void)
+{
+ return test_binary_get_impl("test_binary_getq", PROTOCOL_BINARY_CMD_GETQ);
+}
+
+static enum test_return test_binary_getkq(void)
+{
+ return test_binary_get_impl("test_binary_getkq", PROTOCOL_BINARY_CMD_GETKQ);
+}
+
+static enum test_return test_binary_incr_impl(const char* key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+ arithmetic_command(&cmd, cc, key, strlen(key), 1, 0, 0);
+
+ uint64_t ii;
+ for (ii= 0; ii < 10; ++ii)
+ {
+ if (ii == 0)
+ execute(send_packet(&cmd));
+ else
+ execute(resend_packet(&cmd));
+
+ if (cc == PROTOCOL_BINARY_CMD_INCREMENT)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ verify(ntohll(rsp.incr.message.body.value) == ii);
+ }
+ else
+ execute(test_binary_noop());
+ }
+
+ /* @todo add incorrect CAS */
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_incr(void)
+{
+ return test_binary_incr_impl("test_binary_incr", PROTOCOL_BINARY_CMD_INCREMENT);
+}
+
+static enum test_return test_binary_incrq(void)
+{
+ return test_binary_incr_impl("test_binary_incrq", PROTOCOL_BINARY_CMD_INCREMENTQ);
+}
+
+static enum test_return test_binary_decr_impl(const char* key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+ arithmetic_command(&cmd, cc, key, strlen(key), 1, 9, 0);
+
+ int ii;
+ for (ii= 9; ii > -1; --ii)
+ {
+ if (ii == 9)
+ execute(send_packet(&cmd));
+ else
+ execute(resend_packet(&cmd));
+
+ if (cc == PROTOCOL_BINARY_CMD_DECREMENT)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ verify(ntohll(rsp.decr.message.body.value) == (uint64_t)ii);
+ }
+ else
+ execute(test_binary_noop());
+ }
+
+ /* decr 0 should not wrap */
+ execute(resend_packet(&cmd));
+ if (cc == PROTOCOL_BINARY_CMD_DECREMENT)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ verify(ntohll(rsp.decr.message.body.value) == 0);
+ }
+ else
+ {
+ /* @todo get the value and verify! */
+
+ }
+
+ /* @todo add incorrect cas */
+ execute(test_binary_noop());
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_decr(void)
+{
+ return test_binary_decr_impl("test_binary_decr",
+ PROTOCOL_BINARY_CMD_DECREMENT);
+}
+
+static enum test_return test_binary_decrq(void)
+{
+ return test_binary_decr_impl("test_binary_decrq",
+ PROTOCOL_BINARY_CMD_DECREMENTQ);
+}
+
+static enum test_return test_binary_version(void)
+{
+ command cmd;
+ response rsp;
+ raw_command(&cmd, PROTOCOL_BINARY_CMD_VERSION, NULL, 0, NULL, 0);
+
+ execute(send_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_VERSION,
+ PROTOCOL_BINARY_RESPONSE_SUCCESS));
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_flush_impl(const char *key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+
+ for (int ii= 0; ii < 2; ++ii)
+ {
+ execute(set_item(key, key));
+ flush_command(&cmd, cc, 0, ii == 0);
+ execute(send_packet(&cmd));
+
+ if (cc == PROTOCOL_BINARY_CMD_FLUSH)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ }
+ else
+ execute(test_binary_noop());
+
+ raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0);
+ execute(send_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET,
+ PROTOCOL_BINARY_RESPONSE_KEY_ENOENT));
+ }
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_flush(void)
+{
+ return test_binary_flush_impl("test_binary_flush", PROTOCOL_BINARY_CMD_FLUSH);
+}
+
+static enum test_return test_binary_flushq(void)
+{
+ return test_binary_flush_impl("test_binary_flushq", PROTOCOL_BINARY_CMD_FLUSHQ);
+}
+
+static enum test_return test_binary_concat_impl(const char *key, uint8_t cc)
+{
+ command cmd;
+ response rsp;
+ const char *value;
+
+ if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
+ value="hello";
+ else
+ value=" world";
+
+ execute(set_item(key, value));
+
+ if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_APPENDQ)
+ value=" world";
+ else
+ value="hello";
+
+ raw_command(&cmd, cc, key, strlen(key), value, strlen(value));
+ execute(send_packet(&cmd));
+ if (cc == PROTOCOL_BINARY_CMD_APPEND || cc == PROTOCOL_BINARY_CMD_PREPEND)
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ }
+ else
+ execute(test_binary_noop());
+
+ raw_command(&cmd, PROTOCOL_BINARY_CMD_GET, key, strlen(key), NULL, 0);
+ execute(send_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_GET,
+ PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ verify(rsp.plain.message.header.response.bodylen - 4 == 11);
+ verify(memcmp(rsp.bytes + 28, "hello world", 11) == 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_append(void)
+{
+ return test_binary_concat_impl("test_binary_append", PROTOCOL_BINARY_CMD_APPEND);
+}
+
+static enum test_return test_binary_prepend(void)
+{
+ return test_binary_concat_impl("test_binary_prepend", PROTOCOL_BINARY_CMD_PREPEND);
+}
+
+static enum test_return test_binary_appendq(void)
+{
+ return test_binary_concat_impl("test_binary_appendq", PROTOCOL_BINARY_CMD_APPENDQ);
+}
+
+static enum test_return test_binary_prependq(void)
+{
+ return test_binary_concat_impl("test_binary_prependq", PROTOCOL_BINARY_CMD_PREPENDQ);
+}
+
+static enum test_return test_binary_stat(void)
+{
+ command cmd;
+ response rsp;
+
+ raw_command(&cmd, PROTOCOL_BINARY_CMD_STAT, NULL, 0, NULL, 0);
+ execute(send_packet(&cmd));
+
+ do
+ {
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, PROTOCOL_BINARY_CMD_STAT,
+ PROTOCOL_BINARY_RESPONSE_SUCCESS));
+ } while (rsp.plain.message.header.response.keylen != 0);
+
+ return TEST_PASS;
+}
+
+static enum test_return test_binary_illegal(void)
+{
+ command cmd;
+ response rsp;
+ uint8_t cc= 0x1b;
+
+ while (cc != 0x00)
+ {
+ raw_command(&cmd, cc, NULL, 0, NULL, 0);
+ execute(send_packet(&cmd));
+ execute(recv_packet(&rsp));
+ verify(validate_response_header(&rsp, cc, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND));
+ ++cc;
+ }
+
+ return TEST_PASS;
+}
+
+typedef enum test_return(*TEST_FUNC)(void);
+
+struct testcase
+{
+ const char *description;
+ TEST_FUNC function;
+};
+
+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},
+ { NULL, NULL}
+};
+
+int main(int argc, char **argv)
+{
+ static const char * const status_msg[]= {"[skip]", "[pass]", "[pass]", "[FAIL]"};
+ int total= 0;
+ int failed= 0;
+ const char *hostname= "localhost";
+ const char *port= "11211";
+ int cmd;
+
+ while ((cmd= getopt(argc, argv, "t:vch:p:?")) != EOF)
+ {
+ switch (cmd) {
+ case 't':
+ timeout= atoi(optarg);
+ if (timeout == 0)
+ {
+ fprintf(stderr, "Invalid timeout. Please specify a number for -t\n");
+ return 1;
+ }
+ break;
+ case 'v': verbose= true;
+ break;
+ case 'c': do_core= true;
+ break;
+ case 'h': hostname= optarg;
+ break;
+ case 'p': port= optarg;
+ break;
+ default:
+ fprintf(stderr, "Usage: %s [-h hostname] [-p port] [-c] [-v] [-t n]\n"
+ "\t-c\tGenerate coredump if a test fails\n"
+ "\t-v\tVerbose test output (print out the assertion)\n"
+ "\t-c n\tSet the timeout for io-operations to n seconds\n",
+ argv[0]);
+ return 1;
+ }
+ }
+
+ sock= connect_server(hostname, port);
+ if (sock == -1)
+ {
+ fprintf(stderr, "Failed to connect to <%s:%s>: %s\n",
+ hostname, port, strerror(errno));
+ return 1;
+ }
+
+ for (int ii= 0; testcases[ii].description != NULL; ++ii)
+ {
+ ++total;
+ fprintf(stdout, "%s\t\t", 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;
+ }
+ else if (ret == TEST_PASS_RECONNECT)
+ reconnect= true;
+
+ if (reconnect)
+ {
+ (void) close(sock);
+ if ((sock=connect_server(hostname, port)) == -1)
+ {
+ fprintf(stderr, "Failed to connect to <%s:%s>: %s\n",
+ hostname, port, strerror(errno));
+ fprintf(stderr, "%d of %d tests failed\n", failed, total);
+ return 1;
+ }
+ }
+ }
+
+ (void) close(sock);
+ if (failed == 0)
+ fprintf(stdout, "All tests passed\n");
+ else
+ fprintf(stderr, "%d of %d tests failed\n", failed, total);
+
+ return (failed == 0) ? 0 : 1;
+}
}
gettimeofday(&end_time, NULL);
- elapsed_time= timedif(end_time, start_time);
- elapsed_time /= (long)num_of_tests;
+ elapsed_time= (long) timedif(end_time, start_time);
+ elapsed_time /= (long) num_of_tests;
if (elapsed_time > slowest_time)
{
# the COPYING file in this directory for full text.
AC_PREREQ(2.59)
-AC_INIT([libmemcached],[0.32],[http://tangent.org/552/libmemcached.html])
+AC_INIT([libmemcached],[0.33],[http://tangent.org/552/libmemcached.html])
AC_CONFIG_SRCDIR([libmemcached/memcached.c])
AC_CONFIG_AUX_DIR(config)
AM_CONFIG_HEADER([config.h])
memcached_behavior.pod\
memcached_callback.pod\
memcached_server_st.pod\
+ memcapable.pod \
memcat.pod\
memcached_create.pod\
memcached_pool.pod\
man_MANS = libmemcached.3\
libmemcached_examples.3\
+ memcapable.1\
memcat.1\
memcp.1\
memerror.1\
if BUILD_LIBMEMCACHEDUTIL
man_MANS+= libmemcachedutil.3 \
+ memcached_pool_behavior_set.3 \
+ memcached_pool_behavior_get.3 \
memcached_pool_create.3 \
memcached_pool_destroy.3 \
memcached_pool_push.3 \
endif
-libmemcached.3: libmemcached.pod
+libmemcached.3: libmemcached.pod
${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/libmemcached.pod > libmemcached.3
-libmemcachedutil.3: libmemcachedutil.pod
+libmemcachedutil.3: libmemcachedutil.pod
${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/libmemcachedutil.pod > libmemcachedutil.3
libmemcached_examples.3: libmemcached_examples.pod
${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/libmemcached_examples.pod > libmemcached_examples.3
-memcached_create.3: memcached_create.pod
+memcached_create.3: memcached_create.pod
${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_create.pod > memcached_create.3
memcached_free.3: memcached_create.pod
memcached_set_user_data.3: memcached_user_data.pod
${POD2MAN} -c "libmemcached" -r "" -s 3 ${top_srcdir}/docs/memcached_user_data.pod > memcached_set_user_data.3
-memcached_pool_create.3: memcached_pool.pod
+memcached_pool_behavior_get.3: memcached_pool.pod
+ ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_behavior_get.3
+
+memcached_pool_behavior_set.3: memcached_pool.pod
+ ${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_behavior_set.3
+
+memcached_pool_create.3: memcached_pool.pod
${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_create.3
-memcached_pool_destroy.3: memcached_pool.pod
+memcached_pool_destroy.3: memcached_pool.pod
${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_destroy.3
-memcached_pool_pop.3: memcached_pool.pod
+memcached_pool_pop.3: memcached_pool.pod
${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_pop.3
-memcached_pool_push.3: memcached_pool.pod
+memcached_pool_push.3: memcached_pool.pod
${POD2MAN} -c "libmemcachedutil" -r "" -s 3 ${top_srcdir}/docs/memcached_pool.pod > memcached_pool_push.3
memcp.1: memcp.pod
memslap.1: memslap.pod
${POD2MAN} -c "libmemcached" -r "" -s 1 ${top_srcdir}/docs/memslap.pod > memslap.1
+memcapable.1: memcapable.pod
+ ${POD2MAN} -c "libmemcached" -r "" -s 1 ${top_srcdir}/docs/memcapable.pod > memcapable.1
+
memcat.1: memcat.pod
${POD2MAN} -c "libmemcached" -r "" -s 1 ${top_srcdir}/docs/memcat.pod > memcat.1
memflush.1: memflush.pod
${POD2MAN} -c "libmemcached" -r "" -s 1 ${top_srcdir}/docs/memflush.pod > memflush.1
-test:
+test:
podchecker $(top_srcdir)/docs/libmemcached.pod
podchecker $(top_srcdir)/docs/memcached_flush.pod
podchecker $(top_srcdir)/docs/memcached_stats.pod
podchecker $(top_srcdir)/docs/memcached_pool.pod
podchecker $(top_srcdir)/docs/memcached_memory_allocators.pod
podchecker $(top_srcdir)/docs/memcached_user_data.pod
+ podchecker $(top_srcdir)/docs/memcapable.pod
html-local:
pod2htmltree "/libmemcached" .
memcached_st *memcached_create (memcached_st *ptr);
+ memcached_return memcached_pool_behavior_set(memcached_pool_st *pool,
+ memcached_behavior flag,
+ uint64_t data)
+ memcached_return memcached_pool_behavior_get(memcached_pool_st *pool,
+ memcached_behavior flag,
+ uint64_t *value)
=head1 DESCRIPTION
memcached_pool_push() is used to return a connection structure back to the pool.
+memcached_pool_behavior_set() and memcached_pool_behagior_get() is
+used to get/set behavior flags on all connections in the pool.
+
+
=head1 RETURN
memcached_pool_create() returns a pointer to the newly created
memcached_pool_push() returns MEMCACHED_SUCCESS upon success.
+memcached_pool_behavior_get() and memcached_pool_behavior_get()
+returns MEMCACHED_SUCCESS upon success.
+
=head1 HOME
To find out more information please check:
=head1 SEE ALSO
-memcached(1) libmemcached(3) memcached_create(3) memcached_free(3) libmemcachedutil(3)
+memcached(1) libmemcached(3) memcached_create(3) memcached_free(3) libmemcachedutil(3) memcached_behavior_get(3) memcached_behavior_set(3)
=cut
--- /dev/null
+=head1 NAME
+
+memcapable - Check memcached server capabilites
+
+=head1 SYNOPSIS
+
+ memcat [-h hostname] [-p port] [-c] [-v] [-t n]
+
+=head1 DESCRIPTION
+
+B<memcapable> connects to the specified memcached server and tries to
+determine its capabilities by running the various commands and verifying
+the response.
+
+=head1 OPTIONS
+
+The following options are supported:
+
+=over 3
+
+=item -h hostname
+
+Specify the hostname to connect to. The default is I<localhost>
+
+=item -p port
+
+Specify the port number to connect to. The default is I<11211>
+
+=item -c
+
+Generate a coredump when it detects an error from the server.
+
+=item -v
+
+Print out the comparison when it detects an error from the server.
+
+=item -t n
+
+Set the timeout from an IO operation to/from the server to I<n> seconds.
+
+=back
+
+=head1 LIMITATIONS
+
+The current version of memcapable will only verify the binary protocol.
+
+=head1 HOME
+
+To find out more information please check:
+L<http://tangent.org/552/libmemcached.html>
+
+=head1 AUTHOR
+
+Trond Norbye, E<lt>trond.norbye@sun.comE<gt>
+
+=head1 SEE ALSO
+
+memcached(1) libmemcached(3)
+
+=cut
+
EXTRA_DIST = libmemcached_probes.d memcached/README.txt libmemcached.ver \
memcached_configure.h.in
-EXTRA_HEADERS =
-BUILT_SOURCES=
+EXTRA_HEADERS =
+BUILT_SOURCES=
noinst_HEADERS = libmemcached_probes.h \
memcached_io.h \
libmemcached_la_SOURCES += hsieh_hash.c
endif
-if BUILD_BYTEORDER
-libmemcached_la_SOURCES += byteorder.c
-endif
-
libmemcached_la_DEPENDENCIES= libmemcachedcallbacks.la
libmemcached_la_LIBADD= $(LIBM) libmemcachedcallbacks.la
libmemcached_la_LDFLAGS = -version-info $(MEMCACHED_LIBRARY_VERSION) $(LD_VERSION_SCRIPT) $(LIBM)
+if BUILD_BYTEORDER
+noinst_LTLIBRARIES += libbyteorder.la
+libbyteorder_la_SOURCES= byteorder.c byteorder.h
+libmemcached_la_LIBADD += libbyteorder.la
+libmemcached_la_DEPENDENCIES+= libbyteorder.la
+endif
+
if HAVE_DTRACE
BUILT_SOURCES+= dtrace_probes.h
libmemcached_la_SOURCES += libmemcached_probes.d
-#include "common.h"
+#include "byteorder.h"
/* Byte swap a 64-bit number. */
-static inline uint64_t swap64(uint64_t in)
+static inline uint64_t swap64(uint64_t in)
{
#ifndef BYTEORDER_BIG_ENDIAN
/* Little endian, flip the bytes around until someone makes a faster/better
* way to do this. */
uint64_t rv= 0;
uint8_t x= 0;
- for(x= 0; x < 8; x++)
+ for(x= 0; x < 8; x++)
{
rv= (rv << 8) | (in & 0xff);
in >>= 8;
--- /dev/null
+#ifndef LIBMEMCACHED_BYTEORDER_H
+#define LIBMEMCACHED_BYTEORDER_H
+
+#include "config.h"
+
+/* Define this here, which will turn on the visibilty controls while we're
+ * building libmemcached.
+ */
+#define BUILDING_LIBMEMCACHED 1
+
+#include "libmemcached/memcached.h"
+
+#ifndef HAVE_HTONLL
+LIBMEMCACHED_LOCAL
+extern uint64_t ntohll(uint64_t);
+LIBMEMCACHED_LOCAL
+extern uint64_t htonll(uint64_t);
+#endif
+
+#endif /* LIBMEMCACHED_BYTEORDER_H */
#include "libmemcached/memcached_internal.h"
#include "libmemcached/libmemcached_probes.h"
#include "libmemcached/memcached/protocol_binary.h"
+#include "libmemcached/byteorder.h"
/* string value */
struct memcached_continuum_item_st {
LIBMEMCACHED_LOCAL
uint32_t generate_hash(memcached_st *ptr, const char *key, size_t key_length);
-#ifndef HAVE_HTONLL
-LIBMEMCACHED_LOCAL
-extern uint64_t ntohll(uint64_t);
-LIBMEMCACHED_LOCAL
-extern uint64_t htonll(uint64_t);
-#endif
-
LIBMEMCACHED_LOCAL
memcached_return memcached_purge(memcached_server_st *ptr);
-static inline memcached_return memcached_validate_key_length(size_t key_length,
+static inline memcached_return memcached_validate_key_length(size_t key_length,
bool binary) {
unlikely (key_length == 0)
return MEMCACHED_BAD_KEY_PROVIDED;
-
+
if (binary)
{
unlikely (key_length > 0xffff)
}
else
{
- unlikely (key_length >= MEMCACHED_MAX_KEY)
+ unlikely (key_length >= MEMCACHED_MAX_KEY)
return MEMCACHED_BAD_KEY_PROVIDED;
}
#endif
#define MEMCACHED_VERSION_STRING_LENGTH 24
-#define LIBMEMCACHED_VERSION_STRING "0.32"
+#define LIBMEMCACHED_VERSION_STRING "0.33"
struct memcached_analysis_st {
uint32_t average_item_size;
{
#endif
- /**
- * Definition of the legal "magic" values used in a packet.
- * See section 3.1 Magic byte
- */
- typedef enum {
- PROTOCOL_BINARY_REQ = 0x80,
- PROTOCOL_BINARY_RES = 0x81
- } protocol_binary_magic;
-
- /**
- * Definition of the valid response status numbers.
- * See section 3.2 Response Status
- */
- typedef enum {
- PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00,
- PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01,
- PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02,
- PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03,
- PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04,
- PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05,
- PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81,
- PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82
- } protocol_binary_response_status;
-
- /**
- * Defintion of the different command opcodes.
- * See section 3.3 Command Opcodes
- */
- typedef enum {
- PROTOCOL_BINARY_CMD_GET = 0x00,
- PROTOCOL_BINARY_CMD_SET = 0x01,
- PROTOCOL_BINARY_CMD_ADD = 0x02,
- PROTOCOL_BINARY_CMD_REPLACE = 0x03,
- PROTOCOL_BINARY_CMD_DELETE = 0x04,
- PROTOCOL_BINARY_CMD_INCREMENT = 0x05,
- PROTOCOL_BINARY_CMD_DECREMENT = 0x06,
- PROTOCOL_BINARY_CMD_QUIT = 0x07,
- PROTOCOL_BINARY_CMD_FLUSH = 0x08,
- PROTOCOL_BINARY_CMD_GETQ = 0x09,
- PROTOCOL_BINARY_CMD_NOOP = 0x0a,
- PROTOCOL_BINARY_CMD_VERSION = 0x0b,
- PROTOCOL_BINARY_CMD_GETK = 0x0c,
- PROTOCOL_BINARY_CMD_GETKQ = 0x0d,
- PROTOCOL_BINARY_CMD_APPEND = 0x0e,
- PROTOCOL_BINARY_CMD_PREPEND = 0x0f,
- PROTOCOL_BINARY_CMD_STAT = 0x10,
- PROTOCOL_BINARY_CMD_SETQ = 0x11,
- PROTOCOL_BINARY_CMD_ADDQ = 0x12,
- PROTOCOL_BINARY_CMD_REPLACEQ = 0x13,
- PROTOCOL_BINARY_CMD_DELETEQ = 0x14,
- PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15,
- PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16,
- PROTOCOL_BINARY_CMD_QUITQ = 0x17,
- PROTOCOL_BINARY_CMD_FLUSHQ = 0x18,
- PROTOCOL_BINARY_CMD_APPENDQ = 0x19,
- PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a
- } protocol_binary_command;
-
- /**
- * Definition of the data types in the packet
- * See section 3.4 Data Types
- */
- typedef enum {
- PROTOCOL_BINARY_RAW_BYTES = 0x00
- } protocol_binary_datatypes;
-
- /**
- * Definition of the header structure for a request packet.
- * See section 2
- */
- typedef union {
- struct {
- uint8_t magic;
- uint8_t opcode;
- uint16_t keylen;
- uint8_t extlen;
- uint8_t datatype;
- uint16_t reserved;
- uint32_t bodylen;
- uint32_t opaque;
- uint64_t cas;
- } request;
- uint8_t bytes[24];
- } protocol_binary_request_header;
-
- /**
- * Definition of the header structure for a response packet.
- * See section 2
- */
- typedef union {
- struct {
- uint8_t magic;
- uint8_t opcode;
- uint16_t keylen;
- uint8_t extlen;
- uint8_t datatype;
- uint16_t status;
- uint32_t bodylen;
- uint32_t opaque;
- uint64_t cas;
- } response;
- uint8_t bytes[24];
- } protocol_binary_response_header;
-
- /**
- * Definition of a request-packet containing no extras
- */
- typedef union {
- struct {
- protocol_binary_request_header header;
- } message;
- uint8_t bytes[sizeof(protocol_binary_request_header)];
- } protocol_binary_request_no_extras;
-
- /**
- * Definition of a response-packet containing no extras
- */
- typedef union {
- struct {
- protocol_binary_response_header header;
- } message;
- uint8_t bytes[sizeof(protocol_binary_response_header)];
- } protocol_binary_response_no_extras;
-
- /**
- * Definition of the packet used by the get, getq, getk and getkq command.
- * See section 4
- */
- typedef protocol_binary_request_no_extras protocol_binary_request_get;
- typedef protocol_binary_request_no_extras protocol_binary_request_getq;
- typedef protocol_binary_request_no_extras protocol_binary_request_getk;
- typedef protocol_binary_request_no_extras protocol_binary_request_getkq;
-
- /**
- * Definition of the packet returned from a successful get, getq, getk and
- * getkq.
- * See section 4
- */
- typedef union {
- struct {
- protocol_binary_response_header header;
- struct {
- uint32_t flags;
- } body;
- } message;
- uint8_t bytes[sizeof(protocol_binary_response_header) + 4];
- } protocol_binary_response_get;
-
- typedef protocol_binary_response_get protocol_binary_response_getq;
- typedef protocol_binary_response_get protocol_binary_response_getk;
- typedef protocol_binary_response_get protocol_binary_response_getkq;
-
- /**
- * Definition of the packet used by the delete command
- * See section 4
- */
- typedef protocol_binary_request_no_extras protocol_binary_request_delete;
-
- /**
- * Definition of the packet returned by the delete command
- * See section 4
- */
- typedef protocol_binary_response_no_extras protocol_binary_response_delete;
-
- /**
- * Definition of the packet used by the flush command
- * See section 4
- * Please note that the expiration field is optional, so remember to see
- * check the header.bodysize to see if it is present.
- */
- typedef union {
- struct {
- protocol_binary_request_header header;
- struct {
- uint32_t expiration;
- } body;
- } message;
- uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
- } protocol_binary_request_flush;
-
- /**
- * Definition of the packet returned by the flush command
- * See section 4
- */
- typedef protocol_binary_response_no_extras protocol_binary_response_flush;
-
- /**
- * Definition of the packet used by set, add and replace
- * See section 4
- */
- typedef union {
- struct {
- protocol_binary_request_header header;
- struct {
- uint32_t flags;
- uint32_t expiration;
- } body;
- } message;
- uint8_t bytes[sizeof(protocol_binary_request_header) + 8];
- } protocol_binary_request_set;
- typedef protocol_binary_request_set protocol_binary_request_add;
- typedef protocol_binary_request_set protocol_binary_request_replace;
-
- /**
- * Definition of the packet returned by set, add and replace
- * See section 4
- */
- typedef protocol_binary_response_no_extras protocol_binary_response_set;
- typedef protocol_binary_response_no_extras protocol_binary_response_add;
- typedef protocol_binary_response_no_extras protocol_binary_response_replace;
-
- /**
- * Definition of the noop packet
- * See section 4
- */
- typedef protocol_binary_request_no_extras protocol_binary_request_noop;
-
- /**
- * Definition of the packet returned by the noop command
- * See section 4
- */
- typedef protocol_binary_response_no_extras protocol_binary_response_noop;
-
- /**
- * Definition of the structure used by the increment and decrement
- * command.
- * See section 4
- */
- typedef union {
- struct {
- protocol_binary_request_header header;
- struct {
- uint64_t delta;
- uint64_t initial;
- uint32_t expiration;
- } body;
- } message;
- uint8_t bytes[sizeof(protocol_binary_request_header) + 20];
- } protocol_binary_request_incr;
- typedef protocol_binary_request_incr protocol_binary_request_decr;
-
- /**
- * Definition of the response from an incr or decr command
- * command.
- * See section 4
- */
- typedef union {
- struct {
- protocol_binary_response_header header;
- struct {
- uint64_t value;
- } body;
- } message;
- uint8_t bytes[sizeof(protocol_binary_response_header) + 8];
- } protocol_binary_response_incr;
- typedef protocol_binary_response_incr protocol_binary_response_decr;
-
- /**
- * Definition of the quit
- * See section 4
- */
- typedef protocol_binary_request_no_extras protocol_binary_request_quit;
-
- /**
- * Definition of the packet returned by the quit command
- * See section 4
- */
- typedef protocol_binary_response_no_extras protocol_binary_response_quit;
-
- /**
- * Definition of the packet used by append and prepend command
- * See section 4
- */
- typedef protocol_binary_request_no_extras protocol_binary_request_append;
- typedef protocol_binary_request_no_extras protocol_binary_request_prepend;
-
- /**
- * Definition of the packet returned from a successful append or prepend
- * See section 4
- */
- typedef protocol_binary_response_no_extras protocol_binary_response_append;
- typedef protocol_binary_response_no_extras protocol_binary_response_prepend;
-
- /**
- * Definition of the packet used by the version command
- * See section 4
- */
- typedef protocol_binary_request_no_extras protocol_binary_request_version;
-
- /**
- * Definition of the packet returned from a successful version command
- * See section 4
- */
- typedef protocol_binary_response_no_extras protocol_binary_response_version;
-
-
- /**
- * Definition of the packet used by the stats command.
- * See section 4
- */
- typedef protocol_binary_request_no_extras protocol_binary_request_stats;
-
- /**
- * Definition of the packet returned from a successful stats command
- * See section 4
- */
- typedef protocol_binary_response_no_extras protocol_binary_response_stats;
+ /**
+ * Definition of the legal "magic" values used in a packet.
+ * See section 3.1 Magic byte
+ */
+ typedef enum {
+ PROTOCOL_BINARY_REQ = 0x80,
+ PROTOCOL_BINARY_RES = 0x81
+ } protocol_binary_magic;
+
+ /**
+ * Definition of the valid response status numbers.
+ * See section 3.2 Response Status
+ */
+ typedef enum {
+ PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00,
+ PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01,
+ PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02,
+ PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03,
+ PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04,
+ PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05,
+ PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06,
+ PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81,
+ PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82
+ } protocol_binary_response_status;
+
+ /**
+ * Defintion of the different command opcodes.
+ * See section 3.3 Command Opcodes
+ */
+ typedef enum {
+ PROTOCOL_BINARY_CMD_GET = 0x00,
+ PROTOCOL_BINARY_CMD_SET = 0x01,
+ PROTOCOL_BINARY_CMD_ADD = 0x02,
+ PROTOCOL_BINARY_CMD_REPLACE = 0x03,
+ PROTOCOL_BINARY_CMD_DELETE = 0x04,
+ PROTOCOL_BINARY_CMD_INCREMENT = 0x05,
+ PROTOCOL_BINARY_CMD_DECREMENT = 0x06,
+ PROTOCOL_BINARY_CMD_QUIT = 0x07,
+ PROTOCOL_BINARY_CMD_FLUSH = 0x08,
+ PROTOCOL_BINARY_CMD_GETQ = 0x09,
+ PROTOCOL_BINARY_CMD_NOOP = 0x0a,
+ PROTOCOL_BINARY_CMD_VERSION = 0x0b,
+ PROTOCOL_BINARY_CMD_GETK = 0x0c,
+ PROTOCOL_BINARY_CMD_GETKQ = 0x0d,
+ PROTOCOL_BINARY_CMD_APPEND = 0x0e,
+ PROTOCOL_BINARY_CMD_PREPEND = 0x0f,
+ PROTOCOL_BINARY_CMD_STAT = 0x10,
+ PROTOCOL_BINARY_CMD_SETQ = 0x11,
+ PROTOCOL_BINARY_CMD_ADDQ = 0x12,
+ PROTOCOL_BINARY_CMD_REPLACEQ = 0x13,
+ PROTOCOL_BINARY_CMD_DELETEQ = 0x14,
+ PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15,
+ PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16,
+ PROTOCOL_BINARY_CMD_QUITQ = 0x17,
+ PROTOCOL_BINARY_CMD_FLUSHQ = 0x18,
+ PROTOCOL_BINARY_CMD_APPENDQ = 0x19,
+ PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a,
+
+ /* These commands are used for range operations and exist within
+ * this header for use in other projects. Range operations are
+ * not expected to be implemented in the memcached server itself.
+ */
+ PROTOCOL_BINARY_CMD_RGET = 0x30,
+ PROTOCOL_BINARY_CMD_RSET = 0x31,
+ PROTOCOL_BINARY_CMD_RSETQ = 0x32,
+ PROTOCOL_BINARY_CMD_RAPPEND = 0x33,
+ PROTOCOL_BINARY_CMD_RAPPENDQ = 0x34,
+ PROTOCOL_BINARY_CMD_RPREPEND = 0x35,
+ PROTOCOL_BINARY_CMD_RPREPENDQ = 0x36,
+ PROTOCOL_BINARY_CMD_RDELETE = 0x37,
+ PROTOCOL_BINARY_CMD_RDELETEQ = 0x38,
+ PROTOCOL_BINARY_CMD_RINCR = 0x39,
+ PROTOCOL_BINARY_CMD_RINCRQ = 0x3a,
+ PROTOCOL_BINARY_CMD_RDECR = 0x3b,
+ PROTOCOL_BINARY_CMD_RDECRQ = 0x3c
+ /* End Range operations */
+
+ } protocol_binary_command;
+
+ /**
+ * Definition of the data types in the packet
+ * See section 3.4 Data Types
+ */
+ typedef enum {
+ PROTOCOL_BINARY_RAW_BYTES = 0x00
+ } protocol_binary_datatypes;
+
+ /**
+ * Definition of the header structure for a request packet.
+ * See section 2
+ */
+ typedef union {
+ struct {
+ uint8_t magic;
+ uint8_t opcode;
+ uint16_t keylen;
+ uint8_t extlen;
+ uint8_t datatype;
+ uint16_t reserved;
+ uint32_t bodylen;
+ uint32_t opaque;
+ uint64_t cas;
+ } request;
+ uint8_t bytes[24];
+ } protocol_binary_request_header;
+
+ /**
+ * Definition of the header structure for a response packet.
+ * See section 2
+ */
+ typedef union {
+ struct {
+ uint8_t magic;
+ uint8_t opcode;
+ uint16_t keylen;
+ uint8_t extlen;
+ uint8_t datatype;
+ uint16_t status;
+ uint32_t bodylen;
+ uint32_t opaque;
+ uint64_t cas;
+ } response;
+ uint8_t bytes[24];
+ } protocol_binary_response_header;
+
+ /**
+ * Definition of a request-packet containing no extras
+ */
+ typedef union {
+ struct {
+ protocol_binary_request_header header;
+ } message;
+ uint8_t bytes[sizeof(protocol_binary_request_header)];
+ } protocol_binary_request_no_extras;
+
+ /**
+ * Definition of a response-packet containing no extras
+ */
+ typedef union {
+ struct {
+ protocol_binary_response_header header;
+ } message;
+ uint8_t bytes[sizeof(protocol_binary_response_header)];
+ } protocol_binary_response_no_extras;
+
+ /**
+ * Definition of the packet used by the get, getq, getk and getkq command.
+ * See section 4
+ */
+ typedef protocol_binary_request_no_extras protocol_binary_request_get;
+ typedef protocol_binary_request_no_extras protocol_binary_request_getq;
+ typedef protocol_binary_request_no_extras protocol_binary_request_getk;
+ typedef protocol_binary_request_no_extras protocol_binary_request_getkq;
+
+ /**
+ * Definition of the packet returned from a successful get, getq, getk and
+ * getkq.
+ * See section 4
+ */
+ typedef union {
+ struct {
+ protocol_binary_response_header header;
+ struct {
+ uint32_t flags;
+ } body;
+ } message;
+ uint8_t bytes[sizeof(protocol_binary_response_header) + 4];
+ } protocol_binary_response_get;
+
+ typedef protocol_binary_response_get protocol_binary_response_getq;
+ typedef protocol_binary_response_get protocol_binary_response_getk;
+ typedef protocol_binary_response_get protocol_binary_response_getkq;
+
+ /**
+ * Definition of the packet used by the delete command
+ * See section 4
+ */
+ typedef protocol_binary_request_no_extras protocol_binary_request_delete;
+
+ /**
+ * Definition of the packet returned by the delete command
+ * See section 4
+ */
+ typedef protocol_binary_response_no_extras protocol_binary_response_delete;
+
+ /**
+ * Definition of the packet used by the flush command
+ * See section 4
+ * Please note that the expiration field is optional, so remember to see
+ * check the header.bodysize to see if it is present.
+ */
+ typedef union {
+ struct {
+ protocol_binary_request_header header;
+ struct {
+ uint32_t expiration;
+ } body;
+ } message;
+ uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
+ } protocol_binary_request_flush;
+
+ /**
+ * Definition of the packet returned by the flush command
+ * See section 4
+ */
+ typedef protocol_binary_response_no_extras protocol_binary_response_flush;
+
+ /**
+ * Definition of the packet used by set, add and replace
+ * See section 4
+ */
+ typedef union {
+ struct {
+ protocol_binary_request_header header;
+ struct {
+ uint32_t flags;
+ uint32_t expiration;
+ } body;
+ } message;
+ uint8_t bytes[sizeof(protocol_binary_request_header) + 8];
+ } protocol_binary_request_set;
+ typedef protocol_binary_request_set protocol_binary_request_add;
+ typedef protocol_binary_request_set protocol_binary_request_replace;
+
+ /**
+ * Definition of the packet returned by set, add and replace
+ * See section 4
+ */
+ typedef protocol_binary_response_no_extras protocol_binary_response_set;
+ typedef protocol_binary_response_no_extras protocol_binary_response_add;
+ typedef protocol_binary_response_no_extras protocol_binary_response_replace;
+
+ /**
+ * Definition of the noop packet
+ * See section 4
+ */
+ typedef protocol_binary_request_no_extras protocol_binary_request_noop;
+
+ /**
+ * Definition of the packet returned by the noop command
+ * See section 4
+ */
+ typedef protocol_binary_response_no_extras protocol_binary_response_noop;
+
+ /**
+ * Definition of the structure used by the increment and decrement
+ * command.
+ * See section 4
+ */
+ typedef union {
+ struct {
+ protocol_binary_request_header header;
+ struct {
+ uint64_t delta;
+ uint64_t initial;
+ uint32_t expiration;
+ } body;
+ } message;
+ uint8_t bytes[sizeof(protocol_binary_request_header) + 20];
+ } protocol_binary_request_incr;
+ typedef protocol_binary_request_incr protocol_binary_request_decr;
+
+ /**
+ * Definition of the response from an incr or decr command
+ * command.
+ * See section 4
+ */
+ typedef union {
+ struct {
+ protocol_binary_response_header header;
+ struct {
+ uint64_t value;
+ } body;
+ } message;
+ uint8_t bytes[sizeof(protocol_binary_response_header) + 8];
+ } protocol_binary_response_incr;
+ typedef protocol_binary_response_incr protocol_binary_response_decr;
+
+ /**
+ * Definition of the quit
+ * See section 4
+ */
+ typedef protocol_binary_request_no_extras protocol_binary_request_quit;
+
+ /**
+ * Definition of the packet returned by the quit command
+ * See section 4
+ */
+ typedef protocol_binary_response_no_extras protocol_binary_response_quit;
+
+ /**
+ * Definition of the packet used by append and prepend command
+ * See section 4
+ */
+ typedef protocol_binary_request_no_extras protocol_binary_request_append;
+ typedef protocol_binary_request_no_extras protocol_binary_request_prepend;
+
+ /**
+ * Definition of the packet returned from a successful append or prepend
+ * See section 4
+ */
+ typedef protocol_binary_response_no_extras protocol_binary_response_append;
+ typedef protocol_binary_response_no_extras protocol_binary_response_prepend;
+
+ /**
+ * Definition of the packet used by the version command
+ * See section 4
+ */
+ typedef protocol_binary_request_no_extras protocol_binary_request_version;
+
+ /**
+ * Definition of the packet returned from a successful version command
+ * See section 4
+ */
+ typedef protocol_binary_response_no_extras protocol_binary_response_version;
+
+
+ /**
+ * Definition of the packet used by the stats command.
+ * See section 4
+ */
+ typedef protocol_binary_request_no_extras protocol_binary_request_stats;
+
+ /**
+ * Definition of the packet returned from a successful stats command
+ * See section 4
+ */
+ typedef protocol_binary_response_no_extras protocol_binary_response_stats;
+
+ /**
+ * Definition of a request for a range operation.
+ * See http://code.google.com/p/memcached/wiki/RangeOps
+ *
+ * These types are used for range operations and exist within
+ * this header for use in other projects. Range operations are
+ * not expected to be implemented in the memcached server itself.
+ */
+ typedef union {
+ struct {
+ protocol_binary_response_header header;
+ struct {
+ uint16_t size;
+ uint8_t reserved;
+ uint8_t flags;
+ uint32_t max_results;
+ } body;
+ } message;
+ uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
+ } protocol_binary_request_rangeop;
+
+ typedef protocol_binary_request_rangeop protocol_binary_request_rget;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rset;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rsetq;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rappend;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rappendq;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rprepend;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rprependq;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rdelete;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rdeleteq;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rincr;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rincrq;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rdecr;
+ typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq;
+
#ifdef __cplusplus
}
#endif
MEMCACHED_INVALID_HOST_PROTOCOL,
MEMCACHED_SERVER_MARKED_DEAD,
MEMCACHED_UNKNOWN_STAT_KEY,
+ MEMCACHED_E2BIG,
MEMCACHED_MAXIMUM_RETURN /* Always add new error code before */
} memcached_return;
}
else if (rc == MEMCACHED_END)
break;
+ else if (rc == MEMCACHED_SERVER_ERROR || rc == MEMCACHED_CLIENT_ERROR)
+ {
+ /* If we try to request stats cachedump for a slab class that is too big
+ * the server will return an incorrect error message:
+ * "MEMCACHED_SERVER_ERROR failed to allocate memory"
+ * This isn't really a fatal error, so let's just skip it. I want to
+ * fix the return value from the memcached server to a CLIENT_ERROR,
+ * so let's add support for that as well right now.
+ */
+ rc= MEMCACHED_END;
+ break;
+ }
else
goto error;
}
flags, error);
}
+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);
+
char *memcached_get_by_key(memcached_st *ptr,
const char *master_key,
size_t master_key_length,
}
/* Request the key */
- *error= memcached_mget_by_key(ptr,
+ *error= memcached_mget_by_key_real(ptr,
master_key,
master_key_length,
- (const char **)&key, &key_length, 1);
+ (const char **)&key, &key_length, 1, false);
value= memcached_fetch(ptr, NULL, NULL,
value_length, flags, error);
unsigned int master_server_key,
bool is_master_key_set,
const char **keys, size_t *key_length,
- size_t number_of_keys);
+ size_t number_of_keys,
+ bool mget_mode);
-memcached_return memcached_mget_by_key(memcached_st *ptr,
+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)
+ size_t number_of_keys,
+ bool mget_mode)
{
unsigned int x;
memcached_return rc= MEMCACHED_NOTFOUND;
if (ptr->flags & MEM_BINARY_PROTOCOL)
return binary_mget_by_key(ptr, master_server_key, is_master_key_set, keys,
- key_length, number_of_keys);
+ key_length, number_of_keys, mget_mode);
if (ptr->flags & MEM_SUPPORT_CAS)
{
return rc;
}
+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,
+ size_t number_of_keys)
+{
+ return memcached_mget_by_key_real(ptr, master_key, master_key_length, keys,
+ key_length, number_of_keys, true);
+}
+
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,
- size_t number_of_keys)
+ size_t number_of_keys, bool mget_mode)
{
memcached_return rc= MEMCACHED_NOTFOUND;
uint32_t x;
protocol_binary_request_getk request= {.bytes= {0}};
request.message.header.request.magic= PROTOCOL_BINARY_REQ;
- if (number_of_keys == 1)
- request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
- else
+ if (mget_mode)
request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ;
+ else
+ request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
memcached_return vk;
vk= memcached_validate_key_length(key_length[x],
rc= MEMCACHED_SOME_ERRORS;
}
- if (number_of_keys > 1)
+ if (mget_mode)
{
/*
* Send a noop command to flush the buffers
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)
+ size_t number_of_keys, bool mget_mode)
{
memcached_return rc= MEMCACHED_NOTFOUND;
uint32_t x;
protocol_binary_request_getk request= {.bytes= {0}};
request.message.header.request.magic= PROTOCOL_BINARY_REQ;
- if (number_of_keys == 1)
- request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
- else
+ 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;
memcached_server_response_increment(&ptr->hosts[server]);
}
- if (number_of_keys > 1)
+ if (mget_mode)
{
/*
* Send a noop command to flush the buffers
unsigned int master_server_key,
bool is_master_key_set,
const char **keys, size_t *key_length,
- size_t number_of_keys)
+ size_t number_of_keys, bool mget_mode)
{
memcached_return rc;
if (ptr->number_of_replicas == 0)
{
rc= simple_binary_mget(ptr, master_server_key, is_master_key_set,
- keys, key_length, number_of_keys);
+ keys, key_length, number_of_keys, mget_mode);
}
else
{
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);
+ key_length, number_of_keys, mget_mode);
ptr->call_free(ptr, hash);
ptr->call_free(ptr, dead_servers);
memcached_return memcached_pool_push(memcached_pool_st* pool,
memcached_st* mmc);
+LIBMEMCACHED_API
+memcached_return memcached_pool_behavior_set(memcached_pool_st *ptr, memcached_behavior flag, uint64_t data);
+LIBMEMCACHED_API
+memcached_return memcached_pool_behavior_get(memcached_pool_st *ptr, memcached_behavior flag, uint64_t *value);
+
#ifdef __cplusplus
}
#endif
case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
rc= MEMCACHED_DATA_EXISTS;
break;
+ case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
+ rc= MEMCACHED_NOTSTORED;
+ break;
case PROTOCOL_BINARY_RESPONSE_E2BIG:
+ rc= MEMCACHED_E2BIG;
+ break;
+ case PROTOCOL_BINARY_RESPONSE_ENOMEM:
+ rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+ break;
case PROTOCOL_BINARY_RESPONSE_EINVAL:
- case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
- case PROTOCOL_BINARY_RESPONSE_ENOMEM:
default:
/* @todo fix the error mappings */
rc= MEMCACHED_PROTOCOL_ERROR;
return "SERVER IS MARKED DEAD";
case MEMCACHED_UNKNOWN_STAT_KEY:
return "ENCOUNTERED AN UNKNOWN STAT KEY";
+ case MEMCACHED_E2BIG:
+ return "ITEM TOO BIG";
case MEMCACHED_MAXIMUM_RETURN:
return "Gibberish returned!";
default:
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
#include "libmemcached/common.h"
#include "libmemcached/memcached_pool.h"
int firstfree;
uint32_t size;
uint32_t current_size;
+ char* version;
};
static memcached_return mutex_enter(pthread_mutex_t *mutex)
if (rc != MEMCACHED_SUCCESS)
return rc;
+ char* version = memcached_get_user_data(mmc);
+ /* Someone updated the behavior on the object.. */
+ if (version != pool->version)
+ {
+ memcached_free(mmc);
+ memset(mmc, 0, sizeof(*mmc));
+ if (memcached_clone(mmc, pool->master) == NULL)
+ {
+ rc= MEMCACHED_SOME_ERRORS;
+ }
+ }
+
pool->mmc[++pool->firstfree]= mmc;
if (pool->firstfree == 0 && pool->current_size == pool->size)
pthread_cond_broadcast(&pool->cond);
}
+ memcached_return rval= mutex_exit(&pool->mutex);
+ if (rc == MEMCACHED_SOME_ERRORS)
+ return rc;
+
+ return rval;
+}
+
+
+memcached_return memcached_pool_behavior_set(memcached_pool_st *pool,
+ memcached_behavior flag,
+ uint64_t data)
+{
+
+ memcached_return rc= mutex_enter(&pool->mutex);
+ if (rc != MEMCACHED_SUCCESS)
+ return rc;
+
+ /* update the master */
+ rc= memcached_behavior_set(pool->master, flag, data);
+ if (rc != MEMCACHED_SUCCESS)
+ {
+ mutex_exit(&pool->mutex);
+ return rc;
+ }
+
+ ++pool->version;
+ memcached_set_user_data(pool->master, pool->version);
+ /* update the clones */
+ for (int xx= 0; xx <= pool->firstfree; ++xx)
+ {
+ rc= memcached_behavior_set(pool->mmc[xx], flag, data);
+ if (rc == MEMCACHED_SUCCESS)
+ memcached_set_user_data(pool->mmc[xx], pool->version);
+ else
+ {
+ memcached_free(pool->mmc[xx]);
+ memset(pool->mmc[xx], 0, sizeof(*pool->mmc[xx]));
+ if (memcached_clone(pool->mmc[xx], pool->master) == NULL)
+ {
+ /* I'm not sure what to do in this case.. this would happen
+ if we fail to push the server list inside the client..
+ I should add a testcase for this, but I believe the following
+ would work, except that you would add a hole in the pool list..
+ in theory you could end up with an empty pool....
+ */
+ free(pool->mmc[xx]);
+ pool->mmc[xx]= NULL;
+ }
+ }
+ }
+
+ return mutex_exit(&pool->mutex);
+}
+
+memcached_return memcached_pool_behavior_get(memcached_pool_st *pool,
+ memcached_behavior flag,
+ uint64_t *value)
+{
+ memcached_return rc= mutex_enter(&pool->mutex);
+ if (rc != MEMCACHED_SUCCESS)
+ return rc;
+ *value= memcached_behavior_get(pool->master, flag);
return mutex_exit(&pool->mutex);
}
[
ac_cv_with_memcached=$withval
MEMC_BINARY=$withval
- ],
+ ],
[
AC_PATH_PROG([MEMC_BINARY], [$ac_cv_with_memcached], "no")
AS_IF([test "x$MEMC_BINARY" = "xno"],
])
])
- AC_DEFINE_UNQUOTED([MEMCACHED_BINARY], "$MEMC_BINARY",
+ AC_DEFINE_UNQUOTED([MEMCACHED_BINARY], "$MEMC_BINARY",
[Name of the memcached binary used in make test])
+ AC_SUBST(MEMC_BINARY)
])
t/memslap.test\
t/memstat.test
-LIBS =
+LIBS =
noinst_HEADERS = test.h server.h ketama_test_cases.h
noinst_PROGRAMS = testapp testplus udptest atomsmasher startservers
record-extended:
./testapp extended > output2.res
-test: testapp testplus library_test
+test: testapp testplus library_test memcapable
echo "Tests completed"
library_test:
# ./testplus > output_plus.cmp
# diff output_plus.res output_plus.cmp
+memcapable:
+ @MEMC_BINARY@ -d -P /tmp/Xumemc.pid -p 12555
+ @$(top_builddir)/clients/memcapable -p 12555 || echo "Your memcached server does not support all commands"
+ @cat /tmp/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
+ @rm /tmp/Xumemc.pid
+
clients:
- memcached -d -P /tmp/Xumemc.pid -p 12555
+ @MEMC_BINARY@ -d -P /tmp/Xumemc.pid -p 12555
export MEMCACHED_SERVERS="localhost:12555"
sh t/memcat.test > r/memcat.cmp
diff r/memcat.res r/memcat.cmp
static test_return error_test(memcached_st *memc)
{
memcached_return rc;
- uint32_t values[] = { 851992627U, 2337886783U, 3196981036U, 4001849190U, 982370485U, 1263635348U, 4242906218U, 3829656100U, 1891735253U,
- 334139633U, 2257084983U, 3088286104U, 13199785U, 2542027183U, 1097051614U, 199566778U, 2748246961U, 2465192557U,
- 1664094137U, 2405439045U, 1842224848U, 692413798U, 3479807801U, 919913813U, 4269430871U, 610793021U, 527273862U,
- 1437122909U, 2300930706U, 2943759320U, 674306647U, 2400528935U, 54481931U, 4186304426U, 1741088401U, 2979625118U,
- 4159057246U };
-
- assert(MEMCACHED_MAXIMUM_RETURN == 37); // You have updated the memcache_error messages but not updated docs/tests.
+ uint32_t values[] = { 851992627U, 2337886783U, 3196981036U, 4001849190U,
+ 982370485U, 1263635348U, 4242906218U, 3829656100U,
+ 1891735253U, 334139633U, 2257084983U, 3088286104U,
+ 13199785U, 2542027183U, 1097051614U, 199566778U,
+ 2748246961U, 2465192557U, 1664094137U, 2405439045U,
+ 1842224848U, 692413798U, 3479807801U, 919913813U,
+ 4269430871U, 610793021U, 527273862U, 1437122909U,
+ 2300930706U, 2943759320U, 674306647U, 2400528935U,
+ 54481931U, 4186304426U, 1741088401U, 2979625118U,
+ 4159057246U, 3425930182U};
+
+ // You have updated the memcache_error messages but not updated docs/tests.
+ assert(MEMCACHED_MAXIMUM_RETURN == 38);
for (rc= MEMCACHED_SUCCESS; rc < MEMCACHED_MAXIMUM_RETURN; rc++)
{
uint32_t hash_val;
- hash_val= memcached_generate_hash_value(memcached_strerror(memc, rc), strlen(memcached_strerror(memc, rc)), MEMCACHED_HASH_JENKINS);
+ const char *msg= memcached_strerror(memc, rc);
+ hash_val= memcached_generate_hash_value(msg, strlen(msg),
+ MEMCACHED_HASH_JENKINS);
assert(values[rc] == hash_val);
}
return TEST_SUCCESS;
}
+static test_return mget_end(memcached_st *memc)
+{
+ const char *keys[]= { "foo", "foo2" };
+ size_t lengths[]= { 3, 4 };
+ const char *values[]= { "fjord", "41" };
+
+ memcached_return rc;
+
+ // Set foo and foo2
+ for (int i= 0; i < 2; i++)
+ {
+ rc= memcached_set(memc, keys[i], lengths[i], values[i], strlen(values[i]),
+ (time_t)0, (uint32_t)0);
+ assert(rc == MEMCACHED_SUCCESS);
+ }
+
+ char *string;
+ size_t string_length;
+ uint32_t flags;
+
+ // retrieve both via mget
+ rc= memcached_mget(memc, keys, lengths, 2);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ char key[MEMCACHED_MAX_KEY];
+ size_t key_length;
+
+ // this should get both
+ for (int i = 0; i < 2; i++)
+ {
+ string= memcached_fetch(memc, key, &key_length, &string_length,
+ &flags, &rc);
+ assert(rc == MEMCACHED_SUCCESS);
+ int val = 0;
+ if (key_length == 4)
+ val= 1;
+ assert(string_length == strlen(values[val]));
+ assert(strncmp(values[val], string, string_length) == 0);
+ free(string);
+ }
+
+ // this should indicate end
+ string= memcached_fetch(memc, key, &key_length, &string_length, &flags, &rc);
+ assert(rc == MEMCACHED_END);
+
+ // now get just one
+ rc= memcached_mget(memc, keys, lengths, 1);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ string= memcached_fetch(memc, key, &key_length, &string_length, &flags, &rc);
+ assert(key_length == lengths[0]);
+ assert(strncmp(keys[0], key, key_length) == 0);
+ assert(string_length == strlen(values[0]));
+ assert(strncmp(values[0], string, string_length) == 0);
+ assert(rc == MEMCACHED_SUCCESS);
+ free(string);
+
+ // this should indicate end
+ string= memcached_fetch(memc, key, &key_length, &string_length, &flags, &rc);
+ assert(rc == MEMCACHED_END);
+
+ return TEST_SUCCESS;
+}
+
/* Do not copy the style of this code, I just access hosts to testthis function */
static test_return stats_servername_test(memcached_st *memc)
{
for (int x= 0; x < 10; ++x)
assert(memcached_pool_push(pool, mmc[x]) == MEMCACHED_SUCCESS);
+
+ /* verify that I can set behaviors on the pool when I don't have all
+ * of the connections in the pool. It should however be enabled
+ * when I push the item into the pool
+ */
+ mmc[0]= memcached_pool_pop(pool, false, &rc);
+ assert(mmc[0] != NULL);
+
+ rc= memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK, 9999);
+ assert(rc == MEMCACHED_SUCCESS);
+
+ mmc[1]= memcached_pool_pop(pool, false, &rc);
+ assert(mmc[1] != NULL);
+
+ assert(memcached_behavior_get(mmc[1], MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK) == 9999);
+ assert(memcached_pool_push(pool, mmc[1]) == MEMCACHED_SUCCESS);
+ assert(memcached_pool_push(pool, mmc[0]) == MEMCACHED_SUCCESS);
+
+ mmc[0]= memcached_pool_pop(pool, false, &rc);
+ assert(memcached_behavior_get(mmc[0], MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK) == 9999);
+ assert(memcached_pool_push(pool, mmc[0]) == MEMCACHED_SUCCESS);
+
+
assert(memcached_pool_destroy(pool) == memc);
return TEST_SUCCESS;
}
return TEST_SUCCESS;
}
+static test_return regression_bug_434484(memcached_st *memc)
+{
+ if (pre_binary(memc) != TEST_SUCCESS)
+ return TEST_SUCCESS;
+
+ memcached_return ret;
+ const char *key= "regression_bug_434484";
+ size_t keylen= strlen(key);
+
+ ret= memcached_append(memc, key, keylen, key, keylen, 0, 0);
+ assert(ret == MEMCACHED_NOTSTORED);
+
+ size_t size= 2048 * 1024;
+ void *data= malloc(size);
+ assert(data != NULL);
+ ret= memcached_set(memc, key, keylen, data, size, 0, 0);
+ assert(ret == MEMCACHED_E2BIG);
+ free(data);
+
+ 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_end", 0, mget_end },
{"get_stats", 0, get_stats },
{"add_host_test", 0, add_host_test },
{"add_host_test_1", 0, add_host_test1 },
{0, 0, 0}
};
+/*
+ * The following test suite is used to verify that we don't introduce
+ * regression bugs. If you want more information about the bug / test,
+ * you should look in the bug report at
+ * http://bugs.launchpad.net/libmemcached
+ */
+test_st regression_tests[]= {
+ {"lp:434484", 1, regression_bug_434484 },
+ {0, 0, 0}
+};
+
test_st generate_tests[] ={
{"generate_pairs", 1, generate_pairs },
{"generate_data", 1, generate_data },
{"test_hashes", 0, 0, hash_tests},
{"replication", pre_replication, 0, replication_tests},
{"replication_noblock", pre_replication_noblock, 0, replication_tests},
+ {"regression", 0, 0, regression_tests},
{0, 0, 0, 0}
};