195 lines
5.5 KiB
C++
Executable File
195 lines
5.5 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 "flow/actorcompiler.h"
|
|
#include "workloads.h"
|
|
#include "flow/ActorCollection.h"
|
|
#include "flow/SystemMonitor.h"
|
|
|
|
#include "fdbrpc/IAsyncFile.h"
|
|
#include "AsyncFile.actor.h"
|
|
|
|
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) {
|
|
Void _ = 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 = g_random->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;
|
|
}
|
|
|
|
Void _ = 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(){ }
|
|
|
|
virtual std::string description()
|
|
{
|
|
return "DiskDurability";
|
|
}
|
|
|
|
virtual Future<Void> setup(Database const& cx)
|
|
{
|
|
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() == NULL)
|
|
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();
|
|
}
|
|
|
|
virtual Future<Void> start(Database const& cx)
|
|
{
|
|
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>(g_random->randomInt(0, 1 << g_random->randomInt(0, logfp)), self->filePages - 1)) % self->filePages;
|
|
Void _ = wait(self->blocks[block].test(self->fileHandle, self->pagesPerWrite, buffer));
|
|
}
|
|
}
|
|
|
|
ACTOR static Future<Void> syncLoop(DiskDurabilityWorkload *self) {
|
|
loop {
|
|
Void _ = wait(delay(g_random->random01() * self->syncInterval));
|
|
Void _ = 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));
|
|
|
|
Void _ = wait(timeout(waitForAll(tasks), self->testDuration, Void()));
|
|
|
|
return Void();
|
|
}
|
|
|
|
virtual void getMetrics(vector<PerfMetric>& m)
|
|
{
|
|
}
|
|
};
|
|
|
|
WorkloadFactory<DiskDurabilityWorkload> DiskDurabilityWorkloadFactory("DiskDurability");
|