1095 lines
46 KiB
C++
1095 lines
46 KiB
C++
/*
|
|
* WriteDuringRead.actor.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/ClusterConnectionMemoryRecord.h"
|
|
#include "fdbclient/NativeAPI.actor.h"
|
|
#include "fdbserver/TesterInterface.actor.h"
|
|
#include "fdbclient/ReadYourWrites.h"
|
|
#include "flow/ActorCollection.h"
|
|
#include "fdbserver/workloads/workloads.actor.h"
|
|
#include "fdbclient/Atomic.h"
|
|
#include "flow/actorcompiler.h" // This must be the last #include.
|
|
|
|
struct WriteDuringReadWorkload : TestWorkload {
|
|
double testDuration, slowModeStart;
|
|
int numOps;
|
|
bool rarelyCommit, adjacentKeys;
|
|
PerfIntCounter transactions, retries;
|
|
std::map<Key, Value> memoryDatabase;
|
|
std::map<Key, Value> lastCommittedDatabase;
|
|
KeyRangeMap<int> changeCount;
|
|
int minNode, nodes;
|
|
double initialKeyDensity;
|
|
AsyncTrigger finished;
|
|
KeyRange conflictRange;
|
|
std::pair<int, int> valueSizeRange;
|
|
int maxClearSize;
|
|
CoalescedKeyRangeMap<bool> addedConflicts;
|
|
bool useSystemKeys;
|
|
std::string keyPrefix;
|
|
int64_t maximumTotalData;
|
|
|
|
bool success;
|
|
Database extraDB;
|
|
bool useExtraDB;
|
|
|
|
WriteDuringReadWorkload(WorkloadContext const& wcx)
|
|
: TestWorkload(wcx), transactions("Transactions"), retries("Retries"), success(true) {
|
|
testDuration = getOption(options, LiteralStringRef("testDuration"), 60.0);
|
|
slowModeStart = getOption(options, LiteralStringRef("slowModeStart"), 1000.0);
|
|
numOps = getOption(options, LiteralStringRef("numOps"), 21);
|
|
rarelyCommit = getOption(options, LiteralStringRef("rarelyCommit"), false);
|
|
maximumTotalData = getOption(options, LiteralStringRef("maximumTotalData"), 3e6);
|
|
minNode = getOption(options, LiteralStringRef("minNode"), 0);
|
|
useSystemKeys = getOption(options, LiteralStringRef("useSystemKeys"), deterministicRandom()->random01() < 0.5);
|
|
adjacentKeys = deterministicRandom()->random01() < 0.5;
|
|
initialKeyDensity = deterministicRandom()->random01(); // This fraction of keys are present before the first
|
|
// transaction (and after an unknown result)
|
|
valueSizeRange = std::make_pair(
|
|
0,
|
|
std::min<int>(deterministicRandom()->randomInt(0, 4 << deterministicRandom()->randomInt(0, 16)),
|
|
CLIENT_KNOBS->VALUE_SIZE_LIMIT * 1.2));
|
|
if (adjacentKeys) {
|
|
nodes = std::min<int64_t>(deterministicRandom()->randomInt(1, 4 << deterministicRandom()->randomInt(0, 14)),
|
|
CLIENT_KNOBS->KEY_SIZE_LIMIT * 1.2);
|
|
} else {
|
|
nodes = deterministicRandom()->randomInt(1, 4 << deterministicRandom()->randomInt(0, 20));
|
|
}
|
|
|
|
int newNodes = std::min<int>(nodes, maximumTotalData / (getKeyForIndex(nodes).size() + valueSizeRange.second));
|
|
minNode = std::max(minNode, nodes - newNodes);
|
|
nodes = newNodes;
|
|
|
|
TEST(adjacentKeys &&
|
|
(nodes + minNode) > CLIENT_KNOBS->KEY_SIZE_LIMIT); // WriteDuringReadWorkload testing large keys
|
|
|
|
useExtraDB = g_simulator.extraDB != nullptr;
|
|
if (useExtraDB) {
|
|
auto extraFile = makeReference<ClusterConnectionMemoryRecord>(*g_simulator.extraDB);
|
|
extraDB = Database::createDatabase(extraFile, -1);
|
|
useSystemKeys = false;
|
|
}
|
|
|
|
if (useSystemKeys && deterministicRandom()->random01() < 0.5) {
|
|
keyPrefix = "\xff\x01";
|
|
} else {
|
|
keyPrefix = "\x02";
|
|
}
|
|
|
|
maxClearSize = 1 << deterministicRandom()->randomInt(0, 20);
|
|
conflictRange = KeyRangeRef(LiteralStringRef("\xfe"), LiteralStringRef("\xfe\x00"));
|
|
if (clientId == 0)
|
|
TraceEvent("RYWConfiguration")
|
|
.detail("Nodes", nodes)
|
|
.detail("InitialKeyDensity", initialKeyDensity)
|
|
.detail("AdjacentKeys", adjacentKeys)
|
|
.detail("ValueSizeMin", valueSizeRange.first)
|
|
.detail("ValueSizeMax", valueSizeRange.second)
|
|
.detail("MaxClearSize", maxClearSize);
|
|
}
|
|
|
|
std::string description() const override { return "WriteDuringRead"; }
|
|
|
|
Future<Void> setup(Database const& cx) override { return Void(); }
|
|
|
|
Future<Void> start(Database const& cx) override {
|
|
if (clientId == 0)
|
|
return loadAndRun(cx, this);
|
|
return Void();
|
|
}
|
|
|
|
Future<bool> check(Database const& cx) override { return success; }
|
|
|
|
void getMetrics(std::vector<PerfMetric>& m) override {
|
|
m.push_back(transactions.getMetric());
|
|
m.push_back(retries.getMetric());
|
|
}
|
|
|
|
Key memoryGetKey(std::map<Key, Value>* db, KeySelector key) const {
|
|
std::map<Key, Value>::iterator iter;
|
|
if (key.orEqual)
|
|
iter = db->upper_bound(key.getKey());
|
|
else
|
|
iter = db->lower_bound(key.getKey());
|
|
|
|
int offset = key.offset - 1;
|
|
while (offset > 0) {
|
|
if (iter == db->end())
|
|
return useSystemKeys ? allKeys.end : normalKeys.end;
|
|
++iter;
|
|
--offset;
|
|
}
|
|
while (offset < 0) {
|
|
if (iter == db->begin())
|
|
return allKeys.begin;
|
|
--iter;
|
|
++offset;
|
|
}
|
|
if (iter == db->end())
|
|
return useSystemKeys ? allKeys.end : normalKeys.end;
|
|
return iter->first;
|
|
}
|
|
|
|
ACTOR Future<Void> getKeyAndCompare(ReadYourWritesTransaction* tr,
|
|
KeySelector key,
|
|
Snapshot snapshot,
|
|
bool readYourWritesDisabled,
|
|
bool snapshotRYWDisabled,
|
|
WriteDuringReadWorkload* self,
|
|
bool* doingCommit,
|
|
int64_t* memLimit) {
|
|
state UID randomID = nondeterministicRandom()->randomUniqueID();
|
|
//TraceEvent("WDRGetKey", randomID);
|
|
try {
|
|
state Key memRes = self->memoryGetKey(readYourWritesDisabled || (snapshot && snapshotRYWDisabled)
|
|
? &self->lastCommittedDatabase
|
|
: &self->memoryDatabase,
|
|
key);
|
|
*memLimit -= memRes.expectedSize();
|
|
Key _res = wait(tr->getKey(key, snapshot));
|
|
Key res = _res;
|
|
*memLimit += memRes.expectedSize();
|
|
if (self->useSystemKeys && res > self->getKeyForIndex(self->nodes))
|
|
res = allKeys.end;
|
|
if (res != memRes) {
|
|
TraceEvent(SevError, "WDRGetKeyWrongResult", randomID)
|
|
.detail("Key", key.getKey())
|
|
.detail("Offset", key.offset)
|
|
.detail("OrEqual", key.orEqual)
|
|
.detail("Snapshot", snapshot)
|
|
.detail("MemoryResult", memRes)
|
|
.detail("DbResult", res);
|
|
self->success = false;
|
|
}
|
|
return Void();
|
|
} catch (Error& e) {
|
|
//TraceEvent("WDRGetKeyError", randomID).error(e,true);
|
|
if (e.code() == error_code_used_during_commit) {
|
|
ASSERT(*doingCommit);
|
|
return Void();
|
|
} else if (e.code() == error_code_transaction_cancelled)
|
|
return Void();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
Standalone<VectorRef<KeyValueRef>> memoryGetRange(std::map<Key, Value>* db,
|
|
KeySelector begin,
|
|
KeySelector end,
|
|
GetRangeLimits limit,
|
|
Reverse reverse) {
|
|
Key beginKey = memoryGetKey(db, begin);
|
|
Key endKey = memoryGetKey(db, end);
|
|
//TraceEvent("WDRGetRange").detail("Begin", beginKey).detail("End", endKey);
|
|
if (beginKey >= endKey)
|
|
return Standalone<VectorRef<KeyValueRef>>();
|
|
|
|
auto beginIter = db->lower_bound(beginKey);
|
|
auto endIter = db->lower_bound(endKey);
|
|
|
|
Standalone<VectorRef<KeyValueRef>> results;
|
|
if (reverse) {
|
|
loop {
|
|
if (beginIter == endIter || limit.reachedBy(results))
|
|
break;
|
|
|
|
--endIter;
|
|
results.push_back_deep(results.arena(), KeyValueRef(endIter->first, endIter->second));
|
|
}
|
|
} else {
|
|
for (; beginIter != endIter && !limit.reachedBy(results); ++beginIter)
|
|
results.push_back_deep(results.arena(), KeyValueRef(beginIter->first, beginIter->second));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
ACTOR Future<Void> getRangeAndCompare(ReadYourWritesTransaction* tr,
|
|
KeySelector begin,
|
|
KeySelector end,
|
|
GetRangeLimits limit,
|
|
Snapshot snapshot,
|
|
Reverse reverse,
|
|
bool readYourWritesDisabled,
|
|
bool snapshotRYWDisabled,
|
|
WriteDuringReadWorkload* self,
|
|
bool* doingCommit,
|
|
int64_t* memLimit) {
|
|
state UID randomID = nondeterministicRandom()->randomUniqueID();
|
|
/*TraceEvent("WDRGetRange", randomID).detail("BeginKey", begin.getKey()).detail("BeginOffset", begin.offset).detail("BeginOrEqual", begin.orEqual)
|
|
.detail("EndKey", end.getKey()).detail("EndOffset", end.offset).detail("EndOrEqual", end.orEqual)
|
|
.detail("Limit", limit.rows).detail("Snapshot", snapshot).detail("Reverse",
|
|
reverse).detail("ReadYourWritesDisabled", readYourWritesDisabled);*/
|
|
|
|
try {
|
|
state Standalone<VectorRef<KeyValueRef>> memRes = self->memoryGetRange(
|
|
readYourWritesDisabled || (snapshot && snapshotRYWDisabled) ? &self->lastCommittedDatabase
|
|
: &self->memoryDatabase,
|
|
begin,
|
|
end,
|
|
limit,
|
|
reverse);
|
|
*memLimit -= memRes.expectedSize();
|
|
RangeResult _res = wait(tr->getRange(begin, end, limit, snapshot, reverse));
|
|
RangeResult res = _res;
|
|
*memLimit += memRes.expectedSize();
|
|
|
|
int systemKeyCount = 0;
|
|
bool resized = false;
|
|
if (self->useSystemKeys) {
|
|
if (!reverse) {
|
|
int newSize =
|
|
std::lower_bound(
|
|
res.begin(), res.end(), self->getKeyForIndex(self->nodes), KeyValueRef::OrderByKey()) -
|
|
res.begin();
|
|
if (newSize != res.size()) {
|
|
res.resize(res.arena(), newSize);
|
|
resized = true;
|
|
}
|
|
} else {
|
|
for (; systemKeyCount < res.size(); systemKeyCount++)
|
|
if (res[systemKeyCount].key < self->getKeyForIndex(self->nodes))
|
|
break;
|
|
if (systemKeyCount > 0) {
|
|
res = RangeResultRef(VectorRef<KeyValueRef>(&res[systemKeyCount], res.size() - systemKeyCount),
|
|
true);
|
|
resized = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!limit.hasByteLimit() && systemKeyCount == 0) {
|
|
if (res.size() != memRes.size()) {
|
|
TraceEvent(SevError, "WDRGetRangeWrongResult", randomID)
|
|
.detail("BeginKey", begin.getKey())
|
|
.detail("BeginOffset", begin.offset)
|
|
.detail("BeginOrEqual", begin.orEqual)
|
|
.detail("EndKey", end.getKey())
|
|
.detail("EndOffset", end.offset)
|
|
.detail("EndOrEqual", end.orEqual)
|
|
.detail("LimitRows", limit.rows)
|
|
.detail("LimitBytes", limit.bytes)
|
|
.detail("Snapshot", snapshot)
|
|
.detail("Reverse", reverse)
|
|
.detail("MemorySize", memRes.size())
|
|
.detail("DbSize", res.size())
|
|
.detail("ReadYourWritesDisabled", readYourWritesDisabled);
|
|
|
|
self->success = false;
|
|
return Void();
|
|
}
|
|
|
|
for (int i = 0; i < res.size(); i++) {
|
|
if (res[i] != memRes[i]) {
|
|
TraceEvent(SevError, "WDRGetRangeWrongResult", randomID)
|
|
.detail("BeginKey", begin.getKey())
|
|
.detail("BeginOffset", begin.offset)
|
|
.detail("BeginOrEqual", begin.orEqual)
|
|
.detail("EndKey", end.getKey())
|
|
.detail("EndOffset", end.offset)
|
|
.detail("EndOrEqual", end.orEqual)
|
|
.detail("LimitRows", limit.rows)
|
|
.detail("LimitBytes", limit.bytes)
|
|
.detail("Snapshot", snapshot)
|
|
.detail("Reverse", reverse)
|
|
.detail("Size", memRes.size())
|
|
.detail("WrongLocation", i)
|
|
.detail("MemoryResultKey", memRes[i].key)
|
|
.detail("DbResultKey", res[i].key)
|
|
.detail("MemoryResultValueSize", memRes[i].value.size())
|
|
.detail("DbResultValueSize", res[i].value.size())
|
|
.detail("ReadYourWritesDisabled", readYourWritesDisabled);
|
|
self->success = false;
|
|
return Void();
|
|
}
|
|
}
|
|
} else {
|
|
if (res.size() > memRes.size() || (res.size() < memRes.size() && !res.more) ||
|
|
(res.size() == 0 && res.more && !resized)) {
|
|
TraceEvent(SevError, "WDRGetRangeWrongResult", randomID)
|
|
.detail("BeginKey", begin.getKey())
|
|
.detail("BeginOffset", begin.offset)
|
|
.detail("BeginOrEqual", begin.orEqual)
|
|
.detail("EndKey", end.getKey())
|
|
.detail("EndOffset", end.offset)
|
|
.detail("EndOrEqual", end.orEqual)
|
|
.detail("LimitRows", limit.rows)
|
|
.detail("LimitBytes", limit.bytes)
|
|
.detail("Snapshot", snapshot)
|
|
.detail("Reverse", reverse)
|
|
.detail("MemorySize", memRes.size())
|
|
.detail("DbSize", res.size())
|
|
.detail("ReadYourWritesDisabled", readYourWritesDisabled)
|
|
.detail("More", res.more)
|
|
.detail("SystemKeyCount", systemKeyCount);
|
|
|
|
self->success = false;
|
|
return Void();
|
|
}
|
|
|
|
for (int i = 0; i < res.size(); i++) {
|
|
if (res[i] != memRes[i]) {
|
|
TraceEvent(SevError, "WDRGetRangeWrongResult", randomID)
|
|
.detail("BeginKey", begin.getKey())
|
|
.detail("BeginOffset", begin.offset)
|
|
.detail("BeginOrEqual", begin.orEqual)
|
|
.detail("EndKey", end.getKey())
|
|
.detail("EndOffset", end.offset)
|
|
.detail("EndOrEqual", end.orEqual)
|
|
.detail("LimitRows", limit.rows)
|
|
.detail("LimitBytes", limit.bytes)
|
|
.detail("Snapshot", snapshot)
|
|
.detail("Reverse", reverse)
|
|
.detail("Size", memRes.size())
|
|
.detail("WrongLocation", i)
|
|
.detail("MemoryResultKey", memRes[i].key)
|
|
.detail("DbResultKey", res[i].key)
|
|
.detail("MemoryResultValueSize", memRes[i].value.size())
|
|
.detail("DbResultValueSize", res[i].value.size())
|
|
.detail("ReadYourWritesDisabled", readYourWritesDisabled)
|
|
.detail("More", res.more);
|
|
self->success = false;
|
|
return Void();
|
|
}
|
|
}
|
|
}
|
|
return Void();
|
|
} catch (Error& e) {
|
|
//TraceEvent("WDRGetRangeError", randomID).error(e,true);
|
|
if (e.code() == error_code_used_during_commit) {
|
|
ASSERT(*doingCommit);
|
|
return Void();
|
|
} else if (e.code() == error_code_transaction_cancelled)
|
|
return Void();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
Optional<Value> memoryGet(std::map<Key, Value>* db, Key key) {
|
|
auto iter = db->find(key);
|
|
if (iter == db->end())
|
|
return Optional<Value>();
|
|
else
|
|
return iter->second;
|
|
}
|
|
|
|
ACTOR Future<Void> getAndCompare(ReadYourWritesTransaction* tr,
|
|
Key key,
|
|
Snapshot snapshot,
|
|
bool readYourWritesDisabled,
|
|
bool snapshotRYWDisabled,
|
|
WriteDuringReadWorkload* self,
|
|
bool* doingCommit,
|
|
int64_t* memLimit) {
|
|
state UID randomID = nondeterministicRandom()->randomUniqueID();
|
|
//TraceEvent("WDRGet", randomID);
|
|
try {
|
|
state Optional<Value> memRes = self->memoryGet(readYourWritesDisabled || (snapshot && snapshotRYWDisabled)
|
|
? &self->lastCommittedDatabase
|
|
: &self->memoryDatabase,
|
|
key);
|
|
*memLimit -= memRes.expectedSize();
|
|
Optional<Value> res = wait(tr->get(key, snapshot));
|
|
*memLimit += memRes.expectedSize();
|
|
if (res != memRes) {
|
|
TraceEvent(SevError, "WDRGetWrongResult", randomID)
|
|
.detail("Key", key)
|
|
.detail("Snapshot", snapshot)
|
|
.detail("MemoryResult", memRes.present() ? memRes.get().size() : -1)
|
|
.detail("DbResult", res.present() ? res.get().size() : -1)
|
|
.detail("RywDisable", readYourWritesDisabled);
|
|
self->success = false;
|
|
}
|
|
return Void();
|
|
} catch (Error& e) {
|
|
//TraceEvent("WDRGetError", randomID).error(e,true);
|
|
if (e.code() == error_code_used_during_commit) {
|
|
ASSERT(*doingCommit);
|
|
return Void();
|
|
} else if (e.code() == error_code_transaction_cancelled)
|
|
return Void();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
ACTOR Future<Void> watchAndCompare(ReadYourWritesTransaction* tr,
|
|
Key key,
|
|
bool readYourWritesDisabled,
|
|
WriteDuringReadWorkload* self,
|
|
bool* doingCommit,
|
|
int64_t* memLimit) {
|
|
state UID randomID = nondeterministicRandom()->randomUniqueID();
|
|
// SOMEDAY: test setting a low outstanding watch limit
|
|
if (readYourWritesDisabled) // Only tests RYW activated watches
|
|
return Void();
|
|
|
|
//TraceEvent("WDRWatch", randomID).detail("Key", key);
|
|
try {
|
|
state int changeNum = self->changeCount[key];
|
|
state Optional<Value> memRes = self->memoryGet(&self->memoryDatabase, key);
|
|
*memLimit -= memRes.expectedSize();
|
|
|
|
// This if block prevents formatting issues with clang-format
|
|
if (1) {
|
|
choose {
|
|
when(wait(tr->watch(key))) {
|
|
if (changeNum == self->changeCount[key]) {
|
|
TraceEvent(SevError, "WDRWatchWrongResult", randomID)
|
|
.detail("Reason", "Triggered without changing")
|
|
.detail("Key", key)
|
|
.detail("Value", changeNum)
|
|
.detail("DuringCommit", *doingCommit);
|
|
}
|
|
}
|
|
when(wait(self->finished.onTrigger())) {
|
|
Optional<Value> memRes2 = self->memoryGet(&self->memoryDatabase, key);
|
|
if (memRes != memRes2) {
|
|
TraceEvent(SevError, "WDRWatchWrongResult", randomID)
|
|
.detail("Reason", "Changed without triggering")
|
|
.detail("Key", key)
|
|
.detail("Value1", memRes)
|
|
.detail("Value2", memRes2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*memLimit += memRes.expectedSize();
|
|
|
|
return Void();
|
|
} catch (Error& e) {
|
|
// check for transaction cancelled if the watch was not committed
|
|
//TraceEvent("WDRWatchError", randomID).error(e,true);
|
|
if (e.code() == error_code_used_during_commit) {
|
|
ASSERT(*doingCommit);
|
|
return Void();
|
|
} else if (e.code() == error_code_transaction_cancelled)
|
|
return Void();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
ACTOR Future<Void> commitAndUpdateMemory(ReadYourWritesTransaction* tr,
|
|
WriteDuringReadWorkload* self,
|
|
bool* cancelled,
|
|
bool readYourWritesDisabled,
|
|
bool snapshotRYWDisabled,
|
|
bool readAheadDisabled,
|
|
bool useBatchPriority,
|
|
bool* doingCommit,
|
|
double* startTime,
|
|
Key timebombStr) {
|
|
// state UID randomID = nondeterministicRandom()->randomUniqueID();
|
|
//TraceEvent("WDRCommit", randomID);
|
|
try {
|
|
if (!readYourWritesDisabled && !*cancelled) {
|
|
KeyRangeMap<bool> transactionConflicts;
|
|
tr->getWriteConflicts(&transactionConflicts);
|
|
|
|
auto transactionRanges = transactionConflicts.ranges();
|
|
auto addedRanges = self->addedConflicts.ranges();
|
|
auto transactionIter = transactionRanges.begin();
|
|
auto addedIter = addedRanges.begin();
|
|
|
|
bool failed = false;
|
|
while (transactionIter != transactionRanges.end() && addedIter != addedRanges.end()) {
|
|
if (transactionIter->begin() != addedIter->begin() ||
|
|
transactionIter->value() != addedIter->value()) {
|
|
TraceEvent(SevError, "WriteConflictError")
|
|
.detail("TransactionKey", transactionIter->begin())
|
|
.detail("AddedKey", addedIter->begin())
|
|
.detail("TransactionVal", transactionIter->value())
|
|
.detail("AddedVal", addedIter->value());
|
|
failed = true;
|
|
}
|
|
++transactionIter;
|
|
++addedIter;
|
|
}
|
|
|
|
if (transactionIter != transactionRanges.end() || addedIter != addedRanges.end()) {
|
|
failed = true;
|
|
}
|
|
|
|
if (failed) {
|
|
TraceEvent(SevError, "WriteConflictRangeError").log();
|
|
for (transactionIter = transactionRanges.begin(); transactionIter != transactionRanges.end();
|
|
++transactionIter) {
|
|
TraceEvent("WCRTransaction")
|
|
.detail("Range", transactionIter.range())
|
|
.detail("Value", transactionIter.value());
|
|
}
|
|
for (addedIter = addedRanges.begin(); addedIter != addedRanges.end(); ++addedIter) {
|
|
TraceEvent("WCRAdded").detail("Range", addedIter.range()).detail("Value", addedIter.value());
|
|
}
|
|
}
|
|
}
|
|
|
|
state std::map<Key, Value> committedDB = self->memoryDatabase;
|
|
*doingCommit = true;
|
|
wait(tr->commit());
|
|
*doingCommit = false;
|
|
self->finished.trigger();
|
|
|
|
if (readYourWritesDisabled)
|
|
tr->setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE);
|
|
if (snapshotRYWDisabled)
|
|
tr->setOption(FDBTransactionOptions::SNAPSHOT_RYW_DISABLE);
|
|
if (readAheadDisabled)
|
|
tr->setOption(FDBTransactionOptions::READ_AHEAD_DISABLE);
|
|
if (useBatchPriority)
|
|
tr->setOption(FDBTransactionOptions::PRIORITY_BATCH);
|
|
if (self->useSystemKeys)
|
|
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
|
tr->addWriteConflictRange(self->conflictRange);
|
|
self->addedConflicts.insert(allKeys, false);
|
|
self->addedConflicts.insert(self->conflictRange, true);
|
|
*startTime = now();
|
|
tr->setOption(FDBTransactionOptions::TIMEOUT, timebombStr);
|
|
|
|
//TraceEvent("WDRCommitSuccess", randomID).detail("CommittedVersion", tr->getCommittedVersion());
|
|
self->lastCommittedDatabase = committedDB;
|
|
|
|
return Void();
|
|
} catch (Error& e) {
|
|
//TraceEvent("WDRCommitCancelled", randomID).error(e,true);
|
|
if (e.code() == error_code_actor_cancelled || e.code() == error_code_transaction_cancelled ||
|
|
e.code() == error_code_used_during_commit)
|
|
*cancelled = true;
|
|
if (e.code() == error_code_actor_cancelled || e.code() == error_code_transaction_cancelled)
|
|
throw commit_unknown_result();
|
|
if (e.code() == error_code_transaction_too_old)
|
|
throw not_committed();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
Value getRandomValue() {
|
|
return Value(
|
|
std::string(deterministicRandom()->randomInt(valueSizeRange.first, valueSizeRange.second + 1), 'x'));
|
|
}
|
|
|
|
ACTOR Future<Void> loadAndRun(Database cx, WriteDuringReadWorkload* self) {
|
|
state double startTime = now();
|
|
loop {
|
|
state int i = 0;
|
|
state int keysPerBatch =
|
|
std::min<int64_t>(1000,
|
|
1 + CLIENT_KNOBS->TRANSACTION_SIZE_LIMIT / 6 /
|
|
(self->getKeyForIndex(self->nodes).size() + self->valueSizeRange.second));
|
|
self->memoryDatabase = std::map<Key, Value>();
|
|
for (; i < self->nodes; i += keysPerBatch) {
|
|
state Transaction tr(cx);
|
|
loop {
|
|
if (now() - startTime > self->testDuration)
|
|
return Void();
|
|
try {
|
|
if (i == 0) {
|
|
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
|
tr.addWriteConflictRange(
|
|
allKeys); // To prevent a write only transaction whose commit was previously cancelled
|
|
// from being reordered after this transaction
|
|
tr.clear(normalKeys);
|
|
}
|
|
if (self->useSystemKeys)
|
|
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
|
|
|
int end = std::min(self->nodes, i + keysPerBatch);
|
|
tr.clear(KeyRangeRef(self->getKeyForIndex(i), self->getKeyForIndex(end)));
|
|
self->memoryDatabase.erase(self->memoryDatabase.lower_bound(self->getKeyForIndex(i)),
|
|
self->memoryDatabase.lower_bound(self->getKeyForIndex(end)));
|
|
|
|
for (int j = i; j < end; j++) {
|
|
if (deterministicRandom()->random01() < self->initialKeyDensity) {
|
|
Key key = self->getKeyForIndex(j);
|
|
if (key.size() <= (key.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT)) {
|
|
Value value = self->getRandomValue();
|
|
value =
|
|
value.substr(0, std::min<int>(value.size(), CLIENT_KNOBS->VALUE_SIZE_LIMIT));
|
|
self->memoryDatabase[key] = value;
|
|
tr.set(key, value);
|
|
}
|
|
}
|
|
}
|
|
wait(tr.commit());
|
|
//TraceEvent("WDRInitBatch").detail("I", i).detail("CommittedVersion", tr.getCommittedVersion());
|
|
break;
|
|
} catch (Error& e) {
|
|
wait(tr.onError(e));
|
|
}
|
|
}
|
|
}
|
|
self->lastCommittedDatabase = self->memoryDatabase;
|
|
self->addedConflicts.insert(allKeys, false);
|
|
//TraceEvent("WDRInit");
|
|
|
|
loop {
|
|
wait(delay(now() - startTime > self->slowModeStart ||
|
|
(g_network->isSimulated() && g_simulator.speedUpSimulation)
|
|
? 1.0
|
|
: 0.1));
|
|
try {
|
|
wait(self->randomTransaction(
|
|
(self->useExtraDB && deterministicRandom()->random01() < 0.5) ? self->extraDB : cx,
|
|
self,
|
|
startTime));
|
|
} catch (Error& e) {
|
|
if (e.code() != error_code_not_committed)
|
|
throw;
|
|
break;
|
|
}
|
|
if (now() - startTime > self->testDuration)
|
|
return Void();
|
|
}
|
|
}
|
|
}
|
|
|
|
Key getRandomKey() { return getKeyForIndex(deterministicRandom()->randomInt(0, nodes)); }
|
|
|
|
Key getKeyForIndex(int idx) {
|
|
idx += minNode;
|
|
if (adjacentKeys) {
|
|
return Key(idx ? keyPrefix + std::string(idx, '\x00') : "");
|
|
} else {
|
|
return Key(keyPrefix + format("%010d", idx));
|
|
}
|
|
}
|
|
|
|
Key versionStampKeyForIndex(int idx) {
|
|
Key result = KeyRef(getKeyForIndex(idx).toString() + std::string(14, '\x00'));
|
|
int32_t pos = deterministicRandom()->randomInt(0, result.size() - 13);
|
|
pos = littleEndian32(pos);
|
|
uint8_t* data = mutateString(result);
|
|
memcpy(data + result.size() - sizeof(int32_t), &pos, sizeof(int32_t));
|
|
return result;
|
|
}
|
|
|
|
Key getRandomVersionStampKey() { return versionStampKeyForIndex(deterministicRandom()->randomInt(0, nodes)); }
|
|
|
|
KeySelector getRandomKeySelector() {
|
|
int scale = 1 << deterministicRandom()->randomInt(0, 14);
|
|
return KeySelectorRef(
|
|
getRandomKey(), deterministicRandom()->random01() < 0.5, deterministicRandom()->randomInt(-scale, scale));
|
|
}
|
|
|
|
GetRangeLimits getRandomLimits() {
|
|
int kind = deterministicRandom()->randomInt(0, 3);
|
|
return GetRangeLimits(
|
|
(kind & 1) ? GetRangeLimits::ROW_LIMIT_UNLIMITED
|
|
: deterministicRandom()->randomInt(0, 1 << deterministicRandom()->randomInt(1, 10)),
|
|
(kind & 2) ? GetRangeLimits::BYTE_LIMIT_UNLIMITED
|
|
: deterministicRandom()->randomInt(0, 1 << deterministicRandom()->randomInt(1, 15)));
|
|
}
|
|
|
|
KeyRange getRandomRange(int sizeLimit) {
|
|
int startLocation = deterministicRandom()->randomInt(0, nodes);
|
|
int scale = deterministicRandom()->randomInt(
|
|
0, deterministicRandom()->randomInt(2, 5) * deterministicRandom()->randomInt(2, 5));
|
|
int endLocation = startLocation + deterministicRandom()->randomInt(
|
|
0, 1 + std::min(sizeLimit, std::min(nodes - startLocation, 1 << scale)));
|
|
|
|
return KeyRangeRef(getKeyForIndex(startLocation), getKeyForIndex(endLocation));
|
|
}
|
|
|
|
Value applyAtomicOp(Optional<StringRef> existingValue, Value value, MutationRef::Type type) {
|
|
Arena arena;
|
|
if (type == MutationRef::SetValue)
|
|
return value;
|
|
else if (type == MutationRef::AddValue)
|
|
return doLittleEndianAdd(existingValue, value, arena);
|
|
else if (type == MutationRef::AppendIfFits)
|
|
return doAppendIfFits(existingValue, value, arena);
|
|
else if (type == MutationRef::And)
|
|
return doAndV2(existingValue, value, arena);
|
|
else if (type == MutationRef::Or)
|
|
return doOr(existingValue, value, arena);
|
|
else if (type == MutationRef::Xor)
|
|
return doXor(existingValue, value, arena);
|
|
else if (type == MutationRef::Max)
|
|
return doMax(existingValue, value, arena);
|
|
else if (type == MutationRef::Min)
|
|
return doMinV2(existingValue, value, arena);
|
|
else if (type == MutationRef::ByteMin)
|
|
return doByteMin(existingValue, value, arena);
|
|
else if (type == MutationRef::ByteMax)
|
|
return doByteMax(existingValue, value, arena);
|
|
ASSERT(false);
|
|
return Value();
|
|
}
|
|
|
|
ACTOR Future<Void> randomTransaction(Database cx, WriteDuringReadWorkload* self, double testStartTime) {
|
|
state ReadYourWritesTransaction tr(cx);
|
|
state bool readYourWritesDisabled = deterministicRandom()->random01() < 0.5;
|
|
state bool readAheadDisabled = deterministicRandom()->random01() < 0.5;
|
|
state bool snapshotRYWDisabled = deterministicRandom()->random01() < 0.5;
|
|
state bool useBatchPriority = deterministicRandom()->random01() < 0.5;
|
|
state int64_t timebomb =
|
|
(FLOW_KNOBS->MAX_BUGGIFIED_DELAY == 0.0 && deterministicRandom()->random01() < 0.01)
|
|
? deterministicRandom()->randomInt64(1, 6000)
|
|
: 0; // timebomb check can fail incorrectly if simulation injects delay longer than the timebomb
|
|
state std::vector<Future<Void>> operations;
|
|
state ActorCollection commits(false);
|
|
state std::vector<Future<Void>> watches;
|
|
state int changeNum = 1;
|
|
state bool doingCommit = false;
|
|
state int waitLocation = 0;
|
|
state double startTime = now();
|
|
|
|
state bool disableGetKey = BUGGIFY;
|
|
state bool disableGetRange = BUGGIFY;
|
|
state bool disableGet = BUGGIFY;
|
|
state bool disableCommit = BUGGIFY;
|
|
state bool disableClearRange = BUGGIFY;
|
|
state bool disableClear = BUGGIFY;
|
|
state bool disableWatch = BUGGIFY;
|
|
state bool disableWriteConflictRange = BUGGIFY;
|
|
state bool disableDelay = BUGGIFY;
|
|
state bool disableReset = BUGGIFY;
|
|
state bool disableReadConflictRange = BUGGIFY;
|
|
state bool disableSet = BUGGIFY;
|
|
state bool disableAtomicOp = BUGGIFY;
|
|
|
|
state Key timebombStr = makeString(8);
|
|
uint8_t* data = mutateString(timebombStr);
|
|
memcpy(data, &timebomb, 8);
|
|
|
|
loop {
|
|
if (now() - testStartTime > self->testDuration) {
|
|
return Void();
|
|
}
|
|
|
|
state int64_t memLimit = 1e8;
|
|
state bool cancelled = false;
|
|
if (readYourWritesDisabled)
|
|
tr.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE);
|
|
if (snapshotRYWDisabled)
|
|
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_DISABLE);
|
|
if (readAheadDisabled)
|
|
tr.setOption(FDBTransactionOptions::READ_AHEAD_DISABLE);
|
|
if (useBatchPriority)
|
|
tr.setOption(FDBTransactionOptions::PRIORITY_BATCH);
|
|
if (self->useSystemKeys)
|
|
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
|
tr.setOption(FDBTransactionOptions::TIMEOUT, timebombStr);
|
|
tr.addWriteConflictRange(self->conflictRange);
|
|
self->addedConflicts.insert(self->conflictRange, true);
|
|
try {
|
|
state int numWaits = deterministicRandom()->randomInt(1, 5);
|
|
state int i = 0;
|
|
for (; i < numWaits && memLimit > 0; i++) {
|
|
//TraceEvent("WDROps").detail("Count", i).detail("Max", numWaits).detail("ReadYourWritesDisabled",readYourWritesDisabled);
|
|
state int numOps = deterministicRandom()->randomInt(1, self->numOps);
|
|
state int j = 0;
|
|
for (; j < numOps && memLimit > 0; j++) {
|
|
if (commits.getResult().isError())
|
|
throw commits.getResult().getError();
|
|
try {
|
|
state int operationType = deterministicRandom()->randomInt(0, 21);
|
|
if (operationType == 0 && !disableGetKey) {
|
|
operations.push_back(
|
|
self->getKeyAndCompare(&tr,
|
|
self->getRandomKeySelector(),
|
|
Snapshot{ deterministicRandom()->coinflip() },
|
|
readYourWritesDisabled,
|
|
snapshotRYWDisabled,
|
|
self,
|
|
&doingCommit,
|
|
&memLimit));
|
|
} else if (operationType == 1 && !disableGetRange) {
|
|
operations.push_back(
|
|
self->getRangeAndCompare(&tr,
|
|
self->getRandomKeySelector(),
|
|
self->getRandomKeySelector(),
|
|
self->getRandomLimits(),
|
|
Snapshot{ deterministicRandom()->coinflip() },
|
|
Reverse{ deterministicRandom()->coinflip() },
|
|
readYourWritesDisabled,
|
|
snapshotRYWDisabled,
|
|
self,
|
|
&doingCommit,
|
|
&memLimit));
|
|
} else if (operationType == 2 && !disableGet) {
|
|
operations.push_back(self->getAndCompare(&tr,
|
|
self->getRandomKey(),
|
|
Snapshot{ deterministicRandom()->coinflip() },
|
|
readYourWritesDisabled,
|
|
snapshotRYWDisabled,
|
|
self,
|
|
&doingCommit,
|
|
&memLimit));
|
|
} else if (operationType == 3 && !disableCommit) {
|
|
if (!self->rarelyCommit || deterministicRandom()->random01() < 1.0 / self->numOps) {
|
|
Future<Void> commit = self->commitAndUpdateMemory(&tr,
|
|
self,
|
|
&cancelled,
|
|
readYourWritesDisabled,
|
|
snapshotRYWDisabled,
|
|
readAheadDisabled,
|
|
useBatchPriority,
|
|
&doingCommit,
|
|
&startTime,
|
|
timebombStr);
|
|
operations.push_back(commit);
|
|
commits.add(commit);
|
|
}
|
|
} else if (operationType == 4 && !disableClearRange) {
|
|
KeyRange range = self->getRandomRange(self->maxClearSize);
|
|
self->changeCount.insert(range, changeNum++);
|
|
bool noConflict = deterministicRandom()->random01() < 0.5;
|
|
//TraceEvent("WDRClearRange").detail("Begin", range).detail("NoConflict", noConflict);
|
|
if (noConflict)
|
|
tr.setOption(FDBTransactionOptions::NEXT_WRITE_NO_WRITE_CONFLICT_RANGE);
|
|
tr.clear(range);
|
|
if (!noConflict) {
|
|
KeyRangeRef conflict(
|
|
range.begin.substr(0,
|
|
std::min<int>(range.begin.size(),
|
|
(range.begin.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT) +
|
|
1)),
|
|
range.end.substr(0,
|
|
std::min<int>(range.end.size(),
|
|
(range.end.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT) +
|
|
1)));
|
|
self->addedConflicts.insert(conflict, true);
|
|
}
|
|
self->memoryDatabase.erase(self->memoryDatabase.lower_bound(range.begin),
|
|
self->memoryDatabase.lower_bound(range.end));
|
|
} else if (operationType == 5 && !disableClear) {
|
|
Key key = self->getRandomKey();
|
|
self->changeCount.insert(key, changeNum++);
|
|
bool noConflict = deterministicRandom()->random01() < 0.5;
|
|
//TraceEvent("WDRClear").detail("Key", key).detail("NoConflict", noConflict);
|
|
if (noConflict)
|
|
tr.setOption(FDBTransactionOptions::NEXT_WRITE_NO_WRITE_CONFLICT_RANGE);
|
|
tr.clear(key);
|
|
if (!noConflict && key.size() <= (key.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT)) {
|
|
self->addedConflicts.insert(key, true);
|
|
}
|
|
self->memoryDatabase.erase(key);
|
|
} else if (operationType == 6 && !disableWatch) {
|
|
watches.push_back(self->watchAndCompare(
|
|
&tr, self->getRandomKey(), readYourWritesDisabled, self, &doingCommit, &memLimit));
|
|
} else if (operationType == 7 && !disableWriteConflictRange) {
|
|
KeyRange range = self->getRandomRange(self->nodes);
|
|
//TraceEvent("WDRAddWriteConflict").detail("Range", range);
|
|
tr.addWriteConflictRange(range);
|
|
KeyRangeRef conflict(
|
|
range.begin.substr(0,
|
|
std::min<int>(range.begin.size(),
|
|
(range.begin.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT) +
|
|
1)),
|
|
range.end.substr(0,
|
|
std::min<int>(range.end.size(),
|
|
(range.end.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT) +
|
|
1)));
|
|
self->addedConflicts.insert(conflict, true);
|
|
} else if (operationType == 8 && !disableDelay) {
|
|
double maxTime = 6.0;
|
|
if (timebomb > 0)
|
|
maxTime = startTime + timebomb / 1000.0 - now();
|
|
operations.push_back(
|
|
delay(deterministicRandom()->random01() * deterministicRandom()->random01() *
|
|
deterministicRandom()->random01() * maxTime));
|
|
} else if (operationType == 9 && !disableReset) {
|
|
if (deterministicRandom()->random01() < 0.001) {
|
|
//TraceEvent("WDRReset");
|
|
tr.reset();
|
|
self->memoryDatabase = self->lastCommittedDatabase;
|
|
self->addedConflicts.insert(allKeys, false);
|
|
if (readYourWritesDisabled)
|
|
tr.setOption(FDBTransactionOptions::READ_YOUR_WRITES_DISABLE);
|
|
if (snapshotRYWDisabled)
|
|
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_DISABLE);
|
|
if (readAheadDisabled)
|
|
tr.setOption(FDBTransactionOptions::READ_AHEAD_DISABLE);
|
|
if (self->useSystemKeys)
|
|
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
|
|
tr.addWriteConflictRange(self->conflictRange);
|
|
self->addedConflicts.insert(self->conflictRange, true);
|
|
startTime = now();
|
|
tr.setOption(FDBTransactionOptions::TIMEOUT, timebombStr);
|
|
}
|
|
} else if (operationType == 10 && !disableReadConflictRange) {
|
|
KeyRange range = self->getRandomRange(self->maxClearSize);
|
|
tr.addReadConflictRange(range);
|
|
} else if (operationType == 11 && !disableAtomicOp) {
|
|
if (!self->useSystemKeys && deterministicRandom()->random01() < 0.01) {
|
|
Key versionStampKey = self->getRandomVersionStampKey();
|
|
Value value = self->getRandomValue();
|
|
KeyRangeRef range = getVersionstampKeyRange(versionStampKey.arena(),
|
|
versionStampKey,
|
|
tr.getCachedReadVersion().orDefault(0),
|
|
normalKeys.end);
|
|
self->changeCount.insert(range, changeNum++);
|
|
//TraceEvent("WDRVersionStamp").detail("VersionStampKey", versionStampKey).detail("Range", range);
|
|
tr.atomicOp(versionStampKey, value, MutationRef::SetVersionstampedKey);
|
|
tr.clear(range);
|
|
KeyRangeRef conflict(
|
|
range.begin.substr(0,
|
|
std::min<int>(range.begin.size(),
|
|
(range.begin.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT) +
|
|
1)),
|
|
range.end.substr(0,
|
|
std::min<int>(range.end.size(),
|
|
(range.end.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT) +
|
|
1)));
|
|
self->addedConflicts.insert(conflict, true);
|
|
self->memoryDatabase.erase(self->memoryDatabase.lower_bound(range.begin),
|
|
self->memoryDatabase.lower_bound(range.end));
|
|
} else {
|
|
Key key = self->getRandomKey();
|
|
Value value = self->getRandomValue();
|
|
MutationRef::Type opType;
|
|
switch (deterministicRandom()->randomInt(0, 8)) {
|
|
case 0:
|
|
opType = MutationRef::AddValue;
|
|
break;
|
|
case 1:
|
|
opType = MutationRef::And;
|
|
break;
|
|
case 2:
|
|
opType = MutationRef::Or;
|
|
break;
|
|
case 3:
|
|
opType = MutationRef::Xor;
|
|
break;
|
|
case 4:
|
|
opType = MutationRef::Max;
|
|
break;
|
|
case 5:
|
|
opType = MutationRef::Min;
|
|
break;
|
|
case 6:
|
|
opType = MutationRef::ByteMin;
|
|
break;
|
|
case 7:
|
|
opType = MutationRef::ByteMax;
|
|
break;
|
|
}
|
|
self->changeCount.insert(key, changeNum++);
|
|
bool noConflict = deterministicRandom()->random01() < 0.5;
|
|
//TraceEvent("WDRAtomicOp").detail("Key", key).detail("Value", value.size()).detail("NoConflict", noConflict);
|
|
if (noConflict)
|
|
tr.setOption(FDBTransactionOptions::NEXT_WRITE_NO_WRITE_CONFLICT_RANGE);
|
|
tr.atomicOp(key, value, opType);
|
|
//TraceEvent("WDRAtomicOpSuccess").detail("Key", key).detail("Value", value.size());
|
|
if (!noConflict && key.size() <= (key.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT))
|
|
self->addedConflicts.insert(key, true);
|
|
Optional<Value> existing = self->memoryGet(&self->memoryDatabase, key);
|
|
self->memoryDatabase[key] =
|
|
self->applyAtomicOp(existing.present() ? Optional<StringRef>(existing.get())
|
|
: Optional<StringRef>(),
|
|
value,
|
|
opType);
|
|
}
|
|
} else if (operationType > 11 && !disableSet) {
|
|
Key key = self->getRandomKey();
|
|
Value value = self->getRandomValue();
|
|
self->changeCount.insert(key, changeNum++);
|
|
bool noConflict = deterministicRandom()->random01() < 0.5;
|
|
//TraceEvent("WDRSet").detail("Key", key).detail("Value", value.size()).detail("NoConflict", noConflict);
|
|
if (noConflict)
|
|
tr.setOption(FDBTransactionOptions::NEXT_WRITE_NO_WRITE_CONFLICT_RANGE);
|
|
tr.set(key, value);
|
|
if (!noConflict && key.size() <= (key.startsWith(systemKeys.begin)
|
|
? CLIENT_KNOBS->SYSTEM_KEY_SIZE_LIMIT
|
|
: CLIENT_KNOBS->KEY_SIZE_LIMIT))
|
|
self->addedConflicts.insert(key, true);
|
|
//TraceEvent("WDRSetSuccess").detail("Key", key).detail("Value", value.size());
|
|
self->memoryDatabase[key] = value;
|
|
}
|
|
} catch (Error& e) {
|
|
if (e.code() == error_code_used_during_commit) {
|
|
ASSERT(doingCommit);
|
|
} else if (e.code() != error_code_transaction_cancelled) {
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (waitLocation < operations.size()) {
|
|
int waitOp = deterministicRandom()->randomInt(waitLocation, operations.size());
|
|
//TraceEvent("WDRWait").detail("Op", waitOp).detail("Operations", operations.size()).detail("WaitLocation", waitLocation);
|
|
wait(operations[waitOp]);
|
|
wait(delay(0.000001)); // to ensure errors have propgated from reads to commits
|
|
waitLocation = operations.size();
|
|
}
|
|
}
|
|
wait(waitForAll(operations));
|
|
ASSERT(timebomb == 0 || 1000 * (now() - startTime) <= timebomb + 1);
|
|
wait(tr.debug_onIdle());
|
|
wait(delay(0.000001)); // to ensure triggered watches have a change to register
|
|
self->finished.trigger();
|
|
wait(waitForAll(watches)); // only for errors, should have all returned
|
|
self->changeCount.insert(allKeys, 0);
|
|
break;
|
|
} catch (Error& e) {
|
|
operations.clear();
|
|
commits.clear(false);
|
|
waitLocation = 0;
|
|
watches.clear();
|
|
self->changeCount.insert(allKeys, 0);
|
|
doingCommit = false;
|
|
//TraceEvent("WDRError").errorUnsuppressed(e);
|
|
if (e.code() == error_code_database_locked) {
|
|
self->memoryDatabase = self->lastCommittedDatabase;
|
|
self->addedConflicts.insert(allKeys, false);
|
|
return Void();
|
|
}
|
|
if (e.code() == error_code_not_committed || e.code() == error_code_commit_unknown_result ||
|
|
e.code() == error_code_transaction_too_large || e.code() == error_code_key_too_large ||
|
|
e.code() == error_code_value_too_large || e.code() == error_code_too_many_watches || cancelled)
|
|
throw not_committed();
|
|
try {
|
|
wait(tr.onError(e));
|
|
} catch (Error& e) {
|
|
if (e.code() == error_code_transaction_timed_out) {
|
|
ASSERT(timebomb != 0 && 1000 * (now() - startTime) >= timebomb - 1);
|
|
throw not_committed();
|
|
}
|
|
throw e;
|
|
}
|
|
self->memoryDatabase = self->lastCommittedDatabase;
|
|
self->addedConflicts.insert(allKeys, false);
|
|
}
|
|
}
|
|
self->memoryDatabase = self->lastCommittedDatabase;
|
|
self->addedConflicts.insert(allKeys, false);
|
|
return Void();
|
|
}
|
|
};
|
|
|
|
WorkloadFactory<WriteDuringReadWorkload> WriteDuringReadWorkloadFactory("WriteDuringRead");
|