/* * 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> clients; UnreadableWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) { testDuration = getOption(options, LiteralStringRef("testDuration"), 600.0); nodeCount = getOption(options, LiteralStringRef("nodeCount"), (uint64_t)100000); } std::string description() const override { return "Unreadable"; } Future setup(Database const& cx) override { return Void(); } Future start(Database const& cx) override { if (clientId == 0) return _start(cx, this); return Void(); } Future check(Database const& cx) override { return true; } void getMetrics(std::vector& m) override {} static Optional containsUnreadable(KeyRangeMap& 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(); } static void resolveKeySelector(std::map const& setMap, KeyRangeMap& 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 const& setMap, KeyRangeMap& 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 _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 setMap; state KeyRangeMap 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 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(); unreadableMap = KeyRangeMap(); 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 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(); unreadableMap = KeyRangeMap(); 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> 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(); unreadableMap = KeyRangeMap(); tr = ReadYourWritesTransaction(cx); if (bypassUnreadable) { tr.setOption(FDBTransactionOptions::BYPASS_UNREADABLE); } arena = Arena(); setMap[normalKeys.begin] = ValueRef(); setMap[normalKeys.end] = ValueRef(); } } } } return Void(); } }; WorkloadFactory UnreadableWorkloadFactory("Unreadable");