foundationdb/fdbserver/workloads/Unreadable.actor.cpp

480 lines
17 KiB
C++

/*
* Unreadable.actor.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 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 "fdbrpc/ContinuousSample.h"
#include "fdbclient/NativeAPI.actor.h"
#include "fdbserver/TesterInterface.actor.h"
#include "fdbserver/workloads/BulkSetup.actor.h"
#include "fdbclient/ReadYourWrites.h"
#include "fdbserver/workloads/workloads.actor.h"
#include "flow/actorcompiler.h" // This must be the last #include.
struct UnreadableWorkload : TestWorkload {
uint64_t nodeCount;
double testDuration;
std::vector<Future<Void>> clients;
UnreadableWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
testDuration = getOption(options, "testDuration"_sr, 600.0);
nodeCount = getOption(options, "nodeCount"_sr, (uint64_t)100000);
}
std::string description() const override { return "Unreadable"; }
Future<Void> setup(Database const& cx) override { return Void(); }
Future<Void> start(Database const& cx) override {
if (clientId == 0)
return _start(cx, this);
return Void();
}
Future<bool> check(Database const& cx) override { return true; }
void getMetrics(std::vector<PerfMetric>& m) override {}
static Optional<KeyRef> containsUnreadable(KeyRangeMap<bool>& unreadableMap,
KeyRangeRef const& range,
bool forward) {
auto unreadableRanges = unreadableMap.intersectingRanges(range);
if (forward) {
for (auto it : unreadableRanges) {
if (it.value()) {
//TraceEvent("RYWT_CheckingRange1").detail("Range", printable(range)).detail("UnreadableRange", printable(it.range()));
return it.range().begin;
}
}
} else {
auto it = unreadableRanges.end();
auto itEnd = unreadableRanges.begin();
--it;
--itEnd;
for (; it != itEnd; --it) {
if (it.value()) {
//TraceEvent("RYWT_CheckingRange2").detail("Range", printable(range)).detail("UnreadableRange", printable(it.range()));
return it.range().end;
}
}
}
return Optional<KeyRef>();
}
static void resolveKeySelector(std::map<KeyRef, ValueRef> const& setMap,
KeyRangeMap<bool>& unreadableMap,
KeySelector& key) {
ASSERT(!key.orEqual);
if (key.offset == 1)
return;
auto it = setMap.lower_bound(key.getKey());
if (key.offset >= 1) {
auto unreadable = containsUnreadable(unreadableMap, KeyRangeRef(key.getKey(), it->first), true);
if (unreadable.present()) {
key.setKey(unreadable.get());
return;
}
key.setKey(it->first);
}
while (key.offset < 1 && it->first != normalKeys.begin) {
--it;
auto unreadable = containsUnreadable(unreadableMap, KeyRangeRef(it->first, key.getKey()), false);
if (unreadable.present()) {
key.setKey(unreadable.get());
return;
}
key.setKey(it->first);
++key.offset;
}
while (key.offset > 1 && it->first != normalKeys.end) {
++it;
auto unreadable = containsUnreadable(unreadableMap, KeyRangeRef(key.getKey(), it->first), true);
if (unreadable.present()) {
if (unreadable.get() > key.getKey())
--key.offset;
key.setKey(unreadable.get());
return;
}
key.setKey(it->first);
--key.offset;
}
key.setKey(it->first);
}
static void checkUnreadability(std::map<KeyRef, ValueRef> const& setMap,
KeyRangeMap<bool>& unreadableMap,
KeySelectorRef const& _begin,
KeySelectorRef const& _end,
bool isUnreadable,
int limit,
Reverse reverse) {
/*
for (auto it : setMap) {
TraceEvent("RYWT_SetMapContents").detail("Key", printable(it.first));
}
for (auto it : unreadableMap.ranges()) {
TraceEvent("RYWT_UnreadableMapContents").detail("Key", printable(it.range())).detail("Value", it.value());
}
*/
KeySelector begin = _begin;
KeySelector end = _end;
begin.removeOrEqual(begin.arena());
end.removeOrEqual(end.arena());
if (begin.offset >= end.offset && begin.getKey() >= end.getKey()) {
ASSERT(isUnreadable == false);
return;
}
//TraceEvent("RYWT_CheckUnreadability").detail("Begin", begin.toString()).detail("End", end.toString());
KeySelector resolvedBegin = begin;
KeySelector resolvedEnd = end;
resolveKeySelector(setMap, unreadableMap, resolvedBegin);
resolveKeySelector(setMap, unreadableMap, resolvedEnd);
//TraceEvent("RYWT_CheckUnreadability2").detail("ResolvedBegin", resolvedBegin.toString()).detail("ResolvedEnd", resolvedEnd.toString());
if ((resolvedBegin.offset >= resolvedEnd.offset && resolvedBegin.getKey() >= resolvedEnd.getKey()) ||
(resolvedBegin.offset >= end.offset && resolvedBegin.getKey() >= end.getKey()) ||
(begin.offset >= resolvedEnd.offset && begin.getKey() >= resolvedEnd.getKey())) {
// RYW does not perfectly optimize this scenario, it should be readable but might unnecesarily return as
// unreadable
return;
}
if (resolvedEnd.getKey() == normalKeys.begin || resolvedBegin.getKey() == normalKeys.end) {
// RYW does not perfectly optimize this scenario, it should be readable but might unnecesarily return as
// unreadable
return;
}
bool beginUnreadable = resolvedBegin.offset != 1 && resolvedBegin.getKey() != normalKeys.begin;
bool endUnreadable = resolvedEnd.offset != 1 && resolvedEnd.getKey() != normalKeys.end;
if (!reverse && !beginUnreadable) {
int itemCount = resolvedBegin.getKey() == normalKeys.begin ? 0 : 1;
itemCount += std::min(0, resolvedEnd.offset - 1);
auto setItem = setMap.lower_bound(resolvedBegin.getKey());
while (itemCount < limit && setItem->first != normalKeys.end) {
++setItem;
++itemCount;
}
//TraceEvent("RYWT_ModifiedEnd").detail("SetItem", printable(setItem->first)).detail("ResolvedEnd", printable(resolvedEnd.getKey())).detail("Limit", limit).detail("ItemCount", resolvedBegin.getKey() == normalKeys.begin ? 0 : 1 + std::min(0, resolvedEnd.offset - 1));
KeyRef keyAfterSet = keyAfter(setItem->first, resolvedEnd.arena());
if (keyAfterSet <= resolvedEnd.getKey()) {
if (std::max(begin.getKey(), resolvedBegin.getKey()) > std::min(end.getKey(), resolvedEnd.getKey())) {
// RYW might have to resolve the key selector anyways in this scenario, even though the limit should
// make this unnecessary.
return;
}
if (end.offset < resolvedEnd.offset && end.offset < 1) {
int itemCount2 = resolvedBegin.getKey() == normalKeys.begin ? 0 : 1;
itemCount2 += std::min(0, end.offset - 1);
auto setItem2 = setMap.lower_bound(resolvedBegin.getKey());
while (itemCount2 < limit && setItem2->first != normalKeys.end) {
++setItem2;
++itemCount2;
}
Key keyAfterSet2 = keyAfter(setItem2->first);
if (containsUnreadable(unreadableMap, KeyRangeRef(keyAfterSet, keyAfterSet2), true).present()) {
// RYW might not have resolved the end selector
return;
}
}
resolvedEnd.setKey(keyAfterSet);
resolvedEnd.offset = 1;
endUnreadable = false;
}
} else if (reverse && !endUnreadable) {
int itemCount = 0;
itemCount -= std::max(0, resolvedBegin.offset - 1);
auto setItem = setMap.lower_bound(resolvedEnd.getKey());
while (itemCount < limit && setItem->first != normalKeys.begin) {
--setItem;
++itemCount;
}
//TraceEvent("RYWT_ModifiedBegin").detail("SetItem", printable(setItem->first)).detail("ResolvedBegin", printable(resolvedBegin.getKey())).detail("Limit", limit).detail("ItemCount", -1*std::max(0, resolvedBegin.offset - 1));
if (setItem->first >= resolvedBegin.getKey()) {
if (std::max(begin.getKey(), resolvedBegin.getKey()) > std::min(end.getKey(), resolvedEnd.getKey())) {
// RYW might have to resolve the key selector anyways in this scenario, even though the limit should
// make this unnecessary.
return;
}
if (begin.offset > resolvedBegin.offset && begin.offset > 1) {
int itemCount2 = 0;
itemCount2 -= std::max(0, begin.offset - 1);
auto setItem2 = setMap.lower_bound(resolvedEnd.getKey());
while (itemCount2 < limit && setItem2->first != normalKeys.begin) {
--setItem2;
++itemCount2;
}
if (containsUnreadable(unreadableMap, KeyRangeRef(setItem2->first, setItem->first), true)
.present()) {
// RYW might not have resolved the begin selector
return;
}
}
resolvedBegin.setKey(setItem->first);
resolvedBegin.offset = 1;
beginUnreadable = false;
}
}
if (beginUnreadable || endUnreadable) {
ASSERT(isUnreadable == true);
return;
}
if (!reverse && resolvedEnd.getKey() != normalKeys.end &&
std::max(begin.getKey(), resolvedBegin.getKey()) > std::min(end.getKey(), resolvedEnd.getKey()) &&
!end.isFirstGreaterOrEqual()) {
auto setNext = setMap.lower_bound(resolvedEnd.getKey());
auto unreadableRanges = unreadableMap.intersectingRanges(KeyRangeRef(resolvedEnd.getKey(), normalKeys.end));
KeyRef unreadableNext = normalKeys.end;
for (auto it : unreadableRanges) {
if (it.value()) {
unreadableNext = it.begin();
break;
}
}
//TraceEvent("RYWT_CheckUnreadability3").detail("SetNext", printable(setNext->first)).detail("UnreadableNext", printable(unreadableNext));
if (setNext->first >= unreadableNext) {
// RYW resolves the end key selector, even though it does not need the exact key to answer the query.
return;
}
}
ASSERT(isUnreadable ==
containsUnreadable(unreadableMap, KeyRangeRef(resolvedBegin.getKey(), resolvedEnd.getKey()), true)
.present());
}
ACTOR Future<Void> _start(Database cx, UnreadableWorkload* self) {
state int testCount = 0;
state Reverse reverse = Reverse::False;
state Snapshot snapshot = Snapshot::False;
for (; testCount < 100; testCount++) {
//TraceEvent("RYWT_Start").detail("TestCount", testCount);
state ReadYourWritesTransaction tr(cx);
state Arena arena;
state std::map<KeyRef, ValueRef> setMap;
state KeyRangeMap<bool> unreadableMap;
state int opCount = 0;
state KeyRangeRef range;
state KeyRef key;
state ValueRef value;
state int limit;
state KeySelectorRef begin;
state KeySelectorRef end;
state bool bypassUnreadable = deterministicRandom()->coinflip();
if (bypassUnreadable) {
tr.setOption(FDBTransactionOptions::BYPASS_UNREADABLE);
}
setMap[normalKeys.begin] = ValueRef();
setMap[normalKeys.end] = ValueRef();
for (; opCount < 500; opCount++) {
int r = deterministicRandom()->randomInt(0, 19);
if (r <= 10) {
key = RandomTestImpl::getRandomKey(arena);
value = RandomTestImpl::getRandomValue(arena);
tr.set(key, value);
setMap[key] = value;
//TraceEvent("RYWT_Set").detail("Key", printable(key));
} else if (r == 11) {
range = RandomTestImpl::getRandomRange(arena);
tr.addReadConflictRange(range);
//TraceEvent("RYWT_AddReadConflictRange").detail("Range", printable(range));
} else if (r == 12) {
range = RandomTestImpl::getRandomRange(arena);
tr.addWriteConflictRange(range);
//TraceEvent("RYWT_AddWriteConflictRange").detail("Range", printable(range));
} else if (r == 13) {
range = RandomTestImpl::getRandomRange(arena);
tr.clear(range);
unreadableMap.insert(range, false);
setMap.erase(setMap.lower_bound(range.begin), setMap.lower_bound(range.end));
//TraceEvent("RYWT_Clear").detail("Range", printable(range));
} else if (r == 14) {
key = RandomTestImpl::getRandomKey(arena);
value = RandomTestImpl::getRandomVersionstampValue(arena);
tr.atomicOp(key, value, MutationRef::SetVersionstampedValue);
unreadableMap.insert(key, true);
//TraceEvent("RYWT_SetVersionstampValue").detail("Key", printable(key));
} else if (r == 15) {
key = RandomTestImpl::getRandomVersionstampKey(arena);
value = RandomTestImpl::getRandomValue(arena);
tr.atomicOp(key, value, MutationRef::SetVersionstampedKey);
range = getVersionstampKeyRange(arena, key, tr.getCachedReadVersion().orDefault(0), allKeys.end);
unreadableMap.insert(range, true);
//TraceEvent("RYWT_SetVersionstampKey").detail("Range", printable(range));
} else if (r == 16) {
range = RandomTestImpl::getRandomRange(arena);
snapshot.set(deterministicRandom()->random01() < 0.05);
reverse.set(deterministicRandom()->coinflip());
if (snapshot)
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_DISABLE);
ErrorOr<RangeResult> value =
wait(errorOr(tr.getRange(range, CLIENT_KNOBS->TOO_MANY, snapshot, reverse)));
if (snapshot)
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_ENABLE);
if (!value.isError() || value.getError().code() == error_code_accessed_unreadable) {
//TraceEvent("RYWT_GetRange").detail("Range", printable(range)).detail("IsUnreadable", value.isError());
if (snapshot) {
ASSERT(!value.isError());
} else {
ASSERT(containsUnreadable(unreadableMap, range, true).present() == value.isError());
}
} else {
//TraceEvent("RYWT_Reset1").error(value.getError(), true);
setMap = std::map<KeyRef, ValueRef>();
unreadableMap = KeyRangeMap<bool>();
tr = ReadYourWritesTransaction(cx);
if (bypassUnreadable) {
tr.setOption(FDBTransactionOptions::BYPASS_UNREADABLE);
}
arena = Arena();
setMap[normalKeys.begin] = ValueRef();
setMap[normalKeys.end] = ValueRef();
}
} else if (r == 17) {
begin = RandomTestImpl::getRandomKeySelector(arena);
end = RandomTestImpl::getRandomKeySelector(arena);
limit = deterministicRandom()->randomInt(1, 100); // maximum number of results to return from the db
snapshot.set(deterministicRandom()->random01() < 0.05);
reverse.set(deterministicRandom()->coinflip());
if (snapshot)
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_DISABLE);
//TraceEvent("RYWT_GetRangeBefore").detail("Reverse", reverse).detail("Begin", begin.toString()).detail("End", end.toString()).detail("Limit", limit);
ErrorOr<RangeResult> value = wait(errorOr(tr.getRange(begin, end, limit, snapshot, reverse)));
if (snapshot)
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_ENABLE);
bool isUnreadable = value.isError() && value.getError().code() == error_code_accessed_unreadable;
if (!value.isError() || value.getError().code() == error_code_accessed_unreadable) {
/*
RangeResult result = value.get();
TraceEvent("RYWT_GetKeySelRangeOk")
.detail("Begin", begin.toString())
.detail("End", end.toString())
.detail("Size", result.size())
.detail("Reverse", reverse);
for (auto it : result) {
TraceEvent("RYWT_GetKeySelRange returned").detail("Key", printable(it.key));
}
*/
//TraceEvent("RYWT_GetKeySelRangeUnreadable").detail("Begin", begin.toString()).detail("End", end.toString()).detail("Reverse", reverse);
if (snapshot) {
ASSERT(!isUnreadable);
} else {
checkUnreadability(setMap, unreadableMap, begin, end, isUnreadable, limit, reverse);
}
} else {
//TraceEvent("RYWT_GetKeySelRangeReset");
setMap = std::map<KeyRef, ValueRef>();
unreadableMap = KeyRangeMap<bool>();
tr = ReadYourWritesTransaction(cx);
if (bypassUnreadable) {
tr.setOption(FDBTransactionOptions::BYPASS_UNREADABLE);
}
arena = Arena();
setMap[normalKeys.begin] = ValueRef();
setMap[normalKeys.end] = ValueRef();
}
} else if (r == 18) {
key = RandomTestImpl::getRandomKey(arena);
snapshot.set(deterministicRandom()->random01() < 0.05);
if (snapshot)
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_DISABLE);
ErrorOr<Optional<Value>> value = wait(errorOr(tr.get(key, snapshot)));
if (snapshot)
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_ENABLE);
if (!value.isError() || value.getError().code() == error_code_accessed_unreadable) {
//TraceEvent("RYWT_Get").detail("Key", printable(key)).detail("IsUnreadable", value.isError());
if (snapshot || bypassUnreadable) {
ASSERT(!value.isError());
} else {
ASSERT(unreadableMap[key] == value.isError());
}
} else {
//TraceEvent("RYWT_Reset3");
setMap = std::map<KeyRef, ValueRef>();
unreadableMap = KeyRangeMap<bool>();
tr = ReadYourWritesTransaction(cx);
if (bypassUnreadable) {
tr.setOption(FDBTransactionOptions::BYPASS_UNREADABLE);
}
arena = Arena();
setMap[normalKeys.begin] = ValueRef();
setMap[normalKeys.end] = ValueRef();
}
}
}
}
return Void();
}
};
WorkloadFactory<UnreadableWorkload> UnreadableWorkloadFactory("Unreadable");