Fix operation_failed thrown incorrectly from transactions (#6993)

* Add a test demonstrating the issue

If you write a versionstamped value after a set, then reading throws
operation_failed.

* Treat SetVersionstampedValue as independent in coalesce and mutate
This commit is contained in:
Andrew Noyes 2022-05-02 13:49:42 -07:00 committed by GitHub
parent cf6e39af79
commit 0a4b364379
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 26 additions and 3 deletions

View File

@ -430,6 +430,27 @@ TEST_CASE("/fdbclient/WriteMap/emptiness") {
return Void();
}
TEST_CASE("/fdbclient/WriteMap/VersionstampedvalueAfterSet") {
Arena arena = Arena();
SnapshotCache cache(&arena);
WriteMap writes = WriteMap(&arena);
ASSERT(writes.empty());
writes.mutate("apple"_sr, MutationRef::SetValue, "red"_sr, true);
writes.mutate("apple"_sr, MutationRef::SetVersionstampedValue, metadataVersionRequiredValue, true);
RYWIterator it(&cache, &writes);
it.bypassUnreadableProtection();
it.skip("apple"_sr);
ASSERT(it.is_unreadable());
ASSERT(it.is_kv());
const KeyValueRef* kv = it.kv(arena);
ASSERT(kv->key == "apple"_sr);
ASSERT(kv->value == metadataVersionRequiredValue);
return Void();
}
TEST_CASE("/fdbclient/WriteMap/clear") {
Arena arena = Arena();
WriteMap writes = WriteMap(&arena);

View File

@ -231,7 +231,8 @@ public:
is_unreadable));
}
} else {
if (!it.is_unreadable() && operation == MutationRef::SetValue) {
if (!it.is_unreadable() &&
(operation == MutationRef::SetValue || operation == MutationRef::SetVersionstampedValue)) {
it.tree.clear();
PTreeImpl::remove(writes, ver, key);
PTreeImpl::insert(writes,
@ -523,9 +524,10 @@ public:
static RYWMutation coalesce(RYWMutation existingEntry, RYWMutation newEntry, Arena& arena) {
ASSERT(newEntry.value.present());
if (newEntry.type == MutationRef::SetValue)
if (newEntry.type == MutationRef::SetValue || newEntry.type == MutationRef::SetVersionstampedValue) {
// independent mutations
return newEntry;
else if (newEntry.type == MutationRef::AddValue) {
} else if (newEntry.type == MutationRef::AddValue) {
switch (existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doLittleEndianAdd(existingEntry.value, newEntry.value.get(), arena),