mirror of
https://github.com/XTXMarkets/ternfs.git
synced 2026-01-11 21:39:53 -06:00
The goal here is to not have constant wakeups due to timeout. Do not attempt to clean things up nicely before termination -- just terminate instead. We can setup a proper termination system in the future, I first want to see if this makes a difference. Also, change xmon to use pipes for communication, so that it can wait without timers as well. Also, `write` directly for logging, so that we know the logs will make it to the file after the logging call returns (since we now do not have the chance to flush them afterwards).
619 lines
21 KiB
C++
619 lines
21 KiB
C++
#include <cstring>
|
|
#include <filesystem>
|
|
#include <memory>
|
|
#include <filesystem>
|
|
#include <rocksdb/db.h>
|
|
#include <sstream>
|
|
#include <random>
|
|
#include <unistd.h>
|
|
|
|
#include "Common.hpp"
|
|
#include "Bincode.hpp"
|
|
#include "Msgs.hpp"
|
|
#include "ShardDB.hpp"
|
|
#include "ShardDBData.hpp"
|
|
#include "Crypto.hpp"
|
|
#include "RocksDBUtils.hpp"
|
|
#include "Time.hpp"
|
|
#include "CDCKey.hpp"
|
|
#include "wyhash.h"
|
|
#include "Timings.hpp"
|
|
|
|
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
|
#include "doctest.h"
|
|
|
|
REGISTER_EXCEPTION_TRANSLATOR(AbstractException& ex) {
|
|
std::stringstream ss;
|
|
// Before, we had stack traces and this was useful, now a bit less
|
|
ss << std::endl << ex.what() << std::endl;
|
|
return doctest::String(ss.str().c_str());
|
|
}
|
|
|
|
|
|
template<typename A>
|
|
static void bincodeTestScalar() {
|
|
int bits = sizeof(A) * 8;
|
|
char buf[8];
|
|
for (int i = 0; i < bits; i++) {
|
|
A x = (A)1 << i;
|
|
BincodeBuf bbuf(buf, sizeof(buf));
|
|
bbuf.packScalar<A>(x);
|
|
bbuf = BincodeBuf(buf, sizeof(buf));
|
|
CHECK(x == bbuf.unpackScalar<A>());
|
|
}
|
|
}
|
|
|
|
TEST_CASE("bincode u8") { bincodeTestScalar<uint8_t>(); }
|
|
TEST_CASE("bincode u16") { bincodeTestScalar<uint16_t>(); }
|
|
TEST_CASE("bincode u32") { bincodeTestScalar<uint32_t>(); }
|
|
TEST_CASE("bincode u64") { bincodeTestScalar<uint64_t>(); }
|
|
|
|
TEST_CASE("BincodeBytes") {
|
|
BincodeBytes bytes;
|
|
CHECK(bytes.size() == 0);
|
|
CHECK(strncmp(bytes.data(), "", bytes.size()) == 0);
|
|
uint8_t buf[255];
|
|
uint64_t rand = 0;
|
|
for (int i = 0; i < 10; i++) {
|
|
wyhash64_bytes(&rand, &buf[0], i);
|
|
bytes = BincodeBytes((const char*)buf, i);
|
|
CHECK(bytes.size() == i);
|
|
CHECK(strncmp(bytes.data(), (const char*)buf, bytes.size()) == 0);
|
|
}
|
|
}
|
|
|
|
struct TempRocksDB {
|
|
rocksdb::DB* db;
|
|
std::string dbDir;
|
|
|
|
TempRocksDB() {
|
|
dbDir = std::string("temp-rocks-db.XXXXXX");
|
|
if (mkdtemp(dbDir.data()) == nullptr) {
|
|
throw SYSCALL_EXCEPTION("mkdtemp");
|
|
}
|
|
rocksdb::Options options;
|
|
options.create_if_missing = true;
|
|
ROCKS_DB_CHECKED(rocksdb::DB::Open(options, dbDir, &db));
|
|
}
|
|
|
|
rocksdb::DB* operator->() {
|
|
return db;
|
|
}
|
|
|
|
~TempRocksDB() {
|
|
std::error_code err;
|
|
if (std::filesystem::remove_all(std::filesystem::path(dbDir), err) < 0) {
|
|
std::cerr << "Could not remove " << dbDir << ": " << err << std::endl;
|
|
}
|
|
auto closeStatus = db->Close();
|
|
if (!closeStatus.ok()) {
|
|
std::cerr << "Could not close db: " << closeStatus.ToString() << std::endl;
|
|
}
|
|
delete db;
|
|
}
|
|
};
|
|
|
|
static bool sliceEq(const rocksdb::Slice& lhs, const rocksdb::Slice& rhs) {
|
|
return lhs.size() == rhs.size() && (strncmp(lhs.data(), rhs.data(), lhs.size()) == 0);
|
|
}
|
|
|
|
TEST_CASE("ShardDB data") {
|
|
TempRocksDB db;
|
|
|
|
SUBCASE("TransientFile") {
|
|
auto transientFileId = InodeIdKey::Static({InodeType::FILE, ShardId(), 0});
|
|
BincodeBytes transientFileBodyNote("hello world");
|
|
StaticValue<TransientFileBody> transientFileBody;
|
|
transientFileBody().setVersion(0);
|
|
transientFileBody().setFileSize(123);
|
|
transientFileBody().setMtime({456});
|
|
transientFileBody().setDeadline({789});
|
|
transientFileBody().setLastSpanState(SpanState::CLEAN);
|
|
transientFileBody().setNoteDangerous(transientFileBodyNote.ref());
|
|
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Put({}, transientFileId.toSlice(), transientFileBody.toSlice())
|
|
);
|
|
std::string transientFileValue;
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Get({}, transientFileId.toSlice(), &transientFileValue)
|
|
);
|
|
CHECK(sliceEq(transientFileBody.toSlice(), {transientFileValue}));
|
|
|
|
ExternalValue<TransientFileBody> readTransientFileBody(transientFileValue);
|
|
|
|
CHECK(transientFileBody().mtime() == readTransientFileBody().mtime());
|
|
CHECK(transientFileBody().fileSize() == readTransientFileBody().fileSize());
|
|
CHECK(transientFileBody().deadline() == readTransientFileBody().deadline());
|
|
CHECK(transientFileBody().note() == readTransientFileBody().note());
|
|
}
|
|
|
|
SUBCASE("File") {
|
|
auto fileId = InodeIdKey::Static({InodeType::FILE, ShardId(), 0});
|
|
StaticValue<FileBody> fileBody;
|
|
fileBody().setVersion(0);
|
|
fileBody().setMtime(123);
|
|
fileBody().setAtime(123);
|
|
fileBody().setFileSize(456);
|
|
|
|
ROCKS_DB_CHECKED(db.db->Put({}, fileId.toSlice(), fileBody.toSlice()));
|
|
std::string fileValue;
|
|
ROCKS_DB_CHECKED(db.db->Get({}, fileId.toSlice(), &fileValue));
|
|
CHECK(sliceEq(fileBody.toSlice(), {fileValue}));
|
|
|
|
ExternalValue<FileBody> readFileBody(fileValue);
|
|
|
|
CHECK(fileBody().mtime() == readFileBody().mtime());
|
|
CHECK(fileBody().fileSize() == readFileBody().fileSize());
|
|
}
|
|
|
|
/*
|
|
SUBCASE("Span (ZERO)") {
|
|
SpanKey key(InodeId(InodeType::FILE, ShardId(), 0), 123);
|
|
|
|
auto span = SpanBody::NewZero();
|
|
span->size = 456;
|
|
span->blockSize = 789;
|
|
span->crc32 = 012;
|
|
span->parity = Parity(0);
|
|
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Put({}, key.toSlice(), span->toSlice())
|
|
);
|
|
std::string spanValue;
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Get({}, key.toSlice(), &spanValue)
|
|
);
|
|
CHECK(sliceEq(span->toSlice(), {spanValue}));
|
|
|
|
auto readSpan = SpanBody::FromSlice({spanValue});
|
|
|
|
CHECK(readSpan->storageClass == ZERO_FILL_STORAGE);
|
|
CHECK(readSpan->size == span->size);
|
|
CHECK(readSpan->blockSize == span->blockSize);
|
|
CHECK(readSpan->crc32 == span->crc32);
|
|
CHECK(readSpan->parity == span->parity);
|
|
}
|
|
|
|
SUBCASE("Span (INLINE)") {
|
|
SpanKey key(InodeId(InodeType::FILE, ShardId(), 0), 123);
|
|
|
|
BincodeBytes body("body");
|
|
auto span = SpanBody::NewInline(body.length);
|
|
span->size = 456;
|
|
span->blockSize = 789;
|
|
span->crc32 = 012;
|
|
span->parity = Parity(0);
|
|
span->setInlineBody(body);
|
|
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Put({}, key.toSlice(), span->toSlice())
|
|
);
|
|
std::string spanValue;
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Get({}, key.toSlice(), &spanValue)
|
|
);
|
|
CHECK(sliceEq(span->toSlice(), {spanValue}));
|
|
|
|
auto readSpan = SpanBody::FromSlice({spanValue});
|
|
|
|
CHECK(readSpan->storageClass == INLINE_STORAGE);
|
|
CHECK(readSpan->size == span->size);
|
|
CHECK(readSpan->blockSize == span->blockSize);
|
|
CHECK(readSpan->crc32 == span->crc32);
|
|
CHECK(readSpan->parity == span->parity);
|
|
CHECK(readSpan->inlineBody() == span->inlineBody());
|
|
}
|
|
|
|
SUBCASE("Span (BLOCKS)") {
|
|
SpanKey key(InodeId(InodeType::FILE, ShardId(), 0), 456);
|
|
|
|
Parity parity(10, 4);
|
|
uint8_t storageClass = 2;
|
|
BincodeBytes body("body");
|
|
auto span = SpanBody::New(storageClass, parity);
|
|
span->size = 456;
|
|
span->blockSize = 789;
|
|
span->crc32 = 012;
|
|
for (uint64_t i = 0; i < span->parity.blocks(); i++) {
|
|
auto& block = span->block(i);
|
|
block.blockServiceId = i * 2;
|
|
block.crc32 = i * 2 + 1;
|
|
}
|
|
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Put({}, key.toSlice(), span->toSlice())
|
|
);
|
|
std::string spanValue;
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Get({}, key.toSlice(), &spanValue)
|
|
);
|
|
CHECK(sliceEq(span->toSlice(), {spanValue}));
|
|
|
|
auto readSpan = SpanBody::FromSlice({spanValue});
|
|
|
|
CHECK(readSpan->storageClass == storageClass);
|
|
CHECK(readSpan->size == span->size);
|
|
CHECK(readSpan->blockSize == span->blockSize);
|
|
CHECK(readSpan->crc32 == span->crc32);
|
|
CHECK(readSpan->parity == span->parity);
|
|
for (uint64_t i = 0; i < span->parity.blocks(); i++) {
|
|
const auto& lhs = readSpan->block(i);
|
|
const auto& rhs = span->block(i);
|
|
CHECK(lhs.blockServiceId == rhs.blockServiceId);
|
|
CHECK(lhs.crc32 == rhs.crc32);
|
|
}
|
|
}
|
|
|
|
SUBCASE("Directory") {
|
|
InodeId id(InodeType::DIRECTORY, ShardId(3), 5);
|
|
|
|
StaticValue<DirectoryBody> dir;
|
|
dir().setOwnerId(ROOT_DIR_INODE_ID);
|
|
dir().setMtime(123);
|
|
dir().setHashMode(HashMode::XXH3_63);
|
|
dir().setInfoInherited(true);
|
|
dir().setInfo({});
|
|
|
|
auto slice = dir.toSlice();
|
|
|
|
auto dirKey = InodeIdKey::Static(id);
|
|
|
|
ROCKS_DB_CHECKED(db.db->Put({}, dirKey.toSlice(), dir.toSlice()));
|
|
std::string dirValue;
|
|
ROCKS_DB_CHECKED(db.db->Get({}, dirKey.toSlice(), &dirValue));
|
|
CHECK(sliceEq(dir.toSlice(), {dirValue}));
|
|
|
|
ExternalValue<DirectoryBody> readDir(dirValue);
|
|
|
|
CHECK(readDir().mtime() == dir().mtime());
|
|
CHECK(readDir().ownerId() == dir().ownerId());
|
|
CHECK(readDir().hashMode() == dir().hashMode());
|
|
CHECK(readDir().infoInherited() == dir().infoInherited());
|
|
}
|
|
|
|
SUBCASE("Edges") {
|
|
InodeId dirId(InodeType::DIRECTORY, ShardId(3), 5);
|
|
|
|
BincodeBytes snapshotName("foo");
|
|
auto snapshotKey = EdgeKey::NewSnapshot(snapshotName.length);
|
|
snapshotKey->setDirId(dirId);
|
|
snapshotKey->nameHash = 123;
|
|
snapshotKey->setName(snapshotName);
|
|
snapshotKey->setCreationTime(456);
|
|
|
|
SnapshotEdgeBody snapshot;
|
|
snapshot.setTargetId(InodeIdExtra(InodeId(InodeType::FILE, ShardId(4), 32), false));
|
|
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Put({}, snapshotKey->toSlice(), snapshot.toSlice())
|
|
);
|
|
|
|
BincodeBytes currentName("barrrr");
|
|
auto currentKey = EdgeKey::NewCurrent(currentName.length);
|
|
currentKey->setDirId(dirId);
|
|
currentKey->nameHash = 789;
|
|
currentKey->setName(currentName);
|
|
|
|
CurrentEdgeBody current;
|
|
current.setTargetId(InodeIdExtra(InodeId(InodeType::FILE, ShardId(5), 33), true));
|
|
current.setCreationTime(012);
|
|
|
|
ROCKS_DB_CHECKED(
|
|
db.db->Put({}, currentKey->toSlice(), current.toSlice())
|
|
);
|
|
|
|
std::unique_ptr<rocksdb::Iterator> it(db.db->NewIterator({}));
|
|
// we first go to the current edge, to emulate what readdir does
|
|
{
|
|
auto key = EdgeKey::NewCurrent(0);
|
|
key->setDirId(dirId);
|
|
key->nameHash = currentKey->nameHash;
|
|
it->Seek(key->toSlice());
|
|
}
|
|
CHECK(it->Valid());
|
|
CHECK(sliceEq(it->key(), currentKey->toSlice()));
|
|
auto key = EdgeKey::FromSlice(it->key());
|
|
CHECK(key->current == currentKey->current);
|
|
CHECK(key->dirId() == currentKey->dirId());
|
|
CHECK(key->nameHash == currentKey->nameHash);
|
|
CHECK(key->name() == currentKey->name());
|
|
CHECK(sliceEq(it->value(), current.toSlice()));
|
|
CurrentEdgeBody currentValue(it->value());
|
|
CHECK(currentValue.targetId() == current.targetId());
|
|
CHECK(currentValue.creationTime() == current.creationTime());
|
|
// then we go backwards to the snapshot edge
|
|
it->Prev();
|
|
CHECK(it->Valid());
|
|
CHECK(sliceEq(it->key(), snapshotKey->toSlice()));
|
|
key = EdgeKey::FromSlice(it->key());
|
|
CHECK(key->current == snapshotKey->current);
|
|
CHECK(key->dirId() == snapshotKey->dirId());
|
|
CHECK(key->nameHash == snapshotKey->nameHash);
|
|
CHECK(key->name() == snapshotKey->name());
|
|
CHECK(key->creationTime() == snapshotKey->creationTime());
|
|
CHECK(sliceEq(it->value(), snapshot.toSlice()));
|
|
SnapshotEdgeBody snapshotValue(it->value());
|
|
CHECK(snapshotValue.targetId() == snapshot.targetId());
|
|
}
|
|
*/
|
|
}
|
|
|
|
TEST_CASE("CBC MAC") {
|
|
std::array<uint8_t, 16> userKey;
|
|
memcpy(userKey.data(), "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", sizeof(userKey));
|
|
AES128Key key;
|
|
expandKey(userKey, key);
|
|
const auto test = [&key](const std::vector<uint8_t>& plaintext, const std::array<uint8_t, 8>& expectedMac) {
|
|
CHECK(cbcmac(key, plaintext.data(), plaintext.size()) == expectedMac);
|
|
};
|
|
test(
|
|
{68,235,81,75,124,255,47,151,1,176},
|
|
{25,112,35,208,238,64,6,13}
|
|
);
|
|
test(
|
|
{44,48,69,112,99,225,155,9,121,48,26,199,170,9,87,117},
|
|
{35,130,149,17,102,117,44,174}
|
|
);
|
|
test(
|
|
{174,202,179,110,252,61,96,177,72,45,146,68,134,235,251,99,104,219,39,83},
|
|
{25,197,134,127,44,151,241,223}
|
|
);
|
|
test(
|
|
{30,150,143,64,165,93,232,5,86,160,21,239,228,49,121,199,214,95,153,152,37,35,188,167,111,6,253,215,180,215,85,201},
|
|
{85,212,198,154,164,248,20,252}
|
|
);
|
|
AES128Key expandedCDCKey;
|
|
expandKey(CDCKey, expandedCDCKey);
|
|
const auto testCDC = [&expandedCDCKey](const std::vector<uint8_t>& plaintext, const std::array<uint8_t, 8>& expectedMac) {
|
|
CHECK(cbcmac(expandedCDCKey, plaintext.data(), plaintext.size()) == expectedMac);
|
|
};
|
|
testCDC(
|
|
{30,150,143,64,165,93,232,5,86,160,21,239,228,49,121,199,214,95,153,152,37,35,188,167,111,6,253,215,180,215,85,201},
|
|
{78, 250, 182, 234, 239, 64, 212, 140}
|
|
);
|
|
testCDC(
|
|
{83,72,65,0,202,51,170,123,110,151,72,1,128,7,0,0,0,0,0,0,32,0,0,0,0,0,0,0,32,1,0,0},
|
|
{7,78,66,131,192,124,19,0}
|
|
);
|
|
}
|
|
|
|
struct TempShardDB {
|
|
std::string dbDir;
|
|
Logger logger;
|
|
std::unique_ptr<Env> env;
|
|
ShardId shid;
|
|
std::unique_ptr<ShardDB> db;
|
|
|
|
TempShardDB(LogLevel level, ShardId shid_): logger(level, STDERR_FILENO, false, false), shid(shid_) {
|
|
dbDir = std::string("temp-shard-db.XXXXXX");
|
|
if (mkdtemp(dbDir.data()) == nullptr) {
|
|
throw SYSCALL_EXCEPTION("mkdtemp");
|
|
}
|
|
std::shared_ptr<XmonAgent> xmon;
|
|
db = std::make_unique<ShardDB>(logger, xmon, shid, DEFAULT_DEADLINE_INTERVAL, dbDir);
|
|
}
|
|
|
|
// useful to test recovery
|
|
void restart() {
|
|
std::shared_ptr<XmonAgent> xmon;
|
|
db = std::make_unique<ShardDB>(logger, xmon, shid, DEFAULT_DEADLINE_INTERVAL, dbDir);
|
|
}
|
|
|
|
~TempShardDB() {
|
|
std::error_code err;
|
|
if (std::filesystem::remove_all(std::filesystem::path(dbDir), err) < 0) {
|
|
std::cerr << "Could not remove " << dbDir << ": " << err << std::endl;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<ShardDB>& operator->() {
|
|
return db;
|
|
}
|
|
};
|
|
|
|
#define NO_EGGS_ERROR(expr) \
|
|
do { \
|
|
EggsError err = (expr); \
|
|
ALWAYS_ASSERT(err == NO_ERROR, #expr ", unexpected error %s", err); \
|
|
} while(false)
|
|
|
|
TEST_CASE("touch file") {
|
|
TempShardDB db(LogLevel::LOG_ERROR, ShardId(0));
|
|
|
|
auto reqContainer = std::make_unique<ShardReqContainer>();
|
|
auto respContainer = std::make_unique<ShardRespContainer>();
|
|
auto logEntry = std::make_unique<ShardLogEntry>();
|
|
uint64_t logEntryIndex = 0;
|
|
|
|
InodeId id;
|
|
BincodeFixedBytes<8> cookie;
|
|
EggsTime constructTime, linkTime;
|
|
BincodeBytes name("filename");
|
|
{
|
|
auto& req = reqContainer->setConstructFile();
|
|
req.type = (uint8_t)InodeType::FILE;
|
|
req.note = "test note";
|
|
NO_EGGS_ERROR(db->prepareLogEntry(*reqContainer, *logEntry));
|
|
constructTime = logEntry->time;
|
|
NO_EGGS_ERROR(db->applyLogEntry(true, ShardMessageKind::CONSTRUCT_FILE, ++logEntryIndex, *logEntry, *respContainer));
|
|
auto& resp = respContainer->getConstructFile();
|
|
id = resp.id;
|
|
cookie = resp.cookie;
|
|
}
|
|
{
|
|
auto& req = reqContainer->setVisitTransientFiles();
|
|
NO_EGGS_ERROR(db->read(*reqContainer, *respContainer));
|
|
auto& resp = respContainer->getVisitTransientFiles();
|
|
REQUIRE(resp.nextId == NULL_INODE_ID);
|
|
REQUIRE(resp.files.els.size() == 1);
|
|
REQUIRE(resp.files.els[0].id == id);
|
|
REQUIRE(resp.files.els[0].cookie == cookie);
|
|
}
|
|
{
|
|
auto& req = reqContainer->setLinkFile();
|
|
req.fileId = id;
|
|
req.cookie = cookie;
|
|
req.ownerId = ROOT_DIR_INODE_ID;
|
|
req.name = name;
|
|
NO_EGGS_ERROR(db->prepareLogEntry(*reqContainer, *logEntry));
|
|
linkTime = logEntry->time;
|
|
NO_EGGS_ERROR(db->applyLogEntry(true, ShardMessageKind::LINK_FILE, ++logEntryIndex, *logEntry, *respContainer));
|
|
}
|
|
{
|
|
auto& req = reqContainer->setReadDir();
|
|
req.dirId = ROOT_DIR_INODE_ID;
|
|
req.startHash = 0;
|
|
NO_EGGS_ERROR(db->read(*reqContainer, *respContainer));
|
|
auto& resp = respContainer->getReadDir();
|
|
REQUIRE(resp.nextHash == 0);
|
|
REQUIRE(resp.results.els.size() == 1);
|
|
auto& res = resp.results.els.at(0);
|
|
REQUIRE(res.name == name);
|
|
REQUIRE(res.targetId == id);
|
|
REQUIRE(res.creationTime == linkTime);
|
|
}
|
|
{
|
|
auto& req = reqContainer->setLookup();
|
|
req.dirId = ROOT_DIR_INODE_ID;
|
|
req.name = name;
|
|
NO_EGGS_ERROR(db->read(*reqContainer, *respContainer));
|
|
auto& resp = respContainer->getLookup();
|
|
REQUIRE(resp.targetId == id);
|
|
}
|
|
{
|
|
auto& req = reqContainer->setStatFile();
|
|
req.id = id;
|
|
NO_EGGS_ERROR(db->read(*reqContainer, *respContainer));
|
|
auto& resp = respContainer->getStatFile();
|
|
REQUIRE(resp.size == 0);
|
|
REQUIRE(resp.mtime == linkTime);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("override") {
|
|
TempShardDB db(LogLevel::LOG_ERROR, ShardId(0));
|
|
|
|
auto reqContainer = std::make_unique<ShardReqContainer>();
|
|
auto respContainer = std::make_unique<ShardRespContainer>();
|
|
auto logEntry = std::make_unique<ShardLogEntry>();
|
|
uint64_t logEntryIndex = 0;
|
|
|
|
const auto createFile = [&](const char* name) -> std::tuple<InodeId, EggsTime> {
|
|
InodeId id;
|
|
BincodeFixedBytes<8> cookie;
|
|
{
|
|
auto& req = reqContainer->setConstructFile();
|
|
req.type = (uint8_t)InodeType::FILE;
|
|
req.note = "test note";
|
|
NO_EGGS_ERROR(db->prepareLogEntry(*reqContainer, *logEntry));
|
|
NO_EGGS_ERROR(db->applyLogEntry(true, ShardMessageKind::CONSTRUCT_FILE, ++logEntryIndex, *logEntry, *respContainer));
|
|
auto& resp = respContainer->getConstructFile();
|
|
id = resp.id;
|
|
cookie = resp.cookie;
|
|
}
|
|
{
|
|
auto& req = reqContainer->setVisitTransientFiles();
|
|
NO_EGGS_ERROR(db->read(*reqContainer, *respContainer));
|
|
}
|
|
EggsTime creationTime;
|
|
{
|
|
auto& req = reqContainer->setLinkFile();
|
|
req.fileId = id;
|
|
req.cookie = cookie;
|
|
req.ownerId = ROOT_DIR_INODE_ID;
|
|
req.name = name;
|
|
NO_EGGS_ERROR(db->prepareLogEntry(*reqContainer, *logEntry));
|
|
NO_EGGS_ERROR(db->applyLogEntry(true, ShardMessageKind::LINK_FILE, ++logEntryIndex, *logEntry, *respContainer));
|
|
creationTime = respContainer->getLinkFile().creationTime;
|
|
}
|
|
return {id, creationTime};
|
|
};
|
|
|
|
auto [foo, fooCreationTime] = createFile("foo");
|
|
auto [bar, barCreationTime] = createFile("bar");
|
|
|
|
{
|
|
auto& req = reqContainer->setSameDirectoryRename();
|
|
req.dirId = ROOT_DIR_INODE_ID;
|
|
req.targetId = foo;
|
|
req.oldName = "foo";
|
|
req.oldCreationTime = fooCreationTime;
|
|
req.newName = "bar";
|
|
NO_EGGS_ERROR(db->prepareLogEntry(*reqContainer, *logEntry));
|
|
NO_EGGS_ERROR(db->applyLogEntry(true, ShardMessageKind::SAME_DIRECTORY_RENAME, ++logEntryIndex, *logEntry, *respContainer));
|
|
}
|
|
{
|
|
auto& req = reqContainer->setFullReadDir();
|
|
req.dirId = ROOT_DIR_INODE_ID;
|
|
NO_EGGS_ERROR(db->read(*reqContainer, *respContainer));
|
|
auto& resp = respContainer->getFullReadDir();
|
|
REQUIRE(
|
|
resp.results.els.size() ==
|
|
1 + // the live "bar" edge
|
|
1 + 1 + // the two snapshot "bar" edges
|
|
1 // the snapshot "foo" edge
|
|
);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("test fmt") {
|
|
{
|
|
std::stringstream ss;
|
|
ss << EggsTime(0);
|
|
REQUIRE(ss.str() == "1970-01-01T00:00:00.000000000");
|
|
}
|
|
{
|
|
std::stringstream ss;
|
|
ss << EggsTime(1234567891ull);
|
|
REQUIRE(ss.str() == "1970-01-01T00:00:01.234567891");
|
|
}
|
|
}
|
|
|
|
/*
|
|
TEST_CASE("make/rm directory") {
|
|
// not actually the full lifecycle, just some ad hoc tests
|
|
|
|
TempShardDB db(LogLevel::LOG_ERROR, ShardId(0));
|
|
|
|
auto reqContainer = std::make_unique<ShardReqContainer>();
|
|
auto respContainer = std::make_unique<ShardRespContainer>();
|
|
auto logEntry = std::make_unique<ShardLogEntry>();
|
|
uint64_t logEntryIndex = 0;
|
|
|
|
BincodeBytes defaultInfo = defaultDirectoryInfo();
|
|
InodeId id(InodeType::DIRECTORY, ShardId(0), 1);
|
|
|
|
{
|
|
auto& req = reqContainer->setCreateDirectoryInode();
|
|
req.id = id;
|
|
req.info.inherited = true;
|
|
req.ownerId = ROOT_DIR_INODE_ID;
|
|
NO_EGGS_ERROR(db->prepareLogEntry(*reqContainer, *logEntry));
|
|
NO_EGGS_ERROR(db->applyLogEntry(true, ++logEntryIndex, *logEntry, *respContainer));
|
|
respContainer->getCreateDirectoryInode();
|
|
}
|
|
{
|
|
auto& req = reqContainer->setRemoveDirectoryOwner();
|
|
req.dirId = id;
|
|
req.info = defaultInfo;
|
|
NO_EGGS_ERROR(db->prepareLogEntry(*reqContainer, *logEntry));
|
|
NO_EGGS_ERROR(db->applyLogEntry(true, ++logEntryIndex, *logEntry, *respContainer));
|
|
}
|
|
{
|
|
auto& req = reqContainer->setStatDirectory();
|
|
req.id = id;
|
|
NO_EGGS_ERROR(db->read(*reqContainer, *respContainer));
|
|
const auto& resp = respContainer->getStatDirectory();
|
|
CHECK(resp.info == defaultInfo);
|
|
}
|
|
{
|
|
auto& req = reqContainer->setSetDirectoryOwner();
|
|
req.dirId = id;
|
|
req.ownerId = ROOT_DIR_INODE_ID;
|
|
NO_EGGS_ERROR(db->prepareLogEntry(*reqContainer, *logEntry));
|
|
NO_EGGS_ERROR(db->applyLogEntry(true, ++logEntryIndex, *logEntry, *respContainer));
|
|
}
|
|
}
|
|
*/
|