752 lines
26 KiB
C++
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", 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 = 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", 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", 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", 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", 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", 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();
|
|
}
|