Files
ternfs-XTXMarkets/cpp/cdc/CDCDBData.hpp
Francesco Mazzoli c5562c7ca3 Parallelize CDC by directory
Fixes #66.
2023-11-29 11:08:07 +00:00

265 lines
8.1 KiB
C++

#pragma once
#include <bit>
#include <cstddef>
#include <rocksdb/slice.h>
#include "Assert.hpp"
#include "Bincode.hpp"
#include "Exception.hpp"
#include "Msgs.hpp"
#include "RocksDBUtils.hpp"
#include "Msgs.hpp"
#include "Time.hpp"
#include "CDCDB.hpp"
enum class CDCMetadataKey : uint8_t {
LAST_APPLIED_LOG_ENTRY = 0,
LAST_TXN = 1,
FIRST_TXN_IN_QUEUE = 2,
LAST_TXN_IN_QUEUE = 3,
EXECUTING_TXN = 4,
EXECUTING_TXN_STATE = 6,
LAST_DIRECTORY_ID = 7,
VERSION = 8,
};
constexpr CDCMetadataKey LAST_APPLIED_LOG_ENTRY_KEY = CDCMetadataKey::LAST_APPLIED_LOG_ENTRY; // V0, V1
constexpr CDCMetadataKey LAST_TXN_KEY = CDCMetadataKey::LAST_TXN; // V0, V1
constexpr CDCMetadataKey FIRST_TXN_IN_QUEUE_KEY = CDCMetadataKey::FIRST_TXN_IN_QUEUE; // V0
constexpr CDCMetadataKey LAST_TXN_IN_QUEUE_KEY = CDCMetadataKey::LAST_TXN_IN_QUEUE; // V0
constexpr CDCMetadataKey EXECUTING_TXN_KEY = CDCMetadataKey::EXECUTING_TXN; // V0
constexpr CDCMetadataKey EXECUTING_TXN_STATE_KEY = CDCMetadataKey::EXECUTING_TXN_STATE; // V0
constexpr CDCMetadataKey NEXT_DIRECTORY_ID_KEY = CDCMetadataKey::LAST_DIRECTORY_ID; // V0, V1
constexpr CDCMetadataKey VERSION_KEY = CDCMetadataKey::VERSION; // V1
inline rocksdb::Slice cdcMetadataKey(const CDCMetadataKey* k) {
return rocksdb::Slice((const char*)k, sizeof(*k));
}
struct CDCTxnIdKey {
FIELDS(
BE, CDCTxnId, id, setIdUnchecked,
END_STATIC
)
void setId(CDCTxnId i) {
ALWAYS_ASSERT(i.x != 0);
setIdUnchecked(i);
}
static StaticValue<CDCTxnIdKey> Static(CDCTxnId id) {
auto x = StaticValue<CDCTxnIdKey>();
x().setId(id);
return x;
}
};
struct CDCTxnIdValue {
FIELDS(
LE, CDCTxnId, id, setIdUnchecked,
END_STATIC
)
void setId(CDCTxnId i) {
ALWAYS_ASSERT(i.x != 0);
setIdUnchecked(i);
}
static StaticValue<CDCTxnIdValue> Static(CDCTxnId id) {
auto x = StaticValue<CDCTxnIdValue>();
x().setId(id);
return x;
}
};
// This data structure, conceptually, is std::unordered_map<InodeId, std::vector<CDCTxnId>>.
//
// To encode this in RocksDB, we store (InodeId, CDCTxnId) keys with no values. Txn ids are
// increasing so the order will be naturally what we want.
//
// We also store a sentinel with the head of the list. This is to avoid having to step on many
// deleted keys when checking if a dir is already locked.
//
// The functions adding/removing elements to the list are tasked with bookeeping the sentinel.
struct DirsToTxnsKey {
FIELDS(
BE, InodeId, dirId, setDirId,
BE, uint64_t, maybeTxnId, setMaybeTxnId, // if 0, it's a sentinel
END_STATIC
)
bool isSentinel() const {
return maybeTxnId() == 0;
}
CDCTxnId txnId() const {
ALWAYS_ASSERT(maybeTxnId() != 0);
return maybeTxnId();
}
void setTxnId(CDCTxnId txnId) {
ALWAYS_ASSERT(txnId.x != 0);
setMaybeTxnId(txnId.x);
}
void setSentinel() {
setMaybeTxnId(0);
}
};
struct MakeDirectoryState {
FIELDS(
LE, InodeId, dirId, setDirId,
LE, EggsTime, oldCreationTime, setOldCreationTime,
LE, EggsTime, creationTime, setCreationTime,
LE, EggsError, exitError, setExitError, // error if we're rolling back
END_STATIC
)
void start() {
setDirId(NULL_INODE_ID);
setOldCreationTime({});
setCreationTime({});
setExitError(NO_ERROR);
}
};
struct RenameFileState {
FIELDS(
LE, EggsTime, newOldCreationTime, setNewOldCreationTime,
LE, EggsTime, newCreationTime, setNewCreationTime,
LE, EggsError, exitError, setExitError,
END_STATIC
)
void start() {
setNewOldCreationTime({});
setNewCreationTime({});
setExitError(NO_ERROR);
}
};
struct SoftUnlinkDirectoryState {
FIELDS(
LE, InodeId, statDirId, setStatDirId,
LE, EggsError, exitError, setExitError,
END_STATIC
)
void start() {
setExitError(NO_ERROR);
}
};
struct RenameDirectoryState {
FIELDS(
LE, EggsTime, newOldCreationTime, setNewOldCreationTime,
LE, EggsTime, newCreationTime, setNewCreationTime,
LE, EggsError, exitError, setExitError,
END_STATIC
)
void start() {
setNewOldCreationTime({});
setNewCreationTime({});
setExitError(NO_ERROR);
}
};
struct HardUnlinkDirectoryState {
FIELDS(END_STATIC)
void start() {}
};
struct CrossShardHardUnlinkFileState {
FIELDS(END_STATIC)
void start() {}
};
template<typename Type, typename ...Types>
constexpr size_t maxMaxSize() {
if constexpr (sizeof...(Types) > 0) {
return std::max<size_t>(Type::MAX_SIZE, maxMaxSize<Types...>());
} else {
return Type::MAX_SIZE;
}
}
struct TxnState {
FIELDS(
LE, CDCMessageKind, reqKind, setReqKind,
LE, uint8_t, step, setStep,
EMIT_OFFSET, MIN_SIZE,
END
)
static constexpr size_t MAX_SIZE =
MIN_SIZE +
maxMaxSize<MakeDirectoryState, RenameFileState, SoftUnlinkDirectoryState, RenameDirectoryState, HardUnlinkDirectoryState, CrossShardHardUnlinkFileState>();
size_t size() const {
size_t sz = MIN_SIZE;
switch (reqKind()) {
case CDCMessageKind::MAKE_DIRECTORY:
sz += MakeDirectoryState::MAX_SIZE; break;
case CDCMessageKind::RENAME_FILE:
sz += RenameFileState::MAX_SIZE; break;
case CDCMessageKind::SOFT_UNLINK_DIRECTORY:
sz += SoftUnlinkDirectoryState::MAX_SIZE; break;
case CDCMessageKind::RENAME_DIRECTORY:
sz += RenameDirectoryState::MAX_SIZE; break;
case CDCMessageKind::HARD_UNLINK_DIRECTORY:
sz += RenameDirectoryState::MAX_SIZE; break;
case CDCMessageKind::CROSS_SHARD_HARD_UNLINK_FILE:
sz += CrossShardHardUnlinkFileState::MAX_SIZE; break;
default:
throw EGGS_EXCEPTION("bad cdc message kind %s", reqKind());
}
return sz;
}
void checkSize(size_t sz) {
ALWAYS_ASSERT(sz >= MIN_SIZE);
ALWAYS_ASSERT(sz == size());
}
#define TXN_STATE(kind, type, getName, startName) \
type getName() { \
ALWAYS_ASSERT(reqKind() == CDCMessageKind::kind); \
type v; \
v._data = _data + MIN_SIZE; \
return v; \
} \
type startName() { \
setStep(0); \
setReqKind(CDCMessageKind::kind); \
type v; \
v._data = _data + MIN_SIZE; \
v.start(); \
return v; \
}
TXN_STATE(MAKE_DIRECTORY, MakeDirectoryState, getMakeDirectory, startMakeDirectory)
TXN_STATE(RENAME_FILE, RenameFileState, getRenameFile, startRenameFile)
TXN_STATE(SOFT_UNLINK_DIRECTORY, SoftUnlinkDirectoryState, getSoftUnlinkDirectory, startSoftUnlinkDirectory)
TXN_STATE(RENAME_DIRECTORY, RenameDirectoryState, getRenameDirectory, startRenameDirectory)
TXN_STATE(HARD_UNLINK_DIRECTORY, HardUnlinkDirectoryState, getHardUnlinkDirectory, startHardUnlinkDirectory)
TXN_STATE(CROSS_SHARD_HARD_UNLINK_FILE, CrossShardHardUnlinkFileState, getCrossShardHardUnlinkFile, startCrossShardHardUnlinkFile)
#undef TXN_STATE
void start(CDCMessageKind kind) {
setReqKind(kind);
switch (reqKind()) {
case CDCMessageKind::MAKE_DIRECTORY: startMakeDirectory(); break;
case CDCMessageKind::RENAME_FILE: startRenameFile(); break;
case CDCMessageKind::SOFT_UNLINK_DIRECTORY: startSoftUnlinkDirectory(); break;
case CDCMessageKind::RENAME_DIRECTORY: startRenameDirectory(); break;
case CDCMessageKind::HARD_UNLINK_DIRECTORY: startHardUnlinkDirectory(); break;
case CDCMessageKind::CROSS_SHARD_HARD_UNLINK_FILE: startCrossShardHardUnlinkFile(); break;
default:
throw EGGS_EXCEPTION("bad cdc message kind %s", reqKind());
}
memset(_data+MIN_SIZE, 0, size()-MIN_SIZE);
}
};