Initial commit

This commit is contained in:
dev
2025-02-27 21:53:53 +08:00
commit 815e55e4c0
1291 changed files with 185445 additions and 0 deletions

8
src/fbs/CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
add_subdirectory(core)
add_subdirectory(mgmtd)
add_subdirectory(meta)
add_subdirectory(storage)
#add_subdirectory(lib)
add_subdirectory(monitor_collector)
add_subdirectory(simple_example)
add_subdirectory(migration)

View File

@@ -0,0 +1,3 @@
add_subdirectory("user")
add_subdirectory("service")

View File

@@ -0,0 +1,2 @@
target_add_lib(core-service-fbs common)

View File

@@ -0,0 +1,12 @@
#pragma once
#include "Rpc.h"
#include "common/serde/Service.h"
namespace hf3fs::core {
SERDE_SERVICE_2(CoreServiceBase, Core, 10001) {
#define DEFINE_SERDE_SERVICE_METHOD_FULL(svc, name, Name, id, reqtype, rsptype) \
SERDE_SERVICE_METHOD(name, id, reqtype, rsptype);
#include "CoreServiceDef.h"
};
} // namespace hf3fs::core

View File

@@ -0,0 +1,7 @@
#pragma once
#include "CoreServiceBase.h"
namespace hf3fs::core {
SERDE_SERVICE_CLIENT(CoreServiceClient, CoreServiceBase);
} // namespace hf3fs::core

View File

@@ -0,0 +1,10 @@
#include "fbs/macros/SerdeDef.h"
DEFINE_SERDE_SERVICE_METHOD(Core, echo, Echo, 1)
DEFINE_SERDE_SERVICE_METHOD(Core, getConfig, GetConfig, 2)
DEFINE_SERDE_SERVICE_METHOD(Core, renderConfig, RenderConfig, 3)
DEFINE_SERDE_SERVICE_METHOD(Core, hotUpdateConfig, HotUpdateConfig, 4)
DEFINE_SERDE_SERVICE_METHOD(Core, getLastConfigUpdateRecord, GetLastConfigUpdateRecord, 5)
DEFINE_SERDE_SERVICE_METHOD(Core, shutdown, Shutdown, 6)
#include "fbs/macros/Undef.h"

View File

@@ -0,0 +1,58 @@
#pragma once
#include "common/app/ApplicationBase.h"
#include "common/serde/SerdeHelper.h"
#include "common/utils/RobinHoodUtils.h"
namespace hf3fs::core {
struct EchoMessage : public serde::SerdeHelper<EchoMessage> {
SERDE_STRUCT_FIELD(str, std::string{});
};
using EchoReq = EchoMessage;
using EchoRsp = EchoMessage;
struct GetConfigReq : public serde::SerdeHelper<GetConfigReq> {
SERDE_STRUCT_FIELD(configKey, std::string{});
};
struct GetConfigRsp : public serde::SerdeHelper<GetConfigRsp> {
SERDE_STRUCT_FIELD(config, std::string{});
};
struct RenderConfigReq : public serde::SerdeHelper<RenderConfigReq> {
SERDE_STRUCT_FIELD(configTemplate, std::string{});
SERDE_STRUCT_FIELD(testUpdate, bool{true});
SERDE_STRUCT_FIELD(isHotUpdate, bool{true});
};
struct RenderConfigRsp : public serde::SerdeHelper<RenderConfigRsp> {
SERDE_STRUCT_FIELD(configAfterRender, std::string{});
SERDE_STRUCT_FIELD(updateStatus, Status(StatusCode::kOK));
SERDE_STRUCT_FIELD(configAfterUpdate, std::string{});
};
struct HotUpdateConfigReq : public serde::SerdeHelper<HotUpdateConfigReq> {
SERDE_STRUCT_FIELD(update, String{});
SERDE_STRUCT_FIELD(render, false);
};
struct HotUpdateConfigRsp : public serde::SerdeHelper<HotUpdateConfigRsp> {
SERDE_STRUCT_FIELD(dummy, Void{});
};
struct GetLastConfigUpdateRecordReq : public serde::SerdeHelper<GetLastConfigUpdateRecordReq> {
SERDE_STRUCT_FIELD(dummy, Void{});
};
struct GetLastConfigUpdateRecordRsp : public serde::SerdeHelper<GetLastConfigUpdateRecordRsp> {
SERDE_STRUCT_FIELD(record, std::optional<app::ConfigUpdateRecord>{});
};
DEFINE_SERDE_HELPER_STRUCT(ShutdownReq) {
//
SERDE_STRUCT_FIELD(graceful, true);
};
DEFINE_SERDE_HELPER_STRUCT(ShutdownRsp) { SERDE_STRUCT_FIELD(dummy, Void{}); };
} // namespace hf3fs::core

View File

@@ -0,0 +1,2 @@
target_add_lib(core-user-fbs common)

80
src/fbs/core/user/User.cc Normal file
View File

@@ -0,0 +1,80 @@
#include "User.h"
#include "common/serde/SerdeComparisons.h"
namespace hf3fs::flat {
UserInfo::UserInfo(Uid uid, Gid gid, std::string token)
: UserInfo(uid, gid, {}, std::move(token)) {}
UserInfo::UserInfo(Uid uid, Gid gid, std::vector<Gid> groups, std::string token)
: uid(uid),
gid(gid),
groups(std::move(groups)),
token(std::move(token)) {}
bool UserInfo::isRoot() const { return uid == 0; }
bool UserInfo::inGroup(Gid group) const {
return gid == group || std::find(groups.begin(), groups.end(), group) != groups.end();
}
bool UserInfo::operator==(const UserInfo &o) const {
return uid == o.uid && gid == o.gid && groups == o.groups && token == o.token;
}
bool UserInfo::compareUserAndToken(const UserInfo &a, const UserInfo &b) {
return a.uid == b.uid && a.gid == b.gid && a.token == b.token;
}
bool TokenAttr::active(UtcTime now) const { return isEndless() || endTime > now; }
bool TokenAttr::isEndless() const { return endTime.isZero(); }
bool TokenAttr::operator==(const TokenAttr &other) const { return serde::equals(*this, other); }
Result<Void> UserAttr::validateToken(std::string_view token, UtcTime now) const {
if (this->token == token) {
return Void{};
}
for (auto it = tokens.rbegin(); it != tokens.rend(); ++it) {
const auto &ta = *it;
if (ta.token == token) {
if (ta.active(now)) {
return Void{};
}
return makeError(StatusCode::kTokenStale);
}
}
return makeError(StatusCode::kTokenMismatch);
}
Result<Void> UserAttr::addNewToken(std::string_view token,
UtcTime now,
bool invalidateExisted,
size_t maxTokens,
Duration tokenLifetimeExtendLength) {
if (this->token == token) {
return Void{};
}
if (!invalidateExisted) {
std::vector<TokenAttr> newTokens;
for (auto &sa : tokens) {
if (sa.active(now) && sa.token != token) {
newTokens.push_back(std::move(sa));
}
}
newTokens.push_back(TokenAttr{this->token, now + tokenLifetimeExtendLength.asUs()});
if (newTokens.size() + 1 >= maxTokens) {
return makeError(StatusCode::kTooManyTokens,
fmt::format("count of tokens by now: {}. max: {}", newTokens.size(), maxTokens));
}
this->tokens = std::move(newTokens);
}
this->token = token;
return Void{};
}
bool UserAttr::operator==(const UserAttr &other) const { return serde::equals(*this, other); }
} // namespace hf3fs::flat

103
src/fbs/core/user/User.h Normal file
View File

@@ -0,0 +1,103 @@
#pragma once
#include "common/serde/Serde.h"
#include "common/utils/Duration.h"
#include "common/utils/StrongType.h"
#include "common/utils/Transform.h"
#include "common/utils/UtcTimeSerde.h"
namespace hf3fs::flat {
STRONG_TYPEDEF(uint32_t, Uid);
STRONG_TYPEDEF(uint32_t, Gid);
// todo: add a format func for permission
STRONG_TYPEDEF(uint32_t, Permission);
struct Token : public std::string {
using std::string::string;
using std::string::operator=;
Token() = default;
Token(const std::string &o)
: std::string(o) {}
Token(std::string &&o)
: std::string(std::move(o)) {}
};
struct UserInfo {
SERDE_STRUCT_FIELD(uid, Uid());
SERDE_STRUCT_FIELD(gid, Gid());
SERDE_STRUCT_FIELD(groups, std::vector<Gid>());
SERDE_STRUCT_FIELD(token, Token{});
public:
UserInfo(Uid uid, Gid gid, std::string token);
UserInfo(Uid uid = {}, Gid gid = {}, std::vector<Gid> groups = {}, std::string token = {});
bool isRoot() const;
bool inGroup(Gid group) const;
bool operator==(const UserInfo &o) const;
static bool compareUserAndToken(const UserInfo &a, const UserInfo &b);
};
struct TokenAttr {
SERDE_STRUCT_FIELD(token, Token{});
SERDE_STRUCT_FIELD(endTime, UtcTime()); // 0 means endless
public:
bool active(UtcTime now) const;
bool isEndless() const;
bool operator==(const TokenAttr &other) const;
};
struct UserAttr {
SERDE_STRUCT_FIELD(name, std::string());
SERDE_STRUCT_FIELD(token, Token{}); // the only one endless token
SERDE_STRUCT_FIELD(gid, Gid());
SERDE_STRUCT_FIELD(groups, std::vector<Gid>());
SERDE_STRUCT_FIELD(root, false);
SERDE_STRUCT_FIELD(tokens, std::vector<TokenAttr>()); // tokens with ttl
SERDE_STRUCT_FIELD(uid, Uid());
SERDE_STRUCT_FIELD(admin, false);
public:
Result<Void> validateToken(std::string_view token, UtcTime now) const;
Result<Void> addNewToken(std::string_view token,
UtcTime now,
bool invalidateExisted = true,
size_t maxTokens = 5,
Duration tokenLifetimeExtendLength = Duration(std::chrono::days(7)));
bool operator==(const UserAttr &other) const;
};
} // namespace hf3fs::flat
FMT_BEGIN_NAMESPACE
template <>
struct formatter<hf3fs::flat::UserInfo> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::flat::UserInfo &user, FormatContext &ctx) const {
auto groups = transformTo<std::vector>(std::span{user.groups.begin(), user.groups.size()},
[](hf3fs::flat::Gid gid) { return gid.toUnderType(); });
return format_to(ctx.out(),
"(uid {}, gid {}, groups ({}))",
user.uid.toUnderType(),
user.gid.toUnderType(),
fmt::join(groups.begin(), groups.end(), ","));
}
};
FMT_END_NAMESPACE
template <>
struct ::hf3fs::serde::SerdeMethod<::hf3fs::flat::Token> {
static auto serdeTo(const ::hf3fs::flat::Token &o) { return std::string_view{o}; }
static Result<::hf3fs::flat::Token> serdeFrom(std::string_view in) { return ::hf3fs::flat::Token{in}; }
static constexpr std::string_view serdeToReadable(const ::hf3fs::flat::Token &) { return "SECRET TOKEN"; };
};
template <>
struct hf3fs::serde::SerdeMethod<hf3fs::flat::UserInfo> {
static std::string serdeToReadable(hf3fs::flat::UserInfo user) { return fmt::format("{}", user); }
};

92
src/fbs/lib/Schema.h Normal file
View File

@@ -0,0 +1,92 @@
#pragma once
#include <sys/types.h>
#include <sys/vfs.h>
#include "common/serde/Serde.h"
#include "common/utils/Uuid.h"
namespace hf3fs::lib::agent {
struct ProcessInfo {
SERDE_STRUCT_TYPED_FIELD(int, pid, 0);
SERDE_STRUCT_TYPED_FIELD(int, ppid, 0);
};
struct TimeSpec {
SERDE_STRUCT_TYPED_FIELD(time_t, sec, 0);
SERDE_STRUCT_TYPED_FIELD(long, nsec, 0);
};
struct Stat {
SERDE_STRUCT_TYPED_FIELD(dev_t, dev, 0);
SERDE_STRUCT_TYPED_FIELD(ino_t, ino, 0);
SERDE_STRUCT_TYPED_FIELD(mode_t, mode, 0);
SERDE_STRUCT_TYPED_FIELD(nlink_t, nlink, 0);
SERDE_STRUCT_TYPED_FIELD(uid_t, uid, 0);
SERDE_STRUCT_TYPED_FIELD(gid_t, gid, 0);
SERDE_STRUCT_TYPED_FIELD(dev_t, rdev, 0);
SERDE_STRUCT_TYPED_FIELD(off_t, size, 0);
SERDE_STRUCT_TYPED_FIELD(blksize_t, blksize, 0);
SERDE_STRUCT_TYPED_FIELD(blkcnt_t, blocks, 0);
SERDE_STRUCT_TYPED_FIELD(TimeSpec, atim, TimeSpec{});
SERDE_STRUCT_TYPED_FIELD(TimeSpec, mtim, TimeSpec{});
SERDE_STRUCT_TYPED_FIELD(TimeSpec, ctim, TimeSpec{});
SERDE_STRUCT_TYPED_FIELD(std::optional<String>, ltarg, std::nullopt);
SERDE_STRUCT_TYPED_FIELD(Path, path, Path{"/"});
};
struct StatFs {
SERDE_STRUCT_TYPED_FIELD(__fsword_t, type, 0);
SERDE_STRUCT_TYPED_FIELD(__fsword_t, bsize, 0);
SERDE_STRUCT_TYPED_FIELD(fsblkcnt_t, blocks, 0);
SERDE_STRUCT_TYPED_FIELD(fsblkcnt_t, bfree, 0);
SERDE_STRUCT_TYPED_FIELD(fsblkcnt_t, bavail, 0);
SERDE_STRUCT_TYPED_FIELD(fsfilcnt_t, files, 0);
SERDE_STRUCT_TYPED_FIELD(fsfilcnt_t, ffree, 0);
SERDE_STRUCT_TYPED_FIELD(long, fsid, 0);
SERDE_STRUCT_TYPED_FIELD(__fsword_t, namelen, 0);
SERDE_STRUCT_TYPED_FIELD(__fsword_t, frsize, 0);
SERDE_STRUCT_TYPED_FIELD(__fsword_t, flags, 0);
};
struct DirEnt {
SERDE_STRUCT_TYPED_FIELD(unsigned char, type, 0);
SERDE_STRUCT_TYPED_FIELD(String, name, "");
};
struct DirEntList {
SERDE_STRUCT_TYPED_FIELD(std::vector<DirEnt>, dents, std::vector<DirEnt>{});
SERDE_STRUCT_TYPED_FIELD(bool, hasMore, false);
};
struct SharedFileHandles {
SERDE_STRUCT_TYPED_FIELD(std::vector<String>, fhs, std::vector<String>{});
};
struct AllocatedIov {
SERDE_STRUCT_TYPED_FIELD(int, iovDesc, 0);
SERDE_STRUCT_TYPED_FIELD(Path, path, Path{});
SERDE_STRUCT_TYPED_FIELD(long, offInBuf, 0);
SERDE_STRUCT_TYPED_FIELD(ulong, bytes, 0);
SERDE_STRUCT_TYPED_FIELD(Uuid, id, Uuid{});
SERDE_STRUCT_TYPED_FIELD(ulong, blockSize, 0);
};
struct IoInfo {
SERDE_STRUCT_TYPED_FIELD(int, iovDesc, 0);
SERDE_STRUCT_TYPED_FIELD(Uuid, iovId, Uuid{});
SERDE_STRUCT_TYPED_FIELD(long, iovOff, 0);
SERDE_STRUCT_TYPED_FIELD(ulong, iovLen, 0);
SERDE_STRUCT_TYPED_FIELD(int, fd, 0);
SERDE_STRUCT_TYPED_FIELD(long, off, 0);
SERDE_STRUCT_TYPED_FIELD(ulong, readahead, 0);
SERDE_STRUCT_TYPED_FIELD(uint16_t, track, 0);
};
struct PioVRes {
SERDE_STRUCT_TYPED_FIELD(std::vector<long>, res, std::vector<long>{});
SERDE_STRUCT_TYPED_FIELD(std::vector<long>, offs, std::vector<long>{});
};
} // namespace hf3fs::lib::agent

221
src/fbs/lib/Service.h Normal file
View File

@@ -0,0 +1,221 @@
#pragma once
#include "Schema.h"
#include "fbs/core/user/User.h"
#include "fbs/meta/Common.h"
namespace hf3fs::lib::agent {
using hf3fs::Result;
template <typename ValType>
struct OneValRsp {
SERDE_STRUCT_TYPED_FIELD(ValType, val, ValType{});
};
using EmptyRsp = OneValRsp<Void>;
struct ProcUser {
SERDE_STRUCT_TYPED_FIELD(ProcessInfo, proc, ProcessInfo{});
SERDE_STRUCT_TYPED_FIELD(flat::UserInfo, userInfo, flat::UserInfo{});
};
template <bool OptionalPath>
struct PathAt {
using PathType = std::conditional_t<OptionalPath, std::optional<Path>, Path>;
SERDE_STRUCT_TYPED_FIELD(int, dirfd, 0);
SERDE_STRUCT_TYPED_FIELD(PathType, path, PathType{});
};
struct StatFsReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<true>, pathAt, PathAt<true>{});
};
using StatFsRsp = OneValRsp<StatFs>;
struct StatReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<true>, pathAt, PathAt<true>{});
SERDE_STRUCT_TYPED_FIELD(bool, followLastSymlink, false);
SERDE_STRUCT_TYPED_FIELD(bool, ignoreCache, false);
};
using StatRsp = OneValRsp<Stat>;
struct OpenReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, pathAt, PathAt<false>{});
SERDE_STRUCT_TYPED_FIELD(int, flags, 0);
SERDE_STRUCT_TYPED_FIELD(meta::Permission, mode, meta::Permission{});
};
using OpenRsp = OneValRsp<uint>;
struct CloseReq {
SERDE_STRUCT_TYPED_FIELD(ProcessInfo, proc, ProcessInfo{});
SERDE_STRUCT_TYPED_FIELD(int, fd, 0);
};
using CloseRsp = EmptyRsp;
struct SetAttrsReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<true>, pathAt, PathAt<true>{});
SERDE_STRUCT_TYPED_FIELD(bool, followLastSymlink, false);
SERDE_STRUCT_TYPED_FIELD(std::optional<flat::Uid>, uid, std::nullopt);
SERDE_STRUCT_TYPED_FIELD(std::optional<flat::Gid>, gid, std::nullopt);
SERDE_STRUCT_TYPED_FIELD(std::optional<meta::Permission>, perm, std::nullopt);
SERDE_STRUCT_TYPED_FIELD(std::optional<UtcTime>, atime, std::nullopt);
SERDE_STRUCT_TYPED_FIELD(std::optional<UtcTime>, mtime, std::nullopt);
};
using SetAttrsRsp = EmptyRsp;
struct DupReq {
SERDE_STRUCT_TYPED_FIELD(ProcessInfo, proc, ProcessInfo{});
SERDE_STRUCT_TYPED_FIELD(int, srcfd, 0);
SERDE_STRUCT_TYPED_FIELD(int, dstfd, 0);
SERDE_STRUCT_TYPED_FIELD(int, flags, 0);
};
using DupRsp = OneValRsp<uint>;
struct MkdirsReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, pathAt, PathAt<false>{});
SERDE_STRUCT_TYPED_FIELD(meta::Permission, perm, meta::Permission{0});
SERDE_STRUCT_TYPED_FIELD(bool, recursive, false);
};
using MkdirsRsp = EmptyRsp;
struct SymlinkReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, pathAt, PathAt<false>{});
SERDE_STRUCT_TYPED_FIELD(Path, target, Path{});
};
using SymlinkRsp = EmptyRsp;
struct RemoveReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, pathAt, PathAt<false>{});
SERDE_STRUCT_TYPED_FIELD(bool, recursive, false);
SERDE_STRUCT_TYPED_FIELD(std::optional<bool>, isDir, std::nullopt);
};
using RemoveRsp = EmptyRsp;
struct RenameReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, srcPathAt, PathAt<false>{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, dstPathAt, PathAt<false>{});
};
using RenameRsp = EmptyRsp;
struct ListReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<true>, pathAt, PathAt<true>{});
SERDE_STRUCT_TYPED_FIELD(std::optional<String>, prev, std::nullopt);
};
using ListRsp = OneValRsp<DirEntList>;
struct GetRealPathReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, pathAt, PathAt<false>{});
SERDE_STRUCT_TYPED_FIELD(bool, absolute, false);
};
using GetRealPathRsp = OneValRsp<Path>;
struct FsyncReq {
SERDE_STRUCT_TYPED_FIELD(ProcessInfo, proc, ProcessInfo{});
SERDE_STRUCT_TYPED_FIELD(int, fd, 0);
};
using FsyncRsp = EmptyRsp;
struct FtruncateReq {
SERDE_STRUCT_TYPED_FIELD(ProcessInfo, proc, ProcessInfo{});
SERDE_STRUCT_TYPED_FIELD(int, fd, 0);
SERDE_STRUCT_TYPED_FIELD(long, length, 0);
};
using FtruncateRsp = EmptyRsp;
struct LinkReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, oldPathAt, PathAt<false>{});
SERDE_STRUCT_TYPED_FIELD(PathAt<false>, newPathAt, PathAt<false>{});
SERDE_STRUCT_TYPED_FIELD(bool, followLastSymlink, false);
};
using LinkRsp = EmptyRsp;
struct IovAllocReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(ulong, bytes, 0);
SERDE_STRUCT_TYPED_FIELD(std::optional<int>, numa, std::nullopt);
SERDE_STRUCT_TYPED_FIELD(bool, global, false);
SERDE_STRUCT_TYPED_FIELD(ulong, blockSize, 0);
};
using IovAllocRsp = OneValRsp<AllocatedIov>;
struct IovFreeReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(int, iovDesc, 0);
SERDE_STRUCT_TYPED_FIELD(Uuid, iovId, Uuid{});
SERDE_STRUCT_TYPED_FIELD(long, offInBuf, 0);
SERDE_STRUCT_TYPED_FIELD(ulong, bytes, 0);
};
using IovFreeRsp = EmptyRsp;
struct SeekInFileReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(int, fd, 0);
SERDE_STRUCT_TYPED_FIELD(long, offset, 0);
SERDE_STRUCT_TYPED_FIELD(int, whence, 0);
SERDE_STRUCT_TYPED_FIELD(ulong, readahead, 0);
};
using SeekInFileRsp = OneValRsp<long>;
struct PioVReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(bool, read, false);
SERDE_STRUCT_TYPED_FIELD(std::vector<IoInfo>, iov, std::vector<IoInfo>{});
SERDE_STRUCT_TYPED_FIELD(bool, updateOffset, false);
};
using PioVRsp = OneValRsp<PioVRes>;
struct SharedFileHandlesReq {
SERDE_STRUCT_TYPED_FIELD(ProcessInfo, proc, ProcessInfo{});
SERDE_STRUCT_TYPED_FIELD(std::vector<int>, fds, std::vector<int>{});
};
using SharedFileHandlesRsp = OneValRsp<String>;
struct OpenWithFileHandlesReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(String, fhs, String{});
};
using OpenWithFileHandlesRsp = OneValRsp<std::vector<int>>;
struct OpenIovecHandleReq {
SERDE_STRUCT_TYPED_FIELD(ProcUser, procUser, ProcUser{});
SERDE_STRUCT_TYPED_FIELD(AllocatedIov, aiov, AllocatedIov{});
};
using OpenIovecHandleRsp = OneValRsp<int>;
SERDE_SERVICE(ClientAgentSerde, 10) {
SERDE_SERVICE_METHOD(statFs, 1, StatFsReq, StatFsRsp);
SERDE_SERVICE_METHOD(stat, 2, StatReq, StatRsp);
SERDE_SERVICE_METHOD(open, 3, OpenReq, OpenRsp);
SERDE_SERVICE_METHOD(close, 4, CloseReq, CloseRsp);
SERDE_SERVICE_METHOD(setAttrs, 5, SetAttrsReq, SetAttrsRsp);
SERDE_SERVICE_METHOD(dup, 6, DupReq, DupRsp);
SERDE_SERVICE_METHOD(mkdirs, 7, MkdirsReq, MkdirsRsp);
SERDE_SERVICE_METHOD(symlink, 8, SymlinkReq, SymlinkRsp);
SERDE_SERVICE_METHOD(remove, 9, RemoveReq, RemoveRsp);
SERDE_SERVICE_METHOD(rename, 10, RenameReq, RenameRsp);
SERDE_SERVICE_METHOD(list, 11, ListReq, ListRsp);
SERDE_SERVICE_METHOD(getRealPath, 12, GetRealPathReq, GetRealPathRsp);
SERDE_SERVICE_METHOD(fsync, 13, FsyncReq, FsyncRsp);
SERDE_SERVICE_METHOD(ftruncate, 14, FtruncateReq, FtruncateRsp);
SERDE_SERVICE_METHOD(link, 15, LinkReq, LinkRsp);
SERDE_SERVICE_METHOD(iovalloc, 101, IovAllocReq, IovAllocRsp);
SERDE_SERVICE_METHOD(iovfree, 102, IovFreeReq, IovFreeRsp);
SERDE_SERVICE_METHOD(seekInFile, 103, SeekInFileReq, SeekInFileRsp);
SERDE_SERVICE_METHOD(piov, 104, PioVReq, PioVRsp);
SERDE_SERVICE_METHOD(sharedFileHandles, 201, SharedFileHandlesReq, SharedFileHandlesRsp);
SERDE_SERVICE_METHOD(openWithFileHandles, 202, OpenWithFileHandlesReq, OpenWithFileHandlesRsp);
SERDE_SERVICE_METHOD(openIovecHandle, 203, OpenIovecHandleReq, OpenIovecHandleRsp);
};
} // namespace hf3fs::lib::agent

39
src/fbs/macros/IStub.h Normal file
View File

