diff options
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | doc/protocol.txt | 14 | ||||
-rw-r--r-- | memcached.c | 75 | ||||
-rw-r--r-- | memcached.h | 1 |
4 files changed, 60 insertions, 33 deletions
@@ -14,7 +14,8 @@ from Evan Miller and Dustin Sallings. * Add append command support written by Filipe Laborde. - Tests/protocol doc updates by Paul Lindner. + Thread safe version plus prepend command from Maxim Dounin + <mdounin@mdounin.ru> * The memcached-tool script can now display stats. Patch provided by Dan Christian <dchristian@google.com> diff --git a/doc/protocol.txt b/doc/protocol.txt index 2543737..41cc9a7 100644 --- a/doc/protocol.txt +++ b/doc/protocol.txt @@ -53,8 +53,8 @@ Commands There are three types of commands. -Storage commands (there are five: "set", "add", "replace", "append" -and "cas") ask the server to store some data identified by a key. The +Storage commands (there are six: "set", "add", "replace", "append" +"prepend" and "cas") ask the server to store some data identified by a key. The client sends a command line, and then a data block; after that the client expects one line of response, which will indicate success or faulure. @@ -126,9 +126,9 @@ Storage commands First, the client sends a command line which looks like this: -<command name> <key> <flags> <exptime> <bytes> [<unqiue>]\r\n +<command name> <key> <flags> <exptime> <bytes> [<cas unqiue>]\r\n -- <command name> is "set", "add", "replace", "append", or "cas" +- <command name> is "set", "add", "replace", "append", "prepend", or "cas" "set" means "store this data". @@ -138,7 +138,9 @@ First, the client sends a command line which looks like this: "replace" means "store this data, but only if the server *does* already hold data for this key". - "append" means "add this data to an existing key". + "append" means "add this data to an existing key after existing data". + + "prepend" means "add this data to an existing key before existing data". "cas" is a check and set operation which means "store this data but only if no one else has updated since I last fetched it." @@ -165,6 +167,8 @@ First, the client sends a command line which looks like this: it's followed by an empty data block). - <cas unique> is a unique 64-bit value of an existing entry. + Clients should use the value returned from the "gets" command + when issuing "cas" updates. After this line, the client sends the data block: diff --git a/memcached.c b/memcached.c index d8e717d..2cb9a7f 100644 --- a/memcached.c +++ b/memcached.c @@ -719,14 +719,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,8 +783,11 @@ int do_store_item(item *it, int comm) { stored = 1; } - if (old_it) + if (old_it != NULL) do_item_remove(old_it); /* release our reference */ + if (new_it != NULL) + do_item_remove(new_it); + return stored; } @@ -1208,18 +1252,6 @@ static void process_update_command(conn *c, token_t *tokens, const size_t ntoken } } - /* Check if append -- if yes, search for previous entry, and allocate memory for both */ - if( comm == NREAD_APPEND ){ - old_it = assoc_find(key,nkey); - - if( old_it && (old_it->nbytes)>2 ){ // previous must be more than \r\n - old_vlen = old_it->nbytes - 2; - vlen += old_vlen; // append the length of old data - } else { - comm = NREAD_REPLACE; // no old entry: treat as replace - } - } - it = item_alloc(key, nkey, flags, realtime(exptime), vlen+2); /* HANDLE_CAS VALIDATION */ @@ -1268,18 +1300,6 @@ static void process_update_command(conn *c, token_t *tokens, const size_t ntoken c->item = it; c->ritem = ITEM_data(it); c->rlbytes = it->nbytes; - - - /* If append, prepend old data before new - adjust item, rlbytes variables too - * Now that data has been merged, treat simply as a replace command - */ - if (comm == NREAD_APPEND ){ - memcpy( c->ritem, ITEM_data(old_it), old_vlen ); - c->ritem += old_vlen; - c->rlbytes -= old_vlen; - comm = NREAD_REPLACE; - } - c->item_comm = comm; conn_set_state(c, conn_nread); } @@ -1514,6 +1534,7 @@ static void process_command(conn *c, char *command) { ((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, "replace") == 0 && (comm = NREAD_REPLACE)) || + (strcmp(tokens[COMMAND_TOKEN].value, "prepend") == 0 && (comm = NREAD_PREPEND)) || (strcmp(tokens[COMMAND_TOKEN].value, "append") == 0 && (comm = NREAD_APPEND)) )) { process_update_command(c, tokens, ntokens, comm, false); diff --git a/memcached.h b/memcached.h index 4a58db1..b5411c9 100644 --- a/memcached.h +++ b/memcached.h @@ -138,6 +138,7 @@ enum conn_states { #define NREAD_SET 2 #define NREAD_REPLACE 3 #define NREAD_APPEND 4 +#define NREAD_PREPEND 5 typedef struct { int sfd; |