1
0
mirror of https://github.com/apple/foundationdb.git synced 2025-05-23 23:59:58 +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

635 lines
26 KiB
C++

/*
* WriteMap.h
*
* 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.
*/
#ifndef FDBCLIENT_WRITEMAP_H
#define FDBCLIENT_WRITEMAP_H
#pragma once
#include "fdbclient/FDBTypes.h"
#include "fdbclient/VersionedMap.h"
#include "fdbclient/SnapshotCache.h"
#include "fdbclient/Atomic.h"
struct RYWMutation {
Optional<ValueRef> value;
enum MutationRef::Type type;
RYWMutation(Optional<ValueRef> const& entry, MutationRef::Type type ) : value(entry), type(type) {}
RYWMutation() : value(), type(MutationRef::NoOp) {}
bool operator == (const RYWMutation& r) const {
return value == r.value && type == r.type;
}
};
class OperationStack {
private:
RYWMutation singletonOperation;
Optional<std::vector<RYWMutation>> optionalOperations;
bool hasVector() const { return optionalOperations.present(); }
bool defaultConstructed;
public:
OperationStack () { defaultConstructed = true; } // Don't use this!
explicit OperationStack (RYWMutation initialEntry) { defaultConstructed = false; singletonOperation = initialEntry; }
void reset(RYWMutation initialEntry) { defaultConstructed = false; singletonOperation = initialEntry; optionalOperations = Optional<std::vector<RYWMutation>>(); }
void poppush(RYWMutation entry) { if(hasVector()) { optionalOperations.get().pop_back(); optionalOperations.get().push_back(entry); } else singletonOperation = entry; }
void push(RYWMutation entry) {
if(defaultConstructed) {
singletonOperation = entry;
defaultConstructed = false;
}
else if(hasVector())
optionalOperations.get().push_back(entry);
else {
optionalOperations = std::vector<RYWMutation>();
optionalOperations.get().push_back(entry);
}
}
bool isDependent() const {
if( !size() )
return false;
return singletonOperation.type != MutationRef::SetValue && singletonOperation.type != MutationRef::ClearRange && singletonOperation.type != MutationRef::SetVersionstampedValue && singletonOperation.type != MutationRef::SetVersionstampedKey;
}
const RYWMutation& top() const { return hasVector() ? optionalOperations.get().back() : singletonOperation; }
RYWMutation& operator[] (int n) { return (n==0) ? singletonOperation : optionalOperations.get()[n-1]; }
const RYWMutation& at(int n) const { return (n==0) ? singletonOperation : optionalOperations.get()[n-1]; }
int size() const { return defaultConstructed ? 0 : hasVector() ? optionalOperations.get().size() + 1 : 1; }
bool operator == (const OperationStack& r) const {
if (size() != r.size())
return false;
if (size() == 0)
return true;
if (singletonOperation != r.singletonOperation)
return false;
if (size() == 1)
return true;
for (int i = 0; i < optionalOperations.get().size(); i++) {
if (optionalOperations.get()[i] != r.optionalOperations.get()[i])
return false;
}
return true;
}
};
struct WriteMapEntry {
KeyRef key;
OperationStack stack;
bool following_keys_cleared;
bool following_keys_conflict;
bool is_conflict;
bool following_keys_unreadable;
bool is_unreadable;
WriteMapEntry( KeyRef const& key, OperationStack && stack, bool following_keys_cleared, bool following_keys_conflict, bool is_conflict, bool following_keys_unreadable, bool is_unreadable ) : key(key), stack(std::move(stack)), following_keys_cleared(following_keys_cleared), following_keys_conflict(following_keys_conflict), is_conflict(is_conflict), following_keys_unreadable(following_keys_unreadable), is_unreadable(is_unreadable) {}
std::string toString() const { return printable(key); }
};
inline bool operator < ( const WriteMapEntry& lhs, const WriteMapEntry& rhs ) { return lhs.key < rhs.key; }
inline bool operator < ( const WriteMapEntry& lhs, const StringRef& rhs ) { return lhs.key < rhs; }
inline bool operator < ( const StringRef& lhs, const WriteMapEntry& rhs ) { return lhs < rhs.key; }
inline bool operator < ( const WriteMapEntry& lhs, const ExtStringRef& rhs ) { return rhs.cmp(lhs.key)>0; }
inline bool operator < ( const ExtStringRef& lhs, const WriteMapEntry& rhs ) { return lhs.cmp(rhs.key)<0; }
class WriteMap {
private:
typedef PTreeImpl::PTree< WriteMapEntry > PTreeT;
typedef Reference<PTreeT> Tree;
public:
explicit WriteMap(Arena* arena) : arena(arena), ver(-1), scratch_iterator(this), writeMapEmpty(true) {
PTreeImpl::insert( writes, ver, WriteMapEntry( allKeys.begin, OperationStack(), false, false, false, false, false ) );
PTreeImpl::insert( writes, ver, WriteMapEntry( allKeys.end, OperationStack(), false, false, false, false, false ) );
PTreeImpl::insert( writes, ver, WriteMapEntry( afterAllKeys, OperationStack(), false, false, false, false, false ) );
}
WriteMap(WriteMap&& r) noexcept(true) : writeMapEmpty(r.writeMapEmpty), writes(std::move(r.writes)), ver(r.ver), scratch_iterator(std::move(r.scratch_iterator)), arena(r.arena) {}
WriteMap& operator=(WriteMap&& r) noexcept(true) { writeMapEmpty = r.writeMapEmpty; writes = std::move(r.writes); ver = r.ver; scratch_iterator = std::move(r.scratch_iterator); arena = r.arena; return *this; }
//a write with addConflict false on top of an existing write with a conflict range will not remove the conflict
void mutate( KeyRef key, MutationRef::Type operation, ValueRef param, bool addConflict ) {
writeMapEmpty = false;
auto& it = scratch_iterator;
it.reset(writes, ver);
it.skip( key );
bool is_cleared = it.entry().following_keys_cleared;
bool following_conflict = it.entry().following_keys_conflict;
bool is_conflict = addConflict || it.is_conflict_range();
bool following_unreadable = it.entry().following_keys_unreadable;
bool is_unreadable = it.is_unreadable() || operation == MutationRef::SetVersionstampedValue || operation == MutationRef::SetVersionstampedKey;
bool is_dependent = operation != MutationRef::SetValue && operation != MutationRef::SetVersionstampedValue && operation != MutationRef::SetVersionstampedKey;
if (it.entry().key != key) {
if( it.is_cleared_range() && is_dependent ) {
it.tree.clear();
OperationStack op( RYWMutation( Optional<StringRef>(), MutationRef::SetValue ) );
coalesceOver(op, RYWMutation(param, operation), *arena);
PTreeImpl::insert( writes, ver, WriteMapEntry( key, std::move(op), true, following_conflict, is_conflict, following_unreadable, is_unreadable ) );
} else {
it.tree.clear();
PTreeImpl::insert( writes, ver, WriteMapEntry( key, OperationStack( RYWMutation( param, operation ) ), is_cleared, following_conflict, is_conflict, following_unreadable, is_unreadable ) );
}
} else {
if( !it.is_unreadable() && operation == MutationRef::SetValue ) {
it.tree.clear();
PTreeImpl::remove( writes, ver, key );
PTreeImpl::insert( writes, ver, WriteMapEntry( key, OperationStack( RYWMutation( param, operation ) ), is_cleared, following_conflict, is_conflict, following_unreadable, is_unreadable ) );
} else {
WriteMapEntry e( it.entry() );
e.is_conflict = is_conflict;
e.is_unreadable = is_unreadable;
if (e.stack.size() == 0 && it.is_cleared_range() && is_dependent) {
e.stack.push(RYWMutation(Optional<StringRef>(), MutationRef::SetValue));
coalesceOver(e.stack, RYWMutation(param, operation), *arena);
} else if( !is_unreadable && e.stack.size() > 0 )
coalesceOver( e.stack, RYWMutation( param, operation ), *arena );
else
e.stack.push( RYWMutation( param, operation ) );
it.tree.clear();
PTreeImpl::remove( writes, ver, e.key ); // FIXME: Make PTreeImpl::insert do this automatically (see also VersionedMap.h FIXME)
PTreeImpl::insert( writes, ver, std::move(e) );
}
}
}
void clear( KeyRangeRef keys, bool addConflict ) {
writeMapEmpty = false;
if( !addConflict ) {
clearNoConflict( keys );
return;
}
auto& it = scratch_iterator;
it.reset(writes, ver);
it.skip( keys.begin );
bool insert_begin = !it.is_cleared_range() || !it.is_conflict_range() || it.is_unreadable();
if(it.endKey() == keys.end) {
++it;
} else if(it.endKey() < keys.end) {
it.skip( keys.end );
}
bool insert_end = ( it.is_unmodified_range() || !it.is_conflict_range() || it.is_unreadable() ) && ( !it.keyAtBegin() || it.beginKey() != keys.end );
bool end_coalesce_clear = it.is_cleared_range() && it.beginKey() == keys.end && it.is_conflict_range() && !it.is_unreadable();
bool end_conflict = it.is_conflict_range();
bool end_cleared = it.is_cleared_range();
bool end_unreadable = it.is_unreadable();
it.tree.clear();
PTreeImpl::remove( writes, ver, ExtStringRef(keys.begin, !insert_begin ? 1 : 0), ExtStringRef(keys.end, end_coalesce_clear ? 1 : 0) );
if (insert_begin)
PTreeImpl::insert( writes, ver, WriteMapEntry( keys.begin, OperationStack(), true, true, true, false, false ) );
if (insert_end)
PTreeImpl::insert( writes, ver, WriteMapEntry( keys.end, OperationStack(), end_cleared, end_conflict, end_conflict, end_unreadable, end_unreadable ) );
}
void addUnmodifiedAndUnreadableRange( KeyRangeRef keys ) {
auto& it = scratch_iterator;
it.reset(writes, ver);
it.skip( keys.begin );
bool insert_begin = !it.is_unmodified_range() || it.is_conflict_range() || !it.is_unreadable();
if(it.endKey() == keys.end) {
++it;
} else if(it.endKey() < keys.end) {
it.skip( keys.end );
}
bool insert_end = ( it.is_cleared_range() || it.is_conflict_range() || !it.is_unreadable() ) && ( !it.keyAtBegin() || it.beginKey() != keys.end );
bool end_coalesce_unmodified = it.is_unmodified_range() && it.beginKey() == keys.end && !it.is_conflict_range() && it.is_unreadable();
bool end_conflict = it.is_conflict_range();
bool end_cleared = it.is_cleared_range();
bool end_unreadable = it.is_unreadable();
it.tree.clear();
PTreeImpl::remove( writes, ver, ExtStringRef(keys.begin, !insert_begin ? 1 : 0), ExtStringRef(keys.end, end_coalesce_unmodified ? 1 : 0) );
if (insert_begin)
PTreeImpl::insert( writes, ver, WriteMapEntry( keys.begin, OperationStack(), false, false, false, true, true ) );
if (insert_end)
PTreeImpl::insert( writes, ver, WriteMapEntry( keys.end, OperationStack(), end_cleared, end_conflict, end_conflict, end_unreadable, end_unreadable ) );
}
void addConflictRange( KeyRangeRef keys ) {
writeMapEmpty = false;
auto& it = scratch_iterator;
it.reset(writes, ver);
it.skip( keys.begin );
std::vector<ExtStringRef> removals;
std::vector<WriteMapEntry> insertions;
if( !it.entry().following_keys_conflict || !it.entry().is_conflict ) {
if( it.keyAtBegin() && it.beginKey() == keys.begin ) {
removals.push_back( keys.begin );
}
insertions.push_back( WriteMapEntry( keys.begin, it.is_operation() ? OperationStack( it.op() ) : OperationStack(), it.entry().following_keys_cleared, true, true, it.entry().following_keys_unreadable, it.entry().is_unreadable ) );
}
while ( it.endKey() < keys.end ) {
++it;
if (it.keyAtBegin() && (!it.entry().following_keys_conflict || !it.entry().is_conflict) ) {
WriteMapEntry e( it.entry() );
e.following_keys_conflict = true;
e.is_conflict = true;
removals.push_back( e.key );
insertions.push_back( std::move(e) );
}
}
ASSERT( it.beginKey() != keys.end );
if( !it.entry().following_keys_conflict || !it.entry().is_conflict ) {
bool isCleared = it.entry().following_keys_cleared;
bool isUnreadable = it.entry().is_unreadable;
bool followingUnreadable = it.entry().following_keys_unreadable;
++it;
if ( !it.keyAtBegin() || it.beginKey() != keys.end ) {
insertions.push_back(WriteMapEntry(keys.end, OperationStack(), isCleared, false, false, followingUnreadable, isUnreadable));
}
}
it.tree.clear();
//SOMEDAY: optimize this code by having a PTree removal/insertion that takes and returns an iterator
for( int i = 0; i < removals.size(); i++ ) {
PTreeImpl::remove( writes, ver, removals[i] ); // FIXME: Make PTreeImpl::insert do this automatically (see also VersionedMap.h FIXME)
}
for( int i = 0; i < insertions.size(); i++ ) {
PTreeImpl::insert( writes, ver, std::move(insertions[i]) );
}
}
struct iterator {
// Iterates over three types of segments: unmodified ranges, cleared ranges, and modified keys.
// Modified keys may be dependent (need to be collapsed with a snapshot value) or independent (value is known regardless of the snapshot value)
// Every key will belong to exactly one segment. The first segment begins at "" and the last segment ends at \xff\xff.
explicit iterator( WriteMap* map ) : tree(map->writes), at( map->ver ), offset(false) { ++map->ver; }
// Creates an iterator which is conceptually before the beginning of map (you may essentially only call skip() or ++ on it)
// This iterator also represents a snapshot (will be unaffected by future writes)
enum SEGMENT_TYPE { UNMODIFIED_RANGE, CLEARED_RANGE, INDEPENDENT_WRITE, DEPENDENT_WRITE };
SEGMENT_TYPE type() {
if (offset)
return entry().following_keys_cleared ? CLEARED_RANGE : UNMODIFIED_RANGE;
else
return entry().stack.isDependent() ? DEPENDENT_WRITE : INDEPENDENT_WRITE;
}
bool is_cleared_range() { return offset && entry().following_keys_cleared; }
bool is_unmodified_range() { return offset && !entry().following_keys_cleared; }
bool is_operation() { return !offset; }
bool is_conflict_range() { return offset ? entry().following_keys_conflict : entry().is_conflict; }
bool is_unreadable() { return offset ? entry().following_keys_unreadable : entry().is_unreadable; }
bool is_independent() { return entry().following_keys_cleared || !entry().stack.isDependent(); } // Defined if is_operation()
ExtStringRef beginKey() { return ExtStringRef( entry().key, offset && entry().stack.size() ); }
ExtStringRef endKey() { return offset ? nextEntry().key : ExtStringRef( entry().key, 1 ); }
OperationStack const& op() { return entry().stack; } // Only if is_operation()
iterator& operator++() {
if (!offset && !equalsKeyAfter( entry().key, nextEntry().key )) {
offset = true;
} else {
beginLen = endLen;
finger.resize( beginLen );
endLen = PTreeImpl::halfNext( at, finger );
offset = !entry().stack.size();
}
return *this;
}
iterator& operator--() {
if (offset && entry().stack.size() ) {
offset = false;
} else {
endLen = beginLen;
finger.resize(endLen);
beginLen = PTreeImpl::halfPrevious( at, finger );
offset = !entry().stack.size() || !equalsKeyAfter( entry().key, nextEntry().key );
}
return *this;
}
bool operator == ( const iterator& r ) const { return offset == r.offset && beginLen == r.beginLen && finger[beginLen-1] == r.finger[beginLen-1]; }
void skip( KeyRef key ) { // Changes *this to the segment containing key (so that beginKey()<=key && key < endKey())
finger.clear();
if( key == allKeys.end )
PTreeImpl::last(tree, at, finger);
else
PTreeImpl::upper_bound( tree, at, key, finger );
endLen = finger.size();
beginLen = PTreeImpl::halfPrevious( at, finger );
offset = !entry().stack.size() || (entry().key != key);
}
private:
friend class WriteMap;
void reset( Tree const& tree, Version ver ) { this->tree = tree; this->at = ver; this->finger.clear(); beginLen=endLen=0; offset = false; }
WriteMapEntry const& entry() { return finger[beginLen-1]->data; }
WriteMapEntry const& nextEntry() { return finger[endLen-1]->data; }
bool keyAtBegin() { return !offset || !entry().stack.size(); }
Tree tree;
Version at;
int beginLen, endLen;
vector< PTreeT const* > finger;
bool offset; // false-> the operation stack at entry(); true-> the following cleared or unmodified range
};
bool empty() const { return writeMapEmpty; }
static RYWMutation coalesce(RYWMutation existingEntry, RYWMutation newEntry, Arena& arena) {
ASSERT(newEntry.value.present());
if (newEntry.type == MutationRef::SetValue)
return newEntry;
else if (newEntry.type == MutationRef::AddValue) {
switch(existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doLittleEndianAdd(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::AddValue:
return RYWMutation(doLittleEndianAdd(existingEntry.value, newEntry.value.get(), arena), MutationRef::AddValue);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::CompareAndClear) {
switch (existingEntry.type) {
case MutationRef::SetValue:
if (doCompareAndClear(existingEntry.value, newEntry.value.get(), arena).present()) {
return existingEntry;
} else {
return RYWMutation(Optional<ValueRef>(), MutationRef::SetValue);
}
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::AppendIfFits) {
switch(existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doAppendIfFits(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::AppendIfFits:
return RYWMutation(doAppendIfFits(existingEntry.value, newEntry.value.get(), arena), MutationRef::AppendIfFits);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::And) {
switch(existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doAnd(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::And:
return RYWMutation(doAnd(existingEntry.value, newEntry.value.get(), arena), MutationRef::And);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::Or) {
switch(existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doOr(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::Or:
return RYWMutation(doOr(existingEntry.value, newEntry.value.get(), arena), MutationRef::Or);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::Xor) {
switch(existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doXor(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::Xor:
return RYWMutation(doXor(existingEntry.value, newEntry.value.get(), arena), MutationRef::Xor);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::Max) {
switch (existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doMax(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::Max:
return RYWMutation(doMax(existingEntry.value, newEntry.value.get(), arena), MutationRef::Max);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::Min) {
switch (existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doMin(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::Min:
return RYWMutation(doMin(existingEntry.value, newEntry.value.get(), arena), MutationRef::Min);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::ByteMin) {
switch (existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doByteMin(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::ByteMin:
return RYWMutation(doByteMin(existingEntry.value, newEntry.value.get(), arena), MutationRef::ByteMin);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::ByteMax) {
switch (existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doByteMax(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::ByteMax:
return RYWMutation(doByteMax(existingEntry.value, newEntry.value.get(), arena), MutationRef::ByteMax);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::MinV2) {
switch (existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doMinV2(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::MinV2:
return RYWMutation(doMinV2(existingEntry.value, newEntry.value.get(), arena), MutationRef::MinV2);
default:
throw operation_failed();
}
} else if (newEntry.type == MutationRef::AndV2) {
switch (existingEntry.type) {
case MutationRef::SetValue:
return RYWMutation(doAndV2(existingEntry.value, newEntry.value.get(), arena), MutationRef::SetValue);
case MutationRef::AndV2:
return RYWMutation(doAndV2(existingEntry.value, newEntry.value.get(), arena), MutationRef::AndV2);
default:
throw operation_failed();
}
} else
throw operation_failed();
}
static void coalesceOver(OperationStack& stack, RYWMutation newEntry, Arena& arena) {
RYWMutation existingEntry = stack.top();
if (existingEntry.type == newEntry.type) {
if (isNonAssociativeOp(existingEntry.type) && existingEntry.value.present() && existingEntry.value.get().size() != newEntry.value.get().size()) {
stack.push(newEntry);
}
else {
stack.poppush(coalesce(existingEntry, newEntry, arena));
}
}
else {
if (isAtomicOp(newEntry.type) && isAtomicOp(existingEntry.type)) {
stack.push(newEntry);
}
else {
stack.poppush(coalesce(existingEntry, newEntry, arena));
}
}
}
static RYWMutation coalesceUnder(OperationStack const& stack, Optional<ValueRef> const& value, Arena& arena) {
if( !stack.isDependent() && stack.size() == 1 )
return stack.at(0);
RYWMutation currentEntry = RYWMutation( value, MutationRef::SetValue);
for(int i = 0; i < stack.size(); ++i) {
currentEntry = coalesce(currentEntry, stack.at(i), arena);
}
return currentEntry;
}
private:
friend class ReadYourWritesTransaction;
Arena* arena;
bool writeMapEmpty;
Tree writes;
Version ver; // an internal version number for the tree - no connection to database versions! Currently this is incremented after reads, so that consecutive writes have the same version and those separated by reads have different versions.
iterator scratch_iterator; // Avoid unnecessary memory allocation in write operations
void dump() {
iterator it( this );
it.skip(allKeys.begin);
while( it.beginKey() < allKeys.end ) {
TraceEvent("WriteMapDump").detail("Begin", printable(it.beginKey().toStandaloneStringRef()))
.detail("End", printable(it.endKey().toStandaloneStringRef()))
.detail("Cleared", it.is_cleared_range())
.detail("Conflicted", it.is_conflict_range())
.detail("Operation", it.is_operation())
.detail("Unmodified", it.is_unmodified_range())
.detail("Independent", it.is_operation() && it.is_independent())
.detail("StackSize", it.is_operation() ? it.op().size() : 0);
++it;
}
}
//SOMEDAY: clearNoConflict replaces cleared sets with two map entries for everyone one item cleared
void clearNoConflict( KeyRangeRef keys ) {
auto& it = scratch_iterator;
it.reset(writes, ver);
//Find all write conflict ranges within the cleared range
it.skip( keys.begin );
bool insert_begin = !it.is_cleared_range() || it.is_unreadable();
bool lastConflicted = it.is_conflict_range();
bool conflicted = lastConflicted;
std::vector<ExtStringRef> conflict_ranges;
if( insert_begin ) {
conflict_ranges.push_back( keys.begin );
} else {
conflicted = !conflicted;
}
while( it.endKey() < keys.end ) {
++it;
if ( lastConflicted != it.is_conflict_range() ) {
conflict_ranges.push_back( it.beginKey() );
lastConflicted = it.is_conflict_range();
}
}
if( it.endKey() == keys.end )
++it;
ASSERT( it.beginKey() <= keys.end && keys.end < it.endKey() );
bool insert_end = ( ( it.is_unmodified_range() || it.is_unreadable() ) && ( !it.keyAtBegin() || it.beginKey() != keys.end ) ) || ( it.entry().is_conflict && !it.entry().following_keys_conflict && it.beginKey() == keys.end && !it.keyAtBegin() );
bool end_cleared = it.is_cleared_range();
bool end_coalesce_clear = it.is_cleared_range() && it.beginKey() == keys.end && it.is_conflict_range()==lastConflicted && !it.is_unreadable();
bool end_conflict = it.is_conflict_range();
bool end_unreadable = it.is_unreadable();
TEST( it.is_conflict_range() != lastConflicted );
it.tree.clear();
PTreeImpl::remove( writes, ver, ExtStringRef(keys.begin, !insert_begin ? 1 : 0), ExtStringRef(keys.end, end_coalesce_clear ? 1 : 0) );
for( int i = 0; i < conflict_ranges.size(); i++ ) {
PTreeImpl::insert( writes, ver, WriteMapEntry( conflict_ranges[i].toArenaOrRef(*arena), OperationStack(), true, conflicted, conflicted, false, false ) );
conflicted = !conflicted;
}
ASSERT( conflicted != lastConflicted );
if (insert_end)
PTreeImpl::insert( writes, ver, WriteMapEntry( keys.end, OperationStack(), end_cleared, end_conflict, end_conflict, end_unreadable, end_unreadable ) );
}
};
/*
for write in writes: # write.type in [ 'none', 'clear', 'independent', 'dependent' ]
for read in reads[ write.begin : write.end ]: # read.type in [ 'unknown', 'empty', 'value' ]
if write.type == "none":
yield read
elif write.type == "clear":
yield empty()
elif write.type == "independent":
yield value( write )
else: # Dependent write
if read.type == "unknown":
yield read
else:
yield value( collapse( read, write ) )
*/
#endif