foundationdb/fdbserver/workloads/DiskDurability.actor.cpp

187 lines
5.6 KiB
C++
Executable File

/*
* DiskDurability.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 "fdbserver/workloads/workloads.actor.h"
#include "flow/ActorCollection.h"
#include "flow/SystemMonitor.h"
#include "fdbrpc/IAsyncFile.h"
#include "fdbserver/workloads/AsyncFile.actor.h"
#include "flow/actorcompiler.h" // This must be the last #include.
struct DiskDurabilityWorkload : public AsyncFileWorkload
{
struct FileBlock {
FileBlock(int blockNum) : blockNum(blockNum), lastData(0), lock(new FlowLock(1)) {}
~FileBlock() {}
int blockNum;
int64_t lastData;
Reference<FlowLock> lock;
ACTOR static Future<Void> test_impl(FileBlock *self, Reference<AsyncFileHandle> file, int pages, Reference<AsyncFileBuffer> buffer) {
wait(self->lock->take());
state int64_t offset = (int64_t)self->blockNum * pages * _PAGE_SIZE;
state int size = pages * _PAGE_SIZE;
state int64_t newData;
if(self->lastData == 0)
newData = deterministicRandom()->randomInt64(std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max());
else {
++newData;
int readBytes = wait(file->file->read(buffer->buffer, size, offset));
ASSERT(readBytes == size);
}
if(newData == 0)
newData = 1;
int64_t *arr = (int64_t *)buffer->buffer;
for(int i = 0, imax = size / sizeof(int64_t); i < imax; ++i) {
if(self->lastData != 0 && arr[i] != self->lastData) {
TraceEvent(SevError, "WriteWasNotDurable")
.detail("Filename", file->path)
.detail("Offset", offset)
.detail("OpSize", size)
.detail("Expected", self->lastData)
.detail("Found", arr[i])
.detail("Index", i);
throw io_error();
}
arr[i] = newData;
}
wait(file->file->write(buffer->buffer, size, offset));
self->lock->release(1);
self->lastData = newData;
return Void();
}
Future<Void> test(Reference<AsyncFileHandle> file, int pages, Reference<AsyncFileBuffer> buffer) { return test_impl(this, file, pages, buffer); }
};
vector<FileBlock> blocks;
int pagesPerWrite;
int filePages;
int writers;
double syncInterval;
DiskDurabilityWorkload(WorkloadContext const& wcx)
: AsyncFileWorkload(wcx)
{
writers = getOption(options, LiteralStringRef("writers"), 1);
filePages = getOption(options, LiteralStringRef("filePages"), 1000000);
fileSize = filePages * _PAGE_SIZE;
unbufferedIO = true;
uncachedIO = true;
fillRandom = false;
pagesPerWrite = getOption(options, LiteralStringRef("pagesPerWrite"), 1);
syncInterval = (double)(getOption(options, LiteralStringRef("syncIntervalMs"), 2000)) / 1000;
}
virtual ~DiskDurabilityWorkload(){ }
std::string description() const override { return "DiskDurability"; }
Future<Void> setup(Database const& cx) override {
if(enabled)
return _setup(this);
return Void();
}
ACTOR Future<Void> _setup(DiskDurabilityWorkload *self)
{
ASSERT(!self->path.empty());
int flags = IAsyncFile::OPEN_READWRITE | IAsyncFile::OPEN_CREATE;
if(self->unbufferedIO)
flags |= IAsyncFile::OPEN_UNBUFFERED;
if(self->uncachedIO)
flags |= IAsyncFile::OPEN_UNCACHED;
try
{
state Reference<IAsyncFile> file = wait(IAsyncFileSystem::filesystem()->open(self->path, flags, 0666));
if(self->fileHandle.getPtr() == nullptr)
self->fileHandle = Reference<AsyncFileHandle>(new AsyncFileHandle(file, self->path, false));
else
self->fileHandle->file = file;
}
catch(Error &error)
{
TraceEvent(SevError, "TestFailure").detail("Reason", "Could not open file");
throw;
}
return Void();
}
Future<Void> start(Database const& cx) override {
if(enabled)
return _start(this);
return Void();
}
static unsigned int intHash(unsigned int x) {
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = (x >> 16) ^ x;
return x;
}
ACTOR static Future<Void> worker(DiskDurabilityWorkload *self) {
state Reference<AsyncFileBuffer> buffer = Reference<AsyncFileBuffer>(new AsyncFileBuffer(_PAGE_SIZE, true));
state int logfp = (int)ceil(log2(self->filePages));
loop {
int block = intHash(std::min<int>(deterministicRandom()->randomInt(0, 1 << deterministicRandom()->randomInt(0, logfp)), self->filePages - 1)) % self->filePages;
wait(self->blocks[block].test(self->fileHandle, self->pagesPerWrite, buffer));
}
}
ACTOR static Future<Void> syncLoop(DiskDurabilityWorkload *self) {
loop {
wait(delay(deterministicRandom()->random01() * self->syncInterval));
wait(self->fileHandle->file->sync());
}
}
ACTOR Future<Void> _start(DiskDurabilityWorkload *self)
{
self->blocks.reserve(self->filePages);
for(int i = 0; i < self->filePages; ++i)
self->blocks.push_back(FileBlock(i));
state std::vector<Future<Void>> tasks;
tasks.push_back(syncLoop(self));
for(int i = 0; i < self->writers; ++i)
tasks.push_back(worker(self));
wait(timeout(waitForAll(tasks), self->testDuration, Void()));
return Void();
}
void getMetrics(vector<PerfMetric>& m) override {}
};
WorkloadFactory<DiskDurabilityWorkload> DiskDurabilityWorkloadFactory("DiskDurability");