foundationdb/fdbclient/RYWIterator.cpp

824 lines
27 KiB
C++

/*
* RYWIterator.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "fdbclient/RYWIterator.h"
#include "fdbclient/KeyRangeMap.h"
#include "flow/UnitTest.h"
const RYWIterator::SEGMENT_TYPE RYWIterator::typeMap[12] = {
// UNMODIFIED_RANGE
RYWIterator::UNKNOWN_RANGE,
RYWIterator::EMPTY_RANGE,
RYWIterator::KV,
// CLEARED_RANGE
RYWIterator::EMPTY_RANGE,
RYWIterator::EMPTY_RANGE,
RYWIterator::EMPTY_RANGE,
// INDEPENDENT_WRITE
RYWIterator::KV,
RYWIterator::KV,
RYWIterator::KV,
// DEPENDENT_WRITE
RYWIterator::UNKNOWN_RANGE,
RYWIterator::KV,
RYWIterator::KV
};
RYWIterator::SEGMENT_TYPE RYWIterator::type() const {
if (is_unreadable() && !bypassUnreadable)
throw accessed_unreadable();
return typeMap[writes.type() * 3 + cache.type()];
}
bool RYWIterator::is_kv() const {
return type() == KV;
}
bool RYWIterator::is_unknown_range() const {
return type() == UNKNOWN_RANGE;
}
bool RYWIterator::is_empty_range() const {
return type() == EMPTY_RANGE;
}
bool RYWIterator::is_dependent() const {
return writes.type() == WriteMap::iterator::DEPENDENT_WRITE;
}
bool RYWIterator::is_unreadable() const {
return writes.is_unreadable();
}
ExtStringRef RYWIterator::beginKey() {
return begin_key_cmp <= 0 ? writes.beginKey() : cache.beginKey();
}
ExtStringRef RYWIterator::endKey() {
return end_key_cmp <= 0 ? cache.endKey() : writes.endKey();
}
const KeyValueRef* RYWIterator::kv(Arena& arena) {
if (is_unreadable() && !bypassUnreadable)
throw accessed_unreadable();
if (writes.is_unmodified_range()) {
return cache.kv(arena);
}
auto result = (writes.is_independent() || cache.is_empty_range())
? WriteMap::coalesceUnder(writes.op(), Optional<ValueRef>(), arena)
: WriteMap::coalesceUnder(writes.op(), cache.kv(arena)->value, arena);
if (!result.value.present()) {
// Key is now deleted, which can happen because of CompareAndClear.
return nullptr;
}
temp = KeyValueRef(writes.beginKey().assertRef(), result.value.get());
return &temp;
}
RYWIterator& RYWIterator::operator++() {
if (end_key_cmp <= 0)
++cache;
if (end_key_cmp >= 0)
++writes;
begin_key_cmp = -end_key_cmp;
end_key_cmp = cache.endKey().compare(writes.endKey());
return *this;
}
RYWIterator& RYWIterator::operator--() {
if (begin_key_cmp >= 0)
--cache;
if (begin_key_cmp <= 0)
--writes;
end_key_cmp = -begin_key_cmp;
begin_key_cmp = cache.beginKey().compare(writes.beginKey());
return *this;
}
bool RYWIterator::operator==(const RYWIterator& r) const {
return cache == r.cache && writes == r.writes;
}
bool RYWIterator::operator!=(const RYWIterator& r) const {
return !(*this == r);
}
void RYWIterator::skip(
KeyRef key) { // Changes *this to the segment containing key (so that beginKey()<=key && key < endKey())
cache.skip(key);
writes.skip(key);
updateCmp();
}
void RYWIterator::skipContiguous(KeyRef key) {
if (is_kv() && writes.is_unmodified_range()) {
cache.skipContiguous(std::min(ExtStringRef(key), writes.endKey()));
updateCmp();
}
}
void RYWIterator::skipContiguousBack(KeyRef key) {
if (is_kv() && writes.is_unmodified_range()) {
cache.skipContiguousBack(std::max(ExtStringRef(key), writes.beginKey().keyAfter()));
updateCmp();
}
}
WriteMap::iterator& RYWIterator::extractWriteMapIterator() {
return writes;
}
void RYWIterator::dbg() {
fprintf(stderr,
"cache: %d begin: '%s' end: '%s'\n",
cache.type(),
printable(cache.beginKey().toStandaloneStringRef()).c_str(),
printable(cache.endKey().toStandaloneStringRef()).c_str());
fprintf(stderr,
"writes: %d begin: '%s' end: '%s'\n",
writes.type(),
printable(writes.beginKey().toStandaloneStringRef()).c_str(),
printable(writes.endKey().toStandaloneStringRef()).c_str());
// fprintf(stderr, "summary - offset: %d cleared: %d size: %d\n", writes.offset,
// writes.entry().following_keys_cleared, writes.entry().stack.size());
}
void RYWIterator::updateCmp() {
begin_key_cmp = cache.beginKey().compare(writes.beginKey());
end_key_cmp = cache.endKey().compare(writes.endKey());
}
void testESR() {
printf("testESR\n");
int chars[] = { 0, 1, 127, 128, 255 };
int nchars = sizeof(chars) / sizeof(chars[0]);
std::vector<std::string> bases;
bases.push_back(std::string());
for (int i = 0; i < nchars; i++) {
bases.push_back(std::string(1, chars[i]));
for (int j = 0; j < nchars; j++) {
bases.push_back(std::string(1, chars[i]) + std::string(1, chars[j]));
for (int k = 0; k < nchars; k++)
bases.push_back(std::string(1, chars[i]) + std::string(1, chars[j]) + std::string(1, chars[k]));
}
}
printf("1\n");
std::vector<ExtStringRef> srs;
std::vector<Standalone<StringRef>> ssrs;
for (int e = 0; e < 3; e++)
for (auto b = bases.begin(); b != bases.end(); ++b) {
srs.push_back(ExtStringRef(*b, e));
ssrs.push_back(StringRef(*b + std::string(e, 0)));
}
ASSERT(srs.size() == ssrs.size());
printf("2\n");
for (int i = 0; i < srs.size(); i++)
for (int j = 0; j < srs.size(); j++) {
bool c = ssrs[i] != ssrs[j];
bool c2 = srs[i] != srs[j];
if (c != c2) {
printf("Error: '%s' cmp '%s' = %d\n", printable(ssrs[i]).c_str(), printable(ssrs[j]).c_str(), c2);
return;
}
/*
int c = ssrs[i] < ssrs[j] ? -1 : ssrs[i] == ssrs[j] ? 0 : 1;
int c2 = srs[i].compare(srs[j]);
if ( c != (0<c2)-(c2<0) ) {
printf("Error: '%s' cmp '%s' = %d\n", printable(ssrs[i]).c_str(), printable(ssrs[j]).c_str(), c2);
return;
}*/
/*
bool c = ssrs[i].startsWith( ssrs[j] );
int c2 = srs[i].startsWith( srs[j] );
if ( c != c2 ) {
printf("Error: '%s' + %d cmp '%s' + %d = %d\n", printable(srs[i].base).c_str(), srs[i].extra_zero_bytes,
printable(srs[j].base).c_str(), srs[j].extra_zero_bytes, c2); return;
}
bool c = equalsKeyAfter( ssrs[j], ssrs[i] );
int c2 = srs[i].isKeyAfter( srs[j] );
if ( c != c2 ) {
printf("Error: '%s' + %d cmp '%s' + %d = %d\n", printable(srs[i].base).c_str(), srs[i].extra_zero_bytes,
printable(srs[j].base).c_str(), srs[j].extra_zero_bytes, c2); return;
}
*/
}
printf("OK\n");
}
void testSnapshotCache() {
Arena arena;
SnapshotCache cache(&arena);
WriteMap writes(&arena);
Standalone<VectorRef<KeyValueRef>> keys;
keys.push_back_deep(keys.arena(), KeyValueRef("d"_sr, "doo"_sr));
keys.push_back_deep(keys.arena(), KeyValueRef("e"_sr, "eoo"_sr));
keys.push_back_deep(keys.arena(), KeyValueRef("e\x00"_sr, "zoo"_sr));
keys.push_back_deep(keys.arena(), KeyValueRef("f"_sr, "foo"_sr));
cache.insert(KeyRangeRef("d"_sr, "f\x00"_sr), keys);
cache.insert(KeyRangeRef("g"_sr, "h"_sr), Standalone<VectorRef<KeyValueRef>>());
Standalone<VectorRef<KeyValueRef>> keys2;
keys2.push_back_deep(keys2.arena(), KeyValueRef("k"_sr, "koo"_sr));
keys2.push_back_deep(keys2.arena(), KeyValueRef("l"_sr, "loo"_sr));
cache.insert(KeyRangeRef("j"_sr, "m"_sr), keys2);
writes.mutate("c"_sr, MutationRef::SetValue, "c--"_sr, true);
writes.clear(KeyRangeRef("c\x00"_sr, "e"_sr), true);
writes.mutate("c\x00"_sr, MutationRef::SetValue, "c00--"_sr, true);
WriteMap::iterator it3(&writes);
writes.mutate("d"_sr, MutationRef::SetValue, "d--"_sr, true);
writes.mutate("e"_sr, MutationRef::SetValue, "e++"_sr, true);
writes.mutate("i"_sr, MutationRef::SetValue, "i--"_sr, true);
KeyRange searchKeys = KeyRangeRef("a"_sr, "z"_sr);
RYWIterator it(&cache, &writes);
it.skip(searchKeys.begin);
while (true) {
fprintf(stderr,
"b: '%s' e: '%s' type: %s value: '%s'\n",
printable(it.beginKey().toStandaloneStringRef()).c_str(),
printable(it.endKey().toStandaloneStringRef()).c_str(),
it.is_empty_range() ? "empty" : (it.is_kv() ? "keyvalue" : "unknown"),
it.is_kv() ? printable(it.kv(arena)->value).c_str() : "");
if (it.endKey() >= searchKeys.end)
break;
++it;
}
fprintf(stderr, "end\n");
it.skip(searchKeys.end);
while (true) {
fprintf(stderr,
"b: '%s' e: '%s' type: %s value: '%s'\n",
printable(it.beginKey().toStandaloneStringRef()).c_str(),
printable(it.endKey().toStandaloneStringRef()).c_str(),
it.is_empty_range() ? "empty" : (it.is_kv() ? "keyvalue" : "unknown"),
it.is_kv() ? printable(it.kv(arena)->value).c_str() : "");
if (it.beginKey() <= searchKeys.begin)
break;
--it;
}
fprintf(stderr, "end\n");
/*
SnapshotCache::iterator it(&cache);
it.skip(searchKeys.begin);
while (true) {
if( it.is_kv() ) {
Standalone<VectorRef<KeyValueRef>> result;
KeyValueRef const& start = it.kv();
it.skipContiguous(searchKeys.end);
result.append( result.arena(), &start, &it.kv() - &start + 1 );
fprintf(stderr, "%s\n", printable(result).c_str());
}
if (it.endKey() >= searchKeys.end) break;
++it;
}
fprintf(stderr, "end\n");
it.skip(searchKeys.begin);
while (true) {
fprintf(stderr, "b: '%s' e: '%s' type: %s value: '%s'\n",
printable(it.beginKey().toStandaloneStringRef()).c_str(), printable(it.endKey().toStandaloneStringRef()).c_str(),
it.is_empty_range() ? "empty" : ( it.is_kv() ? "keyvalue" : "unknown" ), it.is_kv() ?
printable(it.kv().value).c_str() : ""); if (it.endKey() >= searchKeys.end) break;
++it;
}
fprintf(stderr, "end\n");
it.skip(searchKeys.end);
while (true) {
fprintf(stderr, "b: '%s' e: '%s' type: %s value: '%s'\n",
printable(it.beginKey().toStandaloneStringRef()).c_str(), printable(it.endKey().toStandaloneStringRef()).c_str(),
it.is_empty_range() ? "empty" : ( it.is_kv() ? "keyvalue" : "unknown" ), it.is_kv() ?
printable(it.kv().value).c_str() : "" ); if (it.beginKey() <= searchKeys.begin) break;
--it;
}
fprintf(stderr, "end\n");
WriteMap::iterator it2(&writes);
it2.skip(searchKeys.begin);
while (true) {
fprintf(stderr, "b: '%s' e: '%s' type: %s value: '%s'\n",
printable(it2.beginKey().toStandaloneStringRef()).c_str(), printable(it2.endKey().toStandaloneStringRef()).c_str(),
it2.is_cleared_range() ? "cleared" : ( it2.is_unmodified_range() ? "unmodified" : "operation" ), it2.is_operation()
? printable(it2.op().top().value).c_str() : ""); if (it2.endKey() >= searchKeys.end) break;
++it2;
}
fprintf(stderr, "end\n");
it3.skip(searchKeys.begin);
while (true) {
fprintf(stderr, "b: '%s' e: '%s' type: %s value: '%s'\n",
printable(it3.beginKey().toStandaloneStringRef()).c_str(), printable(it3.endKey().toStandaloneStringRef()).c_str(),
it3.is_cleared_range() ? "cleared" : ( it3.is_unmodified_range() ? "unmodified" : "operation" ), it3.is_operation()
? printable(it3.op().top().value).c_str() : ""); if (it3.endKey() >= searchKeys.end) break;
++it3;
}
fprintf(stderr, "end\n");
*/
}
/*
ACTOR RangeResult getRange( Transaction* tr, KeySelector begin, KeySelector end, SnapshotCache* cache,
WriteMap* writes, GetRangeLimits limits ) {
RYWIterator it(cache, writes); RYWIterator itEnd(cache, writes);
resolveKeySelectorFromCache( begin, it );
resolveKeySelectorFromCache( end, itEnd );
Standalone<VectorRef<KeyValueRef>> result;
while ( !limits.isReached() ) {
if ( it == itEnd && !it.is_unknown_range() ) break;
if (it.is_unknown_range()) {
RYWIterator ucEnd(it);
ucEnd.skipUncached(itEnd);
state KeySelector read_end = ucEnd==itEnd ? end :
firstGreaterOrEqual(ucEnd.endKey().toStandaloneStringRef()); RangeResult snapshot_read = wait(tr->getRange( begin,
read_end, limits, false, false ) ); cache->insert( getKnownKeyRange( snapshot_read, begin, read_end), snapshot_read );
// TODO: Is there a more efficient way to deal with invalidation?
it = itEnd = RYWIterator( cache, writes );
resolveKeySelectorFromCache( begin, it );
resolveKeySelectorFromCache( end, itEnd );
} else if (it.is_kv()) {
KeyValueRef const& start = it.kv();
it.skipContiguous( end.isFirstGreaterOrEqual() ? end.key : allKeys.end );
result.append( result.arena(), &start, &it.kv() - &start + 1 );
limits;
++it;
} else
++it;
}
ASSERT( end.isFirstGreaterOrEqual() );
result.resize( result.arena(), std::lower_bound( result.begin(), result.end(), end.key ) - result.begin() );
}*/
// static void printWriteMap(WriteMap *p) {
// WriteMap::iterator it(p);
// for (it.skip(allKeys.begin); it.beginKey() < allKeys.end; ++it) {
// if (it.is_cleared_range()) {
// printf("CLEARED ");
// }
// if (it.is_conflict_range()) {
// printf("CONFLICT ");
// }
// if (it.is_operation()) {
// printf("OPERATION ");
// printf(it.is_independent() ? "INDEPENDENT " : "DEPENDENT ");
// }
// if (it.is_unmodified_range()) {
// printf("UNMODIFIED ");
// }
// if (it.is_unreadable()) {
// printf("UNREADABLE ");
// }
// printf(": \"%s\" -> \"%s\"\n",
// printable(it.beginKey().toStandaloneStringRef()).c_str(),
// printable(it.endKey().toStandaloneStringRef()).c_str());
// }
// printf("\n");
//}
static int getWriteMapCount(WriteMap* p) {
// printWriteMap(p);
int count = 0;
WriteMap::iterator it(p);
for (it.skip(allKeys.begin); it.beginKey() < allKeys.end; ++it) {
count += 1;
}
return count;
}
TEST_CASE("/fdbclient/WriteMap/emptiness") {
Arena arena = Arena();
WriteMap writes = WriteMap(&arena);
ASSERT(writes.empty());
writes.mutate("apple"_sr, MutationRef::SetValue, "red"_sr, true);
ASSERT(!writes.empty());
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);
ASSERT(writes.empty());
ASSERT(getWriteMapCount(&writes) == 1);
writes.mutate("apple"_sr, MutationRef::SetValue, "red"_sr, true);
ASSERT(!writes.empty());
ASSERT(getWriteMapCount(&writes) == 3);
KeyRangeRef range = KeyRangeRef("a"_sr, "j"_sr);
writes.clear(range, true);
ASSERT(getWriteMapCount(&writes) == 3);
return Void();
}
TEST_CASE("/fdbclient/WriteMap/setVersionstampedKey") {
Arena arena = Arena();
WriteMap writes = WriteMap(&arena);
ASSERT(writes.empty());
ASSERT(getWriteMapCount(&writes) == 1);
writes.mutate("stamp:XXXXXXXX\x06\x00\x00\x00"_sr, MutationRef::SetVersionstampedKey, "1"_sr, true);
ASSERT(!writes.empty());
ASSERT(getWriteMapCount(&writes) == 3);
writes.mutate("stamp:ZZZZZZZZZZ"_sr, MutationRef::AddValue, "2"_sr, true);
ASSERT(getWriteMapCount(&writes) == 5);
WriteMap::iterator it(&writes);
it.skip(allKeys.begin);
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare(""_sr) == 0);
ASSERT(it.endKey().compare("stamp:XXXXXXXX\x06\x00\x00\x00"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(!it.is_conflict_range());
ASSERT(!it.is_operation());
ASSERT(it.is_unmodified_range());
ASSERT(!it.is_unreadable());
++it;
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare("stamp:XXXXXXXX\x06\x00\x00\x00"_sr) == 0);
ASSERT(it.endKey().compare("stamp:XXXXXXXX\x06\x00\x00\x00\x00"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(it.is_conflict_range());
ASSERT(it.is_operation());
ASSERT(it.is_independent());
ASSERT(!it.is_unmodified_range());
ASSERT(it.is_unreadable());
++it;
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare("stamp:XXXXXXXX\x06\x00\x00\x00\x00"_sr) == 0);
ASSERT(it.endKey().compare("stamp:ZZZZZZZZZZ"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(!it.is_conflict_range());
ASSERT(!it.is_operation());
ASSERT(it.is_unmodified_range());
ASSERT(!it.is_unreadable());
++it;
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare("stamp:ZZZZZZZZZZ"_sr) == 0);
ASSERT(it.endKey().compare("stamp:ZZZZZZZZZZ\x00"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(it.is_conflict_range());
ASSERT(it.is_operation());
ASSERT(!it.is_independent());
ASSERT(!it.is_unmodified_range());
ASSERT(!it.is_unreadable());
++it;
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare("stamp:ZZZZZZZZZZ\x00"_sr) == 0);
ASSERT(it.endKey().compare("\xff\xff"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(!it.is_conflict_range());
ASSERT(!it.is_operation());
ASSERT(it.is_unmodified_range());
ASSERT(!it.is_unreadable());
++it;
ASSERT(it.beginKey() >= allKeys.end);
return Void();
}
TEST_CASE("/fdbclient/WriteMap/setVersionstampedValue") {
Arena arena = Arena();
WriteMap writes = WriteMap(&arena);
ASSERT(writes.empty());
ASSERT(getWriteMapCount(&writes) == 1);
writes.mutate("stamp"_sr, MutationRef::SetVersionstampedValue, "XXXXXXXX\x00\x00\x00\x00\x00\x00"_sr, true);
ASSERT(!writes.empty());
ASSERT(getWriteMapCount(&writes) == 3);
writes.mutate("stamp123"_sr, MutationRef::AddValue, "1"_sr, true);
ASSERT(getWriteMapCount(&writes) == 5);
WriteMap::iterator it(&writes);
it.skip(allKeys.begin);
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare(""_sr) == 0);
ASSERT(it.endKey().compare("stamp"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(!it.is_conflict_range());
ASSERT(!it.is_operation());
ASSERT(it.is_unmodified_range());
ASSERT(!it.is_unreadable());
++it;
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare("stamp"_sr) == 0);
ASSERT(it.endKey().compare("stamp\x00"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(it.is_conflict_range());
ASSERT(it.is_operation());
ASSERT(it.is_independent());
ASSERT(!it.is_unmodified_range());
ASSERT(it.is_unreadable());
++it;
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare("stamp\x00"_sr) == 0);
ASSERT(it.endKey().compare("stamp123"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(!it.is_conflict_range());
ASSERT(!it.is_operation());
ASSERT(it.is_unmodified_range());
ASSERT(!it.is_unreadable());
++it;
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare("stamp123"_sr) == 0);
ASSERT(it.endKey().compare("stamp123\x00"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(it.is_conflict_range());
ASSERT(it.is_operation());
ASSERT(!it.is_independent());
ASSERT(!it.is_unmodified_range());
ASSERT(!it.is_unreadable());
++it;
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().compare("stamp123\x00"_sr) == 0);
ASSERT(it.endKey().compare("\xff\xff"_sr) == 0);
ASSERT(!it.is_cleared_range());
ASSERT(!it.is_conflict_range());
ASSERT(!it.is_operation());
ASSERT(it.is_unmodified_range());
ASSERT(!it.is_unreadable());
++it;
ASSERT(it.beginKey() >= allKeys.end);
return Void();
}
TEST_CASE("/fdbclient/WriteMap/addValue") {
Arena arena = Arena();
WriteMap writes = WriteMap(&arena);
ASSERT(writes.empty());
ASSERT(getWriteMapCount(&writes) == 1);
writes.mutate("apple123"_sr, MutationRef::SetValue, "17"_sr, true);
ASSERT(getWriteMapCount(&writes) == 3);
writes.mutate("apple123"_sr, MutationRef::AddValue, "1"_sr, true);
ASSERT(getWriteMapCount(&writes) == 3);
return Void();
}
TEST_CASE("/fdbclient/WriteMap/random") {
Arena arena = Arena();
WriteMap writes = WriteMap(&arena);
ASSERT(writes.empty());
ASSERT(getWriteMapCount(&writes) == 1);
std::map<KeyRef, OperationStack> setMap;
KeyRangeMap<bool> conflictMap;
KeyRangeMap<bool> clearMap;
KeyRangeMap<bool> unreadableMap;
for (int i = 0; i < 100; i++) {
int r = deterministicRandom()->randomInt(0, 10);
if (r == 0) {
KeyRangeRef range = RandomTestImpl::getRandomRange(arena);
writes.addConflictRange(range);
conflictMap.insert(range, true);
TraceEvent("RWMT_AddConflictRange").detail("Range", range);
} else if (r == 1) {
KeyRangeRef range = RandomTestImpl::getRandomRange(arena);
writes.addUnmodifiedAndUnreadableRange(range);
setMap.erase(setMap.lower_bound(range.begin), setMap.lower_bound(range.end));
conflictMap.insert(range, false);
clearMap.insert(range, false);
unreadableMap.insert(range, true);
TraceEvent("RWMT_AddUnmodifiedAndUnreadableRange").detail("Range", range);
} else if (r == 2) {
bool addConflict = deterministicRandom()->random01() < 0.5;
KeyRangeRef range = RandomTestImpl::getRandomRange(arena);
writes.clear(range, addConflict);
setMap.erase(setMap.lower_bound(range.begin), setMap.lower_bound(range.end));
if (addConflict)
conflictMap.insert(range, true);
clearMap.insert(range, true);
unreadableMap.insert(range, false);
TraceEvent("RWMT_Clear").detail("Range", range).detail("AddConflict", addConflict);
} else if (r == 3) {
bool addConflict = deterministicRandom()->random01() < 0.5;
KeyRef key = RandomTestImpl::getRandomKey(arena);
ValueRef value = RandomTestImpl::getRandomValue(arena);
writes.mutate(key, MutationRef::SetVersionstampedValue, value, addConflict);
if (unreadableMap[key])
setMap[key].push(RYWMutation(value, MutationRef::SetVersionstampedValue));
else
setMap[key] = OperationStack(RYWMutation(value, MutationRef::SetVersionstampedValue));
if (addConflict)
conflictMap.insert(key, true);
clearMap.insert(key, false);
unreadableMap.insert(key, true);
TraceEvent("RWMT_SetVersionstampedValue")
.detail("Key", key)
.detail("Value", value.size())
.detail("AddConflict", addConflict);
} else if (r == 4) {
bool addConflict = deterministicRandom()->random01() < 0.5;
KeyRef key = RandomTestImpl::getRandomKey(arena);
ValueRef value = RandomTestImpl::getRandomValue(arena);
writes.mutate(key, MutationRef::SetVersionstampedKey, value, addConflict);
setMap[key].push(RYWMutation(value, MutationRef::SetVersionstampedKey));
if (addConflict)
conflictMap.insert(key, true);
clearMap.insert(key, false);
unreadableMap.insert(key, true);
TraceEvent("RWMT_SetVersionstampedKey")
.detail("Key", key)
.detail("Value", value.size())
.detail("AddConflict", addConflict);
} else if (r == 5) {
bool addConflict = deterministicRandom()->random01() < 0.5;
KeyRef key = RandomTestImpl::getRandomKey(arena);
ValueRef value = RandomTestImpl::getRandomValue(arena);
writes.mutate(key, MutationRef::And, value, addConflict);
auto& stack = setMap[key];
if (clearMap[key]) {
stack = OperationStack(RYWMutation(StringRef(), MutationRef::SetValue));
WriteMap::coalesceOver(stack, RYWMutation(value, MutationRef::And), arena);
} else if (!unreadableMap[key] && stack.size() > 0)
WriteMap::coalesceOver(stack, RYWMutation(value, MutationRef::And), arena);
else
stack.push(RYWMutation(value, MutationRef::And));
if (addConflict)
conflictMap.insert(key, true);
clearMap.insert(key, false);
TraceEvent("RWMT_And").detail("Key", key).detail("Value", value.size()).detail("AddConflict", addConflict);
} else {
bool addConflict = deterministicRandom()->random01() < 0.5;
KeyRef key = RandomTestImpl::getRandomKey(arena);
ValueRef value = RandomTestImpl::getRandomValue(arena);
writes.mutate(key, MutationRef::SetValue, value, addConflict);
if (unreadableMap[key])
setMap[key].push(RYWMutation(value, MutationRef::SetValue));
else
setMap[key] = OperationStack(RYWMutation(value, MutationRef::SetValue));
if (addConflict)
conflictMap.insert(key, true);
clearMap.insert(key, false);
TraceEvent("RWMT_Set").detail("Key", key).detail("Value", value.size()).detail("AddConflict", addConflict);
}
}
WriteMap::iterator it(&writes);
it.skip(allKeys.begin);
auto setIter = setMap.begin();
auto setEnd = setMap.end();
for (; it.beginKey() < allKeys.end; ++it) {
if (it.is_operation()) {
ASSERT(setIter != setEnd);
TraceEvent("RWMT_CheckOperation")
.detail("WmKey", it.beginKey())
.detail("WmSize", it.op().size())
.detail("WmValue",
it.op().top().value.present() ? std::to_string(it.op().top().value.get().size()) : "Not Found")
.detail("WmType", (int)it.op().top().type)
.detail("SmKey", setIter->first)
.detail("SmSize", setIter->second.size())
.detail("SmValue",
setIter->second.top().value.present() ? std::to_string(setIter->second.top().value.get().size())
: "Not Found")
.detail("SmType", (int)setIter->second.top().type);
ASSERT(it.beginKey() == setIter->first && it.op() == setIter->second);
++setIter;
}
}
TraceEvent("RWMT_CheckOperationFinal").detail("WmKey", it.beginKey()).detail("SmIter", setIter == setEnd);
ASSERT(it.beginKey() >= allKeys.end && setIter == setEnd);
it.skip(allKeys.begin);
auto conflictRanges = conflictMap.ranges();
auto conflictIter = conflictRanges.begin();
auto conflictEnd = conflictRanges.end();
while (it.beginKey() < allKeys.end && conflictIter != conflictEnd) {
ASSERT(conflictIter.value() == it.is_conflict_range());
if (conflictIter.range().end < it.endKey()) {
++conflictIter;
} else if (conflictIter.range().end > it.endKey()) {
++it;
} else {
++it;
++conflictIter;
}
}
it.skip(allKeys.begin);
auto clearRanges = clearMap.ranges();
auto clearIter = clearRanges.begin();
auto clearEnd = clearRanges.end();
while (it.beginKey() < allKeys.end && clearIter != clearEnd) {
ASSERT(clearIter.value() == it.is_cleared_range());
if (clearIter.range().end < it.endKey()) {
++clearIter;
} else if (clearIter.range().end > it.endKey()) {
++it;
} else {
++it;
++clearIter;
}
}
it.skip(allKeys.begin);
auto unreadableRanges = unreadableMap.ranges();
auto unreadableIter = unreadableRanges.begin();
auto unreadableEnd = unreadableRanges.end();
while (it.beginKey() < allKeys.end && unreadableIter != unreadableEnd) {
TraceEvent("RWMT_CheckUnreadable")
.detail("WriteMapRange",
KeyRangeRef(it.beginKey().toStandaloneStringRef(), it.endKey().toStandaloneStringRef()))
.detail("UnreadableMapRange", unreadableIter.range())
.detail("WriteMapValue", it.is_unreadable())
.detail("UnreadableMapValue", unreadableIter.value());
ASSERT(unreadableIter.value() == it.is_unreadable());
if (unreadableIter.range().end < it.endKey()) {
++unreadableIter;
} else if (unreadableIter.range().end > it.endKey()) {
++it;
} else {
++it;
++unreadableIter;
}
}
// printWriteMap(&writes);
return Void();
}