@@ -0,0 +1,39 @@
#include "common/utils/Coroutine.h"
#ifdef DEFINE_FBS_SERVICE
#undef DEFINE_FBS_SERVICE
#endif
#define DEFINE_FBS_SERVICE(name, fbsns) \
class I##name##ServiceStub { \
public: \
using InterfaceType = I##name##ServiceStub; \
\
virtual ~I##name##ServiceStub() = default;
#ifdef FINISH_FBS_SERVICE
#undef FINISH_FBS_SERVICE
#endif
#define FINISH_FBS_SERVICE(name, fbsns) }
#ifdef DEFINE_FBS_SERVICE_METHOD
#undef DEFINE_FBS_SERVICE_METHOD
#endif
#define DEFINE_FBS_SERVICE_METHOD(svc, name, reqtype, rsptype, flatns) \
virtual CoTryTask<flatns::rsptype> name(const flatns::reqtype &req) = 0
#ifdef DEFINE_FBS_SERVICE_METHOD_VOID
#undef DEFINE_FBS_SERVICE_METHOD_VOID
#endif
#define DEFINE_FBS_SERVICE_METHOD_VOID(svc, name, reqtype, rsptype, flatns) \
DEFINE_FBS_SERVICE_METHOD(svc, name, reqtype, rsptype, flatns)
#ifdef DEFINE_FBS_SERVICE_METHOD_RETURNS
#undef DEFINE_FBS_SERVICE_METHOD_RETURNS
#endif
#define DEFINE_FBS_SERVICE_METHOD_RETURNS(svc, name, reqtype, rsptype, flatns, returns, rtype) \
DEFINE_FBS_SERVICE_METHOD(svc, name, reqtype, rsptype, flatns)

View File

@@ -0,0 +1,39 @@
#include "common/utils/Result.h"
#ifdef DEFINE_FBS_SERVICE
#undef DEFINE_FBS_SERVICE
#endif
#define DEFINE_FBS_SERVICE(name, fbsns) \
class I##name##ServiceStub { \
public: \
using InterfaceType = I##name##ServiceStub; \
\
virtual ~I##name##ServiceStub() = default;
#ifdef FINISH_FBS_SERVICE
#undef FINISH_FBS_SERVICE
#endif
#define FINISH_FBS_SERVICE(name, fbsns) }
#ifdef DEFINE_FBS_SERVICE_METHOD
#undef DEFINE_FBS_SERVICE_METHOD
#endif
#define DEFINE_FBS_SERVICE_METHOD(svc, name, reqtype, rsptype, flatns) \
virtual hf3fs::Result<flatns::rsptype> name(const flatns::reqtype &req) = 0
#ifdef DEFINE_FBS_SERVICE_METHOD_VOID
#undef DEFINE_FBS_SERVICE_METHOD_VOID
#endif
#define DEFINE_FBS_SERVICE_METHOD_VOID(svc, name, reqtype, rsptype, flatns) \
virtual hf3fs::Result<Void> name(const flatns::reqtype &req) = 0
#ifdef DEFINE_FBS_SERVICE_METHOD_RETURNS
#undef DEFINE_FBS_SERVICE_METHOD_RETURNS
#endif
#define DEFINE_FBS_SERVICE_METHOD_RETURNS(svc, name, reqtype, rsptype, flatns, returns, rtype) \
virtual hf3fs::Result<rtype> name(const flatns::reqtype &req) = 0

View File

@@ -0,0 +1,8 @@
#ifndef DEFINE_SERDE_SERVICE_METHOD_FULL
#define DEFINE_SERDE_SERVICE_METHOD_FULL(ServiceName, methodName, MethodName, MethodId, RequestType, ResponseType)
#endif
#ifndef DEFINE_SERDE_SERVICE_METHOD
#define DEFINE_SERDE_SERVICE_METHOD(ServiceName, methodName, MethodName, MethodId) \
DEFINE_SERDE_SERVICE_METHOD_FULL(ServiceName, methodName, MethodName, MethodId, MethodName##Req, MethodName##Rsp)
#endif

46
src/fbs/macros/Stub.h Normal file
View File

@@ -0,0 +1,46 @@
#include "common/utils/Coroutine.h"
#include "common/utils/Result.h"
#ifdef DEFINE_FBS_SERVICE
#undef DEFINE_FBS_SERVICE
#endif
#define DEFINE_FBS_SERVICE(name, fbsns) \
template <typename Ctx> \
class name##ServiceStub : public I##name##ServiceStub { \
public: \
using ContextType = Ctx; \
using name##Client = ::fbsns::name##Client<Ctx>; \
\
explicit name##ServiceStub(Ctx ctx) \
: client_(std::move(ctx)) {}
#ifdef FINISH_FBS_SERVICE
#undef FINISH_FBS_SERVICE
#endif
#define FINISH_FBS_SERVICE(name, fbsns) \
private: \
name##Client client_; \
}
#ifdef DEFINE_FBS_SERVICE_METHOD
#undef DEFINE_FBS_SERVICE_METHOD
#endif
#define DEFINE_FBS_SERVICE_METHOD(svc, name, reqtype, rsptype, flatns) \
CoTryTask<flatns::rsptype> name(const flatns::reqtype &req) override
#ifdef DEFINE_FBS_SERVICE_METHOD_VOID
#undef DEFINE_FBS_SERVICE_METHOD_VOID
#endif
#define DEFINE_FBS_SERVICE_METHOD_VOID(svc, name, reqtype, rsptype, flatns) \
DEFINE_FBS_SERVICE_METHOD(svc, name, reqtype, rsptype, flatns)
#ifdef DEFINE_FBS_SERVICE_METHOD_RETURNS
#undef DEFINE_FBS_SERVICE_METHOD_RETURNS
#endif
#define DEFINE_FBS_SERVICE_METHOD_RETURNS(svc, name, reqtype, rsptype, flatns, returns, rtype) \
DEFINE_FBS_SERVICE_METHOD(svc, name, reqtype, rsptype, flatns)

45
src/fbs/macros/SyncStub.h Normal file
View File

@@ -0,0 +1,45 @@
#include "common/utils/Result.h"
#ifdef DEFINE_FBS_SERVICE
#undef DEFINE_FBS_SERVICE
#endif
#define DEFINE_FBS_SERVICE(name, fbsns) \
template <typename Ctx> \
class name##ServiceStub : public I##name##ServiceStub { \
public: \
using ContextType = Ctx; \
using name##Client = ::fbsns::name##Client<Ctx>; \
\
explicit name##ServiceStub(Ctx ctx) \
: client_(std::move(ctx)) {}
#ifdef FINISH_FBS_SERVICE
#undef FINISH_FBS_SERVICE
#endif
#define FINISH_FBS_SERVICE(name, fbsns) \
private: \
name##Client client_; \
}
#ifdef DEFINE_FBS_SERVICE_METHOD
#undef DEFINE_FBS_SERVICE_METHOD
#endif
#define DEFINE_FBS_SERVICE_METHOD(svc, name, reqtype, rsptype, flatns) \
hf3fs::Result<flatns::rsptype> name(const flatns::reqtype &req) override
#ifdef DEFINE_FBS_SERVICE_METHOD_VOID
#undef DEFINE_FBS_SERVICE_METHOD_VOID
#endif
#define DEFINE_FBS_SERVICE_METHOD_VOID(svc, name, reqtype, rsptype, flatns) \
hf3fs::Result<Void> name(const flatns::reqtype &req) override
#ifdef DEFINE_FBS_SERVICE_METHOD_RETURNS
#undef DEFINE_FBS_SERVICE_METHOD_RETURNS
#endif
#define DEFINE_FBS_SERVICE_METHOD_RETURNS(svc, name, reqtype, rsptype, flatns, returns, rtype) \
hf3fs::Result<rtype> name(const flatns::reqtype &req) override

7
src/fbs/macros/Undef.h Normal file
View File

@@ -0,0 +1,7 @@
#ifdef DEFINE_SERDE_SERVICE_METHOD
#undef DEFINE_SERDE_SERVICE_METHOD
#endif
#ifdef DEFINE_SERDE_SERVICE_METHOD_FULL
#undef DEFINE_SERDE_SERVICE_METHOD_FULL
#endif

View File

@@ -0,0 +1 @@
target_add_lib(meta-fbs mgmtd-fbs core-user-fbs storage-client)

285
src/fbs/meta/Common.h Normal file
View File

@@ -0,0 +1,285 @@
#pragma once
#include <array>
#include <boost/detail/bitmask.hpp>
#include <cstdint>
#include <fcntl.h>
#include <optional>
#include <scn/scn.h>
#include <scn/tuple_return.h>
#include <string_view>
#include <utility>
#include <variant>
#include "common/app/ClientId.h"
#include "common/serde/Serde.h"
#include "common/serde/SerdeComparisons.h"
#include "common/utils/Path.h"
#include "common/utils/Result.h"
#include "common/utils/StrongType.h"
#include "common/utils/UtcTime.h"
#include "common/utils/UtcTimeSerde.h"
#include "fbs/core/user/User.h"
#define VALID Void()
#define INVALID(str) makeError(StatusCode::kInvalidArg, str)
#define FS_CHAIN_ALLOCATION_FL FS_INDEX_FL /* reuse attr 'I', this directory has it't own chain allocation counter */
#define FS_NEW_CHUNK_ENGINE FS_SECRM_FL /* reuse attr 's', files under this directory will use new chunk engine */
#define FS_FL_SUPPORTED (FS_IMMUTABLE_FL | FS_CHAIN_ALLOCATION_FL | FS_NEW_CHUNK_ENGINE | FS_HUGE_FILE_FL)
#define FS_FL_INHERITABLE (FS_CHAIN_ALLOCATION_FL | FS_NEW_CHUNK_ENGINE)
namespace hf3fs::meta {
using flat::Gid;
using flat::Permission;
using flat::Uid;
using flat::UserAttr;
using flat::UserInfo;
// see: ioctl_iflags
STRONG_TYPEDEF(uint32_t, IFlags);
struct SessionInfo {
SERDE_STRUCT_FIELD(client, ClientId::zero());
SERDE_STRUCT_FIELD(session, Uuid::zero());
public:
SessionInfo() = default;
SessionInfo(ClientId client, Uuid session)
: client(client),
session(session) {}
constexpr bool valid() const { return client != ClientId::zero() && session != Uuid::zero(); }
constexpr operator bool() const { return valid(); }
};
enum AccessType {
EXEC = 1,
WRITE = 2,
READ = 4,
};
BOOST_BITMASK(AccessType)
template <typename C, typename T>
class BitFlags {
public:
using Self = C;
constexpr BitFlags(T val = 0)
: val_(val) {}
using is_serde_copyable = void;
T mask(T m) const { return val_ & m; }
bool contains(T bits) const { return mask(bits) == bits; }
void set(T bits) { val_ |= bits; }
void clear(T bits) { val_ &= (~bits); }
T toUnderType() const { return val_; }
operator const T &() const { return val_; }
operator T &() { return val_; }
Self operator|(const T other) const { return Self(val_ | other); }
std::string serdeToReadable() const { return fmt::format("{:x}", val_); }
static Result<Self> serdeFromReadable(std::string_view str) {
auto [r, v] = scn::scan_tuple<T>(str, "{:x}");
if (!r)
return makeError(StatusCode::kDataCorruption,
fmt::format("SCN({}): {}", magic_enum::enum_name(r.error().code()), r.error().msg()));
return Self(v);
}
private:
T val_;
};
class OpenFlags : public BitFlags<OpenFlags, int32_t> {
public:
using Base = BitFlags<OpenFlags, int32_t>;
using Base::Base;
AccessType accessType() const {
switch (mask(O_ACCMODE)) {
case O_RDONLY:
return AccessType::READ;
case O_WRONLY:
return AccessType::WRITE;
case O_RDWR:
default:
return AccessType::READ | AccessType::WRITE;
}
}
operator AccessType() const { return accessType(); }
Result<Void> valid() const {
if (contains(O_DIRECTORY)) {
if (accessType() != AccessType::READ) return makeError(StatusCode::kInvalidArg, "O_DIRECTORY & WRITE");
if (contains(O_TRUNC)) return makeError(StatusCode::kInvalidArg, "O_DIRECTORY & O_TRUNC");
}
return Void{};
}
};
class AtFlags : public BitFlags<AtFlags, int32_t> {
public:
explicit AtFlags(int32_t val = 0)
: BitFlags<AtFlags, int32_t>(val) {}
Result<Void> valid() const { return Void{}; }
bool followLastSymlink() const { return contains(AT_SYMLINK_FOLLOW) && !contains(AT_SYMLINK_NOFOLLOW); }
};
class InodeId {
public:
// Use little endian form as key in FoundationDB, it helps to avoid hot spot
using Key = std::array<uint8_t, 8>;
using is_serde_copyable = void;
static constexpr size_t trees() { return 2; }
static constexpr InodeId root() { return InodeId(0); }
static constexpr InodeId gcRoot() { return InodeId(1); }
// InodeId: 0 ~ 0x01ffffffffffffff
// If InodeId starts with 0x01: use new 3FS Chunk Engine
static constexpr uint64_t kNewChunkEngineMask = (1ull << 56);
static_assert(kNewChunkEngineMask == 0x0100000000000000ull);
static constexpr InodeId withNewChunkEngine(InodeId orig) { return InodeId(orig.u64() | kNewChunkEngineMask); }
static constexpr InodeId normalMax() { return InodeId::withNewChunkEngine(InodeId((1ull << 56) - 1)); }
// special InodeId used by Client
// 3fs-virt InodeId: 0xfffffffffffffffe
static constexpr InodeId virt() { return InodeId(uint64_t(-2)); }
// iov InodeId range [0xffffffff7ffe0002, 0xffffffff7fff0001]
static constexpr int iovIidStart = 65535;
static constexpr InodeId iovDir() { return InodeId((uint64_t) - (1ull << 31)); }
static constexpr InodeId iov(int iovd) { return InodeId(iovDir().u64() - iovIidStart - iovd); }
// 3fs-virt/rm-rf: 0xfffffffffffffffd
static constexpr InodeId rmRf() { return InodeId(-3); }
// temporary InodeId used by rm-rf or mv symlink: [0xfdffffe700000001, 0xffffffe700000000]
static constexpr InodeId virtTemporary(InodeId toRemove) { return InodeId(-(100ull << 30) - toRemove.u64()); }
// 0xffffffff00000000
static constexpr InodeId getConf() { return InodeId(-((uint64_t)2 << 31)); }
// 0xfffffffe80000000
static constexpr InodeId setConf() { return InodeId(-((uint64_t)3 << 31)); }
explicit constexpr InodeId(uint64_t val = 0)
: val_(val) {}
// InodeId &operator=(uint64_t val) {
// val_ = val;
// return *this;
// }
Key packKey() const {
auto le = folly::Endian::little(val_);
return folly::bit_cast<Key>(le);
}
static InodeId unpackKey(const Key &key) {
auto le = folly::bit_cast<uint64_t>(key);
return InodeId(folly::Endian::little(le));
}
std::string toHexString() const { return fmt::format("0x{:016x}", val_); }
std::string serdeToReadable() const { return toHexString(); }
operator folly::dynamic() const { return toHexString(); }
constexpr uint64_t u64() const { return val_; }
// constexpr operator uint64_t() const { return u64(); }
constexpr auto operator<=>(const InodeId &o) const { return this->val_ <=> o.val_; }
constexpr auto operator==(const InodeId &o) const { return this->val_ == o.val_; }
bool isTreeRoot() const { return *this == root() || *this == gcRoot(); }
bool useNewChunkEngine() const { return u64() & kNewChunkEngineMask; }
private:
uint64_t val_;
};
// check 3fs-virt InodeIds
static_assert(InodeId::normalMax().u64() == 0x01ffffffffffffffull);
static_assert(InodeId::virt().u64() == 0xfffffffffffffffeull);
static_assert(InodeId::iovDir().u64() == 0xffffffff80000000ull);
static_assert(InodeId::iov(0).u64() == 0xffffffff7fff0001ull);
static_assert(InodeId::iov(65535).u64() == 0xffffffff7ffe0002ull);
static_assert(InodeId::rmRf().u64() == 0xfffffffffffffffdull);
static_assert(InodeId::virtTemporary(InodeId(0)).u64() == 0xffffffe700000000ull);
static_assert(InodeId::virtTemporary(InodeId(0x1ffffffffffffff)).u64() == 0xfdffffe700000001ull);
static_assert(InodeId::getConf().u64() == 0xffffffff00000000ull);
static_assert(InodeId::setConf().u64() == 0xfffffffe80000000ull);
// check 3fs-virt InodeIds won't conflict with each other and normal InodeIds
static constexpr auto spacialInodeRanges =
std::to_array({InodeId::normalMax(),
InodeId(0xfd00000000000000ull), // all virt must start with 0xf
InodeId::virtTemporary(InodeId::normalMax()),
InodeId::virtTemporary(InodeId(0)),
InodeId::setConf(),
InodeId::getConf(),
InodeId::iov(65535),
InodeId::iov(0),
InodeId::iovDir(),
InodeId::rmRf(),
InodeId::virt()});
constexpr inline bool checkSpecialInode() {
for (uint64_t i = 1; i < spacialInodeRanges.size(); i++) {
if (spacialInodeRanges[i] <= spacialInodeRanges[i - 1]) {
return false;
}
}
return true;
}
static_assert(checkSpecialInode());
struct PathAt {
SERDE_STRUCT_FIELD(parent, InodeId(0));
SERDE_STRUCT_FIELD(path, std::optional<Path>());
public:
PathAt(InodeId parent = InodeId::root())
: PathAt(parent, std::nullopt) {}
PathAt(Path path)
: PathAt(InodeId::root(), std::move(path)) {}
PathAt(std::string path)
: PathAt(Path(path)) {}
PathAt(const char *p)
: PathAt(Path(p)) {}
PathAt(InodeId parent, std::optional<Path> path)
: parent(parent),
path(std::move(path)) {}
Result<Void> validForCreate() const {
if (!path.has_value() || path->empty()) return INVALID("path not set");
if (!path->has_filename()) return INVALID("doesn't have filename");
if (path->filename_is_dot()) return INVALID("filename is .");
if (path->filename_is_dot_dot()) return INVALID("filename is ..");
if (path->filename() == "/") return INVALID("filename is /");
return VALID;
}
bool operator==(const PathAt &o) const { return serde::equals(*this, o); }
};
} // namespace hf3fs::meta
template <>
struct std::hash<hf3fs::meta::InodeId> {
size_t operator()(const hf3fs::meta::InodeId &id) const { return robin_hood::hash_int(id.u64()); }
};
template <>
struct hf3fs::serde::SerdeMethod<hf3fs::meta::InodeId> {
static constexpr auto serdeTo(const hf3fs::meta::InodeId &id) { return id.u64(); }
static Result<hf3fs::meta::InodeId> serdeFrom(uint64_t id) { return hf3fs::meta::InodeId(id); }
};
FMT_BEGIN_NAMESPACE
template <>
struct formatter<hf3fs::meta::InodeId> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::meta::InodeId &id, FormatContext &ctx) const {
return fmt::format_to(ctx.out(), "0x{:016x}", id.u64());
}
};
FMT_END_NAMESPACE

View File

@@ -0,0 +1,350 @@
#include "FileOperation.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <folly/Math.h>
#include <folly/logging/xlog.h>
#include <limits>
#include <type_traits>
#include "common/utils/Result.h"
#include "fmt/core.h"
#define RECORD_LATENCY(name) \
auto FB_ANONYMOUS_VARIABLE(guard) = folly::makeGuard([this, begin = std::chrono::steady_clock::now()] { \
if (recorder_.has_value()) { \
recorder_->get().name.addSample(std::chrono::steady_clock::now() - begin); \
} \
})
#define ADD_SAMPLE(name, count) \
if (recorder_.has_value()) { \
recorder_->get().name.addSample(count); \
}
#define ADD_FAILED(name, code) \
if (recorder_.has_value()) { \
auto tag = folly::to<std::string>(code); \
recorder_->get().name.addSample(1, {{"tag", tag}}); \
}
#define CHECK_FILE(inode) \
do { \
if (!inode.isFile()) { \
co_return makeError(MetaCode::kNotFile, "Not file"); \
} \
if (auto valid = inode.asFile().layout.valid(false); valid.hasError()) { \
XLOGF(CRITICAL, "File {} has a invalid layout {}, error {}!", inode.id, inode.asFile().layout, valid.error()); \
CO_RETURN_ERROR(valid); \
} \
} while (0)
namespace hf3fs::meta {
CoTryTask<FileOperation::QueryResult> FileOperation::queryChunks(bool queryTotalChunk, bool dynStripe) {
auto chains = co_await queryChunksByChain(queryTotalChunk, dynStripe);
CO_RETURN_ON_ERROR(chains);
QueryResult result;
for (auto iter = chains->begin(); iter != chains->end(); iter++) {
auto cresult = iter->second;
XLOGF_IF(DFATAL,
(result.lastChunk == cresult.lastChunk && result.lastChunk != 0),
"inode {}, {} == {}",
inode_.id,
result.lastChunk,
cresult.lastChunk);
XLOGF(DBG, "queryChunks for {}, chain {}, length {}", inode_.id, iter->first, iter->second.length);
if (iter == chains->begin() || result.length < cresult.length) {
result.length = cresult.length;
result.lastChunk = cresult.lastChunk;
result.lastChunkLen = cresult.lastChunkLen;
}
result.totalChunkLen += cresult.totalChunkLen;
result.totalNumChunks += cresult.totalNumChunks;
}
auto length = result.lastChunk * inode_.asFile().layout.chunkSize + result.lastChunkLen;
static_assert(std::is_same_v<decltype(length), uint64_t>);
XLOGF_IF(DFATAL, length != result.length, "inode {}, {} != {}", inode_.id, length, result.length);
if (length != result.length) {
co_return makeError(MetaCode::kFoundBug,
fmt::format("inode {}, length {} != {}", inode_.id, length, result.length));
}
co_return result;
}
CoTryTask<std::map<flat::ChainId, FileOperation::QueryResult>> FileOperation::queryChunksByChain(bool queryTotalChunk,
bool dynStripe) {
CHECK_FILE(inode_);
XLOGF(DBG, "query length of {}", inode_.id);
auto chunkRange = ChunkId::range(inode_.id);
CO_RETURN_AND_LOG_ON_ERROR(chunkRange);
auto [begin, end] = *chunkRange;
std::vector<storage::client::QueryLastChunkOp> queries;
auto maxStripe = inode_.asFile().layout.stripeSize;
if (dynStripe && inode_.asFile().dynStripe) {
maxStripe = std::min(inode_.asFile().dynStripe, maxStripe);
XLOGF(DBG, "file {}, dynStripe {}", inode_, inode_.asFile().dynStripe);
}
queries.reserve(maxStripe);
const auto &layout = inode_.asFile().layout;
auto chainIndexList = layout.getChainIndexList();
for (size_t stripe = 0; stripe < maxStripe; stripe++) {
auto chainIndex = chainIndexList[stripe];
auto ref = flat::ChainRef{layout.tableId, layout.tableVersion, chainIndex};
auto cid = routing_.getChainId(ref);
if (!cid) {
XLOGF(ERR, "Failed to get ChainId by {}", ref);
co_return makeError(MgmtdClientCode::kRoutingInfoNotReady, fmt::format("Failed to get ChainId by {}", ref));
}
auto query = storage_.createQueryOp(storage::ChainId(*cid),
storage::ChunkId(begin),
storage::ChunkId(end),
queryTotalChunk ? UINT32_MAX : 1);
queries.push_back(std::move(query));
}
assert(queries.size() <= layout.stripeSize);
auto guard = folly::makeGuard([this, queryTotalChunk, begin = std::chrono::steady_clock::now()] {
if (recorder_.has_value()) {
auto duration = std::chrono::steady_clock::now() - begin;
if (queryTotalChunk) {
recorder_->get().queryTotalChunkLatency.addSample(duration);
} else {
recorder_->get().queryLastChunkLatency.addSample(duration);
}
}
});
auto queryResult = co_await storage_.queryLastChunk(queries, userInfo_);
std::map<flat::ChainId, FileOperation::QueryResult> chunks;
for (auto &query : queries) {
auto &result = query.result;
if (UNLIKELY(result.statusCode.hasError())) {
if (result.statusCode.error().code() == StorageClientCode::kChunkNotFound) {
continue;
}
XLOGF(ERR,
"Failed to quertLastChunk from chain {}, err {}",
flat::ChainId(query.routingTarget.chainId),
result.statusCode.error());
ADD_FAILED(queryChunksFailed, result.statusCode.error().code());
co_return makeError(result.statusCode.error());
}
if (result.totalNumChunks == 0) {
continue;
}
auto chunkId = ChunkId::unpack(result.lastChunkId.data());
if (!chunkId) {
XLOGF(CRITICAL,
"Failed to unpack chunkId queried from storage, {} {}.",
result.lastChunkId.describe(),
result.lastChunkLen);
co_return makeError(StatusCode::kDataCorruption, "Failed to unpack chunkId queried from storage");
}
if (chunkId.inode() != inode_.id) {
XLOGF(CRITICAL,
"InodeId of chunk {} queried from storage not match, {} != {}",
result.lastChunkId.describe(),
chunkId.inode(),
inode_.id);
co_return makeError(StatusCode::kDataCorruption, "InodeId of chunk queried from storage not match");
}
if (dynStripe && maxStripe < inode_.asFile().layout.stripeSize && chunkId.chunk() >= maxStripe) {
// chunk >= maxStripe, this means that another process is writing file, let caller retry again
ADD_FAILED(queryChunksFailed, MetaCode::kBusy);
auto msg = fmt::format("inode {}, dynStripe {}, found chunk {}, retry", inode_, maxStripe, chunkId);
XLOG(WARN, msg);
co_return makeError(MetaCode::kBusy, std::move(msg));
}
chunks[query.routingTarget.chainId] = {
.length = chunkId.chunk() * inode_.asFile().layout.chunkSize + result.lastChunkLen,
.lastChunk = chunkId.chunk(),
.lastChunkLen = result.lastChunkLen,
.totalChunkLen = result.totalChunkLen,
.totalNumChunks = result.totalNumChunks};
}
co_return chunks;
}
CoTryTask<std::pair<uint32_t, bool>> FileOperation::removeChunks(size_t targetLength,
size_t removeChunksBatchSize,
bool dynStripe,
storage::client::RetryOptions retry) {
CHECK_FILE(inode_);
std::vector<storage::client::RemoveChunksOp> removeOps;
auto maxStripe = inode_.asFile().layout.stripeSize;
if (dynStripe && inode_.asFile().dynStripe) {
maxStripe = std::min(inode_.asFile().dynStripe, maxStripe);
XLOGF(DBG, "file {}, dynStripe {}", inode_, inode_.asFile().dynStripe);
}
removeOps.reserve(maxStripe);
const auto &layout = inode_.asFile().layout;
size_t chunksLeft = folly::divCeil(targetLength, layout.chunkSize.u64());
auto chunkRange = ChunkId::range(inode_.id, chunksLeft);
CO_RETURN_AND_LOG_ON_ERROR(chunkRange);
auto [begin, end] = *chunkRange;
auto chainIndexList = layout.getChainIndexList();
for (size_t stripe = 0; stripe < maxStripe; stripe++) {
auto chainIndex = chainIndexList[stripe];
auto ref = flat::ChainRef{layout.tableId, layout.tableVersion, chainIndex};
auto cid = routing_.getChainId(ref);
if (!cid) {
XLOGF(ERR, "Failed to get ChainId by {}", ref);
co_return makeError(MgmtdClientCode::kRoutingInfoNotReady, fmt::format("Failed to get ChainId by {}", ref));
}
auto removeOp = storage_.createRemoveOp(storage::ChainId(*cid),
storage::ChunkId(begin),
storage::ChunkId(end),
removeChunksBatchSize);
removeOps.push_back(std::move(removeOp));
}
RECORD_LATENCY(removeChunksLatency);
storage::client::WriteOptions options;
options.retry() = retry;
CO_RETURN_ON_ERROR(co_await storage_.removeChunks(removeOps, userInfo_, options));
bool more = false;
uint64_t totalRemovedChunks = 0;
for (const auto &op : removeOps) {
if (auto &status = op.result.statusCode; !status.hasError()) {
auto removed = op.result.numChunksRemoved;
auto chain = flat::ChainId(op.routingTarget.chainId);
XLOGF(DBG, "Remove {} chunks in range {} - {} at chain {}, more {}", removed, begin, end, chain, more);
totalRemovedChunks += removed;
more |= op.result.moreChunksInRange;
}
}
ADD_SAMPLE(removeChunksCount, totalRemovedChunks);
ADD_SAMPLE(removeChunksSize, totalRemovedChunks * inode_.asFile().layout.chunkSize);
XLOGF(DBG, "Remove {} chunks in range {}-{}, more {}", totalRemovedChunks, begin, end, more);
for (const auto &op : removeOps) {
if (auto &status = op.result.statusCode; status.hasError()) {
auto chain = flat::ChainId(op.routingTarget.chainId);
XLOGF(ERR, "Failed to remove chunk range {}-{} at chain {}, err {}", begin, end, chain, status.error());
ADD_FAILED(removeChunksFailed, status.error().code());
co_return makeError(status.error());
}
}
co_return std::make_pair(totalRemovedChunks, more);
}
CoTryTask<void> FileOperation::truncateChunk(size_t targetLength) {
CHECK_FILE(inode_);
const auto &layout = inode_.asFile().layout;
size_t chunkCnt = folly::divCeil(targetLength, layout.chunkSize.u64());
if (chunkCnt == 0) {
co_return Void{};
}
auto lastChunkIndex = chunkCnt - 1;
if (lastChunkIndex > std::numeric_limits<uint32_t>::max()) {
co_return MAKE_ERROR_F(MetaCode::kFileTooLarge, "length {} chunk id {}", targetLength, lastChunkIndex);
}
RECORD_LATENCY(truncateChunkLatency);
auto lastChunkId = ChunkId(inode_.id, 0, lastChunkIndex);
auto lastChunkLen = targetLength - lastChunkIndex * layout.chunkSize;
auto lastChunkChainRef = layout.getChainOfChunk(inode_, lastChunkIndex);
auto lastChunkChainId = routing_.getChainId(lastChunkChainRef);
if (!lastChunkChainId) {
XLOGF(ERR, "Failed to get ChainId by {}", lastChunkChainRef);
co_return makeError(MgmtdClientCode::kRoutingInfoNotReady,
fmt::format("Failed to get ChainId by {}", lastChunkChainRef));
}
auto truncateOp = storage_.createTruncateOp(storage::ChainId(*lastChunkChainId),
storage::ChunkId(lastChunkId),
lastChunkLen,
layout.chunkSize);
XLOGF(INFO, "User {} truncate inode {} to targetLength {}", userInfo_.uid, inode_.id, targetLength);
CO_RETURN_ON_ERROR(
co_await storage_.truncateChunks(std::span<storage::client::TruncateChunkOp>(&truncateOp, 1), userInfo_));
if (auto &status = truncateOp.result.lengthInfo; status.hasError()) {
XLOGF(ERR,
"Failed to truncate chunk {} to {}, {}, err {}",
lastChunkId,
lastChunkLen,
layout.chunkSize,
status.error());
ADD_FAILED(truncateChunkFailed, status.error().code());
co_return makeError(truncateOp.result.lengthInfo.error());
}
co_return Void{};
}
CoTryTask<std::map<uint32_t, uint32_t>> FileOperation::queryAllChunks(size_t begin, size_t end) {
CHECK_FILE(inode_);
std::vector<storage::client::QueryLastChunkOp> queries;
for (size_t chunk = begin; chunk < end; chunk++) {
auto ref = inode_.asFile().layout.getChainOfChunk(inode_, chunk);
auto cid = routing_.getChainId(ref);
if (!cid) {
XLOGF(ERR, "Failed to get ChainId by {}", ref);
co_return makeError(MgmtdClientCode::kRoutingInfoNotReady, fmt::format("Failed to get ChainId by {}", ref));
}
auto begin = meta::ChunkId(inode_.id, 0, chunk);
auto end = meta::ChunkId(inode_.id, 0, chunk + 1);
auto query = storage_.createQueryOp(storage::ChainId(*cid),
storage::ChunkId(begin),
storage::ChunkId(end),
1,
(void *)chunk);
queries.push_back(std::move(query));
}
std::map<uint32_t, uint32_t> chunks;
auto queryResult = co_await storage_.queryLastChunk(queries, userInfo_);
for (auto &query : queries) {
auto &result = query.result;
auto begin = meta::ChunkId::unpack(query.range.begin.data());
if (UNLIKELY(result.statusCode.hasError())) {
if (result.statusCode.error().code() == StorageClientCode::kChunkNotFound) {
continue;
}
co_return makeError(result.statusCode.error());
}
if (result.totalNumChunks == 0) {
continue;
}
auto chunkId = meta::ChunkId::unpack(result.lastChunkId.data());
if (!chunkId) {
XLOGF(CRITICAL,
"Failed to unpack chunkId queried from storage, {} {}.",
result.lastChunkId.describe(),
result.lastChunkLen);
co_return makeError(StatusCode::kDataCorruption, "Failed to unpack chunkId queried from storage");
}
if (chunkId != begin) {
XLOGF(CRITICAL, "{} != {}", chunkId, begin);
co_return makeError(StatusCode::kDataCorruption, fmt::format("ChunkId {} != {}", chunkId, begin));
}
chunks[chunkId.chunk()] = result.lastChunkLen;
}
co_return chunks;
}
} // namespace hf3fs::meta

