Full CAS support now enabled in the library.
authorBrian Aker <brian@tangent.org>
Mon, 19 Nov 2007 21:31:46 +0000 (13:31 -0800)
committerBrian Aker <brian@tangent.org>
Mon, 19 Nov 2007 21:31:46 +0000 (13:31 -0800)
ChangeLog
docs/Makefile.am
docs/memcached_behavior.pod
docs/memcached_set.pod
include/memcached.h
lib/common.h
lib/memcached_behavior.c
lib/memcached_get.c
lib/memcached_storage.c
support/libmemcached.spec.in
tests/function.c

index 26c4b141f61f3d5a45ff0d7b1658c77dcbdb908f..ac498778fb465291ef9e2088f11ec0750bbb3ae3 100644 (file)
--- 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.
index 723dfe2c15d98cbaa039ce310c84da0aec17ffa1..378a881934b5e223b366864cabcc16fdbb874cda 100644 (file)
@@ -34,6 +34,7 @@ man_MANS = libmemcached.3\
        memcached_append.3\\r
        memcached_behavior_get.3\\r
        memcached_behavior_set.3\\r
+       memcached_cas.3\\r
        memcached_clone.3\\r
        memcached_create.3\\r
        memcached_decrement.3\\r
@@ -81,6 +82,9 @@ memcached_clone.3: memcached_create.pod
 memcached_set.3: memcached_set.pod\r
        pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_set.3\r
 \r
+memcached_cas.3: memcached_set.pod\r
+       pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_cas.3\r
+\r
 memcached_replace.3: memcached_set.pod\r
        pod2man -c "libmemcached" -r "" -s 3 memcached_set.pod > memcached_replace.3\r
 \r
index 4d7d114037fc7e6e57758b96eef80a89774f9eec..20141907ba8f281cf34ab2567cf53b81986c8dd9 100755 (executable)
@@ -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
index 39dd86d2bd6e21c0d3c0dece90aefaa4d2e1e0bc..0578d17f3b372c39db2e3a1f39e36b1efb758dac 100755 (executable)
@@ -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
 
index 53b51ea03ee94657c13aabb24b27e2902e579e7b..33fa053dc402489228d719e2a2085f123440b019 100644 (file)
@@ -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 {
index 1e564cf5f894d5a00533916ee10bca6e9544b703..73913c1c05e98f187ddee33687470b4821db4f4c 100644 (file)
@@ -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 */
index 67a52fa8ac212c18bc9a37cbced1f1abe728aa06..76d0b61fb54b8be2b65903fede6adbbdc8bc64a7 100644 (file)
@@ -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;
index db626f6fa07eefc54b9159c64adc51f05abb5e64..09ec8a374f8e23b10f658bdb895b855e50c0382b 100644 (file)
@@ -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)
index af763a3ed5dd945f7b0debed1befc5cc519bfcf5..7b48afeedea1e29a5b1146004ee73a59040324bc 100644 (file)
@@ -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)
   {
index bf112562a7c33f8f8a0564be33c59ed1ca65798c..d2eef19c022909f267de429f33fa45a6cbbdf7ad 100644 (file)
@@ -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
index 68eddbd738816fb8ecdc6feed165f35d2ed6ce31..63b56acb043bc0c7214fff95eef10005a738c163 100644 (file)
@@ -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}
 };