foundationdb/fdbserver/workloads/ReadHotDetection.actor.cpp

163 lines
5.8 KiB
C++

/*
* ReadHotDetection.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 "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 ReadHotDetectionWorkload : TestWorkload {
int actorCount, keyCount;
double testDuration, transactionsPerSecond;
vector<Future<Void>> clients;
Future<Void> readHotCheck;
Key readKey;
KeyRange wholeRange;
bool passed;
ReadHotDetectionWorkload(WorkloadContext const& wcx) : TestWorkload(wcx) {
testDuration = getOption(options, LiteralStringRef("testDuration"), 120.0);
transactionsPerSecond = getOption(options, LiteralStringRef("transactionsPerSecond"), 1000.0) / clientCount;
actorCount = getOption(options, LiteralStringRef("actorsPerClient"), transactionsPerSecond / 5);
keyCount = getOption(options, LiteralStringRef("keyCount"), 100);
readKey = StringRef(format("testkey%08x", deterministicRandom()->randomInt(0, keyCount)));
}
std::string description() const override { return "ReadHotDetection"; }
Future<Void> setup(Database const& cx) override { return _setup(cx, this); }
Future<Void> start(Database const& cx) override {
for (int c = 0; c < actorCount; c++) {
clients.push_back(timeout(keyReader(cx->clone(), this, actorCount / transactionsPerSecond,
deterministicRandom()->random01() > 0.4),
testDuration, Void()));
}
readHotCheck = clientId == 0 ? _check(cx->clone(), this) : Void();
return delay(testDuration);
}
Future<bool> check(Database const& cx) override {
if (clientId != 0) return true;
return passed;
}
ACTOR Future<Void> _setup(Database cx, ReadHotDetectionWorkload* self) {
state int g = 0;
state Standalone<StringRef> largeValue;
state Standalone<StringRef> smallValue;
largeValue = self->randomString(largeValue.arena(), 100000);
smallValue = self->randomString(smallValue.arena(), 100);
state ReadYourWritesTransaction tr(cx);
loop {
try {
for (int i = 0; i < self->keyCount; i++) {
Standalone<StringRef> key = StringRef(format("testkey%08x", i));
if (key == self->readKey) {
tr.set(key, largeValue);
} else {
tr.set(key, deterministicRandom()->random01() > 0.8 ? largeValue : smallValue);
}
}
wait(tr.commit());
break;
} catch (Error& e) {
wait(tr.onError(e));
}
}
self->wholeRange = KeyRangeRef(LiteralStringRef(""), LiteralStringRef("\xff"));
// TraceEvent("RHDLog").detail("Phase", "DoneSetup");
return Void();
}
ACTOR Future<Void> _check(Database cx, ReadHotDetectionWorkload* self) {
loop {
state Transaction tr(cx);
try {
StorageMetrics sm = wait(tr.getStorageMetrics(self->wholeRange, 100));
// TraceEvent("RHDCheckPhaseLog")
// .detail("KeyRangeSize", sm.bytes)
// .detail("KeyRangeReadBandwith", sm.bytesReadPerKSecond);
Standalone<VectorRef<ReadHotRangeWithMetrics>> keyRanges = wait(tr.getReadHotRanges(self->wholeRange));
// TraceEvent("RHDCheckPhaseLog")
// .detail("KeyRangesSize", keyRanges.size())
// .detail("ReadKey", self->readKey.printable().c_str())
// .detail("KeyRangesBackBeginKey", keyRanges.back().begin)
// .detail("KeyRangesBackEndKey", keyRanges.back().end);
// Loose check.
for (const auto& kr : keyRanges) {
if (kr.keys.contains(self->readKey)) {
self->passed = true;
return Void();
}
}
// The key ranges deemed read hot does not contain the readKey, which is impossible here.
// TraceEvent("RHDCheckPhaseFailed")
// .detail("KeyRangesSize", keyRanges.size())
// .detail("ReadKey", self->readKey.printable().c_str())
// .detail("KeyRangesBackBeginKey", keyRanges.back().begin)
// .detail("KeyRangesBackEndKey", keyRanges.back().end);
// for(auto kr : keyRanges) {
// TraceEvent("RHDCheckPhaseFailed").detail("KeyRagneBegin", kr.begin).detail("KeyRagneEnd", kr.end);
// }
self->passed = false;
} catch (Error& e) {
// TraceEvent("RHDCheckPhaseReadGotError").error(e);
wait(tr.onError(e));
}
}
}
void getMetrics(vector<PerfMetric>& m) override {}
ACTOR Future<Void> keyReader(Database cx, ReadHotDetectionWorkload* self, double delay, bool useReadKey) {
state double lastTime = now();
loop {
wait(poisson(&lastTime, delay));
state ReadYourWritesTransaction tr(cx);
loop {
try {
Optional<Value> v = wait(tr.get(
useReadKey
? self->readKey
: StringRef(format("testkey%08x", deterministicRandom()->randomInt(0, self->keyCount)))));
break;
} catch (Error& e) {
wait(tr.onError(e));
}
}
}
}
StringRef randomString(Arena& arena, int len, char firstChar = 'a', char lastChar = 'z') {
++lastChar;
StringRef s = makeString(len, arena);
for (int i = 0; i < len; ++i) {
*(uint8_t*)(s.begin() + i) = (uint8_t)deterministicRandom()->randomInt(firstChar, lastChar);
}
return s;
}
};
WorkloadFactory<ReadHotDetectionWorkload> ReadHotDetectionWorkloadFactory("ReadHotDetection");