View File

@@ -0,0 +1,81 @@
#pragma once
#include <functional>
#include <optional>
#include <string_view>
#include "client/storage/StorageClient.h"
#include "client/storage/TargetSelection.h"
#include "common/monitor/Recorder.h"
#include "common/utils/Coroutine.h"
#include "fbs/meta/Schema.h"
#include "fbs/mgmtd/MgmtdTypes.h"
namespace hf3fs::meta {
class FileOperation {
public:
struct Recorder {
Recorder(std::string_view prefix)
: removeChunksCount(fmt::format("{}.remove_chunks", prefix)),
removeChunksSize(fmt::format("{}.remove_chunks_size", prefix)),
queryChunksFailed(fmt::format("{}.query_chunks_failed", prefix)),
removeChunksFailed(fmt::format("{}.remove_chunks_failed", prefix)),
truncateChunkFailed(fmt::format("{}.truncate_chunk_failed", prefix)),
queryLastChunkLatency(fmt::format("{}.query_last_chunk", prefix)),
queryTotalChunkLatency(fmt::format("{}.query_total_chunks", prefix)),
removeChunksLatency(fmt::format("{}.remove_chunks_latency", prefix)),
truncateChunkLatency(fmt::format("{}.truncate_latency", prefix)) {}
monitor::CountRecorder removeChunksCount;
monitor::CountRecorder removeChunksSize;
monitor::CountRecorder queryChunksFailed;
monitor::CountRecorder removeChunksFailed;
monitor::CountRecorder truncateChunkFailed;
monitor::LatencyRecorder queryLastChunkLatency;
monitor::LatencyRecorder queryTotalChunkLatency;
monitor::LatencyRecorder removeChunksLatency;
monitor::LatencyRecorder truncateChunkLatency;
};
FileOperation(storage::client::StorageClient &storage,
const flat::RoutingInfo &routing,
const flat::UserInfo &userInfo,
const meta::Inode &inode,
std::optional<std::reference_wrapper<Recorder>> recorder = {})
: storage_(storage),
routing_(routing),
userInfo_(userInfo),
inode_(inode),
recorder_(recorder) {}
struct QueryResult {
uint64_t length = 0;
uint64_t lastChunk = 0;
uint64_t lastChunkLen = 0;
uint64_t totalChunkLen = 0;
uint64_t totalNumChunks = 0;
};
CoTryTask<QueryResult> queryChunks(bool queryTotalChunk, bool dynStripe);
CoTryTask<std::map<flat::ChainId, QueryResult>> queryChunksByChain(bool queryTotalChunk, bool dynStripe);
CoTryTask<std::pair<uint32_t, bool>> removeChunks(size_t targetLength,
size_t removeChunksBatchSize,
bool dynStripe,
storage::client::RetryOptions retry);
CoTryTask<void> truncateChunk(size_t targetLength);
CoTryTask<std::map<uint32_t, uint32_t>> queryAllChunks(uint64_t begin, uint64_t end);
private:
storage::client::StorageClient &storage_;
const flat::RoutingInfo &routing_;
const flat::UserInfo &userInfo_;
const meta::Inode &inode_;
std::optional<std::reference_wrapper<Recorder>> recorder_;
};
} // namespace hf3fs::meta

View File

@@ -0,0 +1,42 @@
#pragma once
#include "common/serde/CallContext.h"
#include "common/serde/Service.h"
#include "common/utils/Result.h"
#include "fbs/meta/Service.h"
namespace hf3fs::meta {
class MockMetaService : public serde::ServiceWrapper<MockMetaService, MetaSerde> {
public:
virtual ~MockMetaService() = default;
#define META_MOCK_SERVICE_METHOD(NAME, REQ, RESP) \
virtual CoTryTask<RESP> NAME(serde::CallContext &ctx, const REQ &req) { \
co_return makeError(StatusCode::kNotImplemented); \
}
META_MOCK_SERVICE_METHOD(statFs, StatFsReq, StatFsRsp);
META_MOCK_SERVICE_METHOD(stat, StatReq, StatRsp);
META_MOCK_SERVICE_METHOD(create, CreateReq, CreateRsp);
META_MOCK_SERVICE_METHOD(mkdirs, MkdirsReq, MkdirsRsp);
META_MOCK_SERVICE_METHOD(symlink, SymlinkReq, SymlinkRsp);
META_MOCK_SERVICE_METHOD(hardLink, HardLinkReq, HardLinkRsp);
META_MOCK_SERVICE_METHOD(remove, RemoveReq, RemoveRsp);
META_MOCK_SERVICE_METHOD(open, OpenReq, OpenRsp);
META_MOCK_SERVICE_METHOD(sync, SyncReq, SyncRsp);
META_MOCK_SERVICE_METHOD(close, CloseReq, CloseRsp);
META_MOCK_SERVICE_METHOD(rename, RenameReq, RenameRsp);
META_MOCK_SERVICE_METHOD(list, ListReq, ListRsp);
META_MOCK_SERVICE_METHOD(truncate, TruncateReq, TruncateRsp);
META_MOCK_SERVICE_METHOD(getRealPath, GetRealPathReq, GetRealPathRsp);
META_MOCK_SERVICE_METHOD(setAttr, SetAttrReq, SetAttrRsp);
META_MOCK_SERVICE_METHOD(pruneSession, PruneSessionReq, PruneSessionRsp);
META_MOCK_SERVICE_METHOD(dropUserCache, DropUserCacheReq, DropUserCacheRsp);
META_MOCK_SERVICE_METHOD(authenticate, AuthReq, AuthRsp);
META_MOCK_SERVICE_METHOD(lockDirectory, LockDirectoryReq, LockDirectoryRsp);
META_MOCK_SERVICE_METHOD(testRpc, TestRpcReq, TestRpcRsp);
META_MOCK_SERVICE_METHOD(batchStat, BatchStatReq, BatchStatRsp);
META_MOCK_SERVICE_METHOD(batchStatByPath, BatchStatByPathReq, BatchStatByPathRsp);
};
} // namespace hf3fs::meta

196
src/fbs/meta/Schema.cc Normal file
View File

@@ -0,0 +1,196 @@
#include "fbs/meta/Schema.h"
#include <algorithm>
#include <fmt/core.h>
#include <folly/Overload.h>
#include <folly/logging/xlog.h>
#include <limits>
#include <span>
#include <variant>
#include <vector>
#include "common/utils/Result.h"
#include "fbs/core/user/User.h"
#include "fbs/meta/Common.h"
#include "fbs/mgmtd/MgmtdTypes.h"
#ifndef TRACK_OFFSET_FOR_CHAIN
// it's best to be a prime number, so each track can start from different chains
#define TRACK_OFFSET_FOR_CHAIN 7
#endif
namespace hf3fs::meta {
Result<Void> Acl::checkPermission(const UserInfo &user, AccessType type) const {
uint32_t permNeeded = 0;
if (user.uid == 0)
permNeeded = 0;
else if (user.uid == uid)
permNeeded = type << 6; // owner
else if (user.inGroup(gid))
permNeeded = type << 3; // group
else
permNeeded = type;
if ((perm & permNeeded) != permNeeded) {
auto msg = fmt::format("acl {{uid: {}, gid: {}, perm: {:04o}}}, user {} has no perm for {}",
uid.toUnderType(),
gid.toUnderType(),
perm.toUnderType(),
user,
(int)type);
XLOGF(DBG, msg);
return makeError(MetaCode::kNoPermission, std::move(msg));
}
return Void{};
}
Result<Void> Acl::checkRecursiveRmPerm(const UserInfo &user, bool owner) const {
if (iflags & FS_IMMUTABLE_FL) {
return makeError(MetaCode::kNoPermission, "immutable");
}
if (!user.isRoot() && (owner || (perm & S_ISVTX)) && user.uid != uid) {
return makeError(MetaCode::kNoPermission, "not owner");
}
auto rwx = AccessType::READ | AccessType::WRITE | AccessType::EXEC;
if (!checkPermission(user, rwx)) {
return makeError(MetaCode::kNoPermission, "no rwx perm");
}
return Void{};
}
Result<ChunkId> File::getChunkId(InodeId id, uint64_t offset) const {
if (!layout.chunkSize) {
XLOGF(CRITICAL, "File {} chunkSize == 0.", *this);
return makeError(MetaCode::kInvalidFileLayout, "chunk size = 0");
}
auto chunk = offset / layout.chunkSize;
if (chunk > std::numeric_limits<uint32_t>::max()) {
XLOGF(CRITICAL, "File {}, offset {}, chunk {} > uint32_max", *this, offset, chunk);
return MAKE_ERROR_F(MetaCode::kFileTooLarge, "offset {} chunk id {} > uint32_max", offset, chunk);
}
return ChunkId(id, 0, chunk);
}
Result<ChainId> File::getChainId(const Inode &inode,
size_t offset,
const flat::RoutingInfo &routingInfo,
uint16_t track) const {
if ((!layout.chunkSize || !layout.stripeSize)) {
XLOGF(CRITICAL, "File {} chunkSize {}, stripeSize {}.", inode.id, layout.chunkSize, layout.stripeSize);
return makeError(MetaCode::kInvalidFileLayout, !layout.chunkSize ? "empty chunksize" : "empty stripesize");
}
auto ref = layout.getChainOfChunk(inode, offset / layout.chunkSize + track * TRACK_OFFSET_FOR_CHAIN);
auto cid = routingInfo.getChainId(ref);
if (!cid) {
auto msg = fmt::format("Cannot find ChainId by {}, offset is {}", ref, offset);
XLOG(ERR, msg);
return makeError(MgmtdClientCode::kRoutingInfoNotReady, msg);
}
return *cid;
}
Layout Layout::newEmpty(ChainTableId table, uint32_t chunk, uint32_t stripe) {
return {table, ChainTableVersion(0), chunk, stripe, Layout::Empty()};
}
Layout Layout::newEmpty(ChainTableId table, ChainTableVersion tableVer, uint32_t chunk, uint32_t stripe) {
return {table, tableVer, chunk, stripe, Layout::Empty()};
}
Layout Layout::newChainList(ChainTableId table,
ChainTableVersion tableVer,
uint32_t chunk,
std::vector<uint32_t> chains) {
return {table, tableVer, chunk, (uint32_t)chains.size(), ChainList{std::move(chains)}};
}
Layout Layout::newChainList(uint32_t chunk, std::vector<flat::ChainId> chains) {
std::vector<uint32_t> idList;
idList.reserve(chains.size());
for (auto c : chains) {
idList.push_back(c);
}
return {ChainTableId(0), ChainTableVersion(0), chunk, (uint32_t)chains.size(), ChainList{std::move(idList)}};
}
Layout Layout::newChainRange(ChainTableId table,
ChainTableVersion tableVer,
uint32_t chunk,
uint32_t stripe,
uint32_t baseChainIndex) {
auto chains = ChainRange(baseChainIndex, ChainRange::Shuffle::STD_SHUFFLE_MT19937, folly::Random::rand64());
return Layout{table, tableVer, chunk, stripe, chains};
}
Result<Void> Layout::valid(bool allowEmpty) const {
#define INVALID_LAYOUT(msg) makeError(MetaCode::kInvalidFileLayout, msg)
if (chunkSize == 0) return INVALID_LAYOUT("chunksize is 0");
if (!folly::isPowTwo(chunkSize.u64())) return INVALID_LAYOUT("chunksize should be power of 2.");
if (stripeSize == 0) return INVALID_LAYOUT("stripesize is 0");
auto checkEmptyLayout = [&](const Empty &) -> Result<Void> {
if (!allowEmpty) return INVALID_LAYOUT("empty layout");
if (tableId == ChainTableId(0)) return INVALID_LAYOUT("invalid chain table 0");
return VALID;
};
auto checkChainRange = [&](const ChainRange &range) -> Result<Void> {
if (tableId == 0) return INVALID_LAYOUT("invalid chain table 0");
if (tableVersion == 0) return INVALID_LAYOUT("invalid chain table version 0 for ChainRange.");
if (range.baseIndex == 0) return INVALID_LAYOUT("base index is 0");
return VALID;
};
auto checkChainList = [&](const ChainList &list) -> Result<Void> {
if (tableId == 0 != tableVersion == 0)
return INVALID_LAYOUT(
fmt::format("invalid chain table {} version {}", tableId.toUnderType(), tableVersion.toUnderType()));
if (stripeSize != list.chainIndexes.size())
return INVALID_LAYOUT(fmt::format("chain list size {} != stripesize {}", list.chainIndexes.size(), stripeSize));
if (std::any_of(list.chainIndexes.begin(), list.chainIndexes.end(), [](auto v) { return v == 0; }))
return INVALID_LAYOUT("chain list contains chain index 0");
return VALID;
};
XLOGF_IF(FATAL, chains.valueless_by_exception(), "Chains is valueless");
return folly::variant_match(chains, checkEmptyLayout, checkChainRange, checkChainList);
#undef INVALID_LAYOUT
}
std::span<const uint32_t> Layout::ChainRange::getChainIndexList(size_t stripe) const {
const auto &list = chains.try_emplace_with([&]() -> std::vector<uint32_t> {
std::vector<uint32_t> chains(stripe);
for (uint32_t i = 0; i < stripe; i++) {
uint32_t index = baseIndex + i;
chains[i] = index;
}
switch (shuffle) {
case NO_SHUFFLE:
break;
case STD_SHUFFLE_MT19937: {
auto rng = std::mt19937_64(seed);
std::shuffle(chains.begin(), chains.end(), rng);
break;
}
default:
XLOGF(FATAL, "Unknown shuffle func {}", (int)shuffle);
}
return chains;
});
XLOGF_IF(FATAL, stripe != list.size(), "stripe {} != list {}", stripe, list.size());
return std::span<const uint32_t>(list);
}
std::span<const uint32_t> Layout::getChainIndexList() const {
XLOGF_IF(FATAL, chains.valueless_by_exception(), "Chains is valueless");
return folly::variant_match(
chains,
[&](const ChainRange &range) { return range.getChainIndexList(stripeSize); },
[&](const ChainList &list) { return std::span<const uint32_t>(list.chainIndexes); },
[&](auto) { return std::span<const uint32_t>(); });
}
ChainRef Layout::getChainOfChunk(const Inode &inode, size_t chunkIndex) const {
const auto chains = getChainIndexList();
XLOGF_IF(FATAL, (!stripeSize || chains.empty()), "Inode {}'s layout is invalid", inode);
auto stripe = chunkIndex % stripeSize;
return ChainRef{tableId, tableVersion, chains[stripe]};
}
} // namespace hf3fs::meta

467
src/fbs/meta/Schema.h Normal file
View File

