From 5a6b411afb61c7d14aee3f52037f719c42072160 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Tue, 13 Nov 2007 13:31:32 -0800 Subject: [PATCH 01/16] Refactor all code to go through memcached_do(). AKA this removed a bunch of copy and paste. It will also allow me to make commands "safer" in case of mid-cursor exection. --- lib/Makefile.am | 1 + lib/common.h | 2 ++ lib/memcached_auto.c | 11 ++++------- lib/memcached_delete.c | 16 ++++------------ lib/memcached_do.c | 22 ++++++++++++++++++++++ lib/memcached_flush.c | 10 +++++----- lib/memcached_get.c | 16 ++++++---------- lib/memcached_stats.c | 10 +++++----- lib/memcached_verbosity.c | 18 +++++++----------- 9 files changed, 56 insertions(+), 50 deletions(-) create mode 100644 lib/memcached_do.c diff --git a/lib/Makefile.am b/lib/Makefile.am index a84f43f5..2908fbb4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -31,6 +31,7 @@ libmemcached_la_SOURCES = crc.c \ memcached_behavior.c \ memcached_connect.c \ memcached_delete.c \ + memcached_do.c \ memcached_flush.c \ memcached_get.c \ memcached_hash.c \ diff --git a/lib/common.h b/lib/common.h index 3a3a9695..ae531e16 100644 --- a/lib/common.h +++ b/lib/common.h @@ -78,6 +78,8 @@ memcached_return memcached_string_append(memcached_string_st *string, size_t memcached_string_backspace(memcached_string_st *string, size_t remove); memcached_return memcached_string_reset(memcached_string_st *string); void memcached_string_free(memcached_string_st *string); +memcached_return memcached_do(memcached_st *ptr, unsigned int server_key, char *commmand, + size_t command_length, char with_flush); #endif /* __COMMON_H__ */ diff --git a/lib/memcached_auto.c b/lib/memcached_auto.c index d7e30e56..61ad9125 100644 --- a/lib/memcached_auto.c +++ b/lib/memcached_auto.c @@ -6,7 +6,7 @@ static memcached_return memcached_auto(memcached_st *ptr, unsigned int offset, uint64_t *value) { - size_t send_length, sent_length; + size_t send_length; memcached_return rc; char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; unsigned int server_key; @@ -19,19 +19,16 @@ static memcached_return memcached_auto(memcached_st *ptr, server_key= memcached_generate_hash(ptr, key, key_length); - if ((rc= memcached_connect(ptr, server_key)) != MEMCACHED_SUCCESS) - return rc; - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "%s %.*s %u\r\n", verb, (int)key_length, key, offset); if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) return MEMCACHED_WRITE_FAILURE; - sent_length= memcached_io_write(ptr, server_key, buffer, send_length, 1); - if (sent_length == -1 || sent_length != send_length) - return MEMCACHED_WRITE_FAILURE; + rc= memcached_do(ptr, server_key, buffer, send_length, 1); + if (rc != MEMCACHED_SUCCESS) + return rc; memset(buffer, 0, MEMCACHED_DEFAULT_COMMAND_SIZE); diff --git a/lib/memcached_delete.c b/lib/memcached_delete.c index cedfa442..0c36aaeb 100644 --- a/lib/memcached_delete.c +++ b/lib/memcached_delete.c @@ -4,7 +4,7 @@ memcached_return memcached_delete(memcached_st *ptr, char *key, size_t key_lengt time_t expiration) { char to_write; - size_t send_length, sent_length; + size_t send_length; memcached_return rc; char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; unsigned int server_key; @@ -19,10 +19,6 @@ memcached_return memcached_delete(memcached_st *ptr, char *key, size_t key_lengt server_key= memcached_generate_hash(ptr, key, key_length); - if ((rc= memcached_connect(ptr, server_key)) != MEMCACHED_SUCCESS) - return rc; - - if (expiration) send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "delete %.*s %llu\r\n", (int)key_length, key, @@ -42,12 +38,9 @@ memcached_return memcached_delete(memcached_st *ptr, char *key, size_t key_lengt else to_write= 1; - if ((sent_length= memcached_io_write(ptr, server_key, buffer, send_length, to_write)) == -1) - { - memcached_quit_server(ptr, server_key); - rc= MEMCACHED_WRITE_FAILURE; + rc= memcached_do(ptr, server_key, buffer, send_length, to_write); + if (rc != MEMCACHED_SUCCESS) goto error; - } if ((ptr->flags & MEM_NO_BLOCK)) { @@ -61,8 +54,7 @@ memcached_return memcached_delete(memcached_st *ptr, char *key, size_t key_lengt rc= MEMCACHED_SUCCESS; } - LIBMEMCACHED_MEMCACHED_DELETE_END(); - error: + LIBMEMCACHED_MEMCACHED_DELETE_END(); return rc; } diff --git a/lib/memcached_do.c b/lib/memcached_do.c new file mode 100644 index 00000000..5fcfe832 --- /dev/null +++ b/lib/memcached_do.c @@ -0,0 +1,22 @@ +#include "common.h" + +memcached_return memcached_do(memcached_st *ptr, unsigned int server_key, char *command, + size_t command_length, char with_flush) +{ + memcached_return rc; + ssize_t sent_length; + + WATCHPOINT_ASSERT(command); + if ((rc= memcached_connect(ptr, server_key)) != MEMCACHED_SUCCESS) + return rc; + + sent_length= memcached_io_write(ptr, server_key, command, command_length, with_flush); + + if (sent_length == -1 || sent_length != command_length) + { + memcached_quit_server(ptr, server_key); + rc= MEMCACHED_WRITE_FAILURE; + } + + return rc; +} diff --git a/lib/memcached_flush.c b/lib/memcached_flush.c index 58f5bff2..f40c1a6b 100644 --- a/lib/memcached_flush.c +++ b/lib/memcached_flush.c @@ -3,7 +3,7 @@ memcached_return memcached_flush(memcached_st *ptr, time_t expiration) { unsigned int x; - size_t send_length, sent_length; + size_t send_length; memcached_return rc; char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; LIBMEMCACHED_MEMCACHED_FLUSH_START(); @@ -29,10 +29,9 @@ memcached_return memcached_flush(memcached_st *ptr, time_t expiration) if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) return MEMCACHED_WRITE_FAILURE; - sent_length= memcached_io_write(ptr, x, buffer, send_length, 1); - - if (sent_length == -1 || sent_length != send_length) - return MEMCACHED_WRITE_FAILURE; + rc= memcached_do(ptr, x, buffer, send_length, 1); + if (rc != MEMCACHED_SUCCESS) + goto error; rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, x); @@ -40,6 +39,7 @@ memcached_return memcached_flush(memcached_st *ptr, time_t expiration) rc= MEMCACHED_SOME_ERRORS; } +error: LIBMEMCACHED_MEMCACHED_FLUSH_END(); return rc; } diff --git a/lib/memcached_get.c b/lib/memcached_get.c index 70d1b349..cbe94926 100644 --- a/lib/memcached_get.c +++ b/lib/memcached_get.c @@ -144,7 +144,10 @@ char *memcached_get(memcached_st *ptr, char *key, size_t key_length, LIBMEMCACHED_MEMCACHED_GET_START(); if (key_length == 0) - return MEMCACHED_NO_KEY_PROVIDED; + { + *error= MEMCACHED_NO_KEY_PROVIDED; + return NULL; + } if (ptr->hosts == NULL || ptr->number_of_hosts == 0) { @@ -156,11 +159,6 @@ char *memcached_get(memcached_st *ptr, char *key, size_t key_length, result_buffer= &ptr->result_buffer; *value_length= 0; - *error= memcached_connect(ptr, server_key); - - if (*error != MEMCACHED_SUCCESS) - goto error; - memcpy(buf_ptr, "get ", 4); buf_ptr+= 4; memcpy(buf_ptr, key, key_length); @@ -168,11 +166,9 @@ char *memcached_get(memcached_st *ptr, char *key, size_t key_length, memcpy(buf_ptr, "\r\n", 2); buf_ptr+= 2; - if ((memcached_io_write(ptr, server_key, buffer, (size_t)(buf_ptr - buffer), 1)) == -1) - { - *error= MEMCACHED_WRITE_FAILURE; + *error= memcached_do(ptr, server_key, buffer, (size_t)(buf_ptr - buffer), 1); + if (*error != MEMCACHED_SUCCESS) goto error; - } *error= memcached_value_fetch(ptr, key, &key_length, result_buffer, flags, 0, server_key); diff --git a/lib/memcached_stats.c b/lib/memcached_stats.c index e90f1c0f..cbfdef35 100644 --- a/lib/memcached_stats.c +++ b/lib/memcached_stats.c @@ -223,7 +223,7 @@ static memcached_return memcached_stats_fetch(memcached_st *ptr, { memcached_return rc; char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - size_t send_length, sent_length; + size_t send_length; rc= memcached_connect(ptr, server_key); @@ -240,10 +240,9 @@ static memcached_return memcached_stats_fetch(memcached_st *ptr, if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) return MEMCACHED_WRITE_FAILURE; - sent_length= memcached_io_write(ptr, server_key, buffer, send_length, 1); - - if (sent_length == -1 || sent_length != send_length) - return MEMCACHED_WRITE_FAILURE; + rc= memcached_do(ptr, server_key, buffer, send_length, 1); + if (rc != MEMCACHED_SUCCESS) + goto error; while (1) { @@ -271,6 +270,7 @@ static memcached_return memcached_stats_fetch(memcached_st *ptr, break; } +error: if (rc == MEMCACHED_END) return MEMCACHED_SUCCESS; else diff --git a/lib/memcached_verbosity.c b/lib/memcached_verbosity.c index 6d356642..1ee9d070 100644 --- a/lib/memcached_verbosity.c +++ b/lib/memcached_verbosity.c @@ -7,29 +7,25 @@ memcached_return memcached_verbosity(memcached_st *ptr, unsigned int verbosity) memcached_return rc; char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; - rc= memcached_connect(ptr, 0); - - if (rc != MEMCACHED_SUCCESS) - rc= MEMCACHED_SOME_ERRORS; - send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "verbosity %u\r\n", verbosity); if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) return MEMCACHED_WRITE_FAILURE; + rc= MEMCACHED_SUCCESS; for (x= 0; x < ptr->number_of_hosts; x++) { - memcached_return rc; + memcached_return rrc; - if ((memcached_io_write(ptr, x, buffer, send_length, 1)) == -1) + rrc= memcached_do(ptr, x, buffer, send_length, 1); + if (rrc != MEMCACHED_SUCCESS) { + rc= MEMCACHED_SOME_ERRORS; continue; - return MEMCACHED_SOME_ERRORS; } - rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, x); - - if (rc != MEMCACHED_SUCCESS) + rrc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, x); + if (rrc != MEMCACHED_SUCCESS) rc= MEMCACHED_SOME_ERRORS; } -- 2.30.2 From 5845b33395414c577e0d88cc13a011db55cffa02 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Tue, 13 Nov 2007 13:47:31 -0800 Subject: [PATCH 02/16] Testing to make sure that partial reads do not break protocol (aka on active cursors). --- lib/memcached_do.c | 4 ++++ tests/function.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/lib/memcached_do.c b/lib/memcached_do.c index 5fcfe832..ea0e705e 100644 --- a/lib/memcached_do.c +++ b/lib/memcached_do.c @@ -7,6 +7,10 @@ memcached_return memcached_do(memcached_st *ptr, unsigned int server_key, char * ssize_t sent_length; WATCHPOINT_ASSERT(command); + + if (ptr->hosts[server_key].cursor_active) + memcached_quit_server(ptr, server_key); + if ((rc= memcached_connect(ptr, server_key)) != MEMCACHED_SUCCESS) return rc; diff --git a/tests/function.c b/tests/function.c index d5291eae..1e2c6f07 100644 --- a/tests/function.c +++ b/tests/function.c @@ -992,6 +992,65 @@ uint8_t user_supplied_bug5(memcached_st *memc) return 0; } +uint8_t user_supplied_bug6(memcached_st *memc) +{ + memcached_return rc; + char *keys[]= {"036790384900", "036790384902", "036790384904", "036790384906"}; + size_t key_length[]= {strlen("036790384900"), strlen("036790384902"), strlen("036790384904"), strlen("036790384906")}; + char return_key[MEMCACHED_MAX_KEY]; + size_t return_key_length; + char *value; + size_t value_length; + uint16_t flags; + unsigned int count; + unsigned int x; + char insert_data[VALUE_SIZE_BUG5]; + + for (x= 0; x < VALUE_SIZE_BUG5; x++) + insert_data[x]= rand(); + + memcached_flush(memc, 0); + value= memcached_get(memc, keys[0], key_length[0], + &value_length, &flags, &rc); + assert(value == NULL); + rc= memcached_mget(memc, keys, key_length, 4); + + count= 0; + while ((value= memcached_fetch(memc, return_key, &return_key_length, + &value_length, &flags, &rc))) + count++; + assert(count == 0); + + for (x= 0; x < 4; x++) + { + rc= memcached_set(memc, keys[x], key_length[x], + insert_data, VALUE_SIZE_BUG5, + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + } + + for (x= 0; x < 10; x++) + { + value= memcached_get(memc, keys[0], key_length[0], + &value_length, &flags, &rc); + assert(value); + free(value); + + rc= memcached_mget(memc, keys, key_length, 4); + count= 3; + /* We test for purge of partial complete fetches */ + for (count= 3; count; count--) + { + value= memcached_fetch(memc, return_key, &return_key_length, + &value_length, &flags, &rc); + free(value); + assert(rc == MEMCACHED_SUCCESS); + } + } + + return 0; +} + uint8_t result_static(memcached_st *memc) { memcached_result_st result; @@ -1382,6 +1441,7 @@ test_st user_tests[] ={ {"user_supplied_bug3", 0, user_supplied_bug3 }, {"user_supplied_bug4", 0, user_supplied_bug4 }, {"user_supplied_bug5", 1, user_supplied_bug5 }, + {"user_supplied_bug6", 1, user_supplied_bug6 }, {0, 0, 0} }; -- 2.30.2 From 3bcf5c0efffbf64b6b6eccf140f057d2373fa974 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Tue, 13 Nov 2007 21:03:08 -0800 Subject: [PATCH 03/16] Rework of the server connect so that "hostname" lookup does not occur for each reconnect. Some framework for UDP protocol added. --- .hgignore | 3 ++ ChangeLog | 1 + configure.ac | 2 +- include/memcached.h | 5 +++ lib/memcached_connect.c | 91 ++++++++++++++++++++++++++++++++++------- lib/memcached_hosts.c | 22 +++++++--- lib/memcached_io.c | 42 ++++++++++++------- tests/function.c | 17 ++++++++ 8 files changed, 149 insertions(+), 34 deletions(-) diff --git a/.hgignore b/.hgignore index ff7c01f6..1f6ec646 100644 --- a/.hgignore +++ b/.hgignore @@ -20,3 +20,6 @@ Makefile(.in)?$ # Generated man files \.1$ \.3$ + +# Merged files +\.orig$ diff --git a/ChangeLog b/ChangeLog index bda930f2..3ce19953 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ * Modified increment/decrement functions to return uint64_t values * Fixed bug in cases where zero length keys were provided * Thread cleanup issue in memslap + * No hostname lookup on reconnect 0.8 Mon Nov 5 10:40:41 PST 2007 * Adding support for CRC hash method diff --git a/configure.ac b/configure.ac index 58fac5ba..203f7e91 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ MEMCACHED_LIBRARY_NAME=libmemcached #release versioning MEMCACHED_MAJOR_VERSION=0 -MEMCACHED_MINOR_VERSION=8 +MEMCACHED_MINOR_VERSION=9 MEMCACHED_MICRO_VERSION=0 #API version diff --git a/include/memcached.h b/include/memcached.h index bd056ade..29195de0 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -107,6 +108,7 @@ struct memcached_server_st { char read_buffer[MEMCACHED_MAX_BUFFER]; size_t read_buffer_length; char *read_ptr; + struct sockaddr_in servAddr; memcached_connection type; }; @@ -235,6 +237,9 @@ memcached_result_st *memcached_fetch_result(memcached_st *ptr, #define memcached_server_list(A) A->hosts #define memcached_server_response_count(A,B) A->hosts[B].stack_responses +memcached_return memcached_server_add_udp(memcached_st *ptr, + char *hostname, + unsigned int port); memcached_return memcached_server_add_unix_socket(memcached_st *ptr, char *filename); memcached_return memcached_server_add(memcached_st *ptr, char *hostname, diff --git a/lib/memcached_connect.c b/lib/memcached_connect.c index 025fdf6e..cbf57fdc 100644 --- a/lib/memcached_connect.c +++ b/lib/memcached_connect.c @@ -7,6 +7,22 @@ #include #include +static memcached_return set_hostinfo(memcached_server_st *server) +{ + struct hostent *h; + + if ((h= gethostbyname(server->hostname)) == NULL) + { + return MEMCACHED_HOST_LOCKUP_FAILURE; + } + + server->servAddr.sin_family= h->h_addrtype; + memcpy((char *) &server->servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); + server->servAddr.sin_port = htons(server->port); + + return MEMCACHED_SUCCESS; +} + static memcached_return unix_socket_connect(memcached_st *ptr, unsigned int server_key) { struct sockaddr_un servAddr; @@ -22,12 +38,14 @@ static memcached_return unix_socket_connect(memcached_st *ptr, unsigned int serv memset(&servAddr, 0, sizeof (struct sockaddr_un)); servAddr.sun_family= AF_UNIX; - strcpy(servAddr.sun_path, ptr->hosts[server_key].hostname); + strcpy(servAddr.sun_path, ptr->hosts[server_key].hostname); /* Copy filename */ addrlen= strlen(servAddr.sun_path) + sizeof(servAddr.sun_family); test_connect: - if (connect(ptr->hosts[server_key].fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) + if (connect(ptr->hosts[server_key].fd, + (struct sockaddr *)&servAddr, + sizeof(servAddr)) < 0) { switch (errno) { /* We are spinning waiting on connect */ @@ -47,25 +65,48 @@ test_connect: return MEMCACHED_SUCCESS; } -static memcached_return tcp_connect(memcached_st *ptr, unsigned int server_key) +static memcached_return udp_connect(memcached_st *ptr, unsigned int server_key) { - struct sockaddr_in servAddr; - struct hostent *h; - if (ptr->hosts[server_key].fd == -1) { /* Old connection junk still is in the structure */ WATCHPOINT_ASSERT(ptr->hosts[server_key].stack_responses == 0); - if ((h= gethostbyname(ptr->hosts[server_key].hostname)) == NULL) + if (ptr->hosts[server_key].servAddr.sin_family == 0) { - ptr->cached_errno= h_errno; - return MEMCACHED_HOST_LOCKUP_FAILURE; + memcached_return rc; + + rc= set_hostinfo(&ptr->hosts[server_key]); + if (rc != MEMCACHED_SUCCESS) + return rc; } - servAddr.sin_family= h->h_addrtype; - memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); - servAddr.sin_port = htons(ptr->hosts[server_key].port); + /* Create the socket */ + if ((ptr->hosts[server_key].fd= socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + ptr->cached_errno= errno; + return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE; + } + } + + return MEMCACHED_SUCCESS; +} + +static memcached_return tcp_connect(memcached_st *ptr, unsigned int server_key) +{ + if (ptr->hosts[server_key].fd == -1) + { + /* Old connection junk still is in the structure */ + WATCHPOINT_ASSERT(ptr->hosts[server_key].stack_responses == 0); + + if (ptr->hosts[server_key].servAddr.sin_family == 0) + { + memcached_return rc; + + rc= set_hostinfo(&ptr->hosts[server_key]); + if (rc != MEMCACHED_SUCCESS) + return rc; + } /* Create the socket */ if ((ptr->hosts[server_key].fd= socket(AF_INET, SOCK_STREAM, 0)) < 0) @@ -106,7 +147,9 @@ static memcached_return tcp_connect(memcached_st *ptr, unsigned int server_key) /* connect to server */ test_connect: - if (connect(ptr->hosts[server_key].fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) + if (connect(ptr->hosts[server_key].fd, + (struct sockaddr *)&ptr->hosts[server_key].servAddr, + sizeof(struct sockaddr)) < 0) { switch (errno) { /* We are spinning waiting on connect */ @@ -142,7 +185,25 @@ memcached_return memcached_connect(memcached_st *ptr, unsigned int server_key) /* We need to clean up the multi startup piece */ if (server_key) + { rc= tcp_connect(ptr, server_key); + switch (ptr->hosts[server_key].type) + { + case MEMCACHED_CONNECTION_UNKNOWN: + WATCHPOINT_ASSERT(0); + rc= MEMCACHED_NOT_SUPPORTED; + break; + case MEMCACHED_CONNECTION_UDP: + rc= udp_connect(ptr, server_key); + break; + case MEMCACHED_CONNECTION_TCP: + rc= tcp_connect(ptr, server_key); + break; + case MEMCACHED_CONNECTION_UNIX_SOCKET: + rc= unix_socket_connect(ptr, server_key); + break; + } + } else { unsigned int x; @@ -156,10 +217,12 @@ memcached_return memcached_connect(memcached_st *ptr, unsigned int server_key) switch (ptr->hosts[x].type) { case MEMCACHED_CONNECTION_UNKNOWN: - case MEMCACHED_CONNECTION_UDP: WATCHPOINT_ASSERT(0); possible_rc= MEMCACHED_NOT_SUPPORTED; break; + case MEMCACHED_CONNECTION_UDP: + possible_rc= udp_connect(ptr, x); + break; case MEMCACHED_CONNECTION_TCP: possible_rc= tcp_connect(ptr, x); break; diff --git a/lib/memcached_hosts.c b/lib/memcached_hosts.c index c77e5583..f16052b3 100644 --- a/lib/memcached_hosts.c +++ b/lib/memcached_hosts.c @@ -9,14 +9,11 @@ static memcached_return server_add(memcached_st *ptr, char *hostname, static void host_reset(memcached_server_st *host, char *new_hostname, unsigned int port, memcached_connection type) { - host->stack_responses= 0; - host->cursor_active= 0; + memset(host, 0, sizeof(memcached_server_st)); host->hostname= new_hostname; host->port= port; host->fd= -1; host->type= type; - host->write_buffer_offset= 0; - host->read_buffer_length= 0; host->read_ptr= host->read_buffer; } @@ -64,7 +61,22 @@ memcached_return memcached_server_add_unix_socket(memcached_st *ptr, char *filen return server_add(ptr, filename, 0, MEMCACHED_CONNECTION_UNIX_SOCKET); } -memcached_return memcached_server_add(memcached_st *ptr, char *hostname, unsigned int port) +memcached_return memcached_server_add_udp(memcached_st *ptr, + char *hostname, + unsigned int port) +{ + if (!port) + port= MEMCACHED_DEFAULT_PORT; + + if (!hostname) + hostname= "localhost"; + + return server_add(ptr, hostname, port, MEMCACHED_CONNECTION_UDP); +} + +memcached_return memcached_server_add(memcached_st *ptr, + char *hostname, + unsigned int port) { if (!port) port= MEMCACHED_DEFAULT_PORT; diff --git a/lib/memcached_io.c b/lib/memcached_io.c index 473b8881..c0215ac0 100644 --- a/lib/memcached_io.c +++ b/lib/memcached_io.c @@ -140,24 +140,38 @@ ssize_t memcached_io_flush(memcached_st *ptr, unsigned int server_key) } sent_length= 0; - if ((ssize_t)(sent_length= write(ptr->hosts[server_key].fd, write_ptr, - write_length)) == -1) + if (ptr->hosts[server_key].type == MEMCACHED_CONNECTION_UDP) { - switch (errno) + + sent_length= sendto(ptr->hosts[server_key].fd, write_ptr, write_length, + 0, 0, 0); + /* + rc = sendto(sd, argv[i], strlen(argv[i])+1, 0, + (struct sockaddr *) &remoteServAddr, + sizeof(remoteServAddr)); + */ + } + else + { + if ((ssize_t)(sent_length= write(ptr->hosts[server_key].fd, write_ptr, + write_length)) == -1) { - case ENOBUFS: - case EAGAIN: - WATCHPOINT; - continue; - if (loop < 100) + switch (errno) { - loop++; - break; + case ENOBUFS: + case EAGAIN: + WATCHPOINT; + continue; + if (loop < 100) + { + loop++; + break; + } + /* Yes, we want to fall through */ + default: + ptr->cached_errno= errno; + return -1; } - /* Yes, we want to fall through */ - default: - ptr->cached_errno= errno; - return -1; } } diff --git a/tests/function.c b/tests/function.c index 1e2c6f07..7d2de703 100644 --- a/tests/function.c +++ b/tests/function.c @@ -1379,6 +1379,22 @@ memcached_return pre_unix_socket(memcached_st *memc) return rc; } +memcached_return pre_udp(memcached_st *memc) +{ + memcached_return rc; + + memcached_server_list_free(memc->hosts); + memc->hosts= NULL; + memc->number_of_hosts= 0; + + if (0) + return MEMCACHED_FAILURE; + + rc= memcached_server_add_udp(memc, "localhost", MEMCACHED_DEFAULT_PORT); + + return rc; +} + memcached_return pre_nodelay(memcached_st *memc) { memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, NULL); @@ -1467,6 +1483,7 @@ collection_st collection[] ={ {"ketama", pre_hash_ketama, 0, tests}, {"unix_socket", pre_unix_socket, 0, tests}, {"unix_socket_nodelay", pre_nodelay, 0, tests}, +// {"udp", pre_udp, 0, tests}, {"string", 0, 0, string_tests}, {"result", 0, 0, result_tests}, {"user", 0, 0, user_tests}, -- 2.30.2 From 46f12a5edfa0fc0382a9d25355b15d30f857b138 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Tue, 13 Nov 2007 23:46:52 -0800 Subject: [PATCH 04/16] Removed more then a handfull of memset() calls. Also reworked multiget to not need to call malloc(). --- include/memcached.h | 1 + lib/memcached_auto.c | 2 -- lib/memcached_flush.c | 1 - lib/memcached_get.c | 54 +++++++++++++++++++++++----------------- lib/memcached_hosts.c | 2 +- lib/memcached_io.c | 21 +++++++++------- lib/memcached_parse.c | 3 ++- lib/memcached_quit.c | 1 + lib/memcached_response.c | 1 - lib/memcached_stats.c | 3 --- lib/memcached_string.c | 4 --- 11 files changed, 48 insertions(+), 45 deletions(-) diff --git a/include/memcached.h b/include/memcached.h index 29195de0..7cb057d2 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -105,6 +105,7 @@ struct memcached_server_st { unsigned int cursor_active; char write_buffer[MEMCACHED_MAX_BUFFER]; size_t write_buffer_offset; + char *write_ptr; char read_buffer[MEMCACHED_MAX_BUFFER]; size_t read_buffer_length; char *read_ptr; diff --git a/lib/memcached_auto.c b/lib/memcached_auto.c index 61ad9125..f2ed0b8f 100644 --- a/lib/memcached_auto.c +++ b/lib/memcached_auto.c @@ -30,8 +30,6 @@ static memcached_return memcached_auto(memcached_st *ptr, if (rc != MEMCACHED_SUCCESS) return rc; - memset(buffer, 0, MEMCACHED_DEFAULT_COMMAND_SIZE); - rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key); /* diff --git a/lib/memcached_flush.c b/lib/memcached_flush.c index f40c1a6b..af2303ca 100644 --- a/lib/memcached_flush.c +++ b/lib/memcached_flush.c @@ -16,7 +16,6 @@ memcached_return memcached_flush(memcached_st *ptr, time_t expiration) if (rc != MEMCACHED_SUCCESS) rc= MEMCACHED_SOME_ERRORS; - memset(buffer, 0, MEMCACHED_DEFAULT_COMMAND_SIZE); for (x= 0; x < ptr->number_of_hosts; x++) { if (expiration) diff --git a/lib/memcached_get.c b/lib/memcached_get.c index cbe94926..db626f6f 100644 --- a/lib/memcached_get.c +++ b/lib/memcached_get.c @@ -12,7 +12,6 @@ static memcached_return memcached_value_fetch(memcached_st *ptr, char *key, size char *string_ptr; char *end_ptr; - memset(buffer, 0, MEMCACHED_DEFAULT_COMMAND_SIZE); end_ptr= buffer + MEMCACHED_DEFAULT_COMMAND_SIZE; *flags= 0; @@ -32,7 +31,6 @@ static memcached_return memcached_value_fetch(memcached_st *ptr, char *key, size /* We load the key */ if (load_key) { - memset(key, 0, MEMCACHED_MAX_KEY); *key_length= 0; for (; end_ptr > string_ptr && *string_ptr != ' '; string_ptr++) @@ -216,7 +214,6 @@ memcached_return memcached_mget(memcached_st *ptr, { unsigned int x; memcached_return rc= MEMCACHED_NOTFOUND; - char *cursor_key_exec; LIBMEMCACHED_MEMCACHED_MGET_START(); ptr->cursor_server= 0; @@ -226,41 +223,40 @@ memcached_return memcached_mget(memcached_st *ptr, if (ptr->number_of_hosts == 0) return MEMCACHED_NO_SERVERS; - cursor_key_exec= (char *)malloc(sizeof(char) * ptr->number_of_hosts); - memset(cursor_key_exec, 0, sizeof(char) * ptr->number_of_hosts); - for (x= 0; x < number_of_keys; x++) { unsigned int server_key; server_key= memcached_generate_hash(ptr, keys[x], key_length[x]); - if (cursor_key_exec[server_key] == 0) + if (ptr->hosts[server_key].cursor_active == 0) { rc= memcached_connect(ptr, server_key); if ((memcached_io_write(ptr, server_key, "get ", 4, 0)) == -1) { - memcached_quit(ptr); + memcached_quit_server(ptr, server_key); rc= MEMCACHED_SOME_ERRORS; - break; + continue; } + ptr->hosts[server_key].cursor_active= 1; } if ((memcached_io_write(ptr, server_key, keys[x], key_length[x], 0)) == -1) { - memcached_quit(ptr); + ptr->hosts[server_key].cursor_active = 0; + memcached_quit_server(ptr, server_key); rc= MEMCACHED_SOME_ERRORS; - break; + continue; } if ((memcached_io_write(ptr, server_key, " ", 1, 0)) == -1) { - memcached_quit(ptr); + ptr->hosts[server_key].cursor_active = 0; + memcached_quit_server(ptr, server_key); rc= MEMCACHED_SOME_ERRORS; - break; + continue; } - cursor_key_exec[server_key]= 1; } @@ -269,24 +265,17 @@ memcached_return memcached_mget(memcached_st *ptr, */ for (x= 0; x < ptr->number_of_hosts; x++) { - if (cursor_key_exec[x]) + if (ptr->hosts[x].cursor_active == 1) { /* We need to doo something about non-connnected hosts in the future */ if ((memcached_io_write(ptr, x, "\r\n", 2, 1)) == -1) { - memcached_quit(ptr); + memcached_quit_server(ptr, x); rc= MEMCACHED_SOME_ERRORS; - break; } - - ptr->hosts[x].cursor_active= 1; } - else - ptr->hosts[x].cursor_active= 0; } - free(cursor_key_exec); - LIBMEMCACHED_MEMCACHED_MGET_END(); return rc; } @@ -312,18 +301,28 @@ char *memcached_fetch(memcached_st *ptr, char *key, size_t *key_length, *value_length= memcached_string_length(result_buffer); if (*error == MEMCACHED_NOTFOUND) + { + ptr->hosts[ptr->cursor_server].cursor_active = 0; ptr->cursor_server++; + } else if (*error == MEMCACHED_END && *value_length == 0) + { return NULL; + } else if (*error == MEMCACHED_END) { WATCHPOINT_ASSERT(0); /* If this happens we have somehow messed up the fetch */ + *value_length= 0; return NULL; } else if (*error != MEMCACHED_SUCCESS) + { return NULL; + } else + { return memcached_string_c_copy(result_buffer); + } } @@ -352,18 +351,27 @@ memcached_result_st *memcached_fetch_result(memcached_st *ptr, 1, ptr->cursor_server); if (*error == MEMCACHED_NOTFOUND) + { + ptr->hosts[ptr->cursor_server].cursor_active = 0; ptr->cursor_server++; + } else if (*error == MEMCACHED_END && memcached_string_length((memcached_string_st *)(&result->value)) == 0) + { return NULL; + } else if (*error == MEMCACHED_END) { WATCHPOINT_ASSERT(0); /* If this happens we have somehow messed up the fetch */ return NULL; } else if (*error != MEMCACHED_SUCCESS) + { return NULL; + } else + { return result; + } } diff --git a/lib/memcached_hosts.c b/lib/memcached_hosts.c index f16052b3..d5ec5606 100644 --- a/lib/memcached_hosts.c +++ b/lib/memcached_hosts.c @@ -15,6 +15,7 @@ static void host_reset(memcached_server_st *host, char *new_hostname, unsigned i host->fd= -1; host->type= type; host->read_ptr= host->read_buffer; + host->write_ptr= host->write_buffer; } memcached_return memcached_server_push(memcached_st *ptr, memcached_server_st *list) @@ -123,7 +124,6 @@ static memcached_return server_add(memcached_st *ptr, char *hostname, if (!new_hostname) return MEMCACHED_MEMORY_ALLOCATION_FAILURE; - memset(new_hostname, 0, strlen(hostname)+1); memcpy(new_hostname, hostname, strlen(hostname)); host_reset(&ptr->hosts[ptr->number_of_hosts], new_hostname, port, type); ptr->number_of_hosts++; diff --git a/lib/memcached_io.c b/lib/memcached_io.c index c0215ac0..06ac2e9b 100644 --- a/lib/memcached_io.c +++ b/lib/memcached_io.c @@ -93,7 +93,11 @@ ssize_t memcached_io_write(memcached_st *ptr, unsigned int server_key, for (x= 0; x < length; x++) { - ptr->hosts[server_key].write_buffer[ptr->hosts[server_key].write_buffer_offset]= buffer[x]; + if (ptr->hosts[server_key].write_ptr == 0) + ptr->hosts[server_key].write_ptr= ptr->hosts[server_key].write_buffer; + WATCHPOINT_ASSERT(ptr->hosts[server_key].write_ptr); + *ptr->hosts[server_key].write_ptr= buffer[x]; + ptr->hosts[server_key].write_ptr++; ptr->hosts[server_key].write_buffer_offset++; if (ptr->hosts[server_key].write_buffer_offset == MEMCACHED_MAX_BUFFER) @@ -103,6 +107,7 @@ ssize_t memcached_io_write(memcached_st *ptr, unsigned int server_key, sent_length= memcached_io_flush(ptr, server_key); WATCHPOINT_ASSERT(sent_length == MEMCACHED_MAX_BUFFER); + ptr->hosts[server_key].write_ptr= ptr->hosts[server_key].write_buffer; ptr->hosts[server_key].write_buffer_offset= 0; } } @@ -142,14 +147,10 @@ ssize_t memcached_io_flush(memcached_st *ptr, unsigned int server_key) sent_length= 0; if (ptr->hosts[server_key].type == MEMCACHED_CONNECTION_UDP) { - - sent_length= sendto(ptr->hosts[server_key].fd, write_ptr, write_length, - 0, 0, 0); - /* - rc = sendto(sd, argv[i], strlen(argv[i])+1, 0, - (struct sockaddr *) &remoteServAddr, - sizeof(remoteServAddr)); - */ + sent_length= sendto(ptr->hosts[server_key].fd, + write_ptr, write_length, 0, + (struct sockaddr *)&ptr->hosts[server_key].servAddr, + sizeof(struct sockaddr)); } else { @@ -182,6 +183,7 @@ ssize_t memcached_io_flush(memcached_st *ptr, unsigned int server_key) WATCHPOINT_ASSERT(write_length == 0); WATCHPOINT_ASSERT(return_length == ptr->hosts[server_key].write_buffer_offset); + ptr->hosts[server_key].write_ptr= ptr->hosts[server_key].write_buffer; ptr->hosts[server_key].write_buffer_offset= 0; return return_length; @@ -192,6 +194,7 @@ ssize_t memcached_io_flush(memcached_st *ptr, unsigned int server_key) */ void memcached_io_reset(memcached_st *ptr, unsigned int server_key) { + ptr->hosts[server_key].write_ptr= ptr->hosts[server_key].write_buffer; ptr->hosts[server_key].write_buffer_offset= 0; memcached_quit(ptr); } diff --git a/lib/memcached_parse.c b/lib/memcached_parse.c index 069593e5..52154215 100644 --- a/lib/memcached_parse.c +++ b/lib/memcached_parse.c @@ -29,16 +29,17 @@ memcached_server_st *memcached_servers_parse(char *server_strings) char *ptr; port= 0; - memset(buffer, 0, HUGE_STRING_LEN); if (string) { memcpy(buffer, begin_ptr, string - begin_ptr); + buffer[(unsigned int)(string - begin_ptr)]= 0; begin_ptr= string+1; } else { size_t length= strlen(begin_ptr); memcpy(buffer, begin_ptr, length); + buffer[length]= 0; begin_ptr= end_ptr; } diff --git a/lib/memcached_quit.c b/lib/memcached_quit.c index e51692fc..a0cc6172 100644 --- a/lib/memcached_quit.c +++ b/lib/memcached_quit.c @@ -30,6 +30,7 @@ void memcached_quit_server(memcached_st *ptr, unsigned int server_key) ptr->hosts[server_key].write_buffer_offset= 0; ptr->hosts[server_key].read_buffer_length= 0; ptr->hosts[server_key].read_ptr= ptr->hosts[server_key].read_buffer; + ptr->hosts[server_key].write_ptr= ptr->hosts[server_key].write_buffer; } ptr->connected--; diff --git a/lib/memcached_response.c b/lib/memcached_response.c index 19fa6400..b90d861d 100644 --- a/lib/memcached_response.c +++ b/lib/memcached_response.c @@ -18,7 +18,6 @@ memcached_return memcached_response(memcached_st *ptr, unsigned int max_messages; - memset(buffer, 0, buffer_length); send_length= 0; max_messages= memcached_server_response_count(ptr, server_key); diff --git a/lib/memcached_stats.c b/lib/memcached_stats.c index cbfdef35..a2dc17a3 100644 --- a/lib/memcached_stats.c +++ b/lib/memcached_stats.c @@ -152,9 +152,6 @@ char *memcached_stat_get_value(memcached_st *ptr, memcached_stat_st *stat, char buffer[SMALL_STRING_LEN]; *error= MEMCACHED_SUCCESS; - - memset(buffer, 0, SMALL_STRING_LEN); - if (!memcmp("pid", key, strlen("pid"))) snprintf(buffer, SMALL_STRING_LEN,"%u", stat->pid); else if (!memcmp("uptime", key, strlen("uptime"))) diff --git a/lib/memcached_string.c b/lib/memcached_string.c index 2bbda3fa..42f3c326 100644 --- a/lib/memcached_string.c +++ b/lib/memcached_string.c @@ -27,10 +27,6 @@ memcached_return memcached_string_check(memcached_string_st *string, size_t need string->end= string->string + current_offset; string->current_size+= (string->block_size * adjust); - - /* We zero the block structure we just realloced */ - memset((string->string + current_offset), 0, - sizeof(char) * string->block_size); } return MEMCACHED_SUCCESS; -- 2.30.2 From 331a25a55b57056f81690a6573e0ef06158ed108 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Wed, 14 Nov 2007 18:05:09 -0800 Subject: [PATCH 05/16] Flags fix found by Stuart Midgley --- lib/memcached_storage.c | 2 +- tests/function.c | 46 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/memcached_storage.c b/lib/memcached_storage.c index 099c78ac..c6fa63e5 100644 --- a/lib/memcached_storage.c +++ b/lib/memcached_storage.c @@ -56,7 +56,7 @@ static memcached_return memcached_send(memcached_st *ptr, write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "%s %.*s %x %llu %zu\r\n", storage_op_string(verb), + "%s %.*s %u %llu %zu\r\n", storage_op_string(verb), (int)key_length, key, flags, (unsigned long long)expiration, value_length); if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) diff --git a/tests/function.c b/tests/function.c index 7d2de703..0200e40c 100644 --- a/tests/function.c +++ b/tests/function.c @@ -1051,6 +1051,51 @@ uint8_t user_supplied_bug6(memcached_st *memc) return 0; } +/* Test flag store/retrieve */ +uint8_t user_supplied_bug7(memcached_st *memc) +{ + memcached_return rc; + char *keys= "036790384900"; + size_t key_length= strlen("036790384900"); + char return_key[MEMCACHED_MAX_KEY]; + size_t return_key_length; + char *value; + size_t value_length; + uint16_t flags; + unsigned int x; + char insert_data[VALUE_SIZE_BUG5]; + + for (x= 0; x < VALUE_SIZE_BUG5; x++) + insert_data[x]= rand(); + + memcached_flush(memc, 0); + + flags= 245; + rc= memcached_set(memc, keys, key_length, + insert_data, VALUE_SIZE_BUG5, + (time_t)0, flags); + assert(rc == MEMCACHED_SUCCESS); + + flags= 0; + value= memcached_get(memc, keys, key_length, + &value_length, &flags, &rc); + assert(flags == 245); + assert(value); + free(value); + + rc= memcached_mget(memc, &keys, &key_length, 1); + + flags= 0; + value= memcached_fetch(memc, return_key, &return_key_length, + &value_length, &flags, &rc); + assert(flags == 245); + assert(value); + free(value); + + + return 0; +} + uint8_t result_static(memcached_st *memc) { memcached_result_st result; @@ -1458,6 +1503,7 @@ test_st user_tests[] ={ {"user_supplied_bug4", 0, user_supplied_bug4 }, {"user_supplied_bug5", 1, user_supplied_bug5 }, {"user_supplied_bug6", 1, user_supplied_bug6 }, + {"user_supplied_bug7", 1, user_supplied_bug7 }, {0, 0, 0} }; -- 2.30.2 From 93cab40b9112f97376381038e83240a7b28c24ea Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Thu, 15 Nov 2007 08:10:49 -0800 Subject: [PATCH 06/16] More tests, and new code for append/prepend. Also work t make this a release. --- ChangeLog | 6 +- docs/Makefile.am | 9 +++ docs/libmemcached.pod | 2 +- docs/memcached_set.pod | 20 ++++++ include/memcached.h | 22 +++++- lib/Makefile.am | 4 +- lib/common.h | 2 + lib/memcached_do.c | 1 + lib/memcached_response.c | 2 +- lib/memcached_storage.c | 111 ++++++++++++++++++++++++------ lib/memcached_version.c | 44 ++++++++++++ support/libmemcached.spec.in | 5 +- tests/function.c | 128 +++++++++++++++++++++++++++++++++++ 13 files changed, 329 insertions(+), 27 deletions(-) create mode 100644 lib/memcached_version.c diff --git a/ChangeLog b/ChangeLog index 3ce19953..84684535 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -0.9 +0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. * different buffers are now kept for different connections to speed up async efforts @@ -6,6 +6,10 @@ * Fixed bug in cases where zero length keys were provided * Thread cleanup issue in memslap * No hostname lookup on reconnect + * Fix for flag settings (was doing hex by accident!) + * Support for 1.2.4 server additions "prepend" and "append" added. + * Added memcached_version()... not sure if I will make this public + or not. 0.8 Mon Nov 5 10:40:41 PST 2007 * Adding support for CRC hash method diff --git a/docs/Makefile.am b/docs/Makefile.am index 5d8a812b..723dfe2c 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -31,6 +31,7 @@ man_MANS = libmemcached.3\ memslap.1\ memstat.1\ memcached_add.3\ + memcached_append.3\ memcached_behavior_get.3\ memcached_behavior_set.3\ memcached_clone.3\ @@ -38,10 +39,12 @@ man_MANS = libmemcached.3\ memcached_decrement.3\ memcached_delete.3\ memcached_fetch.3\ + memcached_fetch_result.3\ memcached_free.3\ memcached_get.3\ memcached_increment.3\ memcached_mget.3\ + memcached_prepend.3\ memcached_replace.3\ memcached_server_add.3\ memcached_server_count.3\ @@ -84,6 +87,12 @@ memcached_replace.3: memcached_set.pod memcached_add.3: memcached_set.pod pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_add.3 +memcached_prepend.3: memcached_set.pod + pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_prepend.3 + +memcached_append.3: memcached_set.pod + pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_append.3 + memcached_delete.3: memcached_delete.pod pod2man -c "libmemcached" -r "" -s 3 memcached_delete.pod > memcached_delete.3 diff --git a/docs/libmemcached.pod b/docs/libmemcached.pod index 158af960..02bd04e0 100755 --- a/docs/libmemcached.pod +++ b/docs/libmemcached.pod @@ -62,6 +62,6 @@ Brian Aker, Ebrian@tangent.orgE =head1 SEE ALSO -memcached(1) libmemcached_examples(3) libmemcached(1) memcat(1) memcp(1) memflush(1) memrm(1) memslap(1) memstat(1) memcached_fetch(3) memcached_replace(3) memcached_server_list_free(3) libmemcached_examples(3) memcached_clone(3) memcached_free(3) memcached_server_add(3) memcached_server_push(3) memcached_add(3) memcached_get(3) memcached_server_count(3) memcached_servers_parse(3) memcached_create(3) memcached_increment(3) memcached_server_list(3) memcached_set(3) memcached_decrement(3) memcached_mget(3) memcached_server_list_append(3) memcached_strerror(3) memcached_delete(3) memcached_quit(3) memcached_server_list_count(3) memcached_verbosity(3) memcached_server_add_unix_socket(3) memcahed_result_create(3) memcached_result_free(3) memcached_result_key_value(3) memcached_result_key_length(3) memcached_result_value(3) memcached_result_length(3) memcached_result_flags(3) memcached_result_cas(3) memcached_result_st(3) +memcached(1) libmemcached_examples(3) libmemcached(1) memcat(1) memcp(1) memflush(1) memrm(1) memslap(1) memstat(1) memcached_fetch(3) memcached_replace(3) memcached_server_list_free(3) libmemcached_examples(3) memcached_clone(3) memcached_free(3) memcached_server_add(3) memcached_server_push(3) memcached_add(3) memcached_get(3) memcached_server_count(3) memcached_servers_parse(3) memcached_create(3) memcached_increment(3) memcached_server_list(3) memcached_set(3) memcached_decrement(3) memcached_mget(3) memcached_server_list_append(3) memcached_strerror(3) memcached_delete(3) memcached_quit(3) memcached_server_list_count(3) memcached_verbosity(3) memcached_server_add_unix_socket(3) memcahed_result_create(3) memcached_result_free(3) memcached_result_key_value(3) memcached_result_key_length(3) memcached_result_value(3) memcached_result_length(3) memcached_result_flags(3) memcached_result_cas(3) memcached_result_st(3) memcached_append(3) memcached_prepend(3) memcached_fetch_result(3) =cut diff --git a/docs/memcached_set.pod b/docs/memcached_set.pod index 93037c14..39dd86d2 100755 --- a/docs/memcached_set.pod +++ b/docs/memcached_set.pod @@ -31,6 +31,20 @@ C Client Library for memcached (libmemcached, -lmemcached) time_t expiration, uint16_t flags); + memcached_return + memcached_prepend(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags) + + memcached_return + memcached_append(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags) + =head1 DESCRIPTION memcached_set(), memcached_add(), and memcached_replace() are all used to @@ -51,6 +65,12 @@ found on the server an error occurs. memcached_add() adds an object to the server. If the object is found on the server an error occurs, otherwise the value is stored. +memcached_prepend() places a segment of data before the last piece of data +stored. Currently expiration and key are not used in the server. + +memcached_append() places a segment of data at the end of the last piece of +data stored. Currently expiration and key are not used in the server. + memcached_set() with non-blocking IO is the fastest way to store data on the server. diff --git a/include/memcached.h b/include/memcached.h index 7cb057d2..f9ae2706 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -25,6 +25,7 @@ typedef struct memcached_result_st memcached_result_st; typedef struct memcached_string_st memcached_string_st; typedef struct memcached_server_st memcached_server_st; +#define MEMCACHED_VERSION_STRING 12 #define MEMCACHED_DEFAULT_PORT 11211 #define MEMCACHED_DEFAULT_COMMAND_SIZE 350 #define SMALL_STRING_LEN 1024 @@ -111,6 +112,9 @@ struct memcached_server_st { char *read_ptr; struct sockaddr_in servAddr; memcached_connection type; + uint8_t major_version; + uint8_t minor_version; + uint8_t micro_version; }; struct memcached_stat_st { @@ -118,7 +122,7 @@ struct memcached_stat_st { unsigned int uptime; unsigned int threads; time_t time; - char version[8]; + char version[MEMCACHED_VERSION_STRING]; unsigned int pointer_size; unsigned int rusage_user; unsigned int rusage_system; @@ -215,6 +219,22 @@ memcached_return memcached_replace(memcached_st *ptr, char *key, size_t key_leng char *value, size_t value_length, time_t expiration, uint16_t flags); +memcached_return memcached_append(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags); +memcached_return memcached_prepend(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags); +memcached_return memcached_cas(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags, + uint64_t cas); /* Get functions */ char *memcached_get(memcached_st *ptr, char *key, size_t key_length, diff --git a/lib/Makefile.am b/lib/Makefile.am index 2908fbb4..8a96e41e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -46,7 +46,9 @@ libmemcached_la_SOURCES = crc.c \ memcached_string.c \ memcached_stats.c \ memcached_strerror.c \ - memcached_verbosity.c + memcached_verbosity.c \ + memcached_version.c + libmemcached_la_LIBADD = libmemcached_la_LDFLAGS = -version-info $(MEMCACHED_LIBRARY_VERSION) diff --git a/lib/common.h b/lib/common.h index ae531e16..3b8946a1 100644 --- a/lib/common.h +++ b/lib/common.h @@ -80,6 +80,8 @@ memcached_return memcached_string_reset(memcached_string_st *string); void memcached_string_free(memcached_string_st *string); memcached_return memcached_do(memcached_st *ptr, unsigned int server_key, char *commmand, size_t command_length, char with_flush); +memcached_return memcached_version(memcached_st *ptr); + #endif /* __COMMON_H__ */ diff --git a/lib/memcached_do.c b/lib/memcached_do.c index ea0e705e..c6bc96c0 100644 --- a/lib/memcached_do.c +++ b/lib/memcached_do.c @@ -6,6 +6,7 @@ memcached_return memcached_do(memcached_st *ptr, unsigned int server_key, char * memcached_return rc; ssize_t sent_length; + WATCHPOINT_ASSERT(command_length); WATCHPOINT_ASSERT(command); if (ptr->hosts[server_key].cursor_active) diff --git a/lib/memcached_response.c b/lib/memcached_response.c index b90d861d..0755ae0c 100644 --- a/lib/memcached_response.c +++ b/lib/memcached_response.c @@ -51,7 +51,7 @@ memcached_return memcached_response(memcached_st *ptr, switch(buffer[0]) { - case 'V': /* VALUE */ + case 'V': /* VALUE || VERSION */ return MEMCACHED_SUCCESS; case 'O': /* OK */ return MEMCACHED_SUCCESS; diff --git a/lib/memcached_storage.c b/lib/memcached_storage.c index c6fa63e5..8d2ed8c5 100644 --- a/lib/memcached_storage.c +++ b/lib/memcached_storage.c @@ -14,17 +14,40 @@ typedef enum { SET_OP, REPLACE_OP, ADD_OP, + PREPEND_OP, + APPEND_OP, + CAS_OP, } memcached_storage_action; /* Inline this */ -#define storage_op_string(A) A == SET_OP ? "set" : ( A == REPLACE_OP ? "replace" : "add") - -static memcached_return memcached_send(memcached_st *ptr, - char *key, size_t key_length, - char *value, size_t value_length, - time_t expiration, - uint16_t flags, - memcached_storage_action verb) +char *storage_op_string(memcached_storage_action verb) +{ + switch (verb) + { + case SET_OP: + return "set"; + case REPLACE_OP: + return "replace"; + case ADD_OP: + return "add"; + case PREPEND_OP: + return "prepend"; + case APPEND_OP: + return "append"; + case CAS_OP: + return "cas"; + }; + + return SET_OP; +} + +static inline memcached_return memcached_send(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags, + uint64_t cas, + memcached_storage_action verb) { char to_write; size_t write_length; @@ -55,10 +78,18 @@ static memcached_return memcached_send(memcached_st *ptr, return rc; - write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "%s %.*s %u %llu %zu\r\n", storage_op_string(verb), - (int)key_length, key, flags, - (unsigned long long)expiration, value_length); + if (cas) + write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "%s %.*s %u %llu %zu\r\n", storage_op_string(verb), + (int)key_length, key, flags, + (unsigned long long)expiration, value_length); + else + write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "%s %.*s %u %llu %zu %llu\r\n", storage_op_string(verb), + (int)key_length, key, flags, + (unsigned long long)expiration, value_length, + (unsigned long long)cas); + if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) { rc= MEMCACHED_WRITE_FAILURE; @@ -102,7 +133,6 @@ static memcached_return memcached_send(memcached_st *ptr, { rc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key); } - if (rc == MEMCACHED_STORED) return MEMCACHED_SUCCESS; else @@ -117,38 +147,77 @@ error: memcached_return memcached_set(memcached_st *ptr, char *key, size_t key_length, char *value, size_t value_length, time_t expiration, - uint16_t flags) + uint16_t flags) { memcached_return rc; LIBMEMCACHED_MEMCACHED_SET_START(); rc= memcached_send(ptr, key, key_length, value, value_length, - expiration, flags, SET_OP); + expiration, flags, 0, SET_OP); LIBMEMCACHED_MEMCACHED_SET_END(); return rc; } -memcached_return memcached_add(memcached_st *ptr, char *key, size_t key_length, +memcached_return memcached_add(memcached_st *ptr, + char *key, size_t key_length, char *value, size_t value_length, time_t expiration, - uint16_t flags) + uint16_t flags) { memcached_return rc; LIBMEMCACHED_MEMCACHED_ADD_START(); rc= memcached_send(ptr, key, key_length, value, value_length, - expiration, flags, ADD_OP); + expiration, flags, 0, ADD_OP); LIBMEMCACHED_MEMCACHED_ADD_END(); return rc; } -memcached_return memcached_replace(memcached_st *ptr, char *key, size_t key_length, +memcached_return memcached_replace(memcached_st *ptr, + char *key, size_t key_length, char *value, size_t value_length, time_t expiration, - uint16_t flags) + uint16_t flags) { memcached_return rc; LIBMEMCACHED_MEMCACHED_REPLACE_START(); rc= memcached_send(ptr, key, key_length, value, value_length, - expiration, flags, REPLACE_OP); + expiration, flags, 0, REPLACE_OP); LIBMEMCACHED_MEMCACHED_REPLACE_END(); return rc; } + +memcached_return memcached_prepend(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags) +{ + memcached_return rc; + rc= memcached_send(ptr, key, key_length, value, value_length, + expiration, flags, 0, PREPEND_OP); + return rc; +} + +memcached_return memcached_append(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags) +{ + memcached_return rc; + rc= memcached_send(ptr, key, key_length, value, value_length, + expiration, flags, 0, APPEND_OP); + return rc; +} + +memcached_return memcached_cas(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags, + uint64_t cas) +{ + memcached_return rc; + rc= memcached_send(ptr, key, key_length, value, value_length, + expiration, flags, cas, APPEND_OP); + return rc; +} diff --git a/lib/memcached_version.c b/lib/memcached_version.c new file mode 100644 index 00000000..b811a031 --- /dev/null +++ b/lib/memcached_version.c @@ -0,0 +1,44 @@ +#include "common.h" + +memcached_return memcached_version(memcached_st *ptr) +{ + unsigned int x; + size_t send_length; + memcached_return rc; + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + char *response_ptr; + + send_length= strlen("version\r\n"); + memcpy(buffer, "version\r\n", send_length); + + rc= MEMCACHED_SUCCESS; + for (x= 0; x < ptr->number_of_hosts; x++) + { + memcached_return rrc; + + rrc= memcached_do(ptr, x, buffer, send_length, 1); + if (rrc != MEMCACHED_SUCCESS) + { + rc= MEMCACHED_SOME_ERRORS; + continue; + } + + rrc= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, x); + if (rrc != MEMCACHED_SUCCESS) + rc= MEMCACHED_SOME_ERRORS; + + /* Find the space, and then move one past it to copy version */ + response_ptr= index(buffer, ' '); + response_ptr++; + + ptr->hosts[x].major_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + response_ptr= index(response_ptr, '.'); + response_ptr++; + ptr->hosts[x].minor_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + response_ptr= index(response_ptr, '.'); + response_ptr++; + ptr->hosts[x].micro_version= (uint8_t)strtol(response_ptr, (char **)NULL, 10); + } + + return rc; +} diff --git a/support/libmemcached.spec.in b/support/libmemcached.spec.in index 7815df31..bf112562 100644 --- a/support/libmemcached.spec.in +++ b/support/libmemcached.spec.in @@ -52,7 +52,7 @@ memcp - Copy files to memcached servers. %{_libdir}/libmemcached.la %{_libdir}/libmemcached.so %{_libdir}/libmemcached.so.1 -%{_libdir}/libmemcached.so.1.0.0 +%{_libdir}/libmemcached.so.1.0.1 %{_libdir}/pkgconfig/libmemcached.pc %{_mandir}/man1/memcat.1.gz %{_mandir}/man1/memcp.1.gz @@ -63,6 +63,7 @@ memcp - Copy files to memcached servers. %{_mandir}/man3/libmemcached.3.gz %{_mandir}/man3/libmemcached_examples.3.gz %{_mandir}/man3/memcached_add.3.gz +%{_mandir}/man3/memcached_append.3.gz %{_mandir}/man3/memcached_behavior_get.3.gz %{_mandir}/man3/memcached_behavior_set.3.gz %{_mandir}/man3/memcached_clone.3.gz @@ -70,10 +71,12 @@ memcp - Copy files to memcached servers. %{_mandir}/man3/memcached_decrement.3.gz %{_mandir}/man3/memcached_delete.3.gz %{_mandir}/man3/memcached_fetch.3.gz +%{_mandir}/man3/memcached_fetch_result.3.gz %{_mandir}/man3/memcached_free.3.gz %{_mandir}/man3/memcached_get.3.gz %{_mandir}/man3/memcached_increment.3.gz %{_mandir}/man3/memcached_mget.3.gz +%{_mandir}/man3/memcached_prepend.3.gz %{_mandir}/man3/memcached_quit.3.gz %{_mandir}/man3/memcached_replace.3.gz %{_mandir}/man3/memcached_server_add.3.gz diff --git a/tests/function.c b/tests/function.c index 0200e40c..9b3555e4 100644 --- a/tests/function.c +++ b/tests/function.c @@ -117,6 +117,113 @@ uint8_t set_test(memcached_st *memc) return 0; } +uint8_t append_test(memcached_st *memc) +{ + memcached_return rc; + char *key= "fig"; + char *value= "we"; + size_t value_length; + uint16_t flags; + + rc= memcached_flush(memc, 0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_set(memc, key, strlen(key), + value, strlen(value), + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_append(memc, key, strlen(key), + " the", strlen(" the"), + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_append(memc, key, strlen(key), + " people", strlen(" people"), + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + + value= memcached_get(memc, key, strlen(key), + &value_length, &flags, &rc); + assert(!memcmp(value, "we the people", strlen("we the people"))); + assert(strlen("we the people") == value_length); + assert(rc == MEMCACHED_SUCCESS); + + return 0; +} + +uint8_t cas_test(memcached_st *memc) +{ + memcached_return rc; + char *key= "fun"; + size_t key_length= strlen("fun"); + char *value= "we the people"; + size_t value_length= strlen("we the people"); + memcached_result_st results_obj; + memcached_result_st *results; + + rc= memcached_flush(memc, 0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_set(memc, key, strlen(key), + value, strlen(value), + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_mget(memc, &key, &key_length, 1); + + results= memcached_result_create(memc, &results_obj); + + results= memcached_fetch_result(memc, &results_obj, &rc); + assert(results); + assert(rc == MEMCACHED_SUCCESS); + WATCHPOINT_NUMBER(memcached_result_cas(results)); + WATCHPOINT_ASSERT(memcached_result_cas(results)); + + assert(!memcmp(value, "we the people", strlen("we the people"))); + assert(strlen("we the people") == value_length); + assert(rc == MEMCACHED_SUCCESS); + + memcached_result_free(&results_obj); + + return 0; +} + +uint8_t prepend_test(memcached_st *memc) +{ + memcached_return rc; + char *key= "fig"; + char *value= "people"; + size_t value_length; + uint16_t flags; + + rc= memcached_flush(memc, 0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_set(memc, key, strlen(key), + value, strlen(value), + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_prepend(memc, key, strlen(key), + "the ", strlen("the "), + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_prepend(memc, key, strlen(key), + "we ", strlen("we "), + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + + value= memcached_get(memc, key, strlen(key), + &value_length, &flags, &rc); + assert(!memcmp(value, "we the people", strlen("we the people"))); + assert(strlen("we the people") == value_length); + assert(rc == MEMCACHED_SUCCESS); + + return 0; +} + uint8_t add_test(memcached_st *memc) { memcached_return rc; @@ -1407,6 +1514,18 @@ memcached_return pre_hash_ketama(memcached_st *memc) return MEMCACHED_SUCCESS; } +memcached_return check_for_1_2_3(memcached_st *memc) +{ + memcached_version(memc); + + if (memc->hosts[0].major_version >= 1 && + memc->hosts[0].minor_version >= 2 && + memc->hosts[0].micro_version >= 4) + return MEMCACHED_SUCCESS; + + return MEMCACHED_FAILURE; +} + memcached_return pre_unix_socket(memcached_st *memc) { memcached_return rc; @@ -1496,6 +1615,13 @@ test_st result_tests[] ={ {0, 0, 0} }; +test_st version_1_2_3[] ={ + {"append", 0, append_test }, + {"prepend", 0, prepend_test }, +// {"cas", 0, cas_test }, + {0, 0, 0} +}; + test_st user_tests[] ={ {"user_supplied_bug1", 0, user_supplied_bug1 }, {"user_supplied_bug2", 0, user_supplied_bug2 }, @@ -1530,10 +1656,12 @@ collection_st collection[] ={ {"unix_socket", pre_unix_socket, 0, tests}, {"unix_socket_nodelay", pre_nodelay, 0, tests}, // {"udp", pre_udp, 0, tests}, + {"version_1_2_3", check_for_1_2_3, 0, version_1_2_3}, {"string", 0, 0, string_tests}, {"result", 0, 0, result_tests}, {"user", 0, 0, user_tests}, {"generate", 0, 0, generate_tests}, + {"generate_nonblock", pre_nonblock, 0, generate_tests}, {0, 0, 0, 0} }; -- 2.30.2 From 9d926b6c1b6896aa345fa301666b0f98ebf1f6cf Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Thu, 15 Nov 2007 13:17:18 -0800 Subject: [PATCH 07/16] Added test for binary append (works as an example as well!) Fixed issue with bad assert in debug build Updated version --- ChangeLog | 3 +++ configure.ac | 2 +- lib/memcached_storage.c | 4 ++-- tests/function.c | 46 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 84684535..904dd8a5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +0.10 + * Added append binary test. + 0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. * different buffers are now kept for different connections to diff --git a/configure.ac b/configure.ac index 203f7e91..71a8ba53 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ MEMCACHED_LIBRARY_NAME=libmemcached #release versioning MEMCACHED_MAJOR_VERSION=0 -MEMCACHED_MINOR_VERSION=9 +MEMCACHED_MINOR_VERSION=10 MEMCACHED_MICRO_VERSION=0 #API version diff --git a/lib/memcached_storage.c b/lib/memcached_storage.c index 8d2ed8c5..af763a3e 100644 --- a/lib/memcached_storage.c +++ b/lib/memcached_storage.c @@ -56,8 +56,8 @@ static inline memcached_return memcached_send(memcached_st *ptr, char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; unsigned int server_key; - WATCHPOINT_ASSERT(value); - WATCHPOINT_ASSERT(value_length); + WATCHPOINT_ASSERT(!(value == NULL && value_length > 0)); + WATCHPOINT_ASSERT(!(value && value_length == 0)); if (key_length == 0) return MEMCACHED_NO_KEY_PROVIDED; diff --git a/tests/function.c b/tests/function.c index 9b3555e4..837f14ff 100644 --- a/tests/function.c +++ b/tests/function.c @@ -152,6 +152,51 @@ uint8_t append_test(memcached_st *memc) return 0; } +uint8_t append_binary_test(memcached_st *memc) +{ + memcached_return rc; + char *key= "numbers"; + unsigned int *store_ptr; + unsigned int store_list[] = { 23, 56, 499, 98, 32847, 0 }; + char *value; + size_t value_length; + uint16_t flags; + unsigned int x; + + rc= memcached_flush(memc, 0); + assert(rc == MEMCACHED_SUCCESS); + + rc= memcached_set(memc, + key, strlen(key), + NULL, 0, + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + + for (x= 0; store_list[x] ; x++) + { + rc= memcached_append(memc, + key, strlen(key), + (char *)&store_list[x], sizeof(unsigned int), + (time_t)0, (uint16_t)0); + assert(rc == MEMCACHED_SUCCESS); + } + + value= memcached_get(memc, key, strlen(key), + &value_length, &flags, &rc); + assert((value_length == (sizeof(unsigned int) * x))); + assert(rc == MEMCACHED_SUCCESS); + + store_ptr= store_list; + x= 0; + while (*store_ptr) + { + assert(*store_ptr == store_list[x++]); + store_ptr++; + } + + return 0; +} + uint8_t cas_test(memcached_st *memc) { memcached_return rc; @@ -1619,6 +1664,7 @@ test_st version_1_2_3[] ={ {"append", 0, append_test }, {"prepend", 0, prepend_test }, // {"cas", 0, cas_test }, + {"append_binary", 0, append_binary_test }, {0, 0, 0} }; -- 2.30.2 From ef5d49267aea61732cf8b3aceb3ecc10c04cc281 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Fri, 16 Nov 2007 13:40:36 -0800 Subject: [PATCH 08/16] Possible fix for problem where servaddr may not always be correctly inited. --- include/memcached.h | 5 +++-- lib/memcached_auto.c | 4 ++-- lib/memcached_connect.c | 4 +++- tests/function.c | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/memcached.h b/include/memcached.h index f9ae2706..316f1f3f 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -110,6 +110,7 @@ struct memcached_server_st { char read_buffer[MEMCACHED_MAX_BUFFER]; size_t read_buffer_length; char *read_ptr; + memcached_allocated sockaddr_inited; struct sockaddr_in servAddr; memcached_connection type; uint8_t major_version; @@ -189,11 +190,11 @@ memcached_return memcached_delete(memcached_st *ptr, char *key, size_t key_lengt time_t expiration); memcached_return memcached_increment(memcached_st *ptr, char *key, size_t key_length, - unsigned int offset, + uint32_t offset, uint64_t *value); memcached_return memcached_decrement(memcached_st *ptr, char *key, size_t key_length, - unsigned int offset, + uint32_t offset, uint64_t *value); void memcached_stat_free(memcached_st *, memcached_stat_st *); memcached_stat_st *memcached_stat(memcached_st *ptr, char *args, memcached_return *error); diff --git a/lib/memcached_auto.c b/lib/memcached_auto.c index f2ed0b8f..b9b74628 100644 --- a/lib/memcached_auto.c +++ b/lib/memcached_auto.c @@ -60,7 +60,7 @@ static memcached_return memcached_auto(memcached_st *ptr, memcached_return memcached_increment(memcached_st *ptr, char *key, size_t key_length, - unsigned int offset, + uint32_t offset, uint64_t *value) { memcached_return rc; @@ -74,7 +74,7 @@ memcached_return memcached_increment(memcached_st *ptr, memcached_return memcached_decrement(memcached_st *ptr, char *key, size_t key_length, - unsigned int offset, + uint32_t offset, uint64_t *value) { memcached_return rc; diff --git a/lib/memcached_connect.c b/lib/memcached_connect.c index cbf57fdc..0c256b71 100644 --- a/lib/memcached_connect.c +++ b/lib/memcached_connect.c @@ -72,13 +72,15 @@ static memcached_return udp_connect(memcached_st *ptr, unsigned int server_key) /* Old connection junk still is in the structure */ WATCHPOINT_ASSERT(ptr->hosts[server_key].stack_responses == 0); - if (ptr->hosts[server_key].servAddr.sin_family == 0) + if (ptr->hosts[server_key].sockaddr_inited == MEMCACHED_NOT_ALLOCATED) { memcached_return rc; rc= set_hostinfo(&ptr->hosts[server_key]); if (rc != MEMCACHED_SUCCESS) return rc; + + ptr->hosts[server_key].sockaddr_inited= MEMCACHED_ALLOCATED; } /* Create the socket */ diff --git a/tests/function.c b/tests/function.c index 837f14ff..68eddbd7 100644 --- a/tests/function.c +++ b/tests/function.c @@ -186,7 +186,7 @@ uint8_t append_binary_test(memcached_st *memc) assert((value_length == (sizeof(unsigned int) * x))); assert(rc == MEMCACHED_SUCCESS); - store_ptr= store_list; + store_ptr= (unsigned int *)value; x= 0; while (*store_ptr) { -- 2.30.2 From 1a9c3c48878ad91b2403f8a74f9032686ea5246b Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Mon, 19 Nov 2007 09:13:52 -0800 Subject: [PATCH 09/16] Update of docs on memcached_set with hashes. --- docs/memcached_behavior.pod | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/memcached_behavior.pod b/docs/memcached_behavior.pod index 55e82d8d..296988e5 100755 --- a/docs/memcached_behavior.pod +++ b/docs/memcached_behavior.pod @@ -47,9 +47,10 @@ available. Turns on the no-delay feature for connecting sockets (may be faster in some environments). -=item MEMCACHED_BEHAVIOR_MD5_HASHING +=item MEMCACHED_BEHAVIOR_HASH + +Makes the default hashing algorithm for keys use MD5. The value can be set to either MEMCACHED_HASH_DEFAULT, MEMCACHED_HASH_MD5, MEMCACHED_HASH_CRC, MEMCACHED_HASH_FNV1_64, MEMCACHED_HASH_FNV1A_64, MEMCACHED_HASH_FNV1_32, MEMCACHED_HASH_FNV1A_32, MEMCACHED_HASH_KETAMA. The behavior for all hashes but MEMCACHED_HASH_DEFAULT is identitical to the Java driver written by Dustin Sallings. -Makes the default hashing algorithm for keys use MD5. =back -- 2.30.2 From 8d0cd77fc7bafd1f353bc7c50910b751766f931a Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Mon, 19 Nov 2007 09:47:20 -0800 Subject: [PATCH 10/16] Added MEMCACHED_BEHAVIOR_CACHE_LOOKUPS flag (you can now avoid multiple DNS lookups). --- ChangeLog | 2 ++ docs/memcached_behavior.pod | 3 +++ include/memcached.h | 1 + lib/common.h | 1 + lib/memcached_behavior.c | 6 ++++++ lib/memcached_connect.c | 7 ++++++- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 904dd8a5..26c4b141 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 0.10 * Added append binary test. + * Added MEMCACHED_BEHAVIOR_CACHE_LOOKUPS behavior so that you can save on + multiple DNS lookups. 0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. diff --git a/docs/memcached_behavior.pod b/docs/memcached_behavior.pod index 296988e5..4d7d1140 100755 --- a/docs/memcached_behavior.pod +++ b/docs/memcached_behavior.pod @@ -51,6 +51,9 @@ environments). Makes the default hashing algorithm for keys use MD5. The value can be set to either MEMCACHED_HASH_DEFAULT, MEMCACHED_HASH_MD5, MEMCACHED_HASH_CRC, MEMCACHED_HASH_FNV1_64, MEMCACHED_HASH_FNV1A_64, MEMCACHED_HASH_FNV1_32, MEMCACHED_HASH_FNV1A_32, MEMCACHED_HASH_KETAMA. The behavior for all hashes but MEMCACHED_HASH_DEFAULT is identitical to the Java driver written by Dustin Sallings. +=item MEMCACHED_BEHAVIOR_CACHE_LOOKUPS + +Memcached can cache named lookups so that DNS lookups are made only once. =back diff --git a/include/memcached.h b/include/memcached.h index 316f1f3f..53b51ea0 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -73,6 +73,7 @@ typedef enum { MEMCACHED_BEHAVIOR_KETAMA, MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE, MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE, + MEMCACHED_BEHAVIOR_CACHE_LOOKUPS, } memcached_behavior; typedef enum { diff --git a/lib/common.h b/lib/common.h index 3b8946a1..1e564cf5 100644 --- a/lib/common.h +++ b/lib/common.h @@ -43,6 +43,7 @@ typedef enum { MEM_USE_MD5= (1 << 3), MEM_USE_KETAMA= (1 << 4), MEM_USE_CRC= (1 << 5), + MEM_USE_CACHE_LOOKUPS= (1 << 6), } memcached_flags; /* Hashing algo */ diff --git a/lib/memcached_behavior.c b/lib/memcached_behavior.c index fb04d722..67a52fa8 100644 --- a/lib/memcached_behavior.c +++ b/lib/memcached_behavior.c @@ -35,6 +35,9 @@ memcached_return memcached_behavior_set(memcached_st *ptr, case MEMCACHED_BEHAVIOR_HASH: ptr->hash= *(memcached_hash *)(data); break; + case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: + set_behavior_flag(ptr, MEM_USE_CACHE_LOOKUPS, data); + break; case MEMCACHED_BEHAVIOR_KETAMA: set_behavior_flag(ptr, MEM_USE_KETAMA, data); break; @@ -64,6 +67,9 @@ unsigned long long memcached_behavior_get(memcached_st *ptr, switch (flag) { + case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: + temp_flag= MEM_USE_CACHE_LOOKUPS; + break; case MEMCACHED_BEHAVIOR_NO_BLOCK: temp_flag= MEM_NO_BLOCK; break; diff --git a/lib/memcached_connect.c b/lib/memcached_connect.c index 0c256b71..fffd2b88 100644 --- a/lib/memcached_connect.c +++ b/lib/memcached_connect.c @@ -72,7 +72,12 @@ static memcached_return udp_connect(memcached_st *ptr, unsigned int server_key) /* Old connection junk still is in the structure */ WATCHPOINT_ASSERT(ptr->hosts[server_key].stack_responses == 0); - if (ptr->hosts[server_key].sockaddr_inited == MEMCACHED_NOT_ALLOCATED) + /* + If we have not allocated the hosts object. + Or if the cache has not been set. + */ + if (ptr->hosts[server_key].sockaddr_inited == MEMCACHED_NOT_ALLOCATED || + (!(ptr->flags & MEM_USE_CACHE_LOOKUPS))) { memcached_return rc; -- 2.30.2 From 264a1ae95d273b6b32afed45d7c7c7461bfa54e4 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Mon, 19 Nov 2007 13:31:46 -0800 Subject: [PATCH 11/16] Full CAS support now enabled in the library. --- ChangeLog | 2 ++ docs/Makefile.am | 4 +++ docs/memcached_behavior.pod | 4 +++ docs/memcached_set.pod | 18 +++++++++++-- include/memcached.h | 1 + lib/common.h | 1 + lib/memcached_behavior.c | 6 +++++ lib/memcached_get.c | 33 +++++++++++++++++++---- lib/memcached_storage.c | 10 +++---- support/libmemcached.spec.in | 1 + tests/function.c | 51 ++++++++++++++++++++++++++++++++++-- 11 files changed, 117 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 26c4b141..ac498778 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,8 @@ * Added append binary test. * Added MEMCACHED_BEHAVIOR_CACHE_LOOKUPS behavior so that you can save on multiple DNS lookups. + * Added CAS support, though this is optional and must be enabled during + runtime. 0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. diff --git a/docs/Makefile.am b/docs/Makefile.am index 723dfe2c..378a8819 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -34,6 +34,7 @@ man_MANS = libmemcached.3\ memcached_append.3\ memcached_behavior_get.3\ memcached_behavior_set.3\ + memcached_cas.3\ memcached_clone.3\ memcached_create.3\ memcached_decrement.3\ @@ -81,6 +82,9 @@ memcached_clone.3: memcached_create.pod memcached_set.3: memcached_set.pod pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_set.3 +memcached_cas.3: memcached_set.pod + pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_cas.3 + memcached_replace.3: memcached_set.pod pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_replace.3 diff --git a/docs/memcached_behavior.pod b/docs/memcached_behavior.pod index 4d7d1140..20141907 100755 --- a/docs/memcached_behavior.pod +++ b/docs/memcached_behavior.pod @@ -55,6 +55,10 @@ Makes the default hashing algorithm for keys use MD5. The value can be set to ei Memcached can cache named lookups so that DNS lookups are made only once. +=item MEMCACHED_BEHAVIOR_SUPPORT_CAS + +Support CAS operations (this is not enabled by default at this point in the server since it imposes a slight performance penalty). + =back =head1 RETURN diff --git a/docs/memcached_set.pod b/docs/memcached_set.pod index 39dd86d2..0578d17f 100755 --- a/docs/memcached_set.pod +++ b/docs/memcached_set.pod @@ -44,6 +44,13 @@ C Client Library for memcached (libmemcached, -lmemcached) char *value, size_t value_length, time_t expiration, uint16_t flags) + memcached_return + memcached_cas(memcached_st *ptr, + char *key, size_t key_length, + char *value, size_t value_length, + time_t expiration, + uint16_t flags, + uint64_t cas); =head1 DESCRIPTION @@ -71,8 +78,15 @@ stored. Currently expiration and key are not used in the server. memcached_append() places a segment of data at the end of the last piece of data stored. Currently expiration and key are not used in the server. -memcached_set() with non-blocking IO is the fastest way to store data on the -server. +memcached_cas() overwrites data in the server as long as the "cas" value is +still the same in the server. You can get the cas value of a result by +calling memcached_result_cas() on a memcached_result_st(3) structure. At the point +that this note was written cas is still buggy in memached. Turning on support +for it in libmemcached(3) is optional. Please see memcached_set() for +information on how to do this. + +If you are looking for performance, memcached_set() with non-blocking IO is +the fastest way to store data on the server. =head1 RETURN diff --git a/include/memcached.h b/include/memcached.h index 53b51ea0..33fa053d 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -74,6 +74,7 @@ typedef enum { MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE, MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE, MEMCACHED_BEHAVIOR_CACHE_LOOKUPS, + MEMCACHED_BEHAVIOR_SUPPORT_CAS, } memcached_behavior; typedef enum { diff --git a/lib/common.h b/lib/common.h index 1e564cf5..73913c1c 100644 --- a/lib/common.h +++ b/lib/common.h @@ -44,6 +44,7 @@ typedef enum { MEM_USE_KETAMA= (1 << 4), MEM_USE_CRC= (1 << 5), MEM_USE_CACHE_LOOKUPS= (1 << 6), + MEM_SUPPORT_CAS= (1 << 7), } memcached_flags; /* Hashing algo */ diff --git a/lib/memcached_behavior.c b/lib/memcached_behavior.c index 67a52fa8..76d0b61f 100644 --- a/lib/memcached_behavior.c +++ b/lib/memcached_behavior.c @@ -26,6 +26,9 @@ memcached_return memcached_behavior_set(memcached_st *ptr, { switch (flag) { + case MEMCACHED_BEHAVIOR_SUPPORT_CAS: + set_behavior_flag(ptr, MEM_SUPPORT_CAS, data); + break; case MEMCACHED_BEHAVIOR_NO_BLOCK: set_behavior_flag(ptr, MEM_NO_BLOCK, data); break; @@ -67,6 +70,9 @@ unsigned long long memcached_behavior_get(memcached_st *ptr, switch (flag) { + case MEMCACHED_BEHAVIOR_SUPPORT_CAS: + temp_flag= MEM_SUPPORT_CAS; + break; case MEMCACHED_BEHAVIOR_CACHE_LOOKUPS: temp_flag= MEM_USE_CACHE_LOOKUPS; break; diff --git a/lib/memcached_get.c b/lib/memcached_get.c index db626f6f..09ec8a37 100644 --- a/lib/memcached_get.c +++ b/lib/memcached_get.c @@ -4,6 +4,7 @@ static memcached_return memcached_value_fetch(memcached_st *ptr, char *key, size_t *key_length, memcached_string_st *value, uint16_t *flags, + uint64_t *cas, char load_key, unsigned int server_key) { @@ -67,8 +68,19 @@ static memcached_return memcached_value_fetch(memcached_st *ptr, char *key, size if (end_ptr == string_ptr) goto read_error; - /* Skip past the \r\n */ - string_ptr+= 2; + /* Skip spaces */ + if (*string_ptr == '\r') + { + /* Skip past the \r\n */ + string_ptr+= 2; + } + else + { + string_ptr++; + for (next_ptr= string_ptr; end_ptr > string_ptr && *string_ptr != ' '; string_ptr++); + if (cas) + *cas= (size_t)strtoll(next_ptr, &string_ptr, 10); + } if (end_ptr < string_ptr) goto read_error; @@ -169,7 +181,7 @@ char *memcached_get(memcached_st *ptr, char *key, size_t key_length, goto error; *error= memcached_value_fetch(ptr, key, &key_length, result_buffer, - flags, 0, server_key); + flags, NULL, 0, server_key); *value_length= memcached_string_length(result_buffer); if (*error == MEMCACHED_END && *value_length == 0) { @@ -214,6 +226,9 @@ memcached_return memcached_mget(memcached_st *ptr, { unsigned int x; memcached_return rc= MEMCACHED_NOTFOUND; + char *get_command= "get "; + uint8_t get_command_length= 4 + LIBMEMCACHED_MEMCACHED_MGET_START(); ptr->cursor_server= 0; @@ -223,6 +238,12 @@ memcached_return memcached_mget(memcached_st *ptr, if (ptr->number_of_hosts == 0) return MEMCACHED_NO_SERVERS; + if (ptr->flags & MEM_SUPPORT_CAS) + { + get_command= "gets "; + get_command_length= 5; + } + for (x= 0; x < number_of_keys; x++) { unsigned int server_key; @@ -233,7 +254,7 @@ memcached_return memcached_mget(memcached_st *ptr, { rc= memcached_connect(ptr, server_key); - if ((memcached_io_write(ptr, server_key, "get ", 4, 0)) == -1) + if ((memcached_io_write(ptr, server_key, get_command, get_command_length, 0)) == -1) { memcached_quit_server(ptr, server_key); rc= MEMCACHED_SOME_ERRORS; @@ -297,7 +318,7 @@ char *memcached_fetch(memcached_st *ptr, char *key, size_t *key_length, } *error = memcached_value_fetch(ptr, key, key_length, result_buffer, - flags, 1, ptr->cursor_server); + flags, NULL, 1, ptr->cursor_server); *value_length= memcached_string_length(result_buffer); if (*error == MEMCACHED_NOTFOUND) @@ -345,9 +366,11 @@ memcached_result_st *memcached_fetch_result(memcached_st *ptr, continue; } + result->cas= 0; /* We do this so we do not send in any junk */ *error= memcached_value_fetch(ptr, result->key, &result->key_length, &result->value, &result->flags, + &result->cas, 1, ptr->cursor_server); if (*error == MEMCACHED_NOTFOUND) diff --git a/lib/memcached_storage.c b/lib/memcached_storage.c index af763a3e..7b48afee 100644 --- a/lib/memcached_storage.c +++ b/lib/memcached_storage.c @@ -79,16 +79,16 @@ static inline memcached_return memcached_send(memcached_st *ptr, if (cas) - write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, - "%s %.*s %u %llu %zu\r\n", storage_op_string(verb), - (int)key_length, key, flags, - (unsigned long long)expiration, value_length); - else write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "%s %.*s %u %llu %zu %llu\r\n", storage_op_string(verb), (int)key_length, key, flags, (unsigned long long)expiration, value_length, (unsigned long long)cas); + else + write_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, + "%s %.*s %u %llu %zu\r\n", storage_op_string(verb), + (int)key_length, key, flags, + (unsigned long long)expiration, value_length); if (write_length >= MEMCACHED_DEFAULT_COMMAND_SIZE) { diff --git a/support/libmemcached.spec.in b/support/libmemcached.spec.in index bf112562..d2eef19c 100644 --- a/support/libmemcached.spec.in +++ b/support/libmemcached.spec.in @@ -66,6 +66,7 @@ memcp - Copy files to memcached servers. %{_mandir}/man3/memcached_append.3.gz %{_mandir}/man3/memcached_behavior_get.3.gz %{_mandir}/man3/memcached_behavior_set.3.gz +%{_mandir}/man3/memcached_cas.3.gz %{_mandir}/man3/memcached_clone.3.gz %{_mandir}/man3/memcached_create.3.gz %{_mandir}/man3/memcached_decrement.3.gz diff --git a/tests/function.c b/tests/function.c index 68eddbd7..63b56acb 100644 --- a/tests/function.c +++ b/tests/function.c @@ -197,6 +197,50 @@ uint8_t append_binary_test(memcached_st *memc) return 0; } +uint8_t cas2_test(memcached_st *memc) +{ + memcached_return rc; + char *keys[]= {"fudge", "son", "food"}; + size_t key_length[]= {5, 3, 4}; + char *value= "we the people"; + size_t value_length= strlen("we the people"); + unsigned int x; + memcached_result_st results_obj; + memcached_result_st *results; + unsigned int set= 1; + + rc= memcached_flush(memc, 0); + assert(rc == MEMCACHED_SUCCESS); + + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, &set); + + for (x= 0; x < 3; x++) + { + rc= memcached_set(memc, keys[x], key_length[x], + keys[x], key_length[x], + (time_t)50, (uint16_t)9); + assert(rc == MEMCACHED_SUCCESS); + } + + rc= memcached_mget(memc, keys, key_length, 3); + + results= memcached_result_create(memc, &results_obj); + + results= memcached_fetch_result(memc, &results_obj, &rc); + assert(results); + assert(results->cas); + assert(rc == MEMCACHED_SUCCESS); + WATCHPOINT_ASSERT(memcached_result_cas(results)); + + assert(!memcmp(value, "we the people", strlen("we the people"))); + assert(strlen("we the people") == value_length); + assert(rc == MEMCACHED_SUCCESS); + + memcached_result_free(&results_obj); + + return 0; +} + uint8_t cas_test(memcached_st *memc) { memcached_return rc; @@ -206,10 +250,13 @@ uint8_t cas_test(memcached_st *memc) size_t value_length= strlen("we the people"); memcached_result_st results_obj; memcached_result_st *results; + unsigned int set= 1; rc= memcached_flush(memc, 0); assert(rc == MEMCACHED_SUCCESS); + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, &set); + rc= memcached_set(memc, key, strlen(key), value, strlen(value), (time_t)0, (uint16_t)0); @@ -222,7 +269,6 @@ uint8_t cas_test(memcached_st *memc) results= memcached_fetch_result(memc, &results_obj, &rc); assert(results); assert(rc == MEMCACHED_SUCCESS); - WATCHPOINT_NUMBER(memcached_result_cas(results)); WATCHPOINT_ASSERT(memcached_result_cas(results)); assert(!memcmp(value, "we the people", strlen("we the people"))); @@ -1663,7 +1709,8 @@ test_st result_tests[] ={ test_st version_1_2_3[] ={ {"append", 0, append_test }, {"prepend", 0, prepend_test }, -// {"cas", 0, cas_test }, + {"cas", 0, cas_test }, + {"cas2", 0, cas2_test }, {"append_binary", 0, append_binary_test }, {0, 0, 0} }; -- 2.30.2 From 371f1dca550e93a0a9bdc960bac3db3d4112ffb4 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Mon, 19 Nov 2007 17:19:09 -0800 Subject: [PATCH 12/16] Added memerror so that I can find out what an error code is without resorting to reading the source :) --- ChangeLog | 2 + docs/Makefile.am | 5 +++ docs/libmemcached.pod | 2 +- docs/memerror.pod | 30 ++++++++++++++ src/Makefile.am | 5 ++- src/memerror.c | 79 ++++++++++++++++++++++++++++++++++++ support/libmemcached.spec.in | 2 + 7 files changed, 123 insertions(+), 2 deletions(-) create mode 100755 docs/memerror.pod create mode 100644 src/memerror.c diff --git a/ChangeLog b/ChangeLog index ac498778..b880e29d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,8 @@ multiple DNS lookups. * Added CAS support, though this is optional and must be enabled during runtime. + * Added the utility memerror to create human readable error strings + from memcached errors (aka convert ints to strings) 0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. diff --git a/docs/Makefile.am b/docs/Makefile.am index 378a8819..767065d4 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -4,6 +4,7 @@ EXTRA_DIST = libmemcached.pod\ memcached_flush.pod\ memcached_stats.pod\ memrm.pod\ + memerror.pod\ libmemcached_examples.pod\ memcached_get.pod\ memcached_strerror.pod\ @@ -26,6 +27,7 @@ man_MANS = libmemcached.3\ libmemcached_examples.3\ memcat.1\ memcp.1\ + memerror.1\ memflush.1\ memrm.1\ memslap.1\ @@ -217,6 +219,9 @@ memstat.1: memstat.pod memrm.1: memrm.pod pod2man -c "libmemcached" -r "" -s 1 memrm.pod > memrm.1 +memerror.1: memerror.pod + pod2man -c "libmemcached" -r "" -s 1 memerror.pod > memerror.1 + memflush.1: memflush.pod pod2man -c "libmemcached" -r "" -s 1 memflush.pod > memflush.1 diff --git a/docs/libmemcached.pod b/docs/libmemcached.pod index 02bd04e0..9685e01e 100755 --- a/docs/libmemcached.pod +++ b/docs/libmemcached.pod @@ -62,6 +62,6 @@ Brian Aker, Ebrian@tangent.orgE =head1 SEE ALSO -memcached(1) libmemcached_examples(3) libmemcached(1) memcat(1) memcp(1) memflush(1) memrm(1) memslap(1) memstat(1) memcached_fetch(3) memcached_replace(3) memcached_server_list_free(3) libmemcached_examples(3) memcached_clone(3) memcached_free(3) memcached_server_add(3) memcached_server_push(3) memcached_add(3) memcached_get(3) memcached_server_count(3) memcached_servers_parse(3) memcached_create(3) memcached_increment(3) memcached_server_list(3) memcached_set(3) memcached_decrement(3) memcached_mget(3) memcached_server_list_append(3) memcached_strerror(3) memcached_delete(3) memcached_quit(3) memcached_server_list_count(3) memcached_verbosity(3) memcached_server_add_unix_socket(3) memcahed_result_create(3) memcached_result_free(3) memcached_result_key_value(3) memcached_result_key_length(3) memcached_result_value(3) memcached_result_length(3) memcached_result_flags(3) memcached_result_cas(3) memcached_result_st(3) memcached_append(3) memcached_prepend(3) memcached_fetch_result(3) +memcached(1) libmemcached_examples(3) libmemcached(1) memcat(1) memcp(1) memflush(1) memrm(1) memslap(1) memstat(1) memcached_fetch(3) memcached_replace(3) memcached_server_list_free(3) libmemcached_examples(3) memcached_clone(3) memcached_free(3) memcached_server_add(3) memcached_server_push(3) memcached_add(3) memcached_get(3) memcached_server_count(3) memcached_servers_parse(3) memcached_create(3) memcached_increment(3) memcached_server_list(3) memcached_set(3) memcached_decrement(3) memcached_mget(3) memcached_server_list_append(3) memcached_strerror(3) memcached_delete(3) memcached_quit(3) memcached_server_list_count(3) memcached_verbosity(3) memcached_server_add_unix_socket(3) memcahed_result_create(3) memcached_result_free(3) memcached_result_key_value(3) memcached_result_key_length(3) memcached_result_value(3) memcached_result_length(3) memcached_result_flags(3) memcached_result_cas(3) memcached_result_st(3) memcached_append(3) memcached_prepend(3) memcached_fetch_result(3) memerror(1) =cut diff --git a/docs/memerror.pod b/docs/memerror.pod new file mode 100755 index 00000000..9dc2e0ce --- /dev/null +++ b/docs/memerror.pod @@ -0,0 +1,30 @@ +=head1 NAME + +memerror - Translate a memcached error code to a string + +=head1 SYNOPSIS + + memerror [options] error_code + +=head1 DESCRIPTION + +B translate an error code from libmemcached(3) to a human +readable string. + +For a full list of operations run the tool with the B<--help> option. + +=head1 HOME + +To find out more infoerroration please check: +L + +=head1 AUTHOR + +Brian Aker, Ebrian@tangent.orgE + +=head1 SEE ALSO + +memcached(1) libmemcached(3) + +=cut + diff --git a/src/Makefile.am b/src/Makefile.am index 435376bd..0bf6ec87 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ INCLUDES = -I$(top_builddir)/include LDADDS = ../lib/libmemcached.la -bin_PROGRAMS = memcat memcp memstat memrm memflush memslap +bin_PROGRAMS = memcat memcp memstat memrm memflush memslap memerror noinst_HEADERS = client_options.h \ utilities.h \ @@ -23,5 +23,8 @@ memrm_LDADD = $(LDADDS) memflush_SOURCES = memflush.c utilities.c memflush_LDADD = $(LDADDS) +memerror_SOURCES = memerror.c utilities.c +memerror_LDADD = $(LDADDS) + memslap_SOURCES = memslap.c utilities.c generator.c execute.c memslap_LDADD = $(LDADDS) -lpthread diff --git a/src/memerror.c b/src/memerror.c new file mode 100644 index 00000000..d6bfbf7f --- /dev/null +++ b/src/memerror.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "utilities.h" + +#define PROGRAM_NAME "memerror" +#define PROGRAM_DESCRIPTION "Translate a memcached errror code into a string." + + +/* Prototypes */ +void options_parse(int argc, char *argv[]); + +static int opt_verbose= 0; + +int main(int argc, char *argv[]) +{ + options_parse(argc, argv); + + if (argc != 2) + return 1; + + printf("%s\n", memcached_strerror(NULL, atoi(argv[1]))); + + return 0; +} + + +void options_parse(int argc, char *argv[]) +{ + int option_index= 0; + int option_rv; + + memcached_programs_help_st help_options[]= + { + {0}, + }; + + static struct option long_options[]= + { + {"version", no_argument, NULL, OPT_VERSION}, + {"help", no_argument, NULL, OPT_HELP}, + {"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, + {"debug", no_argument, &opt_verbose, OPT_DEBUG}, + {0, 0, 0, 0}, + }; + + while (1) + { + option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); + if (option_rv == -1) break; + switch (option_rv) + { + case 0: + break; + case OPT_VERBOSE: /* --verbose or -v */ + opt_verbose = OPT_VERBOSE; + break; + case OPT_DEBUG: /* --debug or -d */ + opt_verbose = OPT_DEBUG; + break; + case OPT_VERSION: /* --version or -V */ + version_command(PROGRAM_NAME); + break; + case OPT_HELP: /* --help or -h */ + help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); + break; + case '?': + /* getopt_long already printed an error message. */ + exit(1); + default: + abort(); + } + } +} diff --git a/support/libmemcached.spec.in b/support/libmemcached.spec.in index d2eef19c..8e6b1aa7 100644 --- a/support/libmemcached.spec.in +++ b/support/libmemcached.spec.in @@ -24,6 +24,7 @@ memrm - Remove a key(s) from the serrver. memstat - Dump the stats of your servers to standard output. memslap - Generate testing loads on a memcached cluster. memcp - Copy files to memcached servers. +memerror - Creates human readable messages from libmemecached error codes. %prep %setup -q @@ -43,6 +44,7 @@ memcp - Copy files to memcached servers. %files %{_bindir}/memcat %{_bindir}/memcp +%{_bindir}/memerror %{_bindir}/memflush %{_bindir}/memrm %{_bindir}/memstat -- 2.30.2 From 49ab2114abe5e8c3abde24b8ec3f2bc74bfded83 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Mon, 19 Nov 2007 17:42:26 -0800 Subject: [PATCH 13/16] Fix for poorly terminated hostname. Fix in enum return type (misspelling). --- ChangeLog | 2 ++ include/memcached.h | 2 +- lib/memcached_connect.c | 10 +++++++++- lib/memcached_hosts.c | 1 + lib/memcached_strerror.c | 2 +- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index b880e29d..022ee4ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,8 @@ runtime. * Added the utility memerror to create human readable error strings from memcached errors (aka convert ints to strings) + * Fixed type in MEMCACHED_HOST_LOOKUP_FAILURE + * Fixed bug where hostname might not be null terminated 0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. diff --git a/include/memcached.h b/include/memcached.h index 33fa053d..e64bfb5d 100644 --- a/include/memcached.h +++ b/include/memcached.h @@ -36,7 +36,7 @@ typedef struct memcached_server_st memcached_server_st; typedef enum { MEMCACHED_SUCCESS, MEMCACHED_FAILURE, - MEMCACHED_HOST_LOCKUP_FAILURE, + MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_CONNECTION_BIND_FAILURE, MEMCACHED_WRITE_FAILURE, diff --git a/lib/memcached_connect.c b/lib/memcached_connect.c index fffd2b88..f323e79f 100644 --- a/lib/memcached_connect.c +++ b/lib/memcached_connect.c @@ -13,7 +13,9 @@ static memcached_return set_hostinfo(memcached_server_st *server) if ((h= gethostbyname(server->hostname)) == NULL) { - return MEMCACHED_HOST_LOCKUP_FAILURE; + WATCHPOINT_STRING(server->hostname); + WATCHPOINT_STRING(hstrerror(h_errno)); + return MEMCACHED_HOST_LOOKUP_FAILURE; } server->servAddr.sin_family= h->h_addrtype; @@ -210,6 +212,9 @@ memcached_return memcached_connect(memcached_st *ptr, unsigned int server_key) rc= unix_socket_connect(ptr, server_key); break; } + + if (rc != MEMCACHED_SUCCESS) + WATCHPOINT_ERROR(rc); } else { @@ -240,7 +245,10 @@ memcached_return memcached_connect(memcached_st *ptr, unsigned int server_key) rc= MEMCACHED_SUCCESS; if (possible_rc != MEMCACHED_SUCCESS) + { + WATCHPOINT_ERROR(possible_rc); rc= MEMCACHED_SOME_ERRORS; + } } } LIBMEMCACHED_MEMCACHED_CONNECT_END(); diff --git a/lib/memcached_hosts.c b/lib/memcached_hosts.c index d5ec5606..9e9c6673 100644 --- a/lib/memcached_hosts.c +++ b/lib/memcached_hosts.c @@ -125,6 +125,7 @@ static memcached_return server_add(memcached_st *ptr, char *hostname, return MEMCACHED_MEMORY_ALLOCATION_FAILURE; memcpy(new_hostname, hostname, strlen(hostname)); + new_hostname[strlen(hostname)]= 0; host_reset(&ptr->hosts[ptr->number_of_hosts], new_hostname, port, type); ptr->number_of_hosts++; diff --git a/lib/memcached_strerror.c b/lib/memcached_strerror.c index d2da6963..65e837e8 100644 --- a/lib/memcached_strerror.c +++ b/lib/memcached_strerror.c @@ -8,7 +8,7 @@ char *memcached_strerror(memcached_st *ptr, memcached_return rc) return "SUCCESS"; case MEMCACHED_FAILURE: return "FAILURE"; - case MEMCACHED_HOST_LOCKUP_FAILURE: + case MEMCACHED_HOST_LOOKUP_FAILURE: return "HOSTNAME LOOKUP FAILURE"; case MEMCACHED_CONNECTION_FAILURE: return "CONNECTION FAILURE"; -- 2.30.2 From 0104faa2803775d4f1b2c1115f4b6a8025252041 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Mon, 19 Nov 2007 19:19:40 -0800 Subject: [PATCH 14/16] Fixed thread issue on Linux with gethostbyname_r(). --- ChangeLog | 1 + configure.ac | 2 +- lib/memcached_connect.c | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 022ee4ee..6bb0ee1c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,7 @@ from memcached errors (aka convert ints to strings) * Fixed type in MEMCACHED_HOST_LOOKUP_FAILURE * Fixed bug where hostname might not be null terminated + * Moved to using gethostbyname_r() on Linux to solve thread safety issue 0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. diff --git a/configure.ac b/configure.ac index 71a8ba53..db73e65c 100644 --- a/configure.ac +++ b/configure.ac @@ -68,5 +68,5 @@ fi AC_C_CONST AC_TYPE_SIZE_T -AC_CHECK_HEADERS(limits.h syslimits.h) +AC_CHECK_FUNC(gethostbyname_r, AC_DEFINE([HAVE_GETHOSTBYNAME_R], [], [Looking for gethostbyname_r])) AC_OUTPUT(Makefile src/Makefile tests/Makefile docs/Makefile lib/Makefile include/Makefile support/Makefile support/libmemcached.pc support/libmemcached.spec) diff --git a/lib/memcached_connect.c b/lib/memcached_connect.c index f323e79f..1a7cde16 100644 --- a/lib/memcached_connect.c +++ b/lib/memcached_connect.c @@ -10,16 +10,31 @@ static memcached_return set_hostinfo(memcached_server_st *server) { struct hostent *h; - +#ifdef HAVE_GETHOSTBYNAME_R + struct hostent h_static; + char buffer[SMALL_STRING_LEN]; + int tmp_error; + + if (gethostbyname_r(server->hostname, + &h_static, buffer, SMALL_STRING_LEN, + &h, &tmp_error)) + { + WATCHPOINT_STRING(server->hostname); + WATCHPOINT_STRING(hstrerror(tmp_error)); + return MEMCACHED_HOST_LOOKUP_FAILURE; + } +#else if ((h= gethostbyname(server->hostname)) == NULL) { WATCHPOINT_STRING(server->hostname); WATCHPOINT_STRING(hstrerror(h_errno)); return MEMCACHED_HOST_LOOKUP_FAILURE; } +#endif server->servAddr.sin_family= h->h_addrtype; memcpy((char *) &server->servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); + server->servAddr.sin_port = htons(server->port); return MEMCACHED_SUCCESS; -- 2.30.2 From fbe2eb63dd53506cc3acc1507e5226e43d22a6d5 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Tue, 20 Nov 2007 17:24:41 -0800 Subject: [PATCH 15/16] Updates for pkg-config. Tests for "gets" --- ChangeLog | 1 + src/Makefile.am | 10 +++- support/libmemcached.pc.in | 2 +- tests/function.c | 19 +++++++ tests/output.res | 107 +++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6bb0ee1c..2bdfb8e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,7 @@ * Fixed type in MEMCACHED_HOST_LOOKUP_FAILURE * Fixed bug where hostname might not be null terminated * Moved to using gethostbyname_r() on Linux to solve thread safety issue + * Added -rpath support for pkg-config 0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. diff --git a/src/Makefile.am b/src/Makefile.am index 0bf6ec87..004f2e1a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ INCLUDES = -I$(top_builddir)/include -LDADDS = ../lib/libmemcached.la +LDADDS = $(top_builddir)/lib/libmemcached.la bin_PROGRAMS = memcat memcp memstat memrm memflush memslap memerror @@ -10,21 +10,29 @@ noinst_HEADERS = client_options.h \ memcat_SOURCES = memcat.c utilities.c memcat_LDADD = $(LDADDS) +memcat_LDFLAGS = -rpath $(pkglibdir) memcp_SOURCES = memcp.c utilities.c memcp_LDADD = $(LDADDS) +memcp_LDFLAGS = -rpath $(pkglibdir) memstat_SOURCES = memstat.c utilities.c memstat_LDADD = $(LDADDS) +memstat_LDFLAGS = -rpath $(pkglibdir) memrm_SOURCES = memrm.c utilities.c memrm_LDADD = $(LDADDS) +memrm_LDFLAGS = -rpath $(pkglibdir) memflush_SOURCES = memflush.c utilities.c memflush_LDADD = $(LDADDS) +memflush_LDFLAGS = -rpath $(pkglibdir) + memerror_SOURCES = memerror.c utilities.c memerror_LDADD = $(LDADDS) +memerror_LDFLAGS = -rpath $(pkglibdir) memslap_SOURCES = memslap.c utilities.c generator.c execute.c memslap_LDADD = $(LDADDS) -lpthread +memslap_LDFLAGS = -rpath $(pkglibdir) diff --git a/support/libmemcached.pc.in b/support/libmemcached.pc.in index cb9d8c38..dfcc0c22 100644 --- a/support/libmemcached.pc.in +++ b/support/libmemcached.pc.in @@ -6,6 +6,6 @@ includedir=@includedir@ Name: libmemcached Description: libmemcached C library. Version: @VERSION@ -Libs: -L${libdir} -lmemcached +Libs: -L${libdir} -lmemcached -rpath $(libdir) Cflags: -I${includedir}/@MEMCACHED_LIBRARY_NAME@ diff --git a/tests/function.c b/tests/function.c index 63b56acb..8316ad6c 100644 --- a/tests/function.c +++ b/tests/function.c @@ -1605,6 +1605,24 @@ memcached_return pre_hash_ketama(memcached_st *memc) return MEMCACHED_SUCCESS; } +memcached_return enable_cas(memcached_st *memc) +{ + unsigned int set= 1; + + memcached_version(memc); + + if (memc->hosts[0].major_version >= 1 && + memc->hosts[0].minor_version >= 2 && + memc->hosts[0].micro_version >= 4) + { + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, &set); + + return MEMCACHED_SUCCESS; + } + + return MEMCACHED_FAILURE; +} + memcached_return check_for_1_2_3(memcached_st *memc) { memcached_version(memc); @@ -1748,6 +1766,7 @@ collection_st collection[] ={ {"ketama", pre_hash_ketama, 0, tests}, {"unix_socket", pre_unix_socket, 0, tests}, {"unix_socket_nodelay", pre_nodelay, 0, tests}, + {"gets", enable_cas, 0, tests}, // {"udp", pre_udp, 0, tests}, {"version_1_2_3", check_for_1_2_3, 0, version_1_2_3}, {"string", 0, 0, string_tests}, diff --git a/tests/output.res b/tests/output.res index a81bf7af..9bd97459 100644 --- a/tests/output.res +++ b/tests/output.res @@ -1178,3 +1178,110 @@ Found key bytes_read Found key bytes_written Found key limit_maxbytes Found key threads +Error 0 -> SUCCESS +Error 1 -> FAILURE +Error 2 -> HOSTNAME LOOKUP FAILURE +Error 3 -> CONNECTION FAILURE +Error 4 -> CONNECTION BIND FAILURE +Error 5 -> WRITE FAILURE +Error 6 -> READ FAILURE +Error 7 -> UNKNOWN READ FAILURE +Error 8 -> PROTOCOL ERROR +Error 9 -> CLIENT ERROR +Error 10 -> SERVER ERROR +Error 11 -> CONNECTION SOCKET CREATE FAILURE +Error 12 -> CONNECTION DATA EXISTS +Error 13 -> CONNECTION DATA DOES NOT EXIST +Error 14 -> NOT STORED +Error 15 -> STORED +Error 16 -> NOT FOUND +Error 17 -> MEMORY ALLOCATION FAILURE +Error 18 -> PARTIAL READ +Error 19 -> SOME ERRORS WERE REPORTED +Error 20 -> NO SERVERS DEFINED +Error 21 -> SERVER END +Error 22 -> SERVER DELETE +Error 23 -> SERVER VALUE +Error 24 -> STAT VALUE +Error 25 -> SYSTEM ERROR +Error 26 -> COULD NOT OPEN UNIX SOCKET +Error 27 -> ACTION NOT SUPPORTED +Error 28 -> A KEY LENGTH OF ZERO WAS PROVIDED +Found key pid +Found key uptime +Found key time +Found key version +Found key pointer_size +Found key rusage_user +Found key rusage_system +Found key rusage_user_seconds +Found key rusage_user_microseconds +Found key rusage_system_seconds +Found key rusage_system_microseconds +Found key curr_items +Found key total_items +Found key bytes +Found key curr_connections +Found key total_connections +Found key connection_structures +Found key cmd_get +Found key cmd_set +Found key get_hits +Found key get_misses +Found key evictions +Found key bytes_read +Found key bytes_written +Found key limit_maxbytes +Found key threads +Found key pid +Found key uptime +Found key time +Found key version +Found key pointer_size +Found key rusage_user +Found key rusage_system +Found key rusage_user_seconds +Found key rusage_user_microseconds +Found key rusage_system_seconds +Found key rusage_system_microseconds +Found key curr_items +Found key total_items +Found key bytes +Found key curr_connections +Found key total_connections +Found key connection_structures +Found key cmd_get +Found key cmd_set +Found key get_hits +Found key get_misses +Found key evictions +Found key bytes_read +Found key bytes_written +Found key limit_maxbytes +Found key threads +Found key pid +Found key uptime +Found key time +Found key version +Found key pointer_size +Found key rusage_user +Found key rusage_system +Found key rusage_user_seconds +Found key rusage_user_microseconds +Found key rusage_system_seconds +Found key rusage_system_microseconds +Found key curr_items +Found key total_items +Found key bytes +Found key curr_connections +Found key total_connections +Found key connection_structures +Found key cmd_get +Found key cmd_set +Found key get_hits +Found key get_misses +Found key evictions +Found key bytes_read +Found key bytes_written +Found key limit_maxbytes +Found key threads -- 2.30.2 From 3b4f04df3a8a1b4fba8e1024310d29b33d0f03c1 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Tue, 20 Nov 2007 18:31:11 -0800 Subject: [PATCH 16/16] Fix for new manpage --- ChangeLog | 1 + support/libmemcached.spec.in | 1 + 2 files changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 2bdfb8e9..9a874cec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ * Fixed bug where hostname might not be null terminated * Moved to using gethostbyname_r() on Linux to solve thread safety issue * Added -rpath support for pkg-config + * Documentation fix for hash setting using memcached_behavior_set() 0.9 Thu Nov 15 07:44:00 PST 2007 * fix for when no servers are definied. diff --git a/support/libmemcached.spec.in b/support/libmemcached.spec.in index 8e6b1aa7..86220b54 100644 --- a/support/libmemcached.spec.in +++ b/support/libmemcached.spec.in @@ -58,6 +58,7 @@ memerror - Creates human readable messages from libmemecached error codes. %{_libdir}/pkgconfig/libmemcached.pc %{_mandir}/man1/memcat.1.gz %{_mandir}/man1/memcp.1.gz +%{_mandir}/man1/memerror.1.gz %{_mandir}/man1/memflush.1.gz %{_mandir}/man1/memrm.1.gz %{_mandir}/man1/memslap.1.gz -- 2.30.2