1
0
mirror of https://github.com/apple/foundationdb.git synced 2025-05-27 18:37:04 +08:00

Change to send back index of read_conflict_range not keys, which speeds up but fail to give narrowed keyrange

This commit is contained in:
chaoguang 2020-01-15 10:21:51 -08:00
parent 1cf3b5b807
commit 72d39a31f1
8 changed files with 42 additions and 82 deletions

@ -104,7 +104,7 @@ struct CommitID {
Version version; // returns invalidVersion if transaction conflicts
uint16_t txnBatchId;
Optional<Value> metadataVersion;
Optional<Standalone<VectorRef<KeyRangeRef>>> conflictingKeyRanges;
Optional<Standalone<VectorRef<int>>> conflictingKeyRanges;
template <class Ar>
void serialize(Ar& ar) {
@ -112,7 +112,7 @@ struct CommitID {
}
CommitID() : version(invalidVersion), txnBatchId(0) {}
CommitID( Version version, uint16_t txnBatchId, const Optional<Value>& metadataVersion, const Optional<Standalone<VectorRef<KeyRangeRef>>>& conflictingKeyRanges = Optional<Standalone<VectorRef<KeyRangeRef>>>() ) : version(version), txnBatchId(txnBatchId), metadataVersion(metadataVersion), conflictingKeyRanges(conflictingKeyRanges) {}
CommitID( Version version, uint16_t txnBatchId, const Optional<Value>& metadataVersion, const Optional<Standalone<VectorRef<int>>>& conflictingKeyRanges = Optional<Standalone<VectorRef<int>>>() ) : version(version), txnBatchId(txnBatchId), metadataVersion(metadataVersion), conflictingKeyRanges(conflictingKeyRanges) {}
};
struct CommitTransactionRequest : TimedRequest {

@ -2677,29 +2677,6 @@ ACTOR static Future<Void> tryCommit( Database cx, Reference<TransactionLogInfo>
// clear the RYW transaction which contains previous conflicting keys
tr->info.conflictingKeysRYW.reset();
if (ci.conflictingKeyRanges.present()){
// Since the performance of the proxy is important, we push the union of overlapped key range to client
// Union all overlapped key ranges here
std::vector<KeyRangeRef> unmergedConflictingKRs(ci.conflictingKeyRanges.get().begin(), ci.conflictingKeyRanges.get().end());
Standalone<VectorRef<KeyRangeRef>> mergedKeyRanges;
// At least one keyRange returned
ASSERT(unmergedConflictingKRs.size());
// Sort the keyranges by begin key, then union overlapping ranges from left to right
std::sort(unmergedConflictingKRs.begin(), unmergedConflictingKRs.end(), [](KeyRangeRef a, KeyRangeRef b){
return a.begin < b.begin;
});
auto next = unmergedConflictingKRs.begin();
// At least one conflicting keyrange should be returned, which means curr is valid
KeyRangeRef curr = *(next++);
while (next != unmergedConflictingKRs.end()) {
if (curr.end >= next->begin) {
curr = KeyRangeRef(curr.begin, std::max(curr.end, next->end));
} else {
mergedKeyRanges.push_back(mergedKeyRanges.arena(), curr);
curr = *next;
}
next++;
}
mergedKeyRanges.push_back(mergedKeyRanges.arena(), curr);
// In general, if we want to use getRange to expose conflicting keys, we need to support all the parameters getRange provides.
// It is difficult to take care of all corner cases of what getRange does.
// Consequently, we use a hack way here to achieve it.
@ -2714,7 +2691,10 @@ ACTOR static Future<Void> tryCommit( Database cx, Reference<TransactionLogInfo>
// in case system keys are conflicting
tr->info.conflictingKeysRYW->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->info.conflictingKeysRYW->clear(systemKeys);
for (const KeyRangeRef & kr : mergedKeyRanges) {
const auto cKRs = ci.conflictingKeyRanges.get();
std::set<int> mergedIds(cKRs.begin(), cKRs.end());
for (auto const & rCRIndex : mergedIds) {
const KeyRangeRef & kr = req.transaction.read_conflict_ranges[rCRIndex];
tr->info.conflictingKeysRYW->set(kr.begin, conflictingKeysTrue);
tr->info.conflictingKeysRYW->set(kr.end, conflictingKeysFalse);
}

@ -33,7 +33,7 @@ void clearConflictSet( ConflictSet*, Version );
void destroyConflictSet(ConflictSet*);
struct ConflictBatch {
explicit ConflictBatch( ConflictSet*, std::map< int, Standalone<VectorRef<KeyRangeRef>>>* conflictingKeyRangeMap = nullptr);
explicit ConflictBatch( ConflictSet*, std::map< int, std::vector< int > >* conflictingKeyRangeMap = nullptr);
~ConflictBatch();
enum TransactionCommitResult {
@ -54,7 +54,7 @@ private:
std::vector< std::pair<StringRef,StringRef> > combinedWriteConflictRanges;
std::vector< struct ReadConflictRange > combinedReadConflictRanges;
bool* transactionConflictStatus;
std::map< int, Standalone< VectorRef< KeyRangeRef > > >* conflictingKeyRangeMap;
std::map< int, std::vector< int > >* conflictingKeyRangeMap;
void checkIntraBatchConflicts();
void combineWriteConflictRanges();

@ -1034,20 +1034,15 @@ ACTOR Future<Void> commitBatch(
else {
// If enable the option to report conflicting keys from resolvers, we union all conflicting key ranges here and send back through CommitID
if (trs[t].transaction.report_conflicting_keys) {
Standalone<VectorRef<KeyRangeRef>> conflictingEntries;
int conflictingKeyRangeNum = 0;
Standalone<VectorRef<int>> conflictingEntryIds;
for (int resolverInd : transactionResolverMap[t]) {
conflictingKeyRangeNum += resolution[resolverInd].conflictingKeyRangeMap[nextTr[resolverInd]].size();
}
conflictingEntries.reserve(conflictingEntries.arena(), conflictingKeyRangeNum);
for (int resolverInd : transactionResolverMap[t]) {
for (const KeyRangeRef & kr : resolution[resolverInd].conflictingKeyRangeMap[nextTr[resolverInd]]){
conflictingEntries.push_back(conflictingEntries.arena(), kr);
for (auto const & rCRIndex : resolution[resolverInd].conflictingKeyRangeMap[nextTr[resolverInd]]){
conflictingEntryIds.push_back(conflictingEntryIds.arena(), rCRIndex);
}
}
// At least one keyRange should be returned
ASSERT(conflictingEntries.size());
trs[t].reply.send(CommitID(invalidVersion, t, Optional<Value>(), Optional<Standalone<VectorRef<KeyRangeRef>>>(conflictingEntries.contents())));
ASSERT(conflictingEntryIds.size());
trs[t].reply.send(CommitID(invalidVersion, t, Optional<Value>(), Optional<Standalone<VectorRef<int>>>(conflictingEntryIds)));
} else {
trs[t].reply.sendError(not_committed());
}

@ -132,7 +132,7 @@ ACTOR Future<Void> resolveBatch(
vector<int> commitList;
vector<int> tooOldList;
std::map< int, Standalone< VectorRef< KeyRangeRef > > > conflictingKeyRangeMap;
std::map< int, std::vector< int > > conflictingKeyRangeMap;
// Detect conflicts
double expire = now() + SERVER_KNOBS->SAMPLE_EXPIRATION_TIME;

@ -77,7 +77,7 @@ struct ResolveTransactionBatchReply {
VectorRef<uint8_t> committed;
Optional<UID> debugID;
VectorRef<VectorRef<StateTransactionRef>> stateMutations; // [version][transaction#] -> (committed, [mutation#])
std::map<int, Standalone<VectorRef<KeyRangeRef>>> conflictingKeyRangeMap; // transaction index -> conflicting keyRanges given by the resolver
std::map<int, std::vector<int>> conflictingKeyRangeMap; // transaction index -> conflicting read_conflict_range ids given by the resolver
template <class Archive>
void serialize(Archive& ar) {

@ -123,9 +123,10 @@ struct ReadConflictRange {
StringRef begin, end;
Version version;
int transaction;
Standalone< VectorRef< KeyRangeRef > >* conflictingKeyRange;
ReadConflictRange( StringRef begin, StringRef end, Version version, int transaction, Standalone<VectorRef<KeyRangeRef>> * cKR = nullptr )
: begin(begin), end(end), version(version), transaction(transaction), conflictingKeyRange(cKR)
std::vector<int>* conflictingKeyRange;
int indexInTx;
ReadConflictRange( StringRef begin, StringRef end, Version version, int transaction, int indexInTx, std::vector<int> * cKR = nullptr )
: begin(begin), end(end), version(version), transaction(transaction), indexInTx(indexInTx), conflictingKeyRange(cKR)
{
}
bool operator<(const ReadConflictRange& rhs) const { return compare(begin, rhs.begin)<0; }
@ -530,7 +531,7 @@ public:
int started = min(M,count);
for(int i=0; i<started; i++){
inProgress[i].init( ranges[i], header, transactionConflictStatus, ranges[i].conflictingKeyRange );
inProgress[i].init( ranges[i], header, transactionConflictStatus, ranges[i].indexInTx, ranges[i].conflictingKeyRange );
nextJob[i] = i+1;
}
nextJob[started-1] = 0;
@ -547,7 +548,7 @@ public:
}
else {
int temp = started++;
inProgress[job].init( ranges[temp], header, transactionConflictStatus, ranges[temp].conflictingKeyRange );
inProgress[job].init( ranges[temp], header, transactionConflictStatus, ranges[temp].indexInTx, ranges[temp].conflictingKeyRange );
}
}
prevJob = job;
@ -760,31 +761,24 @@ private:
Version version;
bool *result;
int state;
Standalone<VectorRef<KeyRangeRef>>* conflictingKeyRange; // null if report_conflicting_keys is not enabled.
int indexInTx;
std::vector<int>* conflictingKeyRange; // null if report_conflicting_keys is not enabled.
void init( const ReadConflictRange& r, Node* header, bool* tCS, Standalone<VectorRef<KeyRangeRef>>* cKR) {
void init( const ReadConflictRange& r, Node* header, bool* tCS, int indexInTx, std::vector<int>* cKR) {
this->start.init( r.begin, header );
this->end.init( r.end, header );
this->version = r.version;
this->indexInTx = indexInTx;
result = &tCS[ r.transaction ];
conflictingKeyRange = cKR;
this->state = 0;
}
bool noConflict() { return true; }
bool conflict(Node* start_node, Node* end_node) {
bool conflict() {
*result = true;
if(conflictingKeyRange != nullptr){
StringRef startKey(start.value);
StringRef endKey(end.value);
if (start_node != nullptr) {
startKey = StringRef(start_node->value(), start_node->length());
}
if (end_node != nullptr) {
endKey = StringRef(end_node->value(), end_node->length());
}
conflictingKeyRange->push_back_deep(conflictingKeyRange->arena(), KeyRangeRef(startKey, endKey));
}
if(conflictingKeyRange != nullptr)
conflictingKeyRange->push_back(indexInTx);
return true;
}
@ -808,7 +802,7 @@ private:
if (start.finger[l]->getMaxVersion(l) <= version)
return noConflict();
if (l==0)
return conflict(nullptr, nullptr); // The whole read_conflict_range is conflicting with other transactions with higher version number
return conflict(); // The whole read_conflict_range is conflicting with other transactions with higher version number
}
state = 1;
case 1:
@ -817,12 +811,12 @@ private:
Node *e = end.finger[end.level];
while (e->getMaxVersion(end.level) > version) {
if (end.finished())
return conflict(e, nullptr);
return conflict();
end.nextLevel();
Node *f = end.finger[end.level];
while (e != f){
if (e->getMaxVersion(end.level) > version)
return conflict(e, nullptr);
return conflict();
e = e->getNext(end.level);
}
}
@ -834,7 +828,7 @@ private:
Node *p = nextS;
while (p != s){
if (p->getMaxVersion(start.level) > version)
return conflict(p, s);
return conflict();
p = p->getNext(start.level);
}
if (start.finger[start.level]->getMaxVersion(start.level) <= version)
@ -844,7 +838,7 @@ private:
if (nextS->length() == start.value.size() && !memcmp(nextS->value(), start.value.begin(), start.value.size()))
return noConflict();
else
return conflict(nullptr, nextS);
return conflict();
}
start.nextLevel();
}
@ -980,7 +974,7 @@ void destroyConflictSet(ConflictSet* cs) {
delete cs;
}
ConflictBatch::ConflictBatch( ConflictSet* cs, std::map< int, Standalone<VectorRef<KeyRangeRef>> >* conflictingKeyRangeMap )
ConflictBatch::ConflictBatch( ConflictSet* cs, std::map< int, std::vector< int > >* conflictingKeyRangeMap )
: cs(cs), transactionCount(0), conflictingKeyRangeMap(conflictingKeyRangeMap)
{
}
@ -1016,7 +1010,7 @@ void ConflictBatch::addTransaction( const CommitTransactionRef& tr ) {
points.emplace_back(range.begin, false, true, false, t, &info->readRanges[r].first);
//points.back().keyEnd = StringRef(buf,range.second);
points.emplace_back(range.end, false, false, false, t, &info->readRanges[r].second);
combinedReadConflictRanges.emplace_back(range.begin, range.end, tr.read_snapshot, t, tr.report_conflicting_keys ? &(*conflictingKeyRangeMap)[t] : nullptr);
combinedReadConflictRanges.emplace_back(range.begin, range.end, tr.read_snapshot, t, r, tr.report_conflicting_keys ? &(*conflictingKeyRangeMap)[t] : nullptr);
}
for(int r=0; r<tr.write_conflict_ranges.size(); r++) {
const KeyRangeRef& range = tr.write_conflict_ranges[r];
@ -1038,10 +1032,9 @@ public:
for(int i=begin; i<end; i++)
values[i] = true;
}
bool any( int begin, int end, int* conflictingIndex = nullptr ) {
bool any( int begin, int end ) {
for(int i=begin; i<end; i++)
if (values[i]) {
if (conflictingIndex != nullptr) *conflictingIndex = i;
return true;
}
return false;
@ -1134,9 +1127,9 @@ public:
setBits(orValues, beginWord, lastWord+1, true);
}
bool any(int begin, int end, int* conflictingIndex = nullptr) {
bool any(int begin, int end) {
bool a = orImpl(begin,end);
bool b = debug.any(begin,end, conflictingIndex);
bool b = debug.any(begin,end);
ASSERT( a == b );
return b;
}
@ -1163,17 +1156,9 @@ void ConflictBatch::checkIntraBatchConflicts() {
if (transactionConflictStatus[t]) continue;
bool conflict = tr.tooOld;
for(int i=0; i<tr.readRanges.size(); i++){
int startPointIndex = tr.readRanges[i].first;
int endPointIndex = tr.readRanges[i].second;
int conflictingIndex;
if ( mcs.any( startPointIndex , endPointIndex, tr.reportConflictingKeys ? &conflictingIndex : nullptr ) ) {
if ( mcs.any( tr.readRanges[i].first , tr.readRanges[i].second ) ) {
if (tr.reportConflictingKeys){
// The MiniConflictSet is difficult to change, use MiniConflictSet2 to hack here. (Future: use MiniConflictSet)
if (points[conflictingIndex].begin)
startPointIndex = conflictingIndex;
else
endPointIndex = conflictingIndex;
(*conflictingKeyRangeMap)[t].push_back_deep((*conflictingKeyRangeMap)[t].arena(), KeyRangeRef(points[startPointIndex].key, points[endPointIndex].key));
(*conflictingKeyRangeMap)[t].push_back(i);
}
conflict = true;
break;

@ -192,13 +192,13 @@ struct ReportConflictingKeysWorkload : TestWorkload {
ASSERT(startKey.value == conflictingKeysTrue);
KeyValueRef endKey = conflictingKeyRanges[i+1];
ASSERT(endKey.value == conflictingKeysFalse);
KeyRange kr = KeyRangeRef(startKey.key, endKey.key);
KeyRangeRef kr = KeyRangeRef(startKey.key, endKey.key);
if (!std::any_of(readConflictRanges.begin(), readConflictRanges.end(), [&kr](KeyRange rCR) {
// Read_conflict_range remains same in the resolver.
// Thus, the returned keyrange is either the original read_conflict_range or merged
// by several overlapped ones In either case, it contains at least one original
// read_conflict_range
return kr.intersects(rCR);
return kr.contains(rCR);
})) {
++self->invalidReports;
TraceEvent(SevError, "TestFailure").detail("Reason", "InvalidKeyRangeReturned");