@@ -0,0 +1,467 @@
#pragma once
#include <atomic>
#include <boost/filesystem/path.hpp>
#include <cstdint>
#include <fmt/core.h>
#include <folly/Overload.h>
#include <folly/Random.h>
#include <folly/lang/Bits.h>
#include <folly/logging/xlog.h>
#include <folly/synchronization/DelayedInit.h>
#include <limits>
#include <linux/fs.h>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>
#include "common/app/ClientId.h"
#include "common/serde/Serde.h"
#include "common/serde/SerdeComparisons.h"
#include "common/utils/MagicEnum.hpp"
#include "common/utils/Path.h"
#include "common/utils/Result.h"
#include "common/utils/StrongType.h"
#include "common/utils/UtcTimeSerde.h"
#include "common/utils/Uuid.h"
#include "fbs/core/user/User.h"
#include "fbs/meta/Common.h"
#include "fbs/mgmtd/ChainRef.h"
#include "fbs/mgmtd/ChainTable.h"
#include "fbs/mgmtd/MgmtdTypes.h"
#include "fbs/mgmtd/RoutingInfo.h"
#define SETATTR_TIME_NOW hf3fs::UtcTime::fromMicroseconds(-1)
namespace hf3fs::meta {
using flat::ChainId;
using flat::ChainRef;
using flat::ChainTableId;
using flat::ChainTableVersion;
struct Acl {
SERDE_STRUCT_FIELD(uid, Uid(0));
SERDE_STRUCT_FIELD(gid, Gid(0));
SERDE_STRUCT_FIELD(perm, Permission(0));
SERDE_STRUCT_FIELD(iflags, IFlags(0));
public:
Acl() = default;
Acl(Uid uid, Gid gid, Permission perm, IFlags iflags = IFlags(0))
: uid(uid),
gid(gid),
perm(perm),
iflags(iflags) {}
static Acl root() { return Acl(Uid(0), Gid(0), Permission(0755), IFlags(FS_IMMUTABLE_FL)); }
static Acl gcRoot() { return Acl(Uid(0), Gid(0), Permission(0700), IFlags(FS_IMMUTABLE_FL)); }
Result<Void> checkPermission(const UserInfo &user, AccessType type) const;
Result<Void> checkRecursiveRmPerm(const UserInfo &user, bool owner) const;
bool operator==(const Acl &o) const { return serde::equals(*this, o); }
};
struct Inode;
struct Layout {
enum class Type {
Empty,
ChainRange,
ChainList,
};
struct Empty {
static constexpr auto kType = Type::Empty;
using is_serde_copyable = void;
std::string serdeToReadable() const { return "empty"; }
static Result<Empty> serdeFromReadable(std::string_view) { return Empty{}; }
bool operator==(const Empty &) const { return true; }
};
struct ChainRange {
enum Shuffle : uint8_t { NO_SHUFFLE = 0, STD_SHUFFLE_MT19937 };
SERDE_STRUCT_FIELD(baseIndex, uint32_t(0));
SERDE_STRUCT_FIELD(shuffle, Shuffle(0));
SERDE_STRUCT_FIELD(seed, uint64_t(0));
mutable folly::DelayedInit<std::vector<uint32_t>> chains;
public:
static constexpr auto kType = Type::ChainRange;
ChainRange() = default;
ChainRange(uint32_t baseIndex, Shuffle shuffle, uint64_t seed)
: baseIndex(baseIndex),
shuffle(shuffle),
seed(seed),
chains() {}
ChainRange(const ChainRange &o)
: ChainRange(o.baseIndex, o.shuffle, o.seed) {}
ChainRange &operator=(const ChainRange &o) {
if (&o != this) {
new (this) ChainRange(o);
}
return *this;
}
std::span<const uint32_t> getChainIndexList(size_t stripe) const;
bool operator==(const ChainRange &o) const { return serde::equals(*this, o); }
};
struct ChainList {
SERDE_STRUCT_FIELD(chainIndexes, std::vector<uint32_t>());
public:
static constexpr auto kType = Type::ChainList;
bool operator==(const ChainList &o) const { return serde::equals(*this, o); }
};
// make sure uint64_t is used in file length/offset calculate
class ChunkSize {
public:
using is_serde_copyable = void;
ChunkSize(uint32_t val = 0)
: val_(val) {}
uint32_t u32() const { return val_; }
uint64_t u64() const { return val_; }
operator uint64_t() const { return val_; }
constexpr uint32_t serdeToReadable() const { return val_; }
static Result<ChunkSize> serdeFromReadable(uint32_t val) { return ChunkSize(val); }
private:
uint32_t val_;
};
static_assert(sizeof(ChunkSize) == sizeof(uint32_t));
SERDE_STRUCT_FIELD(tableId, ChainTableId());
SERDE_STRUCT_FIELD(tableVersion, ChainTableVersion());
SERDE_STRUCT_FIELD(chunkSize, ChunkSize(0));
SERDE_STRUCT_FIELD(stripeSize, uint32_t(0));
SERDE_STRUCT_FIELD(chains, (std::variant<Empty, ChainRange, ChainList>{}));
public:
static Layout newEmpty(ChainTableId table, uint32_t chunk, uint32_t stripe);
static Layout newEmpty(ChainTableId table, ChainTableVersion tableVer, uint32_t chunk, uint32_t stripe);
static Layout newChainList(ChainTableId table,
ChainTableVersion tableVer,
uint32_t chunk,
std::vector<uint32_t> chains);
static Layout newChainList(uint32_t chunk, std::vector<flat::ChainId> chains);
static Layout newChainRange(ChainTableId table,
ChainTableVersion tableVer,
uint32_t chunk,
uint32_t stripe,
uint32_t baseChainIndex);
std::span<const uint32_t> getChainIndexList() const;
ChainRef getChainOfChunk(const Inode &inode, size_t chunkIndex) const;
bool empty() const { return std::holds_alternative<Empty>(chains); }
Result<Void> valid(bool allowEmpty) const;
Type type() const {
XLOGF_IF(FATAL, chains.valueless_by_exception(), "Chains is valueless");
return folly::variant_match(chains, [](const auto &v) { return std::decay_t<decltype(v)>::kType; });
}
bool operator==(const Layout &o) const { return serde::equals(*this, o); }
};
enum class InodeType : uint8_t {
File = 0,
Directory,
Symlink,
};
class ChunkId {
/**
* Use big endian form to keep order.
* format: [tenent id (0x00)] + [ unused 0x_00] + [ 64bits inode id ] + [ 16bits track id ] + [ 32bits chunk num ]
*/
FOLLY_MAYBE_UNUSED std::array<uint8_t, 1> tenent_;
FOLLY_MAYBE_UNUSED std::array<uint8_t, 1> reserved_;
std::array<uint8_t, 8> inode_;
FOLLY_MAYBE_UNUSED std::array<uint8_t, 2> track_; // reserve for multitrack files.
std::array<uint8_t, 4> chunk_;
public:
// ChunkId(InodeId inode = InodeId(-1), uint32_t chunk = 0)
// : ChunkId(inode, 0, chunk) {}
// ChunkId()
// : ChunkId(InodeId(-1), 0, 0) {}
ChunkId(InodeId inode, uint16_t track, uint32_t chunk)
: tenent_({0}),
reserved_({0}),
inode_(folly::bit_cast<std::array<uint8_t, 8>>(folly::Endian::big64(inode.u64()))),
track_(folly::bit_cast<std::array<uint8_t, 2>>(folly::Endian::big16(track))),
chunk_(folly::bit_cast<std::array<uint8_t, 4>>(folly::Endian::big32(chunk))) {}
std::string pack() const { return std::string((char *)this, sizeof(ChunkId)); }
static ChunkId unpack(std::string_view data) {
ChunkId id(InodeId(-1), 0, 0);
if (data.size() == sizeof(ChunkId)) {
memcpy(&id, data.data(), sizeof(ChunkId));
}
return id;
}
static Result<std::pair<ChunkId, ChunkId>> range(InodeId inodeId, uint32_t chunkBegin = 0) {
if (inodeId.u64() == std::numeric_limits<uint64_t>::max()) {
return makeError(MetaCode::kNotFile, "InodeId is uint64_max");
}
auto begin = ChunkId(inodeId, 0, chunkBegin);
auto end = ChunkId(inodeId, 1, 0);
return std::pair<ChunkId, ChunkId>{begin, end};
}
InodeId inode() const { return InodeId(folly::Endian::big64(folly::bit_cast<uint64_t>(inode_))); }
uint16_t track() const { return folly::Endian::big16(folly::bit_cast<uint16_t>(track_)); }
// use uint64_t to prevent error when calculate file offset or length
uint64_t chunk() const { return folly::Endian::big32(folly::bit_cast<uint32_t>(chunk_)); }
operator std::string() const { return pack(); }
explicit operator bool() const { return *this != ChunkId(InodeId(-1), 0, 0); }
bool operator==(const ChunkId &o) const { return memcmp(this, &o, sizeof(*this)) == 0; }
};
static_assert(sizeof(ChunkId) == 16);
struct VersionedLength {
SERDE_STRUCT_FIELD(length, uint64_t(0));
SERDE_STRUCT_FIELD(truncateVer, uint64_t(0));
public:
static std::optional<VersionedLength> mergeHint(std::optional<VersionedLength> h1,
std::optional<VersionedLength> h2) {
if (!h1 || !h2) {
return std::nullopt;
} else {
return h1->length >= h2->length ? h1 : h2;
}
}
bool operator==(const VersionedLength &o) const { return serde::equals(*this, o); }
};
struct File {
struct Flags : BitFlags<Flags, uint32_t> {
using Base = BitFlags<Flags, uint32_t>;
using Base::Base;
static constexpr uint32_t kHasHole = 1;
};
SERDE_STRUCT_FIELD(length, uint64_t(0));
SERDE_STRUCT_FIELD(truncateVer, uint64_t(0));
SERDE_STRUCT_FIELD(layout, Layout());
SERDE_STRUCT_FIELD(flags, Flags(0));
SERDE_STRUCT_FIELD(dynStripe, uint32_t(0)); // dynStripe = 0 means dynamic stripe size is not enabled for this file
public:
File() = default;
File(Layout layout, uint32_t dynStripe = 0)
: layout(std::move(layout)),
dynStripe(dynStripe) {}
static constexpr auto kType = InodeType::File;
Result<Void> valid() const { return layout.valid(false); }
bool hasHole() const { return flags.contains(File::Flags::kHasHole); }
Result<ChunkId> getChunkId(InodeId id, uint64_t offset) const;
Result<ChainId> getChainId(const Inode &inode,
size_t offset,
const flat::RoutingInfo &routingInfo,
uint16_t track = 0) const;
VersionedLength getVersionedLength() const { return VersionedLength{length, truncateVer}; }
void setVersionedLength(VersionedLength v) {
length = v.length;
truncateVer = v.truncateVer;
}
bool operator==(const File &o) const { return serde::equals(*this, o); }
};
struct Directory {
struct Lock {
SERDE_STRUCT_FIELD(client, ClientId(Uuid::zero(), ""));
public:
bool operator==(const Lock &o) const { return serde::equals(*this, o); }
};
SERDE_STRUCT_FIELD(parent, InodeId());
SERDE_STRUCT_FIELD(layout, Layout());
SERDE_STRUCT_FIELD(name, std::string());
SERDE_STRUCT_FIELD(chainAllocCounter, uint32_t(-1));
SERDE_STRUCT_FIELD(lock, std::optional<Lock>());
public:
static constexpr auto kType = InodeType::Directory;
Result<Void> valid() const { return layout.valid(true); }
Result<Void> checkLock(const ClientId &client) const {
if (lock && lock->client.uuid != client.uuid) {
return makeError(MetaCode::kNoLock, fmt::format("locked by {}", lock->client));
}
return Void{};
}
bool operator==(const Directory &o) const { return serde::equals(*this, o); }
};
struct Symlink {
SERDE_STRUCT_FIELD(target, Path());
public:
static constexpr auto kType = InodeType::Symlink;
Result<Void> valid() const {
if (target.empty()) return INVALID("target is empty");
return VALID;
}
bool operator==(const Symlink &o) const { return serde::equals(*this, o); }
};
struct InodeData {
SERDE_STRUCT_FIELD(type, (std::variant<File, Directory, Symlink>()));
SERDE_STRUCT_FIELD(acl, Acl());
SERDE_STRUCT_FIELD(nlink, uint16_t(1));
// 2023/6/1 as default inode timestamps
SERDE_STRUCT_FIELD(atime, UtcTime(std::chrono::microseconds(1685548800ull * 1000 * 1000)));
SERDE_STRUCT_FIELD(ctime, UtcTime(std::chrono::microseconds(1685548800ull * 1000 * 1000)));
SERDE_STRUCT_FIELD(mtime, UtcTime(std::chrono::microseconds(1685548800ull * 1000 * 1000)));
public:
InodeType getType() const {
XLOGF_IF(FATAL, type.valueless_by_exception(), "InodeType is valueless");
return folly::variant_match(type, [](const auto &v) { return std::decay_t<decltype(v)>::kType; });
}
#define INODE_TYPE_FUNC(type_name) \
bool is##type_name() const { return std::holds_alternative<type_name>(type); } \
type_name &as##type_name() { \
XLOGF_IF(FATAL, type.valueless_by_exception(), "InodeType is valueless"); \
XLOGF_IF(FATAL, \
!std::holds_alternative<type_name>(type), \
"Inode type {} != " #type_name, \
magic_enum::enum_name(getType())); \
return std::get<type_name>(type); \
} \
const type_name &as##type_name() const { \
XLOGF_IF(FATAL, type.valueless_by_exception(), "InodeType is valueless"); \
XLOGF_IF(FATAL, \
!std::holds_alternative<type_name>(type), \
"Inode type {} != " #type_name, \
magic_enum::enum_name(getType())); \
return std::get<type_name>(type); \
}
INODE_TYPE_FUNC(File)
INODE_TYPE_FUNC(Directory)
INODE_TYPE_FUNC(Symlink)
#undef INODE_TYPE_FUNC
Result<Void> valid() const {
XLOGF_IF(FATAL, type.valueless_by_exception(), "InodeType is valueless");
return folly::variant_match(type, [](const auto &v) { return v.valid(); });
}
bool operator==(const InodeData &o) const { return serde::equals(*this, o); }
};
struct Inode : InodeData {
SERDE_STRUCT_FIELD(id, InodeId());
public:
Inode() = default;
explicit Inode(InodeId id)
: Inode(id, {}) {}
Inode(InodeId id, InodeData data)
: InodeData(std::move(data)),
id(id) {}
InodeData &data() { return *this; }
const InodeData &data() const { return *this; }
Result<Void> valid() const { return data().valid(); }
bool operator==(const Inode &o) const { return serde::equals(*this, o); }
};
struct GcInfo {
SERDE_STRUCT_FIELD(user, flat::Uid(0));
SERDE_STRUCT_FIELD(origPath, Path());
public:
bool operator==(const GcInfo &o) const { return serde::equals(*this, o); }
};
struct DirEntryData {
SERDE_STRUCT_FIELD(id, InodeId());
SERDE_STRUCT_FIELD(type, InodeType::File);
SERDE_STRUCT_FIELD(dirAcl, std::optional<Acl>());
SERDE_STRUCT_FIELD(uuid, Uuid::zero());
SERDE_STRUCT_FIELD(gcInfo, std::optional<GcInfo>());
public:
#define INODE_TYPE_FUNC(type_name) \
bool is##type_name() const { return type == InodeType::type_name; }
INODE_TYPE_FUNC(File)
INODE_TYPE_FUNC(Directory)
INODE_TYPE_FUNC(Symlink)
#undef INODE_TYPE_FUNC
Result<Void> valid() const {
if (!magic_enum::enum_contains(type)) return INVALID(fmt::format("invalid type {}", (int)type));
if ((type == InodeType::Directory) != dirAcl.has_value())
return INVALID(fmt::format("type {}, dirAcl {}", (type == InodeType::Directory), dirAcl.has_value()));
return VALID;
}
bool operator==(const DirEntryData &o) const { return serde::equals(*this, o); }
};
struct DirEntry : DirEntryData {
SERDE_STRUCT_FIELD(parent, InodeId());
SERDE_STRUCT_FIELD(name, std::string());
public:
DirEntry() = default;
DirEntry(InodeId parent, std::string name, DirEntryData data = {})
: DirEntryData(std::move(data)),
parent(parent),
name(std::move(name)) {}
DirEntryData &data() { return *this; }
const DirEntryData &data() const { return *this; }
Result<Void> valid() const { return data().valid(); }
bool operator==(const DirEntry &o) const { return serde::equals(*this, o); }
};
} // namespace hf3fs::meta
template <>
struct hf3fs::serde::SerdeMethod<hf3fs::meta::InodeType> {
static constexpr std::string_view serdeToReadable(hf3fs::meta::InodeType t) { return magic_enum::enum_name(t); }
};
FMT_BEGIN_NAMESPACE
template <>
struct formatter<hf3fs::meta::InodeType> : formatter<std::string_view> {
template <typename FormatContext>
auto format(hf3fs::meta::InodeType type, FormatContext &ctx) const {
return format_to(ctx.out(), "{}", magic_enum::enum_name(type));
}
};
template <>
struct formatter<hf3fs::meta::ChunkId> : formatter<std::string_view> {
template <typename FormatContext>
auto format(hf3fs::meta::ChunkId chunk, FormatContext &ctx) const {
return format_to(ctx.out(), "{}-{}-{}", chunk.inode(), chunk.track(), chunk.chunk());
}
};
template <>
struct formatter<hf3fs::meta::Layout::ChunkSize> : formatter<std::string_view> {
template <typename FormatContext>
auto format(hf3fs::meta::Layout::ChunkSize chunk, FormatContext &ctx) const {
return format_to(ctx.out(), "{}", (uint64_t)chunk);
}
};
template <>
struct formatter<hf3fs::meta::VersionedLength> : formatter<std::string_view> {
template <typename FormatContext>
auto format(hf3fs::meta::VersionedLength v, FormatContext &ctx) const {
return format_to(ctx.out(), "{}@{}", v.length, v.truncateVer);
}
};
FMT_END_NAMESPACE

746
src/fbs/meta/Service.h Normal file
View File

