1
0
mirror of https://github.com/apple/foundationdb.git synced 2025-05-21 05:53:02 +08:00
Vishesh Yadav c532d5c277 Implements CompareAndClear AtomicOp
Adds CompareAndClear mutation. If the given parameter is equal to the
current value of the key, the key is cleared. At client, the mutation
is added to the operation stack. Hence if the mutation evaluates to
clear, we only get to know so when `read()` evaluates the stack in
`RYWIterator::kv()`, which is unlike what we currently do for typical
ClearRange.
2019-02-04 14:59:56 -08:00

752 lines
26 KiB
C++

/*
* RYWIterator.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 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() {
if (is_unreadable())
throw accessed_unreadable();
return typeMap[ writes.type()*3 + cache.type() ];
}
bool RYWIterator::is_kv() { return type() == KV; }
bool RYWIterator::is_unknown_range() { return type() == UNKNOWN_RANGE; }
bool RYWIterator::is_empty_range() { return type() == EMPTY_RANGE; }
bool RYWIterator::is_dependent() { return writes.type() == WriteMap::iterator::DEPENDENT_WRITE; }
bool RYWIterator::is_unreadable() { 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())
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().cmp(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().cmp(writes.beginKey());
return *this;
}
bool RYWIterator::operator == ( const RYWIterator& r ) const { return cache == r.cache && writes == r.writes; }
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().cmp(writes.beginKey());
end_key_cmp = cache.endKey().cmp(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].cmp(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(LiteralStringRef("d"), LiteralStringRef("doo")));
keys.push_back_deep(keys.arena(), KeyValueRef(LiteralStringRef("e"), LiteralStringRef("eoo")));
keys.push_back_deep(keys.arena(), KeyValueRef(LiteralStringRef("e\x00"), LiteralStringRef("zoo")));
keys.push_back_deep(keys.arena(), KeyValueRef(LiteralStringRef("f"), LiteralStringRef("foo")));
cache.insert(KeyRangeRef(LiteralStringRef("d"), LiteralStringRef("f\x00")), keys);
cache.insert(KeyRangeRef(LiteralStringRef("g"), LiteralStringRef("h")), Standalone<VectorRef<KeyValueRef>>());
Standalone<VectorRef<KeyValueRef>> keys2;
keys2.push_back_deep(keys2.arena(), KeyValueRef(LiteralStringRef("k"), LiteralStringRef("koo")));
keys2.push_back_deep(keys2.arena(), KeyValueRef(LiteralStringRef("l"), LiteralStringRef("loo")));
cache.insert(KeyRangeRef(LiteralStringRef("j"), LiteralStringRef("m")), keys2);
writes.mutate( LiteralStringRef("c"), MutationRef::SetValue, LiteralStringRef("c--"), true);
writes.clear(KeyRangeRef(LiteralStringRef("c\x00"), LiteralStringRef("e")), true);
writes.mutate( LiteralStringRef("c\x00"), MutationRef::SetValue, LiteralStringRef("c00--"), true);
WriteMap::iterator it3(&writes);
writes.mutate( LiteralStringRef("d"), MutationRef::SetValue, LiteralStringRef("d--"), true);
writes.mutate( LiteralStringRef("e"), MutationRef::SetValue, LiteralStringRef("e++"), true);
writes.mutate( LiteralStringRef("i"), MutationRef::SetValue, LiteralStringRef("i--"), true);
KeyRange searchKeys = KeyRangeRef(LiteralStringRef("a"), LiteralStringRef("z"));
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 Standalone<RangeResultRef> 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());
Standalone<RangeResultRef> 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(LiteralStringRef("apple"), MutationRef::SetValue, LiteralStringRef("red"), true);
ASSERT(!writes.empty());
return Void();
}
TEST_CASE("/fdbclient/WriteMap/clear") {
Arena arena = Arena();
WriteMap writes = WriteMap(&arena);
ASSERT(writes.empty());
ASSERT(getWriteMapCount(&writes) == 1);
writes.mutate(LiteralStringRef("apple"), MutationRef::SetValue, LiteralStringRef("red"), true);
ASSERT(!writes.empty());
ASSERT(getWriteMapCount(&writes) == 3);
KeyRangeRef range = KeyRangeRef(LiteralStringRef("a"), LiteralStringRef("j"));
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(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00"), MutationRef::SetVersionstampedKey, LiteralStringRef("1"), true);
ASSERT(!writes.empty());
ASSERT(getWriteMapCount(&writes) == 3);
writes.mutate(LiteralStringRef("stamp:ZZZZZZZZZZ"), MutationRef::AddValue, LiteralStringRef("2"), true);
ASSERT(getWriteMapCount(&writes) == 5);
WriteMap::iterator it(&writes);
it.skip(allKeys.begin);
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().cmp(LiteralStringRef("")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00")) == 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().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00\x00")) == 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().cmp(LiteralStringRef("stamp:XXXXXXXX\x06\x00\x00\x00\x00")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("stamp:ZZZZZZZZZZ")) == 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().cmp(LiteralStringRef("stamp:ZZZZZZZZZZ")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("stamp:ZZZZZZZZZZ\x00")) == 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().cmp(LiteralStringRef("stamp:ZZZZZZZZZZ\x00")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("\xff\xff")) == 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(LiteralStringRef("stamp"), MutationRef::SetVersionstampedValue, LiteralStringRef("XXXXXXXX\x00\x00\x00\x00\x00\x00"), true);
ASSERT(!writes.empty());
ASSERT(getWriteMapCount(&writes) == 3);
writes.mutate(LiteralStringRef("stamp123"), MutationRef::AddValue, LiteralStringRef("1"), true);
ASSERT(getWriteMapCount(&writes) == 5);
WriteMap::iterator it(&writes);
it.skip(allKeys.begin);
ASSERT(it.beginKey() < allKeys.end);
ASSERT(it.beginKey().cmp(LiteralStringRef("")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("stamp")) == 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().cmp(LiteralStringRef("stamp")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("stamp\x00")) == 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().cmp(LiteralStringRef("stamp\x00")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("stamp123")) == 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().cmp(LiteralStringRef("stamp123")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("stamp123\x00")) == 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().cmp(LiteralStringRef("stamp123\x00")) == 0);
ASSERT(it.endKey().cmp(LiteralStringRef("\xff\xff")) == 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(LiteralStringRef("apple123"), MutationRef::SetValue, LiteralStringRef("17"), true);
ASSERT(getWriteMapCount(&writes) == 3);
writes.mutate(LiteralStringRef("apple123"), MutationRef::AddValue, LiteralStringRef("1"), 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 = g_random->randomInt(0, 10);
if (r == 0) {
KeyRangeRef range = RandomTestImpl::getRandomRange(arena);
writes.addConflictRange(range);
conflictMap.insert(range, true);
TraceEvent("RWMT_AddConflictRange").detail("Range", printable(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", printable(range));
}
else if (r == 2) {
bool addConflict = g_random->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", printable(range)).detail("AddConflict", addConflict);
}
else if (r == 3) {
bool addConflict = g_random->random01() < 0.5;
KeyRef key = RandomTestImpl::getRandomKey(arena);
ValueRef value = RandomTestImpl::getRandomValue(arena);
writes.mutate(key, MutationRef::SetVersionstampedValue, value, addConflict);
setMap[key].push(RYWMutation(value, MutationRef::SetVersionstampedValue));
if (addConflict)
conflictMap.insert(key, true);
clearMap.insert(key, false);
unreadableMap.insert(key, true);
TraceEvent("RWMT_SetVersionstampedValue").detail("Key", printable(key)).detail("Value", value.size()).detail("AddConflict", addConflict);
}
else if (r == 4) {
bool addConflict = g_random->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", printable(key)).detail("Value", value.size()).detail("AddConflict", addConflict);
}
else if (r == 5) {
bool addConflict = g_random->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", printable(key)).detail("Value", value.size()).detail("AddConflict", addConflict);
}
else {
bool addConflict = g_random->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", printable(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", printable(it.beginKey().toStandaloneStringRef()))
.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", printable(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", printable(it.beginKey().toStandaloneStringRef()))
.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", printable(KeyRangeRef(it.beginKey().toStandaloneStringRef(), it.endKey().toStandaloneStringRef())))
.detail("UnreadableMapRange", printable(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();
}