Mercurial > hg > memcached
comparison memcached.c @ 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 | 30782bb1fc04 |
children | 0b6ac95a09bb |
comparison
equal
deleted
inserted
replaced
1:7ba3a424d509 | 2:e28ab6bd21fa |
---|---|
715 char *key = ITEM_key(it); | 715 char *key = ITEM_key(it); |
716 bool delete_locked = false; | 716 bool delete_locked = false; |
717 item *old_it = do_item_get_notedeleted(key, it->nkey, &delete_locked); | 717 item *old_it = do_item_get_notedeleted(key, it->nkey, &delete_locked); |
718 int stored = 0; | 718 int stored = 0; |
719 | 719 |
720 item *new_it = NULL; | |
721 int flags; | |
722 | |
720 if (old_it != NULL && comm == NREAD_ADD) { | 723 if (old_it != NULL && comm == NREAD_ADD) { |
721 /* add only adds a nonexistent item, but promote to head of LRU */ | 724 /* add only adds a nonexistent item, but promote to head of LRU */ |
722 do_item_update(old_it); | 725 do_item_update(old_it); |
723 } else if (!old_it && comm == NREAD_REPLACE) { | 726 } else if (!old_it && (comm == NREAD_REPLACE |
727 || comm == NREAD_APPEND || comm == NREAD_PREPEND)) | |
728 { | |
724 /* replace only replaces an existing value; don't store */ | 729 /* replace only replaces an existing value; don't store */ |
725 } else if (delete_locked && (comm == NREAD_REPLACE || comm == NREAD_ADD)) { | 730 } else if (delete_locked && (comm == NREAD_REPLACE || comm == NREAD_ADD |
731 || comm == NREAD_APPEND || comm == NREAD_PREPEND)) | |
732 { | |
726 /* replace and add can't override delete locks; don't store */ | 733 /* replace and add can't override delete locks; don't store */ |
727 } else { | 734 } else { |
735 | |
736 /* | |
737 * Append - combine new and old record into single one. Here it's | |
738 * atomic and thread-safe. | |
739 */ | |
740 | |
741 if (comm == NREAD_APPEND || comm == NREAD_PREPEND) { | |
742 | |
743 /* we have it and old_it here - alloc memory to hold both */ | |
744 /* flags was already lost - so recover them from ITEM_suffix(it) */ | |
745 | |
746 flags = (int) strtol(ITEM_suffix(it), (char **) NULL, 10); | |
747 | |
748 new_it = do_item_alloc(key, it->nkey, flags, it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */); | |
749 | |
750 if (new_it == NULL) { | |
751 /* SERVER_ERROR out of memory */ | |
752 return 0; | |
753 } | |
754 | |
755 /* copy data from it and old_it to new_it */ | |
756 | |
757 if (comm == NREAD_APPEND) { | |
758 memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes); | |
759 memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(it), it->nbytes); | |
760 } else { | |
761 /* NREAD_PREPEND */ | |
762 memcpy(ITEM_data(new_it), ITEM_data(it), it->nbytes); | |
763 memcpy(ITEM_data(new_it) + it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes); | |
764 } | |
765 | |
766 it = new_it; | |
767 } | |
768 | |
728 /* "set" commands can override the delete lock | 769 /* "set" commands can override the delete lock |
729 window... in which case we have to find the old hidden item | 770 window... in which case we have to find the old hidden item |
730 that's in the namespace/LRU but wasn't returned by | 771 that's in the namespace/LRU but wasn't returned by |
731 item_get.... because we need to replace it */ | 772 item_get.... because we need to replace it */ |
732 if (delete_locked) | 773 if (delete_locked) |
740 stored = 1; | 781 stored = 1; |
741 } | 782 } |
742 | 783 |
743 if (old_it) | 784 if (old_it) |
744 do_item_remove(old_it); /* release our reference */ | 785 do_item_remove(old_it); /* release our reference */ |
786 if (new_it) | |
787 do_item_remove(new_it); | |
788 | |
745 return stored; | 789 return stored; |
746 } | 790 } |
747 | 791 |
748 typedef struct token_s { | 792 typedef struct token_s { |
749 char *value; | 793 char *value; |
1454 process_get_command(c, tokens, ntokens); | 1498 process_get_command(c, tokens, ntokens); |
1455 | 1499 |
1456 } else if (ntokens == 6 && | 1500 } else if (ntokens == 6 && |
1457 ((strcmp(tokens[COMMAND_TOKEN].value, "add") == 0 && (comm = NREAD_ADD)) || | 1501 ((strcmp(tokens[COMMAND_TOKEN].value, "add") == 0 && (comm = NREAD_ADD)) || |
1458 (strcmp(tokens[COMMAND_TOKEN].value, "set") == 0 && (comm = NREAD_SET)) || | 1502 (strcmp(tokens[COMMAND_TOKEN].value, "set") == 0 && (comm = NREAD_SET)) || |
1503 (strcmp(tokens[COMMAND_TOKEN].value, "append") == 0 && (comm = NREAD_APPEND)) || | |
1504 (strcmp(tokens[COMMAND_TOKEN].value, "prepend") == 0 && (comm = NREAD_PREPEND)) || | |
1459 (strcmp(tokens[COMMAND_TOKEN].value, "replace") == 0 && (comm = NREAD_REPLACE)))) { | 1505 (strcmp(tokens[COMMAND_TOKEN].value, "replace") == 0 && (comm = NREAD_REPLACE)))) { |
1460 | 1506 |
1461 process_update_command(c, tokens, ntokens, comm); | 1507 process_update_command(c, tokens, ntokens, comm); |
1462 | 1508 |
1463 } else if (ntokens == 4 && (strcmp(tokens[COMMAND_TOKEN].value, "incr") == 0)) { | 1509 } else if (ntokens == 4 && (strcmp(tokens[COMMAND_TOKEN].value, "incr") == 0)) { |