@@ -0,0 +1,746 @@
#pragma once
#include <fcntl.h>
#include <folly/logging/xlog.h>
#include <functional>
#include <optional>
#include <type_traits>
#include <variant>
#include <vector>
#include "common/app/ClientId.h"
#include "common/app/NodeId.h"
#include "common/serde/Serde.h"
#include "common/serde/Service.h"
#include "common/utils/Result.h"
#include "common/utils/Status.h"
#include "common/utils/StrongType.h"
#include "common/utils/Uuid.h"
#include "fbs/core/user/User.h"
#include "fbs/meta/Common.h"
#include "fbs/meta/Schema.h"
#include "fbs/mgmtd/MgmtdTypes.h"
#define META_SERVICE_VERSION 1
#define CHECK_SESSION(session) \
do { \
if (!session.has_value()) { \
return INVALID(#session " not set"); \
} \
if (!session->valid()) { \
return INVALID(#session " invalid"); \
} \
} while (0)
namespace hf3fs::meta {
struct ReqBase {
SERDE_STRUCT_FIELD(user, UserInfo{});
SERDE_STRUCT_FIELD(client, ClientId{Uuid::zero()});
SERDE_STRUCT_FIELD(forward, flat::NodeId(0));
SERDE_STRUCT_FIELD(uuid, Uuid::zero());
public:
// set client id in unittest
static std::optional<ClientId> &currentClientId() {
static std::optional<ClientId> clientId;
return clientId;
}
ReqBase(UserInfo user = {}, Uuid uuid = Uuid::zero())
: user(std::move(user)),
uuid(uuid) {
if (currentClientId()) {
client = *currentClientId();
}
}
Result<Void> checkUuid() const {
if (client.uuid == Uuid::zero()) return INVALID("Invalid client uuid");
if (uuid == Uuid::zero()) return INVALID("Invalid request uuid");
return VALID;
}
};
struct RspBase {
SERDE_STRUCT_FIELD(dummy, Void{});
};
// authentication
struct AuthReq : ReqBase {
SERDE_STRUCT_FIELD(dummy, Void{});
public:
AuthReq() = default;
AuthReq(UserInfo user)
: ReqBase(std::move(user)) {}
Result<Void> valid() const { return VALID; }
};
struct AuthRsp : RspBase {
SERDE_STRUCT_FIELD(user, UserInfo{});
public:
AuthRsp() = default;
AuthRsp(UserInfo user)
: user(std::move(user)) {}
};
// statFs
struct StatFsReq : ReqBase {
public:
StatFsReq() = default;
StatFsReq(UserInfo user)
: ReqBase(std::move(user)) {}
Result<Void> valid() const { return VALID; }
};
struct StatFsRsp : RspBase {
SERDE_STRUCT_FIELD(capacity, uint64_t(0));
SERDE_STRUCT_FIELD(used, uint64_t(0));
SERDE_STRUCT_FIELD(free, uint64_t(0));
public:
StatFsRsp() = default;
StatFsRsp(uint64_t capacity, uint64_t used, uint64_t free)
: capacity(capacity),
used(used),
free(free) {}
};
// stat
struct StatReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(flags, AtFlags());
public:
StatReq() = default;
StatReq(UserInfo user, PathAt path, AtFlags flags)
: ReqBase(std::move(user)),
path(std::move(path)),
flags(flags) {}
Result<Void> valid() const { return flags.valid(); }
};
struct StatRsp : RspBase {
SERDE_STRUCT_FIELD(stat, Inode());
public:
StatRsp() = default;
StatRsp(Inode stat)
: stat(std::move(stat)) {}
};
// batchStat & batchStatByPath
struct BatchStatReq : ReqBase {
SERDE_STRUCT_FIELD(inodeIds, std::vector<InodeId>());
public:
BatchStatReq() = default;
BatchStatReq(UserInfo user, std::vector<InodeId> inodeIds)
: ReqBase(std::move(user)),
inodeIds(std::move(inodeIds)) {}
Result<Void> valid() const { return VALID; }
};
struct BatchStatRsp : RspBase {
SERDE_STRUCT_FIELD(inodes, std::vector<std::optional<Inode>>());
public:
BatchStatRsp() = default;
BatchStatRsp(std::vector<std::optional<Inode>> inodes)
: inodes(std::move(inodes)) {}
};
struct BatchStatByPathReq : ReqBase {
SERDE_STRUCT_FIELD(paths, std::vector<PathAt>());
SERDE_STRUCT_FIELD(flags, AtFlags());
public:
BatchStatByPathReq() = default;
BatchStatByPathReq(UserInfo user, std::vector<PathAt> paths, AtFlags flags)
: ReqBase(std::move(user)),
paths(std::move(paths)),
flags(flags) {}
Result<Void> valid() const { return flags.valid(); }
};
struct BatchStatByPathRsp : RspBase {
SERDE_STRUCT_FIELD(inodes, std::vector<Result<Inode>>());
public:
BatchStatByPathRsp() = default;
BatchStatByPathRsp(std::vector<Result<Inode>> inodes)
: inodes(std::move(inodes)) {}
};
// create
struct CreateReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(session, std::optional<SessionInfo>());
SERDE_STRUCT_FIELD(flags, OpenFlags());
SERDE_STRUCT_FIELD(perm, Permission());
SERDE_STRUCT_FIELD(layout, std::optional<Layout>());
SERDE_STRUCT_FIELD(removeChunksBatchSize, uint32_t(0));
SERDE_STRUCT_FIELD(dynStripe, false);
public:
CreateReq() = default;
CreateReq(UserInfo user,
PathAt path,
std::optional<SessionInfo> session,
OpenFlags flags,
Permission perm,
std::optional<Layout> layout = std::nullopt,
bool dynStripe = false)
: ReqBase(std::move(user), Uuid::random()),
path(std::move(path)),
session(session),
flags(flags),
perm(perm),
layout(std::move(layout)),
removeChunksBatchSize(32),
dynStripe(dynStripe) {}
Result<Void> valid() const {
RETURN_ON_ERROR(path.validForCreate());
RETURN_ON_ERROR(flags.valid());
if (flags.accessType() != AccessType::READ) CHECK_SESSION(session);
if (layout.has_value()) RETURN_ON_ERROR(layout->valid(true));
if (flags.contains(O_TRUNC) && removeChunksBatchSize == 0) return INVALID("removeChunksBatchSize == 0");
return VALID;
}
};
struct CreateRsp : RspBase {
SERDE_STRUCT_FIELD(stat, Inode());
SERDE_STRUCT_FIELD(needTruncate, false);
public:
CreateRsp() = default;
CreateRsp(Inode stat, bool needTruncate)
: stat(std::move(stat)),
needTruncate(needTruncate) {}
};
// mkdirs
struct MkdirsReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(perm, Permission());
SERDE_STRUCT_FIELD(recursive, false);
SERDE_STRUCT_FIELD(layout, std::optional<Layout>());
public:
MkdirsReq() = default;
MkdirsReq(UserInfo user, PathAt path, Permission perm, bool recursive, std::optional<Layout> layout = std::nullopt)
: ReqBase(std::move(user), Uuid::random()),
path(std::move(path)),
perm(perm),
recursive(recursive),
layout(std::move(layout)) {}
Result<Void> valid() const {
if (!path.path.has_value() || path.path->empty()) return INVALID("path not set");
if (layout.has_value()) RETURN_ON_ERROR(layout->valid(true));
return Void{};
}
};
struct MkdirsRsp : RspBase {
SERDE_STRUCT_FIELD(stat, Inode());
public:
MkdirsRsp() = default;
MkdirsRsp(Inode stat)
: stat(std::move(stat)) {}
};
// symlink
struct SymlinkReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(target, Path());
public:
SymlinkReq() = default;
SymlinkReq(UserInfo user, PathAt path, Path target)
: ReqBase(std::move(user), Uuid::random()),
path(std::move(path)),
target(std::move(target)) {}
Result<Void> valid() const { return path.validForCreate(); }
};
struct SymlinkRsp : RspBase {
SERDE_STRUCT_FIELD(stat, Inode());
public:
SymlinkRsp() = default;
SymlinkRsp(Inode stat)
: stat(std::move(stat)) {}
};
// hardlink
struct HardLinkReq : ReqBase {
SERDE_STRUCT_FIELD(oldPath, PathAt());
SERDE_STRUCT_FIELD(newPath, PathAt());
SERDE_STRUCT_FIELD(flags, AtFlags());
public:
HardLinkReq() = default;
HardLinkReq(UserInfo user, PathAt oldPath, PathAt newPath, AtFlags flags)
: ReqBase(std::move(user), Uuid::random()),
oldPath(std::move(oldPath)),
newPath(std::move(newPath)),
flags(flags) {}
Result<Void> valid() const {
RETURN_ON_ERROR(newPath.validForCreate());
RETURN_ON_ERROR(flags.valid());
return VALID;
}
};
struct HardLinkRsp : RspBase {
SERDE_STRUCT_FIELD(stat, Inode());
public:
HardLinkRsp() = default;
HardLinkRsp(Inode stat)
: stat(std::move(stat)) {}
};
// remove
struct RemoveReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(atFlags, AtFlags());
SERDE_STRUCT_FIELD(recursive, false);
SERDE_STRUCT_FIELD(checkType, false);
SERDE_STRUCT_FIELD(inodeId, std::optional<InodeId>());
public:
RemoveReq() = default;
RemoveReq(UserInfo user,
PathAt path,
AtFlags flags,
bool recursive,
bool checkType = false,
std::optional<InodeId> inodeId = std::nullopt)
: ReqBase(std::move(user), Uuid::random()),
path(std::move(path)),
atFlags(flags),
recursive(recursive),
checkType(checkType),
inodeId(inodeId) {}
Result<Void> valid() const {
if (path.path.has_value()) RETURN_ON_ERROR(path.validForCreate());
if (recursive) RETURN_ON_ERROR(checkUuid());
return VALID;
}
};
struct RemoveRsp : RspBase {
SERDE_STRUCT_FIELD(dummy, Void{});
};
// open
struct OpenReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(session, std::optional<SessionInfo>());
SERDE_STRUCT_FIELD(flags, OpenFlags());
SERDE_STRUCT_FIELD(removeChunksBatchSize, uint32_t(0));
SERDE_STRUCT_FIELD(dynStripe, false);
public:
OpenReq() = default;
OpenReq(UserInfo user, PathAt path, std::optional<SessionInfo> session, OpenFlags flags, bool dynStripe = false)
: ReqBase(std::move(user)),
path(std::move(path)),
session(session),
flags(flags),
removeChunksBatchSize(32),
dynStripe(dynStripe) {}
Result<Void> valid() const {
RETURN_ON_ERROR(flags.valid());
if (flags.accessType() != AccessType::READ) CHECK_SESSION(session);
if (flags.accessType() == AccessType::READ && (flags.contains(O_TRUNC) || flags.contains(O_APPEND)))
return INVALID("O_RDONLY with O_TRUNC or O_APPEND");
if (flags.contains(O_TRUNC) && removeChunksBatchSize == 0) return INVALID("removeChunksBatchSize == 0");
return VALID;
}
};
struct OpenRsp : RspBase {
SERDE_STRUCT_FIELD(_unused, (uint32_t)0);
SERDE_STRUCT_FIELD(stat, Inode());
SERDE_STRUCT_FIELD(needTruncate, false);
public:
OpenRsp() = default;
OpenRsp(Inode stat, bool needTruncate)
: stat(std::move(stat)),
needTruncate(needTruncate) {}
};
// sync
struct SyncReq : ReqBase {
SERDE_STRUCT_FIELD(inode, InodeId());
SERDE_STRUCT_FIELD(updateLength, false);
SERDE_STRUCT_FIELD(atime, std::optional<UtcTime>());
SERDE_STRUCT_FIELD(mtime, std::optional<UtcTime>());
SERDE_STRUCT_FIELD(truncated, false);
SERDE_STRUCT_FIELD(lengthHint, std::optional<VersionedLength>());
public:
SyncReq() = default;
SyncReq(UserInfo user,
InodeId inode,
bool updateLength,
std::optional<UtcTime> atime,
std::optional<UtcTime> mtime,
bool truncated = false,
std::optional<VersionedLength> hint = std::nullopt)
: ReqBase(std::move(user)),
inode(inode),
updateLength(updateLength),
atime(atime),
mtime(mtime),
truncated(truncated),
lengthHint(hint) {}
Result<Void> valid() const {
if (truncated && !updateLength) return INVALID("truncate but not updateLength");
return VALID;
}
};
struct SyncRsp : RspBase {
SERDE_STRUCT_FIELD(_unused, (uint32_t)0);
SERDE_STRUCT_FIELD(stat, Inode());
public:
SyncRsp() = default;
SyncRsp(Inode stat)
: stat(std::move(stat)) {}
};
// close
struct CloseReq : ReqBase {
SERDE_STRUCT_FIELD(inode, InodeId());
SERDE_STRUCT_FIELD(session, std::optional<SessionInfo>());
SERDE_STRUCT_FIELD(updateLength, false);
SERDE_STRUCT_FIELD(atime, std::optional<UtcTime>());
SERDE_STRUCT_FIELD(mtime, std::optional<UtcTime>());
SERDE_STRUCT_FIELD(lengthHint, std::optional<VersionedLength>());
public:
bool pruneSession = false;
CloseReq() = default;
CloseReq(UserInfo user,
InodeId inode,
std::optional<SessionInfo> session,
bool updateLength,
std::optional<UtcTime> atime,
std::optional<UtcTime> mtime)
: ReqBase(std::move(user)),
inode(inode),
session(session),
updateLength(updateLength),
atime(atime),
mtime(mtime) {}
Result<Void> valid() const {
if (updateLength || mtime.has_value()) CHECK_SESSION(session);
return VALID;
}
};
struct CloseRsp : RspBase {
SERDE_STRUCT_FIELD(_unused, (uint32_t)0);
SERDE_STRUCT_FIELD(stat, Inode());
public:
CloseRsp() = default;
CloseRsp(Inode stat)
: stat(std::move(stat)) {}
};
// rename
struct RenameReq : ReqBase {
SERDE_STRUCT_FIELD(src, PathAt());
SERDE_STRUCT_FIELD(dest, PathAt());
SERDE_STRUCT_FIELD(moveToTrash, false);
SERDE_STRUCT_FIELD(inodeId, std::optional<InodeId>());
public:
RenameReq() = default;
RenameReq(UserInfo user,
PathAt src,
PathAt dest,
bool moveToTrash = false,
std::optional<InodeId> inodeId = std::nullopt)
: ReqBase(std::move(user), Uuid::random()),
src(std::move(src)),
dest(std::move(dest)),
moveToTrash(moveToTrash),
inodeId(inodeId) {}
Result<Void> valid() const {
RETURN_ON_ERROR(src.validForCreate());
RETURN_ON_ERROR(dest.validForCreate());
if (moveToTrash) RETURN_ON_ERROR(checkUuid());
return VALID;
}
};
struct RenameRsp : RspBase {
SERDE_STRUCT_FIELD(dummy, Void{});
SERDE_STRUCT_FIELD(stat, std::optional<Inode>());
public:
RenameRsp() = default;
RenameRsp(Inode stat)
: stat(std::move(stat)) {}
};
// list
struct ListReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(prev, std::string());
SERDE_STRUCT_FIELD(limit, (int32_t)-1);
SERDE_STRUCT_FIELD(status, false);
public:
ListReq() = default;
ListReq(UserInfo user, PathAt path, std::string prev = {}, int32_t limit = -1, bool status = false)
: ReqBase(std::move(user)),
path(std::move(path)),
prev(std::move(prev)),
limit(limit),
status(status) {}
Result<Void> valid() const { return VALID; }
};
struct ListRsp : RspBase {
SERDE_STRUCT_FIELD(entries, std::vector<DirEntry>());
SERDE_STRUCT_FIELD(inodes, std::vector<Inode>());
SERDE_STRUCT_FIELD(more, false);
public:
ListRsp() = default;
ListRsp(std::vector<DirEntry> entries, std::vector<Inode> inodes, bool more)
: entries(std::move(entries)),
inodes(std::move(inodes)),
more(more) {}
};
// truncate
struct TruncateReq : ReqBase {
SERDE_STRUCT_FIELD(inode, InodeId(0));
SERDE_STRUCT_FIELD(length, uint64_t(0));
SERDE_STRUCT_FIELD(removeChunksBatchSize, uint32_t(0));
public:
TruncateReq() = default;
TruncateReq(UserInfo user, InodeId inode, uint64_t length, uint32_t removeChunksBatchSize)
: ReqBase(std::move(user)),
inode(inode),
length(length),
removeChunksBatchSize(removeChunksBatchSize) {}
Result<Void> valid() const {
if (removeChunksBatchSize == 0) return INVALID("removeChunksBatchSize == 0");
return VALID;
}
};
struct TruncateRsp : RspBase {
SERDE_STRUCT_FIELD(chunksRemoved, uint32_t(0));
SERDE_STRUCT_FIELD(stat, Inode());
SERDE_STRUCT_FIELD(finished, true);
public:
TruncateRsp() = default;
TruncateRsp(Inode stat)
: TruncateRsp(std::move(stat), 0, true) {}
TruncateRsp(Inode stat, uint32_t chunksRemoved, bool finished)
: chunksRemoved(chunksRemoved),
stat(std::move(stat)),
finished(finished) {}
};
// getRealPath
struct GetRealPathReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(absolute, false);
public:
GetRealPathReq() = default;
GetRealPathReq(UserInfo user, PathAt path, bool absolute)
: ReqBase(std::move(user)),
path(std::move(path)),
absolute(absolute) {}
Result<Void> valid() const { return VALID; }
};
struct GetRealPathRsp : RspBase {
SERDE_STRUCT_FIELD(path, Path());
public:
GetRealPathRsp() = default;
GetRealPathRsp(Path path)
: path(std::move(path)) {}
};
// setAttr
struct SetAttrReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(flags, AtFlags());
SERDE_STRUCT_FIELD(uid, std::optional<Uid>());
SERDE_STRUCT_FIELD(gid, std::optional<Gid>());
SERDE_STRUCT_FIELD(perm, std::optional<Permission>());
SERDE_STRUCT_FIELD(atime, std::optional<UtcTime>());
SERDE_STRUCT_FIELD(mtime, std::optional<UtcTime>());
SERDE_STRUCT_FIELD(layout, std::optional<Layout>());
SERDE_STRUCT_FIELD(iflags, std::optional<IFlags>());
SERDE_STRUCT_FIELD(dynStripe, uint32_t(0));
public:
static SetAttrReq setLayout(UserInfo user, PathAt path, AtFlags flags, Layout layout) {
return {std::move(user), std::move(path), flags, {}, {}, {}, {}, {}, std::move(layout)};
}
static SetAttrReq setPermission(UserInfo user,
PathAt path,
AtFlags flags,
std::optional<Uid> uid,
std::optional<Gid> gid,
std::optional<Permission> perm,
std::optional<IFlags> iflags = std::nullopt) {
return {std::move(user), std::move(path), flags, uid, gid, perm, {}, {}, {}, iflags};
}
static SetAttrReq setIFlags(UserInfo user, PathAt path, IFlags iflags) {
return {std::move(user), std::move(path), AtFlags(), {}, {}, {}, {}, {}, {}, iflags};
}
static SetAttrReq utimes(UserInfo user,
PathAt path,
AtFlags flags,
std::optional<UtcTime> atime,
std::optional<UtcTime> mtime) {
return {std::move(user), std::move(path), flags, {}, {}, {}, atime, mtime, {}};
}
static SetAttrReq extendStripe(UserInfo user, InodeId inode, uint32_t stripe) {
return {std::move(user), PathAt(inode), AtFlags{}, {}, {}, {}, {}, {}, {}, {}, stripe};
}
Result<Void> valid() const {
if (layout.has_value()) RETURN_ON_ERROR(layout->valid(true));
if (iflags && (*iflags & ~FS_FL_SUPPORTED)) {
return MAKE_ERROR_F(StatusCode::kInvalidArg, "only support {:x}", (uint32_t)FS_FL_SUPPORTED);
}
return VALID;
}
};
struct SetAttrRsp : RspBase {
SERDE_STRUCT_FIELD(stat, Inode());
public:
SetAttrRsp() = default;
SetAttrRsp(Inode stat)
: stat(std::move(stat)) {}
};
// pruneSession
struct PruneSessionReq : ReqBase {
SERDE_STRUCT_FIELD(client, ClientId::zero());
SERDE_STRUCT_FIELD(sessions, std::vector<Uuid>());
SERDE_STRUCT_FIELD(needSync, std::vector<bool>()); // deperated
public:
PruneSessionReq() = default;
PruneSessionReq(ClientId client, std::vector<Uuid> sessions)
: ReqBase(),
client(client),
sessions(std::move(sessions)) {}
Result<Void> valid() const { return VALID; }
};
struct PruneSessionRsp : RspBase {
SERDE_STRUCT_FIELD(dummy, Void{});
};
// dropUserCache
struct DropUserCacheReq : ReqBase {
SERDE_STRUCT_FIELD(uid, std::optional<Uid>());
SERDE_STRUCT_FIELD(dropAll, false);
public:
DropUserCacheReq() = default;
DropUserCacheReq(std::optional<Uid> u, bool d)
: uid(std::move(u)),
dropAll(d) {}
Result<Void> valid() const { return VALID; }
};
struct DropUserCacheRsp : RspBase {
SERDE_STRUCT_FIELD(dummy, Void{});
};
// lockDirectory
struct LockDirectoryReq : ReqBase {
enum class LockAction : uint8_t {
TryLock,
PreemptLock,
UnLock,
Clear,
};
SERDE_STRUCT_FIELD(inode, InodeId());
SERDE_STRUCT_FIELD(action, LockAction::TryLock);
public:
LockDirectoryReq() = default;
LockDirectoryReq(UserInfo user, InodeId inode, LockAction action)
: ReqBase(user),
inode(inode),
action(action) {}
Result<Void> valid() const { return VALID; }
};
struct LockDirectoryRsp : RspBase {
SERDE_STRUCT_FIELD(dummy, Void{});
public:
LockDirectoryRsp() = default;
};
// testRpc
struct TestRpcReq : ReqBase {
SERDE_STRUCT_FIELD(path, PathAt());
SERDE_STRUCT_FIELD(flags, uint32_t(0));
public:
Result<Void> valid() const { return VALID; }
};
struct TestRpcRsp : RspBase {
SERDE_STRUCT_FIELD(stat, Inode());
};
/* MetaSerde service */
SERDE_SERVICE(MetaSerde, 4) {
#define META_SERVICE_METHOD(NAME, CODE, REQ, RSP) \
SERDE_SERVICE_METHOD(NAME, CODE, REQ, RSP); \
\
public: \
static constexpr std::string_view getRpcName(const REQ &) { return #NAME; } \
static_assert(serde::SerializableToBytes<REQ> && serde::SerializableToJson<REQ>); \
static_assert(serde::SerializableToBytes<RSP> && serde::SerializableToJson<RSP>)
META_SERVICE_METHOD(statFs, 1, StatFsReq, StatFsRsp);
META_SERVICE_METHOD(stat, 2, StatReq, StatRsp);
META_SERVICE_METHOD(create, 3, CreateReq, CreateRsp);
META_SERVICE_METHOD(mkdirs, 4, MkdirsReq, MkdirsRsp);
META_SERVICE_METHOD(symlink, 5, SymlinkReq, SymlinkRsp);
META_SERVICE_METHOD(hardLink, 6, HardLinkReq, HardLinkRsp);
META_SERVICE_METHOD(remove, 7, RemoveReq, RemoveRsp);
META_SERVICE_METHOD(open, 8, OpenReq, OpenRsp);
META_SERVICE_METHOD(sync, 9, SyncReq, SyncRsp);
META_SERVICE_METHOD(close, 10, CloseReq, CloseRsp);
META_SERVICE_METHOD(rename, 11, RenameReq, RenameRsp);
META_SERVICE_METHOD(list, 12, ListReq, ListRsp);
// deperated:
META_SERVICE_METHOD(truncate, 13, TruncateReq, TruncateRsp);
META_SERVICE_METHOD(getRealPath, 14, GetRealPathReq, GetRealPathRsp);
META_SERVICE_METHOD(setAttr, 15, SetAttrReq, SetAttrRsp);
META_SERVICE_METHOD(pruneSession, 16, PruneSessionReq, PruneSessionRsp);
META_SERVICE_METHOD(dropUserCache, 17, DropUserCacheReq, DropUserCacheRsp);
META_SERVICE_METHOD(authenticate, 18, AuthReq, AuthRsp);
META_SERVICE_METHOD(lockDirectory, 19, LockDirectoryReq, LockDirectoryRsp);
META_SERVICE_METHOD(batchStat, 20, BatchStatReq, BatchStatRsp);
META_SERVICE_METHOD(batchStatByPath, 21, BatchStatByPathReq, BatchStatByPathRsp);
META_SERVICE_METHOD(testRpc, 50, TestRpcReq, TestRpcRsp);
#undef META_SERVICE_METHOD
};
} // namespace hf3fs::meta

447
src/fbs/meta/Utils.h Normal file
View File

@@ -0,0 +1,447 @@
#pragma once
#include <array>
#include <cassert>
#include <cstdint>
#include <folly/Conv.h>
#include <folly/logging/xlog.h>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
#include "common/app/NodeId.h"
#include "common/monitor/Recorder.h"
#include "common/monitor/Sample.h"
#include "common/serde/Serde.h"
#include "common/utils/Duration.h"
#include "common/utils/MurmurHash3.h"
#include "common/utils/Reflection.h"
#include "common/utils/Result.h"
#include "common/utils/SerDeser.h"
#include "common/utils/Status.h"
#include "common/utils/StatusCode.h"
#include "common/utils/TypeTraits.h"
#include "common/utils/Uuid.h"
#include "fbs/core/user/User.h"
#include "fbs/meta/Common.h"
#include "fbs/meta/Schema.h"
#include "fbs/meta/Service.h"
#include "fbs/mgmtd/ChainRef.h"
#include "fbs/mgmtd/RoutingInfo.h"
#include "fmt/core.h"
namespace hf3fs::meta {
struct ErrorHandling {
static bool success(const auto &result) { return !result.hasError() || success(result.error()); }
// The operation was performed successfully, or an expected error code was returned
static bool success(const Status &status) {
auto code = status.code();
switch (StatusCode::typeOf(code)) {
case StatusCodeType::Common:
switch (code) {
case StatusCode::kOK:
case StatusCode::kInvalidArg:
case StatusCode::kAuthenticationFail:
return true;
default:
return false;
}
case StatusCodeType::Meta:
switch (code) {
case MetaCode::kNotFound:
case MetaCode::kNotEmpty:
case MetaCode::kNotDirectory:
case MetaCode::kTooManySymlinks:
case MetaCode::kIsDirectory:
case MetaCode::kExists:
case MetaCode::kNoPermission:
case MetaCode::kNotFile:
case MetaCode::kInvalidFileLayout:
case MetaCode::kMoreChunksToRemove:
case MetaCode::kNameTooLong:
return true;
default:
return (code >= MetaCode::kExpected && code < MetaCode::kRetryable);
}
default:
return false;
}
}
static bool retryable(const Status &status) {
auto code = status.code();
switch (StatusCode::typeOf(code)) {
case StatusCodeType::Common:
return false;
case StatusCodeType::Meta:
switch (code) {
case MetaCode::kNotFound:
case MetaCode::kNotEmpty:
case MetaCode::kNotDirectory:
case MetaCode::kTooManySymlinks:
case MetaCode::kIsDirectory:
case MetaCode::kExists:
case MetaCode::kNoPermission:
case MetaCode::kInconsistent:
case MetaCode::kNotFile:
case MetaCode::kBadFileSystem:
case MetaCode::kInvalidFileLayout:
case MetaCode::kFileHasHole:
case MetaCode::kNameTooLong:
case MetaCode::kRequestCanceled:
case MetaCode::kFoundBug:
return false;
case MetaCode::kOTruncFailed:
case MetaCode::kMoreChunksToRemove:
case MetaCode::kBusy:
case MetaCode::kInodeIdAllocFailed:
return true;
default:
return code < MetaCode::kNotRetryable;
}
case StatusCodeType::StorageClient:
switch (code) {
case StorageClientCode::kChunkNotFound:
case StorageClientCode::kChecksumMismatch:
case StorageClientCode::kReadOnlyServer:
case StorageClientCode::kFoundBug:
return false;
default:
return true;
}
default:
return code != RPCCode::kInvalidMethodID;
}
}
static bool serverError(const Status &status) {
switch (StatusCode::typeOf(status.code())) {
case StatusCodeType::Transaction:
return status.code() == TransactionCode::kNetworkError;
case StatusCodeType::RPC:
return true;
default:
return false;
}
}
static bool needPruneSession(const Status &error) {
auto code = error.code();
switch (StatusCode::typeOf(code)) {
case StatusCodeType::Transaction:
return code == TransactionCode::kMaybeCommitted;
case StatusCodeType::Meta:
return code == MetaCode::kOTruncFailed;
case StatusCodeType::RPC:
switch (code) {
case RPCCode::kSendFailed:
case RPCCode::kConnectFailed:
case RPCCode::kIBInitFailed:
case RPCCode::kInvalidMethodID:
return false;
default:
return true;
}
default:
return false;
}
}
};
class OperationRecorder {
public:
OperationRecorder(std::string_view prefix)
: total_(fmt::format("{}_total", prefix)),
failed_(fmt::format("{}_failed", prefix)),
running_(fmt::format("{}_running", prefix), false),
code_(fmt::format("{}_code", prefix)),
latency_(fmt::format("{}_latency", prefix)),
retry_(fmt::format("{}_retry", prefix)),
idempotent_(fmt::format("{}_idempotent", prefix)),
duplicate_(fmt::format("{}_duplicate", prefix)) {}
static OperationRecorder &server() {
static OperationRecorder recorder("meta_server.op");
return recorder;
}
static OperationRecorder &client() {
static OperationRecorder recorder("meta_client.op");
return recorder;
}
class Guard {
public:
Guard(OperationRecorder &recorder, std::string_view op, flat::Uid user)
: recorder_(recorder),
op_("instance", std::string(op)),
user_("uid", folly::to<std::string>((int32_t)user.toUnderType())),
begin_(RelativeTime::now()) {
begin_ = RelativeTime::now();
recorder_.running_.addSample(1, {{op_}});
}
~Guard() {
auto op = monitor::TagSet{op_};
auto opUser = monitor::TagSet{{op_, user_}};
auto latency = RelativeTime::now() - begin_;
recorder_.total_.addSample(1, opUser);
if (!succ_) {
recorder_.failed_.addSample(1, opUser);
recorder_.code_.addSample(1, {{"tag", folly::to<std::string>(code_.value_or(0))}, user_});
} else {
if (code_) {
recorder_.code_.addSample(1, {{"tag", folly::to<std::string>(*code_)}, user_});
}
}
recorder_.running_.addSample(-1, op);
recorder_.latency_.addSample(latency, op);
recorder_.retry_.addSample(retry_, op);
if (duplicate_) {
recorder_.duplicate_.addSample(1, op);
}
}
void finish(const auto &result, bool duplicate = false) {
duplicate_ = duplicate;
succ_ = ErrorHandling::success(result);
code_ = result.hasError() ? result.error().code() : 0;
}
void finish(const Status &status, bool duplicate = false) {
duplicate_ = duplicate;
succ_ = ErrorHandling::success(status) || status.code() == MetaCode::kRequestCanceled;
code_ = status.code();
}
int &retry() { return retry_; }
private:
using Tag = std::pair<std::string, std::string>;
OperationRecorder &recorder_;
Tag op_;
Tag user_;
RelativeTime begin_;
std::optional<status_code_t> code_;
bool succ_ = false;
int retry_ = 1;
bool duplicate_ = false;
};
void addIdempotentCount() { idempotent_.addSample(1); }
private:
monitor::CountRecorder total_;
monitor::CountRecorder failed_;
monitor::CountRecorder running_;
monitor::CountRecorder code_;
monitor::LatencyRecorder latency_;
monitor::DistributionRecorder retry_;
monitor::CountRecorder idempotent_;
monitor::CountRecorder duplicate_;
};
struct Weight : std::array<uint8_t, 16> {
static Weight calculate(flat::NodeId node, InodeId inodeId) {
// Note: don't change this
auto key = Serializer::serRawArgs((uint64_t)node.toUnderType(), inodeId.u64());
return hash(key.data(), key.size());
}
static flat::NodeId select(const std::vector<flat::NodeId> &nodes, InodeId inodeId) {
return selectImpl(nodes, inodeId);
}
static Weight calculate(flat::NodeId node, Uuid clientId) {
// todo: maybe should change to client host name?
auto key = Serializer::serRawArgs((uint64_t)node.toUnderType(), clientId.data);
return hash(key.data(), key.size());
}
static flat::NodeId select(const std::vector<flat::NodeId> &nodes, Uuid clientId) {
return selectImpl(nodes, clientId);
}
private:
static Weight hash(void *key, size_t len) {
// NOTE: don't change this
Weight w;
MurmurHash3_x64_128(key, len, 0, &w);
return w;
}
static flat::NodeId selectImpl(const std::vector<flat::NodeId> &nodes, auto &key) {
if (nodes.empty()) {
return flat::NodeId(0); // flat::NodeId(0) is invalid
}
auto node = nodes.at(0);
auto weight = calculate(node, key);
for (size_t i = 1; i < nodes.size(); i++) {
auto n = nodes.at(i);
auto w = calculate(n, key);
if (w > weight) {
node = n;
weight = w;
}
}
return node;
}
};
static_assert(sizeof(Weight) == 16);
struct RoutingInfoChecker {
template <typename T>
static constexpr bool hasInode() {
return false;
}
template <typename T>
requires serde::SerdeType<T>
static constexpr bool hasInode() {
if constexpr (std::is_same_v<Inode, T>) {
return true;
}
bool inode = false;
refl::Helper::iterate<T>([&](auto info) {
using Item = std::remove_reference_t<decltype(std::declval<T>().*info.getter)>;
inode |= hasInode<Item>();
});
return inode;
}
template <typename T>
requires is_variant_v<T>
static constexpr bool hasInode() { return variantHasInode<T>(); }
template <typename T, size_t I = 0>
static constexpr bool variantHasInode() {
if constexpr (I < std::variant_size_v<T>) {
if constexpr (hasInode<std::variant_alternative<I, T>>()) {
return true;
}
return variantHasInode<T, I + 1>();
}
return false;
}
template <typename T>
requires is_vector_v<T> || is_set_v<T>
static constexpr bool hasInode() { return hasInode<typename T::value_type>(); }
template <typename T>
requires is_map_v<T>
static constexpr bool hasInode() { return hasInode<typename T::key_type>() || hasInode<typename T::mapped_type>(); }
template <typename T>
requires is_optional_v<T>
static constexpr bool hasInode() { return hasInode<typename T::value_type>(); }
static bool checkRoutingInfo(const Inode &inode, const flat::RoutingInfo &routing) {
if (inode.isFile()) {
auto table = inode.asFile().layout.tableId;
auto tableVer = inode.asFile().layout.tableVersion;
switch (inode.asFile().layout.type()) {
case Layout::Type::ChainRange:
XLOGF_IF(DFATAL, (!table || !tableVer), "File {}, invalid layout", inode);
if (!routing.getChainTable(table, tableVer)) {
XLOGF(WARN, "File {}, chain table {} version {}, not found in RoutingInfo", inode.id, table, tableVer);
return false;
}
break;
case Layout::Type::ChainList:
if (table && tableVer && !routing.getChainTable(table, tableVer)) {
XLOGF(WARN, "File {}, chain table {} version {}, not found in RoutingInfo", inode.id, table, tableVer);
return false;
}
break;
case Layout::Type::Empty:
break;
}
}
return true;
}
template <typename T>
static bool checkRoutingInfo(const T &, const flat::RoutingInfo &) {
return true;
}
template <typename T>
requires serde::SerdeType<T>
static bool checkRoutingInfo(const T &t, const flat::RoutingInfo &routing) {
if constexpr (!hasInode<T>()) {
return true;
}
bool succ = true;
refl::Helper::iterate<T>([&](auto info) { succ &= checkRoutingInfo(t.*info.getter, routing); });
return succ;
}
template <typename T>
requires is_variant_v<T>
static bool checkRoutingInfo(const T &t, const flat::RoutingInfo &routing) {
if constexpr (!hasInode<T>()) {
return true;
}
return std::visit([&](const auto &v) { return checkRoutingInfo(v, routing); }, t);
}
template <typename T>
requires is_vector_v<T> || is_set_v<T>
static bool checkRoutingInfo(const T &t, const flat::RoutingInfo &routing) {
if constexpr (!hasInode<T>()) {
return true;
}
for (auto &i : t) {
if (!checkRoutingInfo(i, routing)) {
return false;
}
}
return true;
}
template <typename T>
requires is_map_v<T>
static bool checkRoutingInfo(const T &t, const flat::RoutingInfo &routing) {
if constexpr (!hasInode<T>()) {
return true;
}
for (auto &[k, v] : t) {
if (!checkRoutingInfo(k, routing) || !checkRoutingInfo(v, routing)) {
return false;
}
}
return true;
}
template <typename T>
requires is_optional_v<T>
static bool checkRoutingInfo(const T &t, const flat::RoutingInfo &routing) {
if constexpr (!hasInode<T>()) {
return true;
}
return t ? checkRoutingInfo(*t, routing) : true;
}
};
static_assert(RoutingInfoChecker::hasInode<Inode>());
static_assert(RoutingInfoChecker::hasInode<std::vector<Inode>>());
static_assert(RoutingInfoChecker::hasInode<std::set<Inode>>());
static_assert(RoutingInfoChecker::hasInode<std::optional<Inode>>());
static_assert(RoutingInfoChecker::hasInode<std::map<Inode, int>>());
static_assert(RoutingInfoChecker::hasInode<std::map<int, Inode>>());
static_assert(RoutingInfoChecker::hasInode<OpenRsp>());
static_assert(RoutingInfoChecker::hasInode<StatRsp>());
static_assert(RoutingInfoChecker::hasInode<SyncRsp>());
static_assert(RoutingInfoChecker::hasInode<CloseRsp>());
static_assert(RoutingInfoChecker::hasInode<BatchStatRsp>());
} // namespace hf3fs::meta

View File

@@ -0,0 +1,2 @@
target_add_lib(mgmtd-fbs common)

View File

