From 2096c491453f4278fdc2fc03759fccfd98ad4168 Mon Sep 17 00:00:00 2001 From: Dustin Sallings Date: Wed, 28 Sep 2011 01:07:55 -0700 Subject: 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). --- memcached.c | 3 +++ t/binary.t | 40 ++++++++++++++++++++++++++++++++++++---- 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; @@ -199,6 +199,22 @@ is($mc->incr("x", undef, 5), 5, "Initial value"); 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); @@ -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) = @_; -- cgit v1.2.1