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 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.
 
 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_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
        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_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
 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.
 
 
 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
 =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)
                       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
 
 
 =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_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
 
 
 =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_SOCKET_SEND_SIZE,
   MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE,
   MEMCACHED_BEHAVIOR_CACHE_LOOKUPS,
+  MEMCACHED_BEHAVIOR_SUPPORT_CAS,
 } memcached_behavior;
 
 typedef enum {
 } 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_USE_KETAMA= (1 << 4),
   MEM_USE_CRC= (1 << 5),
   MEM_USE_CACHE_LOOKUPS= (1 << 6),
+  MEM_SUPPORT_CAS= (1 << 7),
 } memcached_flags;
 
 /* Hashing algo */
 } memcached_flags;
 
 /* Hashing algo */
index 67a52fa8ac212c18bc9a37cbced1f1abe728aa06..76d0b61fb54b8be2b65903fede6adbbdc8bc64a7 100644 (file)
@@ -26,6 +26,9 @@ memcached_return memcached_behavior_set(memcached_st *ptr,
 {
   switch (flag)
   {
 {
   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;
   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)
   {
 
   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;
   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,
 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)
 {
                                               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;
 
     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;
 
     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, 
     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)
   {
   *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;
 {
   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;
 
   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->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;
   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);
 
     {
       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;
       {
         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, 
     }
 
     *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)
     *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;
     }
 
       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,
     *error= memcached_value_fetch(ptr, result->key, &result->key_length, 
                                        &result->value, 
                                        &result->flags,
+                                       &result->cas,
                                        1, ptr->cursor_server);
     
     if (*error == MEMCACHED_NOTFOUND)
                                        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)
 
 
   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);
     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)
   {
 
   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_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
 %{_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;
 }
 
   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;
 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;
   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);
 
 
   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);
   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);
   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")));
   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 },
 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}
 };
   {"append_binary", 0, append_binary_test },
   {0, 0, 0}
 };