@@ -0,0 +1,7 @@
#include "ChainInfo.h"
#include "common/serde/SerdeComparisons.h"
namespace hf3fs::flat {
bool ChainInfo::operator==(const ChainInfo &other) const { return serde::equals(*this, other); }
} // namespace hf3fs::flat

14
src/fbs/mgmtd/ChainInfo.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "ChainTargetInfo.h"
namespace hf3fs::flat {
struct ChainInfo : public serde::SerdeHelper<ChainInfo> {
SERDE_STRUCT_FIELD(chainId, ChainId(0));
SERDE_STRUCT_FIELD(chainVersion, ChainVersion(0));
SERDE_STRUCT_FIELD(targets, std::vector<ChainTargetInfo>{});
SERDE_STRUCT_FIELD(preferredTargetOrder, std::vector<TargetId>{});
bool operator==(const ChainInfo &other) const;
};
} // namespace hf3fs::flat

49
src/fbs/mgmtd/ChainRef.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include <fmt/core.h>
#include <functional>
#include "MgmtdTypes.h"
#include "common/utils/StrongType.h"
namespace hf3fs::flat {
class ChainRef {
public:
ChainRef() {}
ChainRef(ChainTableId tableId, ChainTableVersion tableVer, uint64_t index)
: chainTableId(tableId),
chainTableVersion(tableVer),
chainIndex(index) {}
std::tuple<ChainTableId, ChainTableVersion, uint64_t> decode() const {
return std::make_tuple(chainTableId, chainTableVersion, chainIndex);
}
ChainTableId tableId() const { return chainTableId; }
ChainTableVersion tableVersion() const { return chainTableVersion; }
uint64_t index() const { return chainIndex; }
auto operator<=>(const ChainRef &) const = default;
private:
ChainTableId chainTableId{0};
ChainTableVersion chainTableVersion{0};
uint64_t chainIndex{0};
};
} // namespace hf3fs::flat
FMT_BEGIN_NAMESPACE
template <>
struct formatter<hf3fs::flat::ChainRef> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::flat::ChainRef &ref, FormatContext &ctx) const {
auto [id, v, i] = ref.decode();
return format_to(ctx.out(), "ChainRef({}@{}-{})", id.toUnderType(), v.toUnderType(), i);
}
};
FMT_END_NAMESPACE

View File

@@ -0,0 +1,11 @@
#pragma once
#include "ChainTargetSetting.h"
namespace hf3fs::flat {
struct ChainSetting : public serde::SerdeHelper<ChainSetting> {
SERDE_STRUCT_FIELD(chainId, ChainId(0));
SERDE_STRUCT_FIELD(targets, std::vector<ChainTargetSetting>{});
SERDE_STRUCT_FIELD(setPreferredTargetOrder, false);
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,15 @@
#pragma once
#include "ChainInfo.h"
#include "common/serde/SerdeComparisons.h"
namespace hf3fs::flat {
struct ChainTable : public serde::SerdeHelper<ChainTable> {
bool operator==(const ChainTable &other) const { return serde::equals(*this, other); }
SERDE_STRUCT_FIELD(chainTableId, ChainTableId(0));
SERDE_STRUCT_FIELD(chainTableVersion, ChainTableVersion(0));
SERDE_STRUCT_FIELD(chains, std::vector<ChainId>{});
SERDE_STRUCT_FIELD(desc, String{});
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,14 @@
#pragma once
#include "MgmtdTypes.h"
#include "common/serde/SerdeComparisons.h"
#include "common/serde/SerdeHelper.h"
namespace hf3fs::flat {
struct ChainTargetInfo : public serde::SerdeHelper<ChainTargetInfo> {
bool operator==(const ChainTargetInfo &other) const { return serde::equals(*this, other); }
SERDE_STRUCT_FIELD(targetId, TargetId(0));
SERDE_STRUCT_FIELD(publicState, PublicTargetState(PublicTargetState::INVALID));
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,13 @@
#pragma once
#include "MgmtdTypes.h"
#include "common/serde/SerdeComparisons.h"
#include "common/serde/SerdeHelper.h"
namespace hf3fs::flat {
struct ChainTargetSetting : public serde::SerdeHelper<ChainTargetSetting> {
SERDE_STRUCT_FIELD(targetId, TargetId(0));
auto operator<=>(const ChainTargetSetting &other) const { return serde::compare(*this, other); }
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,18 @@
#include "ClientSession.h"
#include "Rpc.h"
namespace hf3fs::flat {
ClientSession::ClientSession(const mgmtd::ExtendClientSessionReq &req, UtcTime now)
: clientId(req.clientId),
configVersion(req.configVersion),
start(now),
lastExtend(now),
universalId(req.data.universalId),
description(req.data.description),
serviceGroups(req.data.serviceGroups),
releaseVersion(req.data.releaseVersion),
configStatus(req.configStatus),
type(req.type),
clientStart(req.clientStart) {}
} // namespace hf3fs::flat

View File

@@ -0,0 +1,29 @@
#pragma once
#include "ClientSessionData.h"
#include "common/app/ConfigStatus.h"
#include "common/utils/UtcTimeSerde.h"
namespace hf3fs::mgmtd {
struct ExtendClientSessionReq;
}
namespace hf3fs::flat {
struct ClientSession : public serde::SerdeHelper<ClientSession> {
SERDE_STRUCT_FIELD(clientId, String{});
SERDE_STRUCT_FIELD(configVersion, ConfigVersion(0));
SERDE_STRUCT_FIELD(start, UtcTime{});
SERDE_STRUCT_FIELD(lastExtend, UtcTime{});
SERDE_STRUCT_FIELD(universalId, String{});
SERDE_STRUCT_FIELD(description, String{});
SERDE_STRUCT_FIELD(serviceGroups, std::vector<ServiceGroupInfo>{});
SERDE_STRUCT_FIELD(releaseVersion, ReleaseVersion{});
SERDE_STRUCT_FIELD(configStatus, ConfigStatus::NORMAL);
SERDE_STRUCT_FIELD(type, flat::NodeType::CLIENT);
SERDE_STRUCT_FIELD(clientStart, UtcTime{});
public:
ClientSession() = default;
explicit ClientSession(const mgmtd::ExtendClientSessionReq &req, UtcTime now = UtcClock::now());
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,17 @@
#pragma once
#include "MgmtdTypes.h"
#include "common/app/AppInfo.h"
#include "common/serde/SerdeComparisons.h"
#include "common/serde/SerdeHelper.h"
namespace hf3fs::flat {
struct ClientSessionData : public serde::SerdeHelper<ClientSessionData> {
bool operator==(const ClientSessionData &other) const { return serde::equals(*this, other); }
SERDE_STRUCT_FIELD(universalId, String{});
SERDE_STRUCT_FIELD(description, String{});
SERDE_STRUCT_FIELD(serviceGroups, std::vector<ServiceGroupInfo>{});
SERDE_STRUCT_FIELD(releaseVersion, ReleaseVersion{});
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,15 @@
#pragma once
#include "MgmtdTypes.h"
#include "common/serde/SerdeHelper.h"
namespace hf3fs::flat {
struct ConfigInfo : public serde::SerdeHelper<ConfigInfo> {
SERDE_STRUCT_FIELD(configVersion, ConfigVersion(0));
SERDE_STRUCT_FIELD(content, String{});
SERDE_STRUCT_FIELD(desc, String{});
public:
String genUpdateDesc() const { return fmt::format("version: {} desc: {}", configVersion.toUnderType(), desc); }
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,94 @@
#pragma once
#include <variant>
#include "LocalTargetInfo.h"
#include "MgmtdTypes.h"
#include "common/app/AppInfo.h"
#include "common/app/ConfigStatus.h"
namespace hf3fs::flat {
class MetaHeartbeatInfo : public serde::SerdeHelper<MetaHeartbeatInfo> {
public:
static constexpr auto kTypeCode = NodeType::META;
SERDE_STRUCT_FIELD(dummy, Void{});
};
class StorageHeartbeatInfo : public serde::SerdeHelper<StorageHeartbeatInfo> {
public:
static constexpr auto kTypeCode = NodeType::STORAGE;
SERDE_STRUCT_FIELD(targets, std::vector<LocalTargetInfo>{});
};
class MgmtdHeartbeatInfo : public serde::SerdeHelper<MgmtdHeartbeatInfo> {
public:
static constexpr auto kTypeCode = NodeType::MGMTD;
SERDE_STRUCT_FIELD(dummy, Void{});
};
class HeartbeatInfo : public serde::SerdeHelper<HeartbeatInfo> {
public:
HeartbeatInfo() = default;
explicit HeartbeatInfo(FbsAppInfo appInfo)
: app(std::move(appInfo)) {}
HeartbeatInfo(FbsAppInfo appInfo, MetaHeartbeatInfo info)
: HeartbeatInfo(std::move(appInfo)) {
set(std::move(info));
}
HeartbeatInfo(FbsAppInfo appInfo, StorageHeartbeatInfo info)
: HeartbeatInfo(std::move(appInfo)) {
set(std::move(info));
}
HeartbeatInfo(FbsAppInfo appInfo, MgmtdHeartbeatInfo info)
: HeartbeatInfo(std::move(appInfo)) {
set(std::move(info));
}
NodeType type() const {
return std::visit(
[](auto &&v) {
using T = std::decay_t<decltype(v)>;
return T::kTypeCode;
},
info_);
}
MetaHeartbeatInfo &asMeta() { return std::get<MetaHeartbeatInfo>(info_); }
const MetaHeartbeatInfo &asMeta() const { return std::get<MetaHeartbeatInfo>(info_); }
StorageHeartbeatInfo &asStorage() { return std::get<StorageHeartbeatInfo>(info_); }
const StorageHeartbeatInfo &asStorage() const { return std::get<StorageHeartbeatInfo>(info_); }
MgmtdHeartbeatInfo &asMgmtd() { return std::get<MgmtdHeartbeatInfo>(info_); }
const MgmtdHeartbeatInfo &asMgmtd() const { return std::get<MgmtdHeartbeatInfo>(info_); }
void set(MetaHeartbeatInfo info) { info_ = std::move(info); }
void set(StorageHeartbeatInfo info) { info_ = std::move(info); }
void set(MgmtdHeartbeatInfo info) { info_ = std::move(info); }
using Payload = std::variant<MetaHeartbeatInfo, StorageHeartbeatInfo, MgmtdHeartbeatInfo>;
void set(Payload payload) { info_ = std::move(payload); }
SERDE_CLASS_FIELD(info, Payload{});
SERDE_STRUCT_FIELD(app, FbsAppInfo{});
SERDE_STRUCT_FIELD(hbVersion,
HeartbeatVersion(1)); // HeartbeatVersion should be larger than server's so use 1 as default value
SERDE_STRUCT_FIELD(
configVersion,
ConfigVersion(0)); // ConfigVersion should be smaller than or equal to server's so use 0 as default value
SERDE_STRUCT_FIELD(configStatus, ConfigStatus::NORMAL);
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,18 @@
#pragma once
#include "MgmtdTypes.h"
#include "common/serde/SerdeComparisons.h"
#include "common/serde/SerdeHelper.h"
namespace hf3fs::flat {
struct LocalTargetInfo : public serde::SerdeHelper<LocalTargetInfo> {
bool operator==(const LocalTargetInfo &other) const { return serde::equals(*this, other); }
SERDE_STRUCT_FIELD(targetId, TargetId(0));
SERDE_STRUCT_FIELD(localState, LocalTargetState(LocalTargetState::INVALID));
SERDE_STRUCT_FIELD(diskIndex, std::optional<uint32_t>{});
SERDE_STRUCT_FIELD(usedSize, uint64_t{});
SERDE_STRUCT_FIELD(chainVersion, ChainVersion{});
SERDE_STRUCT_FIELD(lowSpace, false);
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,25 @@
#pragma once
#include "PersistentNodeInfo.h"
#include "common/utils/UtcTimeSerde.h"
namespace hf3fs::flat {
struct MgmtdLeaseInfo : public serde::SerdeHelper<MgmtdLeaseInfo> {
MgmtdLeaseInfo() = default;
MgmtdLeaseInfo(PersistentNodeInfo primaryInfo, UtcTime leaseStart, UtcTime leaseEnd)
: primary(std::move(primaryInfo)),
leaseStart(leaseStart),
leaseEnd(leaseEnd),
releaseVersion(ReleaseVersion::fromVersionInfo()) {}
MgmtdLeaseInfo(PersistentNodeInfo primaryInfo, UtcTime leaseStart, UtcTime leaseEnd, ReleaseVersion rv)
: primary(std::move(primaryInfo)),
leaseStart(leaseStart),
leaseEnd(leaseEnd),
releaseVersion(rv) {}
SERDE_STRUCT_FIELD(primary, PersistentNodeInfo{});
SERDE_STRUCT_FIELD(leaseStart, UtcTime{});
SERDE_STRUCT_FIELD(leaseEnd, UtcTime{});
SERDE_STRUCT_FIELD(releaseVersion, ReleaseVersion());
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,12 @@
#pragma once
#include "Rpc.h"
#include "common/serde/Service.h"
namespace hf3fs::mgmtd {
SERDE_SERVICE_2(MgmtdServiceBase, Mgmtd, 217) {
#define DEFINE_SERDE_SERVICE_METHOD_FULL(svc, name, Name, id, reqtype, rsptype) \
SERDE_SERVICE_METHOD_REFL(name, id, reqtype, rsptype);
#include "MgmtdServiceDef.h"
};
} // namespace hf3fs::mgmtd

View File

@@ -0,0 +1,7 @@
#pragma once
#include "MgmtdServiceBase.h"
namespace hf3fs::mgmtd {
SERDE_SERVICE_CLIENT(MgmtdServiceClient, MgmtdServiceBase);
} // namespace hf3fs::mgmtd

View File

@@ -0,0 +1,28 @@
#include "fbs/macros/SerdeDef.h"
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, getPrimaryMgmtd, GetPrimaryMgmtd, 1)
// 2 is deprecated
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, heartbeat, Heartbeat, 3)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, registerNode, RegisterNode, 4)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, getRoutingInfo, GetRoutingInfo, 5)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, setConfig, SetConfig, 6)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, getConfig, GetConfig, 7)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, setChainTable, SetChainTable, 8)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, enableNode, EnableNode, 9)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, disableNode, DisableNode, 10)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, extendClientSession, ExtendClientSession, 11)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, listClientSessions, ListClientSessions, 12)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, setNodeTags, SetNodeTags, 13)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, unregisterNode, UnregisterNode, 14)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, setChains, SetChains, 15)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, setUniversalTags, SetUniversalTags, 16)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, getUniversalTags, GetUniversalTags, 17)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, getConfigVersions, GetConfigVersions, 18)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, getClientSession, GetClientSession, 19)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, rotateLastSrv, RotateLastSrv, 20)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, listOrphanTargets, ListOrphanTargets, 21)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, setPreferredTargetOrder, SetPreferredTargetOrder, 22)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, rotateAsPreferredOrder, RotateAsPreferredOrder, 23)
DEFINE_SERDE_SERVICE_METHOD(Mgmtd, updateChain, UpdateChain, 24)
#include "fbs/macros/Undef.h"

View File

@@ -0,0 +1,57 @@
#pragma once
#include "common/app/NodeId.h"
#include "common/utils/Address.h"
#include "common/utils/Result.h"
#include "common/utils/StrongType.h"
#include "common/utils/Uuid.h"
namespace hf3fs::flat {
enum class PublicTargetState : uint8_t {
INVALID = 0,
SERVING = 1,
LASTSRV = 2,
SYNCING = 4,
WAITING = 8,
OFFLINE = 16,
MIN = INVALID,
MAX = OFFLINE
};
enum class LocalTargetState : uint8_t {
INVALID = 0,
UPTODATE = 1,
ONLINE = 2,
OFFLINE = 4,
MIN = INVALID,
MAX = OFFLINE
};
enum class NodeStatus : uint8_t {
PRIMARY_MGMTD = 0,
HEARTBEAT_CONNECTED = 1,
HEARTBEAT_CONNECTING = 2,
HEARTBEAT_FAILED = 3,
DISABLED = 4,
MIN = HEARTBEAT_CONNECTED,
MAX = DISABLED
};
enum class NodeType : uint8_t { MGMTD = 0, META = 1, STORAGE = 2, CLIENT = 3, FUSE = 4, MIN = MGMTD, MAX = FUSE };
enum class SetTagMode : uint8_t { REPLACE = 0, UPSERT = 1, REMOVE = 2, MIN = REPLACE, MAX = REMOVE };
STRONG_TYPEDEF(uint64_t, TargetId);
STRONG_TYPEDEF(uint64_t, HeartbeatVersion);
STRONG_TYPEDEF(uint64_t, RoutingInfoVersion);
STRONG_TYPEDEF(uint64_t, ConfigVersion);
STRONG_TYPEDEF(uint64_t, ClientSessionVersion);
// ChainVersion is space sensitive and uint32 is enough for changes of one chain
STRONG_TYPEDEF(uint32_t, ChainVersion);
STRONG_TYPEDEF(uint32_t, ChainTableId);
STRONG_TYPEDEF(uint32_t, ChainTableVersion);
STRONG_TYPEDEF(uint32_t, ChainId);
} // namespace hf3fs::flat

View File

@@ -0,0 +1,16 @@
#include "NodeConversion.h"
namespace hf3fs::flat {
NodeInfo toNode(const PersistentNodeInfo &pn) {
auto status = findTag(pn.tags, kDisabledTagKey) == -1 ? NodeStatus::HEARTBEAT_CONNECTING : NodeStatus::DISABLED;
return NodeInfo::create(FbsAppInfo::create(pn.nodeId, pn.hostname, 0u, pn.serviceGroups),
pn.type,
status,
UtcTime{},
pn.tags);
}
PersistentNodeInfo toPersistentNode(const NodeInfo &node) {
return PersistentNodeInfo::create(node.app.nodeId, node.type, node.app.serviceGroups, node.tags, node.app.hostname);
}
} // namespace hf3fs::flat

View File

@@ -0,0 +1,9 @@
#pragma once
#include "NodeInfo.h"
#include "PersistentNodeInfo.h"
namespace hf3fs::flat {
NodeInfo toNode(const PersistentNodeInfo &pn);
PersistentNodeInfo toPersistentNode(const NodeInfo &node);
} // namespace hf3fs::flat

19
src/fbs/mgmtd/NodeInfo.cc Normal file
View File

@@ -0,0 +1,19 @@
#include "NodeInfo.h"
namespace hf3fs::flat {
std::vector<net::Address> NodeInfo::extractAddresses(const String &serviceName,
std::optional<net::Address::Type> addressType) const {
return flat::extractAddresses(app.serviceGroups, serviceName, addressType);
}
std::map<String, std::vector<net::Address>> NodeInfo::getAllServices() const {
std::map<String, std::vector<net::Address>> res;
for (const auto &group : app.serviceGroups) {
for (const auto &service : group.services) {
auto &v = res[service];
v.insert(v.end(), group.endpoints.begin(), group.endpoints.end());
}
}
return res;
}
} // namespace hf3fs::flat

71
src/fbs/mgmtd/NodeInfo.h Normal file
View File

@@ -0,0 +1,71 @@
#pragma once
#include <boost/algorithm/string.hpp>
#include <span>
#include "MgmtdTypes.h"
#include "common/app/AppInfo.h"
#include "common/app/ConfigStatus.h"
#include "common/serde/SerdeHelper.h"
#include "common/utils/Selector.h"
#include "common/utils/UtcTimeSerde.h"
namespace hf3fs::flat {
struct NodeInfo : public serde::SerdeHelper<NodeInfo> {
std::vector<net::Address> extractAddresses(const String &serviceName,
std::optional<net::Address::Type> addressType = std::nullopt) const;
std::map<String, std::vector<net::Address>> getAllServices() const;
bool operator==(const NodeInfo &other) const { return serde::equals(*this, other); }
SERDE_STRUCT_FIELD(app, FbsAppInfo{});
SERDE_STRUCT_FIELD(type, NodeType(NodeType::MGMTD));
SERDE_STRUCT_FIELD(status, NodeStatus(NodeStatus::HEARTBEAT_CONNECTING));
SERDE_STRUCT_FIELD(lastHeartbeatTs, UtcTime{});
SERDE_STRUCT_FIELD(tags, std::vector<TagPair>{});
SERDE_STRUCT_FIELD(configVersion, ConfigVersion(0));
SERDE_STRUCT_FIELD(configStatus, ConfigStatus::NORMAL);
};
inline auto selectNodeByType(flat::NodeType type) {
auto p = [type](const flat::NodeInfo &node) { return node.type == type; };
return makeSelector<flat::NodeInfo>(std::move(p));
}
inline auto selectActiveNode() {
auto p = [](const flat::NodeInfo &node) {
return node.status == flat::NodeStatus::HEARTBEAT_CONNECTED || node.status == flat::NodeStatus::PRIMARY_MGMTD;
};
return makeSelector<flat::NodeInfo>(std::move(p));
}
inline auto selectNodeById(flat::NodeId id) {
auto p = [id](const flat::NodeInfo &node) { return node.app.nodeId == id; };
return makeSelector<flat::NodeInfo>(std::move(p));
}
inline auto selectNodeByStatus(flat::NodeStatus status) {
auto p = [status](const flat::NodeInfo &node) { return node.status == status; };
return makeSelector<flat::NodeInfo>(std::move(p));
}
inline auto selectNodeByTrafficZone(std::string_view zone) {
std::vector<std::string_view> zones;
if (!zone.empty()) {
std::vector<std::string_view> tmp;
boost::split(tmp, zone, boost::is_any_of(", "));
std::copy_if(tmp.begin(), tmp.end(), std::back_inserter(zones), [](std::string_view s) { return !s.empty(); });
}
auto p = [zones = std::move(zones)](const flat::NodeInfo &node) {
if (zones.empty()) return true; // select any node if zone is empty
for (const auto &tp : node.tags) {
if (tp.key == kTrafficZoneTagKey && std::find(zones.begin(), zones.end(), tp.value) != zones.end()) return true;
}
return false;
};
return makeSelector<flat::NodeInfo>(std::move(p));
}
} // namespace hf3fs::flat

View File

