changeset 2:e28ab6bd21fa

Append/prepend commands. Introduce new storage commands "append" and "prepend" for atomic value modification. They follow generic storage commands interface as described in doc/protocol.txt: <command name> <key> <flags> <exptime> <bytes>\r\n <data block>\r\n Current implementation involves memcpy() twice to combine values on writing (i.e. full resulting value are copied), but truly atomic (and even threadsafe).
author Maxim Dounin <mdounin@mdounin.ru>
date Sun, 30 Sep 2007 04:14:57 +0400
parents 7ba3a424d509
children 0b6ac95a09bb
files memcached.c memcached.h
diffstat 2 files changed, 50 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/memcached.c
+++ b/memcached.c
@@ -717,14 +717,55 @@ int do_store_item(item *it, int comm) {
     item *old_it = do_item_get_notedeleted(key, it->nkey, &delete_locked);
     int stored = 0;
 
+    item *new_it = NULL;
+    int flags;
+
     if (old_it != NULL && comm == NREAD_ADD) {
         /* add only adds a nonexistent item, but promote to head of LRU */
         do_item_update(old_it);
-    } else if (!old_it && comm == NREAD_REPLACE) {
+    } else if (!old_it && (comm == NREAD_REPLACE
+        || comm == NREAD_APPEND || comm == NREAD_PREPEND))
+    {
         /* replace only replaces an existing value; don't store */
-    } else if (delete_locked && (comm == NREAD_REPLACE || comm == NREAD_ADD)) {
+    } else if (delete_locked && (comm == NREAD_REPLACE || comm == NREAD_ADD
+        || comm == NREAD_APPEND || comm == NREAD_PREPEND))
+    {
         /* replace and add can't override delete locks; don't store */
     } else {
+
+        /*
+         * Append - combine new and old record into single one. Here it's
+         * atomic and thread-safe.
+         */
+
+        if (comm == NREAD_APPEND || comm == NREAD_PREPEND) {
+
+            /* we have it and old_it here - alloc memory to hold both */
+            /* flags was already lost - so recover them from ITEM_suffix(it) */
+
+            flags = (int) strtol(ITEM_suffix(it), (char **) NULL, 10);
+
+            new_it = do_item_alloc(key, it->nkey, flags, it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */);
+
+            if (new_it == NULL) {
+                /* SERVER_ERROR out of memory */
+                return 0;
+            }
+
+            /* copy data from it and old_it to new_it */
+
+            if (comm == NREAD_APPEND) {
+                memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);
+                memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(it), it->nbytes);
+            } else {
+                /* NREAD_PREPEND */ 
+                memcpy(ITEM_data(new_it), ITEM_data(it), it->nbytes);
+                memcpy(ITEM_data(new_it) + it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);
+            }
+
+            it = new_it;
+        }
+
         /* "set" commands can override the delete lock
            window... in which case we have to find the old hidden item
            that's in the namespace/LRU but wasn't returned by
@@ -742,6 +783,9 @@ int do_store_item(item *it, int comm) {
 
     if (old_it)
         do_item_remove(old_it);         /* release our reference */
+    if (new_it)
+        do_item_remove(new_it);
+
     return stored;
 }
 
@@ -1456,6 +1500,8 @@ static void process_command(conn *c, cha
     } else if (ntokens == 6 &&
                ((strcmp(tokens[COMMAND_TOKEN].value, "add") == 0 && (comm = NREAD_ADD)) ||
                 (strcmp(tokens[COMMAND_TOKEN].value, "set") == 0 && (comm = NREAD_SET)) ||
+                (strcmp(tokens[COMMAND_TOKEN].value, "append") == 0 && (comm = NREAD_APPEND)) ||
+                (strcmp(tokens[COMMAND_TOKEN].value, "prepend") == 0 && (comm = NREAD_PREPEND)) ||
                 (strcmp(tokens[COMMAND_TOKEN].value, "replace") == 0 && (comm = NREAD_REPLACE)))) {
 
         process_update_command(c, tokens, ntokens, comm);
--- a/memcached.h
+++ b/memcached.h
@@ -134,6 +134,8 @@ enum conn_states {
 #define NREAD_ADD 1
 #define NREAD_SET 2
 #define NREAD_REPLACE 3
+#define NREAD_APPEND 4
+#define NREAD_PREPEND 5
 
 typedef struct {
     int    sfd;