From 264a1ae95d273b6b32afed45d7c7c7461bfa54e4 Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Mon, 19 Nov 2007 13:31:46 -0800 Subject: [PATCH] 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