@@ -0,0 +1,12 @@
#pragma once
#include "MgmtdTypes.h"
#include "common/serde/SerdeHelper.h"
#include "common/utils/UtcTimeSerde.h"
namespace hf3fs::flat {
struct NodeStatusChange : public serde::SerdeHelper<NodeStatusChange> {
SERDE_STRUCT_FIELD(status, NodeStatus(NodeStatus::MIN));
SERDE_STRUCT_FIELD(time, UtcTime{});
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,18 @@
#pragma once
#include "MgmtdTypes.h"
#include "common/app/AppInfo.h"
#include "common/serde/SerdeComparisons.h"
#include "common/serde/SerdeHelper.h"
namespace hf3fs::flat {
struct PersistentNodeInfo : public serde::SerdeHelper<PersistentNodeInfo> {
bool operator==(const PersistentNodeInfo &other) const { return serde::equals(*this, other); }
SERDE_STRUCT_FIELD(nodeId, NodeId(0));
SERDE_STRUCT_FIELD(type, NodeType(NodeType::MIN));
SERDE_STRUCT_FIELD(serviceGroups, std::vector<ServiceGroupInfo>{});
SERDE_STRUCT_FIELD(tags, std::vector<TagPair>{});
SERDE_STRUCT_FIELD(hostname, String{});
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1,84 @@
#include "RoutingInfo.h"
#include <folly/logging/xlog.h>
namespace hf3fs::flat {
const ChainTable *RoutingInfo::getChainTable(ChainTableId tableId, ChainTableVersion tableVersion) const {
auto tit = chainTables.find(tableId);
if (tit == chainTables.end()) return nullptr;
if (tit->second.empty()) return nullptr;
// tv == 0 means latest version
auto vit = tableVersion != 0 ? tit->second.find(tableVersion) : (--tit->second.end());
if (vit == tit->second.end()) return nullptr;
return &vit->second;
}
ChainTable *RoutingInfo::getChainTable(ChainTableId tableId, ChainTableVersion tableVersion) {
const auto *self = this;
auto *res = self->getChainTable(tableId, tableVersion);
return const_cast<ChainTable *>(res);
}
std::optional<ChainId> RoutingInfo::getChainId(ChainRef ref) const {
auto [tid, tv, index] = ref.decode();
if (tid == 0 && tv == 0) {
return ChainId(index);
}
const auto *table = getChainTable(tid, tv);
if (!table) return std::nullopt;
if (index == 0) return std::nullopt;
index = (index - 1) % table->chains.size();
return table->chains[index];
}
const ChainInfo *RoutingInfo::getChain(ChainId id) const {
auto it = chains.find(id);
if (it != chains.end()) return &it->second;
return nullptr;
}
ChainInfo *RoutingInfo::getChain(ChainId id) {
const auto *self = this;
auto *res = self->getChain(id);
return const_cast<ChainInfo *>(res);
}
const ChainInfo *RoutingInfo::getChain(ChainRef ref) const {
auto cid = getChainId(ref);
return cid ? getChain(*cid) : nullptr;
}
ChainInfo *RoutingInfo::getChain(ChainRef ref) {
const auto *self = this;
auto *res = self->getChain(ref);
return const_cast<ChainInfo *>(res);
}
const NodeInfo *RoutingInfo::getNode(NodeId id) const {
auto it = nodes.find(id);
if (it != nodes.end()) return &it->second;
return nullptr;
}
NodeInfo *RoutingInfo::getNode(NodeId id) {
const auto *self = this;
auto *res = self->getNode(id);
return const_cast<NodeInfo *>(res);
}
const TargetInfo *RoutingInfo::getTarget(TargetId id) const {
auto it = targets.find(id);
if (it != targets.end()) return &it->second;
return nullptr;
}
TargetInfo *RoutingInfo::getTarget(TargetId id) {
const auto *self = this;
auto *res = self->getTarget(id);
return const_cast<TargetInfo *>(res);
}
} // namespace hf3fs::flat

View File

@@ -0,0 +1,49 @@
#pragma once
#include "ChainRef.h"
#include "ChainTable.h"
#include "MgmtdTypes.h"
#include "NodeInfo.h"
#include "TargetInfo.h"
#include "common/utils/RobinHoodUtils.h"
namespace hf3fs::flat {
struct RoutingInfo : public serde::SerdeHelper<RoutingInfo> {
// return nullptr if not found
const ChainTable *getChainTable(ChainTableId tableId, ChainTableVersion tableVersion = ChainTableVersion(0)) const;
ChainTable *getChainTable(ChainTableId tableId, ChainTableVersion tableVersion = ChainTableVersion(0));
// return std::nullopt if not found
std::optional<ChainId> getChainId(ChainRef ref) const;
// return nullptr if not found
const ChainInfo *getChain(ChainId id) const;
ChainInfo *getChain(ChainId id);
// return nullptr if not found
const ChainInfo *getChain(ChainRef ref) const;
ChainInfo *getChain(ChainRef ref);
// return nullptr if not found
const NodeInfo *getNode(NodeId id) const;
NodeInfo *getNode(NodeId id);
// return nullptr if not found
const TargetInfo *getTarget(TargetId id) const;
TargetInfo *getTarget(TargetId id);
using NodeMap = robin_hood::unordered_map<NodeId, NodeInfo>;
// ordered
using ChainTableVersionMap = std::map<ChainTableVersion, ChainTable>;
using ChainTableMap = robin_hood::unordered_map<ChainTableId, ChainTableVersionMap>;
using ChainMap = robin_hood::unordered_map<ChainId, ChainInfo>;
using TargetMap = robin_hood::unordered_map<TargetId, TargetInfo>;
SERDE_STRUCT_FIELD(routingInfoVersion, RoutingInfoVersion(0));
SERDE_STRUCT_FIELD(bootstrapping, bool(false));
SERDE_STRUCT_FIELD(nodes, NodeMap{});
SERDE_STRUCT_FIELD(chainTables, ChainTableMap{});
SERDE_STRUCT_FIELD(chains, ChainMap{});
SERDE_STRUCT_FIELD(targets, TargetMap{});
};
} // namespace hf3fs::flat

235
src/fbs/mgmtd/Rpc.h Normal file
View File

@@ -0,0 +1,235 @@
#pragma once
#include "ChainSetting.h"
#include "ClientSession.h"
#include "ClientSessionData.h"
#include "ConfigInfo.h"
#include "HeartbeatInfo.h"
#include "NodeInfo.h"
#include "PersistentNodeInfo.h"
#include "RoutingInfo.h"
#include "common/app/ConfigStatus.h"
#include "fbs/core/user/User.h"
namespace hf3fs::mgmtd {
DEFINE_SERDE_HELPER_STRUCT(GetPrimaryMgmtdReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(GetPrimaryMgmtdRsp) {
SERDE_STRUCT_FIELD(primary, std::optional<flat::PersistentNodeInfo>{});
};
DEFINE_SERDE_HELPER_STRUCT(HeartbeatReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(info, flat::HeartbeatInfo{});
SERDE_STRUCT_FIELD(timestamp, UtcTime{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(HeartbeatRsp) { SERDE_STRUCT_FIELD(config, std::optional<flat::ConfigInfo>{}); };
DEFINE_SERDE_HELPER_STRUCT(RegisterNodeReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(nodeId, flat::NodeId(0));
SERDE_STRUCT_FIELD(type, flat::NodeType(flat::NodeType::MIN));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(RegisterNodeRsp) { SERDE_STRUCT_FIELD(dummy, Void{}); };
DEFINE_SERDE_HELPER_STRUCT(GetRoutingInfoReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(routingInfoVersion, flat::RoutingInfoVersion(0));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(GetRoutingInfoRsp) { SERDE_STRUCT_FIELD(info, std::optional<flat::RoutingInfo>{}); };
DEFINE_SERDE_HELPER_STRUCT(SetConfigReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(nodeType, flat::NodeType(flat::NodeType::MIN));
SERDE_STRUCT_FIELD(content, String{});
SERDE_STRUCT_FIELD(desc, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(SetConfigRsp) { SERDE_STRUCT_FIELD(configVersion, flat::ConfigVersion(0)); };
DEFINE_SERDE_HELPER_STRUCT(GetConfigReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(nodeType, flat::NodeType(flat::NodeType::MIN));
SERDE_STRUCT_FIELD(configVersion, flat::ConfigVersion(0));
SERDE_STRUCT_FIELD(exactVersion, bool{false});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(GetConfigRsp) { SERDE_STRUCT_FIELD(info, std::optional<flat::ConfigInfo>{}); };
DEFINE_SERDE_HELPER_STRUCT(SetChainTableReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(chainTableId, flat::ChainTableId(0));
SERDE_STRUCT_FIELD(chains, std::vector<flat::ChainId>{});
SERDE_STRUCT_FIELD(desc, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(SetChainTableRsp) { SERDE_STRUCT_FIELD(chainTableVersion, flat::ChainTableVersion(0)); };
DEFINE_SERDE_HELPER_STRUCT(EnableNodeReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(nodeId, flat::NodeId(0));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(EnableNodeRsp) { SERDE_STRUCT_FIELD(dummy, Void{}); };
DEFINE_SERDE_HELPER_STRUCT(DisableNodeReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(nodeId, flat::NodeId(0));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(DisableNodeRsp) { SERDE_STRUCT_FIELD(dummy, Void{}); };
DEFINE_SERDE_HELPER_STRUCT(ExtendClientSessionReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(clientId, String{});
SERDE_STRUCT_FIELD(clientSessionVersion, flat::ClientSessionVersion(1));
SERDE_STRUCT_FIELD(configVersion, flat::ConfigVersion(0));
SERDE_STRUCT_FIELD(data, flat::ClientSessionData{});
SERDE_STRUCT_FIELD(configStatus, ConfigStatus::NORMAL);
SERDE_STRUCT_FIELD(type, flat::NodeType::CLIENT);
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
SERDE_STRUCT_FIELD(clientStart, UtcTime{});
};
DEFINE_SERDE_HELPER_STRUCT(ExtendClientSessionRsp) {
SERDE_STRUCT_FIELD(config, std::optional<flat::ConfigInfo>{});
SERDE_STRUCT_FIELD(tags, std::vector<flat::TagPair>{});
};
DEFINE_SERDE_HELPER_STRUCT(ListClientSessionsReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(ListClientSessionsRsp) {
SERDE_STRUCT_FIELD(bootstrapping, bool(false));
SERDE_STRUCT_FIELD(sessions, std::vector<flat::ClientSession>{});
SERDE_STRUCT_FIELD(referencedTags, RHStringHashMap<std::vector<flat::TagPair>>{});
};
DEFINE_SERDE_HELPER_STRUCT(SetNodeTagsReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(nodeId, flat::NodeId(0));
SERDE_STRUCT_FIELD(tags, std::vector<flat::TagPair>{});
SERDE_STRUCT_FIELD(mode, flat::SetTagMode(flat::SetTagMode::REPLACE));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(SetNodeTagsRsp) { SERDE_STRUCT_FIELD(info, flat::NodeInfo{}); };
DEFINE_SERDE_HELPER_STRUCT(UnregisterNodeReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(nodeId, flat::NodeId(0));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(UnregisterNodeRsp) { SERDE_STRUCT_FIELD(dummy, Void{}); };
DEFINE_SERDE_HELPER_STRUCT(SetChainsReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(chains, std::vector<flat::ChainSetting>{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(SetChainsRsp) { SERDE_STRUCT_FIELD(dummy, Void{}); };
DEFINE_SERDE_HELPER_STRUCT(SetUniversalTagsReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(universalId, String{});
SERDE_STRUCT_FIELD(tags, std::vector<flat::TagPair>{});
SERDE_STRUCT_FIELD(mode, flat::SetTagMode(flat::SetTagMode::REPLACE));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(SetUniversalTagsRsp) { SERDE_STRUCT_FIELD(tags, std::vector<flat::TagPair>{}); };
DEFINE_SERDE_HELPER_STRUCT(GetUniversalTagsReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(universalId, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(GetUniversalTagsRsp) { SERDE_STRUCT_FIELD(tags, std::vector<flat::TagPair>{}); };
DEFINE_SERDE_HELPER_STRUCT(GetConfigVersionsReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(GetConfigVersionsRsp) {
SERDE_STRUCT_FIELD(versions, RHStringHashMap<flat::ConfigVersion>{});
};
DEFINE_SERDE_HELPER_STRUCT(GetClientSessionReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(clientId, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(GetClientSessionRsp) {
SERDE_STRUCT_FIELD(bootstrapping, bool(false));
SERDE_STRUCT_FIELD(session, std::optional<flat::ClientSession>{});
SERDE_STRUCT_FIELD(referencedTags, std::vector<flat::TagPair>{});
};
DEFINE_SERDE_HELPER_STRUCT(RotateLastSrvReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(chainId, flat::ChainId(0));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(RotateLastSrvRsp) { SERDE_STRUCT_FIELD(chain, flat::ChainInfo{}); };
DEFINE_SERDE_HELPER_STRUCT(ListOrphanTargetsReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(ListOrphanTargetsRsp) { SERDE_STRUCT_FIELD(targets, std::vector<flat::TargetInfo>()); };
DEFINE_SERDE_HELPER_STRUCT(SetPreferredTargetOrderReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(chainId, flat::ChainId{0});
SERDE_STRUCT_FIELD(preferredTargetOrder, std::vector<flat::TargetId>{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(SetPreferredTargetOrderRsp) { SERDE_STRUCT_FIELD(chain, flat::ChainInfo{}); };
DEFINE_SERDE_HELPER_STRUCT(RotateAsPreferredOrderReq) {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(chainId, flat::ChainId(0));
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
};
DEFINE_SERDE_HELPER_STRUCT(RotateAsPreferredOrderRsp) { SERDE_STRUCT_FIELD(chain, flat::ChainInfo{}); };
DEFINE_SERDE_HELPER_STRUCT(UpdateChainReq) {
enum class Mode : uint8_t {
ADD = 0,
REMOVE = 1,
};
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(user, flat::UserInfo{});
SERDE_STRUCT_FIELD(chainId, flat::ChainId(0));
SERDE_STRUCT_FIELD(targetId, flat::TargetId(0));
SERDE_STRUCT_FIELD(mode, Mode::ADD);
};
DEFINE_SERDE_HELPER_STRUCT(UpdateChainRsp) { SERDE_STRUCT_FIELD(chain, flat::ChainInfo{}); };
} // namespace hf3fs::mgmtd

View File

@@ -0,0 +1,19 @@
#pragma once
#include "ChainTargetInfo.h"
#include "LocalTargetInfo.h"
#include "common/serde/SerdeComparisons.h"
namespace hf3fs::flat {
struct TargetInfo : public serde::SerdeHelper<TargetInfo> {
bool operator==(const TargetInfo &other) const { return serde::equals(*this, other); }
SERDE_STRUCT_FIELD(targetId, TargetId(0));
SERDE_STRUCT_FIELD(publicState, PublicTargetState(PublicTargetState::INVALID));
SERDE_STRUCT_FIELD(localState, LocalTargetState(LocalTargetState::INVALID));
SERDE_STRUCT_FIELD(chainId, ChainId(0));
SERDE_STRUCT_FIELD(nodeId, std::optional<NodeId>{});
SERDE_STRUCT_FIELD(diskIndex, std::optional<uint32_t>{});
SERDE_STRUCT_FIELD(usedSize, uint64_t{});
};
} // namespace hf3fs::flat

View File

@@ -0,0 +1 @@
target_add_lib(migration-fbs mgmtd-fbs core-user-fbs)

View File

@@ -0,0 +1,80 @@
#pragma once
#include "common/serde/Serde.h"
#include "common/serde/Service.h"
#include "common/utils/UtcTime.h"
#include "common/utils/Uuid.h"
namespace hf3fs::migration {
/* job state transition
NotSubmitted --> Pending --> Running --> Succeeded
| ^ ^
| | |
| +--------+ |
| | |
v v v
Stopped Failed
*/
enum JobStatus {
NotSubmitted = 0,
Pending,
Running,
Failed,
Stopped,
Succeeded,
};
struct StartJobReq {
SERDE_STRUCT_FIELD(id,
Uuid::zero()); // start a new job if not found at server side; otherwise resume an existing job
SERDE_STRUCT_FIELD(path, String{});
SERDE_STRUCT_FIELD(mtime, UtcTime{}); // only files with mtime greater than this value
};
struct StartJobRsp {
SERDE_STRUCT_FIELD(status, JobStatus::NotSubmitted);
};
struct StopJobReq {
SERDE_STRUCT_FIELD(id, Uuid::zero());
};
struct StopJobRsp {
SERDE_STRUCT_FIELD(status, JobStatus::NotSubmitted);
};
struct JobInfo {
SERDE_STRUCT_FIELD(id, Uuid::zero());
SERDE_STRUCT_FIELD(path, String{});
SERDE_STRUCT_FIELD(mtime, UtcTime{}); // only files with mtime greater than this value
SERDE_STRUCT_FIELD(status, JobStatus::NotSubmitted);
SERDE_STRUCT_FIELD(startTime, UtcTime{});
SERDE_STRUCT_FIELD(finishTime, UtcTime{});
SERDE_STRUCT_FIELD(numFilesFound, uint64_t{});
SERDE_STRUCT_FIELD(numFilesCopied, uint64_t{});
SERDE_STRUCT_FIELD(numSrcFilesRemoved, uint64_t{});
SERDE_STRUCT_FIELD(bytesOfFilesFound, uint64_t{});
SERDE_STRUCT_FIELD(bytesOfFilesCopied, uint64_t{});
};
struct ListJobsReq {
SERDE_STRUCT_FIELD(path, String{});
SERDE_STRUCT_FIELD(ids, std::vector<Uuid>{}); // only jobs with specific ids
SERDE_STRUCT_FIELD(status, JobStatus::Running); // only jobs with this status
SERDE_STRUCT_FIELD(after, UtcTime{}); // only jobs with start time greater than this value
SERDE_STRUCT_FIELD(limit, uint32_t{10}); // max number of jobs in response
};
struct ListJobsRsp {
SERDE_STRUCT_FIELD(jobs, std::vector<JobInfo>{});
};
SERDE_SERVICE(MigrationSerde, 0xF1) {
SERDE_SERVICE_METHOD(start, 1, StartJobReq, StartJobRsp);
SERDE_SERVICE_METHOD(stop, 2, StopJobReq, StopJobRsp);
SERDE_SERVICE_METHOD(list, 3, ListJobsReq, ListJobsRsp);
};
} // namespace hf3fs::migration

View File

@@ -0,0 +1 @@
target_add_lib(MonitorCollectorService-fbs common)

View File

@@ -0,0 +1,15 @@
#pragma once
#include "common/monitor/Sample.h"
#include "common/serde/Serde.h"
#include "common/serde/Service.h"
namespace hf3fs::monitor {
struct MonitorCollectorRsp {
SERDE_STRUCT_FIELD(dummy, Void{});
};
SERDE_SERVICE(MonitorCollector, 194) { SERDE_SERVICE_METHOD(write, 1, std::vector<Sample>, MonitorCollectorRsp); };
} // namespace hf3fs::monitor

View File

@@ -0,0 +1 @@
target_add_lib(simple_example-fbs mgmtd-fbs core-user-fbs)

View File

@@ -0,0 +1,18 @@
#pragma once
#include "common/serde/Serde.h"
#include "common/serde/Service.h"
namespace hf3fs::simple_example {
struct SimpleExampleReq {
SERDE_STRUCT_FIELD(message, String{});
};
struct SimpleExampleRsp {
SERDE_STRUCT_FIELD(message, String{});
};
SERDE_SERVICE(SimpleExampleSerde, 0xF0) { SERDE_SERVICE_METHOD(echo, 1, SimpleExampleReq, SimpleExampleRsp); };
} // namespace hf3fs::simple_example

View File

@@ -0,0 +1 @@
target_add_lib(storage-fbs meta-fbs mgmtd-fbs)

109
src/fbs/storage/Common.cc Normal file
View File

@@ -0,0 +1,109 @@
#include "fbs/storage/Common.h"
#include <fmt/format.h>
#include "common/utils/Result.h"
#include "scn/tuple_return/tuple_return.h"
namespace hf3fs::storage {
ChunkId::ChunkId(uint64_t high, uint64_t low) {
data_.resize(sizeof(high) + sizeof(low));
if constexpr (std::endian::native == std::endian::little) {
high = __builtin_bswap64(high);
low = __builtin_bswap64(low);
}
*(reinterpret_cast<uint64_t *>(&data_[0])) = high;
*(reinterpret_cast<uint64_t *>(&data_[sizeof(high)])) = low;
}
ChunkId::ChunkId(const ChunkId &baseChunkId, uint64_t chunkIndex)
: data_(baseChunkId.data_) {
if (data_.size() < sizeof(uint64_t)) {
data_.insert(data_.begin(), sizeof(uint64_t) - data_.size(), 0);
return;
}
auto lowPtr = reinterpret_cast<uint64_t *>(&data_[data_.size() - sizeof(uint64_t)]);
if constexpr (std::endian::native == std::endian::little) {
*lowPtr = __builtin_bswap64(__builtin_bswap64(*lowPtr) + chunkIndex);
} else {
*lowPtr = *lowPtr + chunkIndex;
}
}
ChunkId ChunkId::nextChunkId() const {
auto data = data_;
for (auto it = data.rbegin(); it != data.rend(); ++it) {
if (++*it != 0) {
break;
}
}
return ChunkId{std::move(data)};
}
ChunkId ChunkId::rangeEndForCurrentChunk() const {
std::string data;
data.reserve(data.size() + 1);
data.append(data_);
data.push_back(0);
return ChunkId{std::move(data)};
}
std::string ChunkId::describe() const {
std::string ret;
ret.reserve(36);
for (uint32_t i = 0; i < data_.size(); ++i) {
if (i && i % 4 == 0) {
ret += '-';
}
fmt::format_to(std::back_inserter(ret), "{:02X}", uint8_t(data_[i]));
}
return ret;
}
Result<ChunkId> ChunkId::fromString(std::string_view str) {
if (str.empty()) {
return ChunkId{};
}
ChunkId out;
std::optional<uint8_t> half;
for (auto ch : str) {
uint8_t value = 0;
if ('0' <= ch && ch <= '9') {
value = ch - '0';
} else if ('A' <= ch && ch <= 'F') {
value = ch - 'A' + 10;
} else if ('a' <= ch && ch <= 'f') {
value = ch - 'a' + 10;
} else if (ch == '-') {
continue;
} else {
return makeError(StatusCode::kInvalidFormat, fmt::format("invalid chunk id: {}", str));
}
if (half) {
out.data_.push_back(*half * 16u + value);
half = std::nullopt;
} else {
half = value;
}
}
if (out.data_.length() != 16u) {
return makeError(StatusCode::kInvalidFormat, fmt::format("invalid chunk id: {}, {}", str, out.describe()));
}
return out;
}
GlobalKey GlobalKey::fromFileOffset(const std::vector<VersionedChainId> &chainIds,
const ChunkId &baseChunkId,
const size_t chunkSize,
const size_t fileOffset) {
size_t chunkIndex = fileOffset / chunkSize;
if (chunkIndex > UINT32_MAX) return {};
ChunkId chunkId(baseChunkId, chunkIndex);
return {chainIds[chunkIndex], chunkId};
}
} // namespace hf3fs::storage

805
src/fbs/storage/Common.h Normal file
View File

@@ -0,0 +1,805 @@
#pragma once
#include <array>
#include <bit>
#include <cstdint>
#include <folly/hash/Checksum.h>
#include <iterator>
#include <string>
#include <vector>
#include "client/mgmtd/RoutingInfo.h"
#include "common/app/ClientId.h"
#include "common/net/ib/RDMABuf.h"
#include "common/utils/Reflection.h"
#include "common/utils/Result.h"
#include "common/utils/StrongType.h"
#include "common/utils/Uuid.h"
#include "fbs/core/user/User.h"
#include "fbs/mgmtd/MgmtdTypes.h"
#include "fbs/mgmtd/NodeInfo.h"
namespace hf3fs::storage {
using ChainId = ::hf3fs::flat::ChainId;
using ChainVer = ::hf3fs::flat::ChainVersion;
using TargetId = ::hf3fs::flat::TargetId;
using NodeId = ::hf3fs::flat::NodeId;
STRONG_TYPEDEF(uint32_t, ChunkVer);
STRONG_TYPEDEF(uint64_t, RequestId);
STRONG_TYPEDEF(uint16_t, ChannelId);
STRONG_TYPEDEF(uint64_t, ChannelSeqNum);
#define BITFLAGS_SET(x, bits) ((x) |= static_cast<uint32_t>(bits))
#define BITFLAGS_CLEAR(x, bits) ((x) &= (~static_cast<uint32_t>(bits)))
#define BITFLAGS_CONTAIN(x, bits) (((x) & static_cast<uint32_t>(bits)) == static_cast<uint32_t>(bits))
#define ALIGN_LOWER(mem, align) ((mem) / (align) * (align))
#define ALIGN_UPPER(mem, align) (((mem) + (align)-1) / (align) * (align))
/* start of fault injection helpers */
#ifndef NDEBUG
#define FAULT_INJECTION_POINT(injectError, injectedRes, funcRes) \
(UNLIKELY(injectError) ? (XLOGF(WARN, "Injected error: {}", (injectedRes)), (injectedRes)) : (funcRes))
#else
#define FAULT_INJECTION_POINT(injectError, injectedRes, funcRes) (boost::ignore_unused(injectError), (funcRes))
#endif
/* end of fault injection helpers */
enum class UpdateType : uint8_t {
INVALID = 0,
WRITE = 1,
REMOVE = 2,
TRUNCATE = 4,
EXTEND = 8,
COMMIT = 16,
};
enum class ChunkState : uint8_t {
COMMIT = 0,
DIRTY = 1,
CLEAN = 2,
};
enum class ChecksumType : uint8_t {
NONE = 0,
CRC32C = 1,
CRC32 = 2,
};
enum class FeatureFlags : uint32_t {
DEFAULT = 0,
BYPASS_DISKIO = 1,
BYPASS_RDMAXMIT = 2,
SEND_DATA_INLINE = 4,
ALLOW_READ_UNCOMMITTED = 8,
};
constexpr auto kAIOAlignSize = 512ul;
class ChunkId {
public:
ChunkId() = default;
explicit ChunkId(const std::string &data)
: data_(data) {}
explicit ChunkId(std::string_view data)
: data_(data) {}
explicit ChunkId(std::string &&data)
: data_(std::move(data)) {}
// special constructors to create 128-bit chunk id from 64-bit integers
ChunkId(uint64_t high, uint64_t low);
ChunkId(const ChunkId &baseChunkId, uint64_t chunkIndex);
bool operator==(const ChunkId &other) const { return other.data_ == data_; }
auto operator<=>(const ChunkId &other) const { return data_ <=> other.data_; }
ChunkId nextChunkId() const;
ChunkId rangeEndForCurrentChunk() const;
auto &data() const { return data_; }
std::string describe() const;
std::string toString() const { return describe(); }
static Result<ChunkId> fromString(std::string_view str);
private:
std::string data_;
};
static_assert(serde::Serializable<ChunkId>);
struct ChecksumInfo {
SERDE_STRUCT_FIELD(type, ChecksumType::NONE);
SERDE_STRUCT_FIELD(value, uint32_t{});
public:
static constexpr size_t kChunkSize = 1_MB;
class DataIterator {
public:
virtual ~DataIterator() = default;
virtual std::pair<const uint8_t *, size_t> next() = 0;
};
class MemoryDataIterator : public DataIterator {
public:
MemoryDataIterator(const uint8_t *buffer, size_t length)
: buffer_(buffer),
length_(length) {}
std::pair<const uint8_t *, size_t> next() override {
if (length_ == 0) return {nullptr, 0};
const uint8_t *data = buffer_;
size_t size = std::min(length_, ChecksumInfo::kChunkSize);
buffer_ += size;
length_ -= size;
return {data, size};
}
private:
const uint8_t *buffer_;
size_t length_;
};
static ChecksumInfo create(ChecksumType type, DataIterator *iter, size_t length, uint32_t startingChecksum = ~0U) {
ChecksumInfo checksum = {type, startingChecksum};
size_t iterBytes = 0;
if (type == ChecksumType::NONE) return ChecksumInfo{ChecksumType::NONE, 0U};
for (auto data = iter->next(); data.first != nullptr && iterBytes < length; data = iter->next()) {
iterBytes += data.second;
switch (checksum.type) {
case ChecksumType::NONE:
break;
case ChecksumType::CRC32C:
checksum.value = folly::crc32c(data.first, data.second, checksum.value);
break;
case ChecksumType::CRC32:
checksum.value = folly::crc32(data.first, data.second, checksum.value);
break;
}
}
if (iterBytes != length) {
XLOGF(WARN, "Iterated bytes {} not equal to length {}, checksum {}", iterBytes, length, checksum);
return ChecksumInfo{ChecksumType::NONE, 0U};
}
return checksum;
}
static ChecksumInfo create(ChecksumType type, const uint8_t *buffer, size_t length, uint32_t startingChecksum = ~0U) {
MemoryDataIterator iter(buffer, length);
return create(type, &iter, length, startingChecksum);
}
Result<Void> combine(const ChecksumInfo &o, size_t length) {
if (type != ChecksumType::NONE && type != o.type) {
return makeError(StorageCode::kChecksumMismatch,
fmt::format("diffrent type {} != {}", serde::toJsonString(*this), serde::toJsonString(o)));
}
if (length == 0) return Void{};
switch (type) {
case ChecksumType::NONE:
*this = o;
return Void{};
case ChecksumType::CRC32C:
value = folly::crc32c_combine(~value, o.value, length);
return Void{};
case ChecksumType::CRC32:
value = folly::crc32_combine(~value, o.value, length);
return Void{};
}
}
bool operator==(const ChecksumInfo &) const = default;
};
static_assert(serde::Serializable<ChecksumInfo>);
} // namespace hf3fs::storage
template <>
struct ::hf3fs::serde::SerdeMethod<::hf3fs::storage::ChunkId> {
static constexpr std::string_view serdeTo(const storage::ChunkId &chunkId) { return chunkId.data(); }
static Result<storage::ChunkId> serdeFrom(std::string_view str) { return storage::ChunkId(str); }
static std::string serdeToReadable(const storage::ChunkId &chunkId) { return chunkId.describe(); };
static Result<storage::ChunkId> serdeFromReadable(std::string_view s) { return storage::ChunkId::fromString(s); }
};
template <>
struct std::hash<hf3fs::storage::ChunkId> {
size_t operator()(const hf3fs::storage::ChunkId &chunkId) const { return std::hash<std::string>{}(chunkId.data()); }
};
namespace hf3fs::storage {
struct IOResult {
SERDE_STRUCT_FIELD(lengthInfo, Result<uint32_t>{makeError(StorageClientCode::kNotInitialized)});
SERDE_STRUCT_FIELD(commitVer, ChunkVer{});
SERDE_STRUCT_FIELD(updateVer, ChunkVer{});
SERDE_STRUCT_FIELD(checksum, ChecksumInfo{});
SERDE_STRUCT_FIELD(commitChainVer, ChainVer{});
public:
IOResult() = default;
// for IOResult(makeError())
IOResult(folly::Unexpected<Status> &&status)
: lengthInfo(std::move(status)) {}
IOResult(uint32_t statusCode)
: IOResult(makeError(statusCode)) {}
IOResult(Result<uint32_t> lengthInfo,
ChunkVer commitVer,
ChunkVer updateVer,
ChecksumInfo checksum = {},
ChainVer commitChainVer = {})
: lengthInfo(lengthInfo),
commitVer(commitVer),
updateVer(updateVer),
checksum(checksum),
commitChainVer(commitChainVer) {}
bool operator==(const IOResult &other) const = default;
friend void PrintTo(const IOResult &res, std::ostream *os) { *os << fmt::to_string(res); }
};
static_assert(serde::Serializable<IOResult>);
struct VersionedChainId {
bool operator==(const VersionedChainId &) const = default;
SERDE_STRUCT_FIELD(chainId, ChainId{});
SERDE_STRUCT_FIELD(chainVer, ChainVer{});
};
static_assert(serde::Serializable<VersionedChainId>);
struct GlobalKey {
SERDE_STRUCT_FIELD(vChainId, VersionedChainId{});
SERDE_STRUCT_FIELD(chunkId, ChunkId{});
static GlobalKey fromFileOffset(const std::vector<VersionedChainId> &chainIds,
const ChunkId &baseChunkId,
const size_t chunkSize,
const size_t fileOffset);
};
static_assert(serde::Serializable<GlobalKey>);
struct UpdateChannel {
SERDE_STRUCT_FIELD(id, ChannelId{});
SERDE_STRUCT_FIELD(seqnum, ChannelSeqNum{});
};
static_assert(serde::Serializable<UpdateChannel>);
struct MessageTag {
SERDE_STRUCT_FIELD(clientId, ClientId{});
SERDE_STRUCT_FIELD(requestId, RequestId{});
SERDE_STRUCT_FIELD(channel, UpdateChannel{});
public:
MessageTag() = default;
MessageTag(ClientId clientId, RequestId requestId, UpdateChannel channel = UpdateChannel{})
: clientId(clientId),
requestId(requestId),
channel(channel) {}
};
static_assert(serde::Serializable<MessageTag>);
struct DebugFlags {
SERDE_STRUCT_FIELD(injectRandomServerError, false);
SERDE_STRUCT_FIELD(injectRandomClientError, false);
SERDE_STRUCT_FIELD(numOfInjectPtsBeforeFail, uint16_t{});
private:
bool isFailPoint() {
bool fail = numOfInjectPtsBeforeFail == 1;
if (numOfInjectPtsBeforeFail > 0) numOfInjectPtsBeforeFail--;
return fail;
}
public:
bool faultInjectionEnabled() const { return injectRandomServerError || injectRandomClientError; }
bool injectServerError() { return injectRandomServerError && isFailPoint(); }
bool injectClientError() { return injectRandomClientError && isFailPoint(); }
};
static_assert(serde::Serializable<DebugFlags>);
struct ReadIO {
SERDE_STRUCT_FIELD(offset, uint32_t{});
SERDE_STRUCT_FIELD(length, uint32_t{});
SERDE_STRUCT_FIELD(key, GlobalKey{});
SERDE_STRUCT_FIELD(rdmabuf, net::RDMARemoteBuf{});
};
static_assert(serde::Serializable<ReadIO>);
struct UInt8Vector {
SERDE_STRUCT_FIELD(data, std::vector<uint8_t>{});
public:
std::string serdeToReadable() const { return fmt::format("std::vector<uint8_t>({})", data.size()); }
static Result<UInt8Vector> serdeFromReadable(const std::string &) { return UInt8Vector{}; }
};
static_assert(serde::Serializable<UInt8Vector>);
struct UpdateIO {
SERDE_STRUCT_FIELD(offset, uint32_t{});
SERDE_STRUCT_FIELD(length, uint32_t{});
SERDE_STRUCT_FIELD(chunkSize, uint32_t{});
SERDE_STRUCT_FIELD(key, GlobalKey{});
SERDE_STRUCT_FIELD(rdmabuf, net::RDMARemoteBuf{});
SERDE_STRUCT_FIELD(updateVer, ChunkVer{});
SERDE_STRUCT_FIELD(updateType, UpdateType{});
SERDE_STRUCT_FIELD(checksum, ChecksumInfo{});
SERDE_STRUCT_FIELD(inlinebuf, UInt8Vector{});
public:
bool isWrite() const { return updateType == UpdateType::WRITE; }
bool isTruncate() const { return updateType == UpdateType::TRUNCATE; }
bool isRemove() const { return updateType == UpdateType::REMOVE; }
bool isExtend() const { return updateType == UpdateType::EXTEND; }
bool isCommit() const { return updateType == UpdateType::COMMIT; }
bool isWriteTruncateExtend() const { return isWrite() || isTruncate() || isExtend(); }
};
static_assert(serde::Serializable<UpdateIO>);
struct CommitIO {
SERDE_STRUCT_FIELD(key, GlobalKey{});
SERDE_STRUCT_FIELD(commitVer, ChunkVer{});
SERDE_STRUCT_FIELD(isSyncing, false);
SERDE_STRUCT_FIELD(commitChainVer, ChainVer{});
SERDE_STRUCT_FIELD(isRemove, false);
SERDE_STRUCT_FIELD(isForce, false);
};
struct BatchReadReq {
SERDE_STRUCT_FIELD(payloads, std::vector<ReadIO>{});
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(retryCount, uint32_t{});
SERDE_STRUCT_FIELD(userInfo, flat::UserInfo{});
SERDE_STRUCT_FIELD(featureFlags, uint32_t{});
SERDE_STRUCT_FIELD(checksumType, ChecksumType{});
SERDE_STRUCT_FIELD(debugFlags, DebugFlags{});
};
static_assert(serde::Serializable<BatchReadReq>);
struct BatchReadRsp {
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(results, std::vector<IOResult>{});
SERDE_STRUCT_FIELD(inlinebuf, UInt8Vector{});
};
static_assert(serde::Serializable<BatchReadRsp>);
struct WriteReq {
SERDE_STRUCT_FIELD(payload, UpdateIO{});
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(retryCount, uint32_t{});
SERDE_STRUCT_FIELD(userInfo, flat::UserInfo{});
SERDE_STRUCT_FIELD(featureFlags, uint32_t{});
SERDE_STRUCT_FIELD(debugFlags, DebugFlags{});
};
static_assert(serde::Serializable<WriteReq>);
struct WriteRsp {
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(result, IOResult{});
};
static_assert(serde::Serializable<WriteRsp>);
struct UpdateOptions {
SERDE_STRUCT_FIELD(isSyncing, bool{});
SERDE_STRUCT_FIELD(fromClient, bool{});
SERDE_STRUCT_FIELD(commitChainVer, ChainVer{});
};
struct UpdateReq {
SERDE_STRUCT_FIELD(payload, UpdateIO{});
SERDE_STRUCT_FIELD(options, UpdateOptions{});
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(retryCount, uint32_t{});
SERDE_STRUCT_FIELD(userInfo, flat::UserInfo{});
SERDE_STRUCT_FIELD(featureFlags, uint32_t{});
SERDE_STRUCT_FIELD(debugFlags, DebugFlags{});
};
static_assert(serde::Serializable<UpdateReq>);
struct UpdateRsp {
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(result, IOResult{});
};
static_assert(serde::Serializable<UpdateRsp>);
/* queryLastChunk */
struct ChunkIdRange {
SERDE_STRUCT_FIELD(begin, ChunkId{});
SERDE_STRUCT_FIELD(end, ChunkId{});
SERDE_STRUCT_FIELD(maxNumChunkIdsToProcess, uint32_t{});
};
static_assert(serde::Serializable<ChunkIdRange>);
struct QueryLastChunkOp {
SERDE_STRUCT_FIELD(vChainId, VersionedChainId{});
SERDE_STRUCT_FIELD(chunkIdRange, ChunkIdRange{});
};
static_assert(serde::Serializable<QueryLastChunkOp>);
struct QueryLastChunkReq {
SERDE_STRUCT_FIELD(payloads, std::vector<QueryLastChunkOp>{});
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(retryCount, uint32_t{});
SERDE_STRUCT_FIELD(userInfo, flat::UserInfo{});
SERDE_STRUCT_FIELD(featureFlags, uint32_t{});
SERDE_STRUCT_FIELD(debugFlags, DebugFlags{});
};
static_assert(serde::Serializable<QueryLastChunkReq>);
struct QueryLastChunkResult {
SERDE_STRUCT_FIELD(statusCode, Result<Void>{makeError(StorageClientCode::kNotInitialized)});
SERDE_STRUCT_FIELD(lastChunkId, ChunkId{});
SERDE_STRUCT_FIELD(lastChunkLen, uint32_t{});
SERDE_STRUCT_FIELD(totalChunkLen, uint64_t{});
SERDE_STRUCT_FIELD(totalNumChunks, uint64_t{});
SERDE_STRUCT_FIELD(moreChunksInRange, bool{});
public:
QueryLastChunkResult() = default;
QueryLastChunkResult(uint32_t statusCode)
: QueryLastChunkResult(makeError(statusCode), ChunkId{}, 0, 0, 0, false) {}
QueryLastChunkResult(const Result<Void> &statusCode,
const ChunkId &lastChunkId,
uint32_t lastChunkLen,
uint64_t totalChunkLen,
uint64_t totalNumChunks,
bool moreChunksInRange)
: statusCode(statusCode),
lastChunkId(lastChunkId),
lastChunkLen(lastChunkLen),
totalChunkLen(totalChunkLen),
totalNumChunks(totalNumChunks),
moreChunksInRange(moreChunksInRange) {}
};
static_assert(serde::Serializable<QueryLastChunkResult>);
struct QueryLastChunkRsp {
SERDE_STRUCT_FIELD(results, std::vector<QueryLastChunkResult>{});
};
static_assert(serde::Serializable<QueryLastChunkRsp>);
/* removeChunks */
struct RemoveChunksOp {
SERDE_STRUCT_FIELD(vChainId, VersionedChainId{});
SERDE_STRUCT_FIELD(chunkIdRange, ChunkIdRange{});
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(retryCount, uint32_t{});
};
static_assert(serde::Serializable<RemoveChunksOp>);
struct RemoveChunksReq {
SERDE_STRUCT_FIELD(payloads, std::vector<RemoveChunksOp>{});
SERDE_STRUCT_FIELD(userInfo, flat::UserInfo{});
SERDE_STRUCT_FIELD(featureFlags, uint32_t{});
SERDE_STRUCT_FIELD(debugFlags, DebugFlags{});
};
static_assert(serde::Serializable<RemoveChunksReq>);
struct RemoveChunksResult {
SERDE_STRUCT_FIELD(statusCode, Result<Void>{makeError(StorageClientCode::kNotInitialized)});
SERDE_STRUCT_FIELD(numChunksRemoved, uint32_t{});
SERDE_STRUCT_FIELD(moreChunksInRange, bool{});
public:
RemoveChunksResult() = default;
RemoveChunksResult(uint32_t statusCode)
: RemoveChunksResult(makeError(statusCode), 0, false) {}
RemoveChunksResult(const Result<Void> &statusCode, uint32_t numChunksRemoved, bool moreChunksInRange)
: statusCode(statusCode),
numChunksRemoved(numChunksRemoved),
moreChunksInRange(moreChunksInRange) {}
};
static_assert(serde::Serializable<RemoveChunksResult>);
struct RemoveChunksRsp {
SERDE_STRUCT_FIELD(results, std::vector<RemoveChunksResult>{});
};
static_assert(serde::Serializable<RemoveChunksRsp>);
/* truncateChunks */
struct TruncateChunkOp {
SERDE_STRUCT_FIELD(vChainId, VersionedChainId{});
SERDE_STRUCT_FIELD(chunkId, ChunkId{});
SERDE_STRUCT_FIELD(chunkLen, uint32_t{});
SERDE_STRUCT_FIELD(chunkSize, uint32_t{});
SERDE_STRUCT_FIELD(onlyExtendChunk, bool{});
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(retryCount, uint32_t{});
};
static_assert(serde::Serializable<TruncateChunkOp>);
struct TruncateChunksReq {
SERDE_STRUCT_FIELD(payloads, std::vector<TruncateChunkOp>{});
SERDE_STRUCT_FIELD(userInfo, flat::UserInfo{});
SERDE_STRUCT_FIELD(featureFlags, uint32_t{});
SERDE_STRUCT_FIELD(debugFlags, DebugFlags{});
};
static_assert(serde::Serializable<TruncateChunksReq>);
struct TruncateChunksRsp {
SERDE_STRUCT_FIELD(results, std::vector<IOResult>{});
};
static_assert(serde::Serializable<TruncateChunksRsp>);
/* GetAllChunkMetadata */
struct ChunkMeta {
SERDE_STRUCT_FIELD(chunkId, ChunkId{});
SERDE_STRUCT_FIELD(updateVer, ChunkVer{});
SERDE_STRUCT_FIELD(commitVer, ChunkVer{});
SERDE_STRUCT_FIELD(chainVer, ChainVer{});
SERDE_STRUCT_FIELD(chunkState, ChunkState{});
SERDE_STRUCT_FIELD(checksum, ChecksumInfo{});
SERDE_STRUCT_FIELD(length, uint32_t{});
};
static_assert(serde::Serializable<ChunkMeta>);
using ChunkMetaVector = std::vector<ChunkMeta>;
struct GetAllChunkMetadataReq {
SERDE_STRUCT_FIELD(targetId, TargetId{});
};
static_assert(serde::Serializable<GetAllChunkMetadataReq>);
struct GetAllChunkMetadataRsp {
SERDE_STRUCT_FIELD(chunkMetaVec, ChunkMetaVector{});
};
static_assert(serde::Serializable<GetAllChunkMetadataRsp>);
struct SyncStartReq {
SERDE_STRUCT_FIELD(vChainId, VersionedChainId{});
};
static_assert(serde::Serializable<SyncStartReq>);
struct TargetSyncInfo {
SERDE_STRUCT_FIELD(metas, ChunkMetaVector{});
};
static_assert(serde::Serializable<TargetSyncInfo>);
struct SyncDoneReq {
SERDE_STRUCT_FIELD(vChainId, VersionedChainId{});
};
static_assert(serde::Serializable<SyncDoneReq>);
struct SyncDoneRsp {
SERDE_STRUCT_FIELD(result, IOResult{});
};
static_assert(serde::Serializable<SyncDoneRsp>);
struct SpaceInfoReq {
SERDE_STRUCT_FIELD(tag, MessageTag{});
SERDE_STRUCT_FIELD(force, bool{});
};
static_assert(serde::Serializable<SpaceInfoReq>);
struct SpaceInfo {
SERDE_STRUCT_FIELD(path, std::string{});
SERDE_STRUCT_FIELD(capacity, uint64_t{});
SERDE_STRUCT_FIELD(free, uint64_t{});
SERDE_STRUCT_FIELD(available, uint64_t{});
SERDE_STRUCT_FIELD(targetIds, std::vector<hf3fs::flat::TargetId>{});
SERDE_STRUCT_FIELD(manufacturer, std::string{});
};
static_assert(serde::Serializable<SpaceInfo>);
struct SpaceInfoRsp {
SERDE_STRUCT_FIELD(spaceInfos, std::vector<SpaceInfo>{});
};
static_assert(serde::Serializable<SpaceInfoRsp>);
struct CreateTargetReq {
SERDE_STRUCT_FIELD(targetId, TargetId{});
SERDE_STRUCT_FIELD(diskIndex, uint32_t{});
SERDE_STRUCT_FIELD(physicalFileCount, 256u);
SERDE_STRUCT_FIELD(chunkSizeList, (std::vector<Size>{512_KB, 1_MB, 2_MB, 4_MB, 16_MB, 64_MB}));
SERDE_STRUCT_FIELD(allowExistingTarget, true);
SERDE_STRUCT_FIELD(chainId, ChainId{});
SERDE_STRUCT_FIELD(addChunkSize, false);
SERDE_STRUCT_FIELD(onlyChunkEngine, false);
};
struct CreateTargetRsp {
SERDE_STRUCT_FIELD(dummy, Void{});
};
struct OfflineTargetReq {
SERDE_STRUCT_FIELD(targetId, TargetId{});
SERDE_STRUCT_FIELD(force, false);
};
struct OfflineTargetRsp {
SERDE_STRUCT_FIELD(dummy, Void{});
};
struct RemoveTargetReq {
SERDE_STRUCT_FIELD(targetId, TargetId{});
SERDE_STRUCT_FIELD(force, false);
};
struct RemoveTargetRsp {
SERDE_STRUCT_FIELD(dummy, Void{});
};
struct QueryChunkReq {
SERDE_STRUCT_FIELD(chainId, ChainId{});
SERDE_STRUCT_FIELD(chunkId, ChunkId{});
};
struct ChunkFileId {
bool operator==(const ChunkFileId &o) const = default;
SERDE_STRUCT_FIELD(chunkSize, uint32_t{}, nullptr);
SERDE_STRUCT_FIELD(chunkIdx, uint32_t{}, nullptr);
};
enum class RecycleState : uint8_t {
NORMAL,
REMOVAL_IN_PROGRESS,
REMOVAL_IN_RETRYING,
};
// Metadata of chunk. The order of members has been adjusted for smaller size.
struct ChunkMetadata {
bool operator==(const ChunkMetadata &o) const = default;
SERDE_STRUCT_FIELD(commitVer, ChunkVer{}, nullptr);
SERDE_STRUCT_FIELD(updateVer, ChunkVer{}, nullptr);
SERDE_STRUCT_FIELD(chainVer, ChainVer{}, nullptr);
SERDE_STRUCT_FIELD(size, uint32_t{}, nullptr);
SERDE_STRUCT_FIELD(chunkState, ChunkState{}, nullptr);
SERDE_STRUCT_FIELD(recycleState, RecycleState::NORMAL, nullptr);
SERDE_STRUCT_FIELD(checksumType, ChecksumType::NONE, nullptr);
SERDE_STRUCT_FIELD(checksumValue, uint32_t{}, 0);
SERDE_STRUCT_FIELD(lastNodeId, uint16_t{}, nullptr);
SERDE_STRUCT_FIELD(lastRequestId, RequestId{}, nullptr);
SERDE_STRUCT_FIELD(innerOffset, 0_B, nullptr);
SERDE_STRUCT_FIELD(innerFileId, ChunkFileId{}, nullptr);
SERDE_STRUCT_FIELD(lastClientUuid, Uuid{});
SERDE_STRUCT_FIELD(timestamp, UtcTime{});
public:
bool readyToRemove() const { return recycleState != RecycleState::NORMAL; }
ChecksumInfo checksum() const { return ChecksumInfo{checksumType, checksumValue}; }
};
struct Successor {
SERDE_STRUCT_FIELD(nodeInfo, flat::NodeInfo{});
SERDE_STRUCT_FIELD(targetInfo, flat::TargetInfo{});
};
class StorageTarget;
struct Target {
std::shared_ptr<StorageTarget> storageTarget;
std::weak_ptr<bool> weakStorageTarget;
SERDE_STRUCT_FIELD(targetId, TargetId{});
SERDE_STRUCT_FIELD(path, Path{});
SERDE_STRUCT_FIELD(diskError, false);
SERDE_STRUCT_FIELD(lowSpace, false);
SERDE_STRUCT_FIELD(rejectCreateChunk, false);
SERDE_STRUCT_FIELD(isHead, false);
SERDE_STRUCT_FIELD(isTail, false);
SERDE_STRUCT_FIELD(vChainId, VersionedChainId{});
SERDE_STRUCT_FIELD(localState, flat::LocalTargetState::INVALID);
SERDE_STRUCT_FIELD(publicState, flat::PublicTargetState::INVALID);
SERDE_STRUCT_FIELD(successor, std::optional<Successor>{});
SERDE_STRUCT_FIELD(diskIndex, uint32_t{});
SERDE_STRUCT_FIELD(chainId, ChainId{});
SERDE_STRUCT_FIELD(offlineUponUserRequest, false);
SERDE_STRUCT_FIELD(useChunkEngine, false);
public:
Result<net::Address> getSuccessorAddr() const;
bool upToDate() const {
return localState == flat::LocalTargetState::UPTODATE && publicState == flat::PublicTargetState::SERVING;
}
bool unrecoverableOffline() const { return diskError || offlineUponUserRequest; }
};
using TargetPtr = std::shared_ptr<const Target>;
struct QueryChunkRsp {
SERDE_STRUCT_FIELD(target, Target{});
SERDE_STRUCT_FIELD(meta, Result<ChunkMetadata>{makeError(StatusCode::kInvalidArg)});
};
struct ServiceRequestContext {
const std::string_view requestType = "dummyReq";
const MessageTag tag{};
const uint32_t retryCount{};
const flat::UserInfo userInfo{};
DebugFlags debugFlags{};
};
struct StorageEventTrace {
SERDE_STRUCT_FIELD(clusterId, String{});
SERDE_STRUCT_FIELD(nodeId, NodeId{});
SERDE_STRUCT_FIELD(targetId, TargetId{});
SERDE_STRUCT_FIELD(updateReq, UpdateReq{});
SERDE_STRUCT_FIELD(updateRes, IOResult{});
SERDE_STRUCT_FIELD(forwardRes, IOResult{});
SERDE_STRUCT_FIELD(commitIO, CommitIO{});
SERDE_STRUCT_FIELD(commitRes, IOResult{});
};
} // namespace hf3fs::storage
FMT_BEGIN_NAMESPACE
template <>
struct formatter<hf3fs::storage::ChunkId> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::storage::ChunkId &chunkId, FormatContext &ctx) const {
return format_to(ctx.out(), "ChunkId({})", chunkId.describe());
}
};
template <>
struct formatter<hf3fs::storage::ChunkIdRange> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::storage::ChunkIdRange &range, FormatContext &ctx) const {
if (range.maxNumChunkIdsToProcess == 1) {
return format_to(ctx.out(), "{}", range.begin);
} else {
return format_to(ctx.out(),
"ChunkIdRange[{}, {}){{{}}}",
range.begin.describe(),
range.end.describe(),
range.maxNumChunkIdsToProcess);
}
}
};
template <>
struct formatter<hf3fs::storage::ChecksumInfo> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::storage::ChecksumInfo &checksum, FormatContext &ctx) const {
return format_to(ctx.out(), "{}#{:08X}", magic_enum::enum_name(checksum.type), ~checksum.value);
}
};
template <>
struct formatter<hf3fs::storage::IOResult> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::storage::IOResult &result, FormatContext &ctx) const {
return format_to(ctx.out(),
"length{{{}}} version{{{}/{}}} checksum{{{}}} {{{}}}",
result.lengthInfo,
result.updateVer,
result.commitVer,
result.checksum,
result.commitChainVer);
}
};
template <>
struct formatter<hf3fs::storage::UpdateChannel> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::storage::UpdateChannel &channel, FormatContext &ctx) const {
return format_to(ctx.out(), "@{}#{}", channel.id, channel.seqnum);
}
};
template <>
struct formatter<hf3fs::storage::MessageTag> : formatter<std::string_view> {
template <typename FormatContext>
auto format(const hf3fs::storage::MessageTag &tag, FormatContext &ctx) const {
return format_to(ctx.out(), "@{}#{}:{}", tag.clientId, tag.requestId, tag.channel);
}
};
FMT_END_NAMESPACE

25
src/fbs/storage/Service.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include "common/serde/Service.h"
#include "fbs/storage/Common.h"
namespace hf3fs::storage {
SERDE_SERVICE(StorageSerde, 3) {
SERDE_SERVICE_METHOD(batchRead, 1, BatchReadReq, BatchReadRsp);
SERDE_SERVICE_METHOD(write, 2, WriteReq, WriteRsp);
SERDE_SERVICE_METHOD(update, 3, UpdateReq, UpdateRsp);
SERDE_SERVICE_METHOD(queryLastChunk, 5, QueryLastChunkReq, QueryLastChunkRsp);
SERDE_SERVICE_METHOD(truncateChunks, 6, TruncateChunksReq, TruncateChunksRsp);
SERDE_SERVICE_METHOD(removeChunks, 7, RemoveChunksReq, RemoveChunksRsp);
SERDE_SERVICE_METHOD(syncStart, 8, SyncStartReq, TargetSyncInfo);
SERDE_SERVICE_METHOD(syncDone, 9, SyncDoneReq, SyncDoneRsp);
SERDE_SERVICE_METHOD(spaceInfo, 10, SpaceInfoReq, SpaceInfoRsp);
SERDE_SERVICE_METHOD(createTarget, 11, CreateTargetReq, CreateTargetRsp);
SERDE_SERVICE_METHOD(queryChunk, 12, QueryChunkReq, QueryChunkRsp);
SERDE_SERVICE_METHOD(getAllChunkMetadata, 13, GetAllChunkMetadataReq, GetAllChunkMetadataRsp);
SERDE_SERVICE_METHOD(offlineTarget, 16, OfflineTargetReq, OfflineTargetRsp);
SERDE_SERVICE_METHOD(removeTarget, 17, RemoveTargetReq, RemoveTargetRsp);
};
} // namespace hf3fs::storage