From 93cab40b9112f97376381038e83240a7b28c24ea Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Thu, 15 Nov 2007 08:10:49 -0800 Subject: [PATCH] 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