2017-05-26 04:48:44 +08:00
/*
* WriteMap . h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013 - 2018 Apple Inc . and the FoundationDB project authors
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* 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
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* http : //www.apache.org/licenses/LICENSE-2.0
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* 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
2018-10-20 01:30:13 +08:00
# include "fdbclient/FDBTypes.h"
# include "fdbclient/VersionedMap.h"
# include "fdbclient/SnapshotCache.h"
# include "fdbclient/Atomic.h"
2017-05-26 04:48:44 +08:00
struct RYWMutation {
2017-10-24 07:48:13 +08:00
Optional < ValueRef > value ;
2017-05-26 04:48:44 +08:00
enum MutationRef : : Type type ;
2017-10-24 07:48:13 +08:00
RYWMutation ( Optional < ValueRef > const & entry , MutationRef : : Type type ) : value ( entry ) , type ( type ) { }
RYWMutation ( ) : value ( ) , type ( MutationRef : : NoOp ) { }
2017-05-26 04:48:44 +08:00
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 ;
2018-03-22 09:58:19 +08:00
return singletonOperation . type ! = MutationRef : : SetValue & & singletonOperation . type ! = MutationRef : : ClearRange & & singletonOperation . type ! = MutationRef : : SetVersionstampedValue & & singletonOperation . type ! = MutationRef : : SetVersionstampedKey ;
2017-05-26 04:48:44 +08:00
}
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 ) ) ;
}
2019-01-26 08:49:59 +08:00
WriteMap ( WriteMap & & r ) BOOST_NOEXCEPT : 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 ) BOOST_NOEXCEPT { writeMapEmpty = r . writeMapEmpty ; writes = std : : move ( r . writes ) ; ver = r . ver ; scratch_iterator = std : : move ( r . scratch_iterator ) ; arena = r . arena ; return * this ; }
2017-05-26 04:48:44 +08:00
//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 ;
2018-03-22 09:58:19 +08:00
bool is_unreadable = it . is_unreadable ( ) | | operation = = MutationRef : : SetVersionstampedValue | | operation = = MutationRef : : SetVersionstampedKey ;
bool is_dependent = operation ! = MutationRef : : SetValue & & operation ! = MutationRef : : SetVersionstampedValue & & operation ! = MutationRef : : SetVersionstampedKey ;
2017-05-26 04:48:44 +08:00
if ( it . entry ( ) . key ! = key ) {
if ( it . is_cleared_range ( ) & & is_dependent ) {
it . tree . clear ( ) ;
2017-10-24 07:48:13 +08:00
OperationStack op ( RYWMutation ( Optional < StringRef > ( ) , MutationRef : : SetValue ) ) ;
2017-05-26 04:48:44 +08:00
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 ) {
2017-10-24 07:48:13 +08:00
e . stack . push ( RYWMutation ( Optional < StringRef > ( ) , MutationRef : : SetValue ) ) ;
2017-05-26 04:48:44 +08:00
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 ) ) ;
2019-01-31 17:23:32 +08:00
2017-05-26 04:48:44 +08:00
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 ) {
2017-10-24 07:48:13 +08:00
ASSERT ( newEntry . value . present ( ) ) ;
2017-05-26 04:48:44 +08:00
if ( newEntry . type = = MutationRef : : SetValue )
return newEntry ;
else if ( newEntry . type = = MutationRef : : AddValue ) {
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doLittleEndianAdd ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-05-26 04:48:44 +08:00
case MutationRef : : AddValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doLittleEndianAdd ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : AddValue ) ;
2017-05-26 04:48:44 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} 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 ) {
2017-05-26 04:48:44 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doAppendIfFits ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-05-26 04:48:44 +08:00
case MutationRef : : AppendIfFits :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doAppendIfFits ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : AppendIfFits ) ;
2017-05-26 04:48:44 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : And ) {
2017-05-26 04:48:44 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doAnd ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-05-26 04:48:44 +08:00
case MutationRef : : And :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doAnd ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : And ) ;
2017-05-26 04:48:44 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : Or ) {
2017-05-26 04:48:44 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doOr ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-05-26 04:48:44 +08:00
case MutationRef : : Or :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doOr ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : Or ) ;
2017-05-26 04:48:44 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : Xor ) {
2017-05-26 04:48:44 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doXor ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-05-26 04:48:44 +08:00
case MutationRef : : Xor :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doXor ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : Xor ) ;
2017-05-26 04:48:44 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : Max ) {
2017-05-26 04:48:44 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doMax ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-05-26 04:48:44 +08:00
case MutationRef : : Max :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doMax ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : Max ) ;
2017-05-26 04:48:44 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : Min ) {
2017-05-26 04:48:44 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doMin ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-05-26 04:48:44 +08:00
case MutationRef : : Min :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doMin ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : Min ) ;
2017-05-26 04:48:44 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : ByteMin ) {
2017-10-11 04:02:22 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doByteMin ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-10-11 04:02:22 +08:00
case MutationRef : : ByteMin :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doByteMin ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : ByteMin ) ;
2017-10-11 04:02:22 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : ByteMax ) {
2017-10-11 04:02:22 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doByteMax ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : SetValue ) ;
2017-10-11 04:02:22 +08:00
case MutationRef : : ByteMax :
2017-10-24 07:48:13 +08:00
return RYWMutation ( doByteMax ( existingEntry . value , newEntry . value . get ( ) , arena ) , MutationRef : : ByteMax ) ;
2017-10-11 04:02:22 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : MinV2 ) {
2017-10-11 04:02:22 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-26 05:48:05 +08:00
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 ) ;
2017-10-11 04:02:22 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else if ( newEntry . type = = MutationRef : : AndV2 ) {
2017-10-11 04:02:22 +08:00
switch ( existingEntry . type ) {
case MutationRef : : SetValue :
2017-10-26 05:48:05 +08:00
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 ) ;
2017-10-11 04:02:22 +08:00
default :
throw operation_failed ( ) ;
}
2019-01-31 17:23:32 +08:00
} else
throw operation_failed ( ) ;
2017-05-26 04:48:44 +08:00
}
static void coalesceOver ( OperationStack & stack , RYWMutation newEntry , Arena & arena ) {
RYWMutation existingEntry = stack . top ( ) ;
2019-03-23 06:55:09 +08:00
if ( existingEntry . type = = newEntry . type & & newEntry . type ! = MutationRef : : CompareAndClear ) {
2017-10-24 07:48:13 +08:00
if ( isNonAssociativeOp ( existingEntry . type ) & & existingEntry . value . present ( ) & & existingEntry . value . get ( ) . size ( ) ! = newEntry . value . get ( ) . size ( ) ) {
2017-05-26 04:48:44 +08:00
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 ) ;
2017-10-24 07:48:13 +08:00
RYWMutation currentEntry = RYWMutation ( value , MutationRef : : SetValue ) ;
2017-05-26 04:48:44 +08:00
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 ) {
2019-03-19 06:03:43 +08:00
TraceEvent ( " WriteMapDump " ) . detail ( " Begin " , it . beginKey ( ) . toStandaloneStringRef ( ) )
. detail ( " End " , it . endKey ( ) . toStandaloneStringRef ( ) )
2017-05-26 04:48:44 +08:00
. 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 ) )
*/
2018-02-16 13:35:00 +08:00
# endif