Incomming fixes from Charles on the replication branch.
[awesomized/libmemcached] / lib / memcached_get.c
index 560a50de07dffb36274fcaff566ecc350a6dac48..12d301ec35319d0faabbe336f75e002a0d024777 100644 (file)
-#include <memcached.h>
+#include "common.h"
+#include "memcached_io.h"
 
+/* 
+  What happens if no servers exist?
+*/
 char *memcached_get(memcached_st *ptr, char *key, size_t key_length, 
                     size_t *value_length, 
-                    uint16_t *flags,
+                    uint32_t *flags,
                     memcached_return *error)
 {
-  size_t send_length, sent_length;
-  char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
-  char *string_ptr;
-  unsigned int server_key;
-
-  *value_length= 0;
-  *error= memcached_connect(ptr);
-
-  if (*error != MEMCACHED_SUCCESS)
-    return NULL;
-
-  server_key= memcached_generate_hash(key, key_length) % ptr->number_of_hosts;
+  return memcached_get_by_key(ptr, NULL, 0, key, key_length, value_length, 
+                              flags, error);
+}
 
-  send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, "get %.*s\r\n", 
-                        (int)key_length, key);
+char *memcached_get_by_key(memcached_st *ptr, 
+                           char *master_key, size_t master_key_length, 
+                           char *key, size_t key_length, 
+                           size_t *value_length, 
+                           uint32_t *flags,
+                           memcached_return *error)
+{
+  unsigned int server_key;
+  size_t send_length;
+  char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
+  memcached_result_st *result_buffer= &ptr->result;
+  memcached_return rc[MEMCACHED_MAX_REPLICAS];
+  uint8_t replicas= 0;
 
-  if (send_length >= MEMCACHED_DEFAULT_COMMAND_SIZE)
+  if (ptr->number_of_hosts == 0)
   {
-    *error= MEMCACHED_WRITE_FAILURE;
+    *error= MEMCACHED_NO_SERVERS;
     return NULL;
   }
 
-  sent_length= write(ptr->hosts[server_key].fd, buffer, send_length);
-
-  if (sent_length == -1)
+  if ((ptr->flags & MEM_VERIFY_KEY) && (memcachd_key_test(&key, &key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED))
   {
-    fprintf(stderr, "error %s: write: %m\n", __FUNCTION__);
-    *error= MEMCACHED_WRITE_FAILURE;
+    *value_length= 0;
+    *error= MEMCACHED_BAD_KEY_PROVIDED;
     return NULL;
   }
 
-  if (sent_length != send_length)
-  {
-    fprintf(stderr, "error %s: short write %d %d: %m\n",
-           __FUNCTION__, sent_length, send_length);
-    *error= MEMCACHED_WRITE_FAILURE;
-    return NULL;
-  }
+  if (master_key)
+    server_key= memcached_generate_hash(ptr, master_key, master_key_length);
+  else
+    server_key= memcached_generate_hash(ptr, key, key_length);
 
-  memset(buffer, 0, MEMCACHED_DEFAULT_COMMAND_SIZE);
-  *error= memcached_response(ptr, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, server_key);
+  send_length= snprintf(buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, 
+                        "get %.*s\r\n", (int)key_length, key);
 
-  if (*error == MEMCACHED_SUCCESS)
+  do
   {
-    char *end_ptr;
-
-    string_ptr= buffer;
-    string_ptr+= 6; /* "VALUE " */
+    char response_buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
 
-    /* We do nothing with the key, since we only asked for one key */
-    for (end_ptr= string_ptr; *end_ptr != ' '; end_ptr++);
-
-    /* Flags fetch */
-    string_ptr= end_ptr + 1;
-    for (end_ptr= string_ptr; *end_ptr != ' '; end_ptr++);
-    *flags= (uint16_t)strtol(string_ptr, &end_ptr, 10);
-
-    /* Length fetch */
-    string_ptr= end_ptr + 1;
-    for (end_ptr= string_ptr; *end_ptr != ' '; end_ptr++);
-    *value_length= strtoll(string_ptr, &end_ptr, 10);
-
-    /* Skip past the \r\n */
-    string_ptr= end_ptr +2;
-
-    if (*value_length)
+    if (memcached_server_response_count(&ptr->hosts[server_key]))
     {
-      size_t need_to_copy;
-      char *pos_ptr;
-      char *value;
+      if (ptr->flags & MEM_NO_BLOCK)
+        (void)memcached_io_write(&ptr->hosts[server_key], NULL, 0, 1);
 
-      value= (char *)malloc(*value_length * sizeof(char));
-
-      if (!value)
-      {
-        *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
-        return NULL;
-      }
+      while(memcached_server_response_count(&ptr->hosts[server_key]))
+        (void)memcached_response(&ptr->hosts[server_key], response_buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, result_buffer);
+    }
 
-      need_to_copy= (*value_length < (size_t)(buffer-string_ptr)) 
-        ? *value_length 
-        : (size_t)(buffer-string_ptr) ;
+    rc[replicas]= memcached_do(&ptr->hosts[server_key], buffer, send_length, 1);
+    if (rc[replicas] != MEMCACHED_SUCCESS)
+      goto error;
 
-      pos_ptr= memcpy(value, string_ptr, need_to_copy);
+    rc[replicas]= memcached_response(&ptr->hosts[server_key], response_buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, result_buffer);
 
-      if ( need_to_copy > *value_length)
-      {
-        size_t read_length;
-        size_t need_to_read;
+    /* On no key found, we check the replica */
+    if (rc[replicas] == MEMCACHED_END) /* END means that we move on to the next */
+    {
+      memcached_server_response_reset(&ptr->hosts[server_key]);
+    }
+    else if (rc[replicas] == MEMCACHED_SUCCESS)
+    {
+      *value_length= memcached_string_length(&result_buffer->value);
+    
+      if (result_buffer->flags)
+        *flags= result_buffer->flags;
 
-        need_to_read= *value_length - need_to_copy;
+      *error= MEMCACHED_SUCCESS;
+      return  memcached_string_c_copy(&result_buffer->value);
+    }
 
-        read_length= read(ptr->hosts[server_key].fd, pos_ptr, need_to_read);
-        if (read_length != need_to_read)
-        {
-          free(value);
-          *error= MEMCACHED_PARTIAL_READ;
+    /* On error we just jump to the next potential server */
+error:
+    if (ptr->number_of_replicas > 1)
+    {
+      if (server_key == (ptr->number_of_hosts - 1))
+        server_key= 0;
+      else
+        server_key++;
+    }
+  } while ((++replicas) < ptr->number_of_replicas);
 
-          return NULL;
-        }
-      }
+  /* TODO: An error on replica 1 of host down, but not found on 2, will give wrong error */
+  /* This is for historical reasons */
+  if (rc[0] == MEMCACHED_END)
+    *error= MEMCACHED_NOTFOUND;
+  else
+    *error= rc[0];
 
-      return value;
-    }
-  }
+  *value_length= 0;
 
   return NULL;
 }