foundationdb/fdbserver/workloads/Unreadable.actor.cpp

468 lines
16 KiB
C++

/*
* Unreadable.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 "flow/actorcompiler.h"
#include "fdbrpc/ContinuousSample.h"
#include "fdbclient/NativeAPI.h"
#include "fdbserver/TesterInterface.h"
#include "BulkSetup.actor.h"
#include "fdbclient/ReadYourWrites.h"
#include "workloads.h"
struct UnreadableWorkload : TestWorkload {
uint64_t nodeCount;
double testDuration;
vector<Future<Void>> clients;
UnreadableWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
testDuration = getOption( options, LiteralStringRef("testDuration"), 600.0 );
nodeCount = getOption( options, LiteralStringRef("nodeCount"), (uint64_t)100000 );
}
virtual std::string description() { return "Unreadable"; }
virtual Future<Void> setup( Database const& cx ) { return Void(); }
virtual Future<Void> start( Database const& cx ) {
if (clientId == 0)
return _start( cx, this );
return Void();
}
virtual Future<bool> check( Database const& cx ) {
return true;
}
virtual void getMetrics( vector<PerfMetric>& m ) {
}
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 checkUnreadablity(Arena arena, std::map<KeyRef, ValueRef> const& setMap, KeyRangeMap<bool> & unreadableMap,
KeySelectorRef const& _begin, KeySelectorRef const& _end, bool isUnreadable, int limit, bool 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;
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 bool reverse;
state int limit;
state bool snapshot;
state KeySelectorRef begin;
state KeySelectorRef end;
setMap[normalKeys.begin] = ValueRef();
setMap[normalKeys.end] = ValueRef();
for (; opCount < 500; opCount++) {
int r = g_random->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, allKeys.end);
unreadableMap.insert(range, true);
//TraceEvent("RYWT_SetVersionstampKey").detail("Range", printable(range));
}
else if (r == 16) {
range = RandomTestImpl::getRandomRange(arena);
snapshot = g_random->random01() < 0.05;
reverse = g_random->random01() < 0.5;
if (snapshot)
tr.setOption(FDBTransactionOptions::SNAPSHOT_RYW_DISABLE);
ErrorOr<Standalone<RangeResultRef>> 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);
arena = Arena();
setMap[normalKeys.begin] = ValueRef();
setMap[normalKeys.end] = ValueRef();
}
}
else if (r == 17) {
begin = RandomTestImpl::getRandomKeySelector(arena);
end = RandomTestImpl::getRandomKeySelector(arena);
limit = g_random->randomInt(1, 100); // maximum number of results to return from the db
snapshot = g_random->random01() < 0.05;
reverse = g_random->random01() < 0.5;
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<Standalone<RangeResultRef>> 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) {
/*
Standalone<RangeResultRef> 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 {
checkUnreadablity(arena, setMap, unreadableMap, begin, end, isUnreadable, limit, reverse);
}
}
else {
//TraceEvent("RYWT_GetKeySelRangeReset");
setMap = std::map<KeyRef, ValueRef>();
unreadableMap = KeyRangeMap<bool>();
tr = ReadYourWritesTransaction(cx);
arena = Arena();
setMap[normalKeys.begin] = ValueRef();
setMap[normalKeys.end] = ValueRef();
}
}
else if (r == 18) {
key = RandomTestImpl::getRandomKey(arena);
snapshot = g_random->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) {
ASSERT(!value.isError());
}
else {
ASSERT(unreadableMap[key] == value.isError());
}
}
else {
//TraceEvent("RYWT_Reset3");
setMap = std::map<KeyRef, ValueRef>();
unreadableMap = KeyRangeMap<bool>();
tr = ReadYourWritesTransaction(cx);
arena = Arena();
setMap[normalKeys.begin] = ValueRef();
setMap[normalKeys.end] = ValueRef();
}
}
}
}
return Void();
}
};
WorkloadFactory<UnreadableWorkload> UnreadableWorkloadFactory("Unreadable");