summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDustin Sallings <dustin@spy.net>2011-09-28 01:07:55 -0700
committerdormando <dormando@rydia.net>2011-09-28 01:26:45 -0700
commit2096c491453f4278fdc2fc03759fccfd98ad4168 (patch)
tree46611e7b8575dd20d4a67f3e34a1e00dad2f4f99
parentcff1f1403a1e575955993854ea087bc83ec6ebaf (diff)
downloadmemcached-2096c491453f4278fdc2fc03759fccfd98ad4168.tar.gz
bug220: incr would sometimes return the previous item's CAS
This happens when we allocate a new item instead of reusing the space of an existing one, but consistently set the CAS from the original item's CAS (which is being discarded).
-rw-r--r--memcached.c3
-rwxr-xr-xt/binary.t40
2 files changed, 39 insertions, 4 deletions
diff --git a/memcached.c b/memcached.c
index 95a0124..a578b10 100644
--- a/memcached.c
+++ b/memcached.c
@@ -3069,6 +3069,9 @@ enum delta_result_type do_add_delta(conn *c, const char *key, const size_t nkey,
memcpy(ITEM_data(new_it), buf, res);
memcpy(ITEM_data(new_it) + res, "\r\n", 2);
item_replace(it, new_it);
+ // Overwrite the older item's CAS with our new CAS since we're
+ // returning the CAS of the old item below.
+ ITEM_set_cas(it, (settings.use_cas) ? ITEM_get_cas(new_it) : 0);
do_item_remove(new_it); /* release our reference */
} else { /* replace in-place */
/* When changing the value without replacing the item, we
diff --git a/t/binary.t b/t/binary.t
index 8fa27d1..99dcc52 100755
--- a/t/binary.t
+++ b/t/binary.t
@@ -2,7 +2,7 @@
use strict;
use warnings;
-use Test::More tests => 3450;
+use Test::More tests => 3470;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
@@ -200,6 +200,22 @@ is($mc->decr("x"), 4, "Decrease by one");
is($mc->decr("x", 211), 0, "Floor is zero");
{
+ # diag "bug220
+ my ($rv, $cas) = $mc->set("bug220", "100", 0, 0);
+ my ($irv, $icas) = $mc->incr_cas("bug220", 999);
+ ok($icas != $cas);
+ is($irv, 1099, "Incr amount failed");
+ my ($flags, $val, $gcas) = $mc->get("bug220");
+ is($gcas, $icas, "CAS didn't match after incr/gets");
+
+ ($irv, $icas) = $mc->incr_cas("bug220", 999);
+ ok($icas != $cas);
+ is($irv, 2098, "Incr amount failed");
+ ($flags, $val, $gcas) = $mc->get("bug220");
+ is($gcas, $icas, "CAS didn't match after incr/gets");
+}
+
+{
# diag "bug21";
$mc->add("bug21", "9223372036854775807", 0, 0);
is($mc->incr("bug21"), 9223372036854775808, "First incr for bug21.");
@@ -620,18 +636,24 @@ sub _incrdecr_header {
return $extra_header;
}
-sub _incrdecr {
+sub _incrdecr_cas {
my $self = shift;
my ($cmd, $key, $amt, $init, $exp) = @_;
- my ($data, undef) = $self->_do_command($cmd, $key, '',
+ my ($data, $rcas) = $self->_do_command($cmd, $key, '',
$self->_incrdecr_header($amt, $init, $exp));
my $header = substr $data, 0, 8, '';
my ($resp_hi, $resp_lo) = unpack "NN", $header;
my $resp = ($resp_hi * 2 ** 32) + $resp_lo;
- return $resp;
+ return $resp, $rcas;
+}
+
+sub _incrdecr {
+ my $self = shift;
+ my ($v, $c) = $self->_incrdecr_cas(@_);
+ return $v
}
sub silent_incrdecr {
@@ -779,6 +801,16 @@ sub incr {
return $self->_incrdecr(::CMD_INCR, $key, $amt, $init, $exp);
}
+sub incr_cas {
+ my $self = shift;
+ my ($key, $amt, $init, $exp) = @_;
+ $amt = 1 unless defined $amt;
+ $init = 0 unless defined $init;
+ $exp = 0 unless defined $exp;
+
+ return $self->_incrdecr_cas(::CMD_INCR, $key, $amt, $init, $exp);
+}
+
sub decr {
my $self = shift;
my ($key, $amt, $init, $exp) = @_;