mirror of
https://github.com/deepseek-ai/3FS
synced 2025-06-26 18:16:45 +00:00
Initial commit
This commit is contained in:
8
src/fbs/CMakeLists.txt
Normal file
8
src/fbs/CMakeLists.txt
Normal 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)
|
||||
3
src/fbs/core/CMakeLists.txt
Normal file
3
src/fbs/core/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
add_subdirectory("user")
|
||||
add_subdirectory("service")
|
||||
|
||||
2
src/fbs/core/service/CMakeLists.txt
Normal file
2
src/fbs/core/service/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
target_add_lib(core-service-fbs common)
|
||||
|
||||
12
src/fbs/core/service/CoreServiceBase.h
Normal file
12
src/fbs/core/service/CoreServiceBase.h
Normal 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
|
||||
7
src/fbs/core/service/CoreServiceClient.h
Normal file
7
src/fbs/core/service/CoreServiceClient.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreServiceBase.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
SERDE_SERVICE_CLIENT(CoreServiceClient, CoreServiceBase);
|
||||
} // namespace hf3fs::core
|
||||
10
src/fbs/core/service/CoreServiceDef.h
Normal file
10
src/fbs/core/service/CoreServiceDef.h
Normal 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"
|
||||
58
src/fbs/core/service/Rpc.h
Normal file
58
src/fbs/core/service/Rpc.h
Normal 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
|
||||
2
src/fbs/core/user/CMakeLists.txt
Normal file
2
src/fbs/core/user/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
target_add_lib(core-user-fbs common)
|
||||
|
||||
80
src/fbs/core/user/User.cc
Normal file
80
src/fbs/core/user/User.cc
Normal 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
103
src/fbs/core/user/User.h
Normal 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
92
src/fbs/lib/Schema.h
Normal 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
221
src/fbs/lib/Service.h
Normal 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
39
src/fbs/macros/IStub.h
Normal 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)
|
||||
39
src/fbs/macros/ISyncStub.h
Normal file
39
src/fbs/macros/ISyncStub.h
Normal 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
|
||||
8
src/fbs/macros/SerdeDef.h
Normal file
8
src/fbs/macros/SerdeDef.h
Normal 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
46
src/fbs/macros/Stub.h
Normal 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
45
src/fbs/macros/SyncStub.h
Normal 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
7
src/fbs/macros/Undef.h
Normal 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
|
||||
1
src/fbs/meta/CMakeLists.txt
Normal file
1
src/fbs/meta/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_add_lib(meta-fbs mgmtd-fbs core-user-fbs storage-client)
|
||||
285
src/fbs/meta/Common.h
Normal file
285
src/fbs/meta/Common.h
Normal 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
|
||||
350
src/fbs/meta/FileOperation.cc
Normal file
350
src/fbs/meta/FileOperation.cc
Normal 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
|
||||
81
src/fbs/meta/FileOperation.h
Normal file
81
src/fbs/meta/FileOperation.h
Normal 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
|
||||
42
src/fbs/meta/MockService.h
Normal file
42
src/fbs/meta/MockService.h
Normal 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
196
src/fbs/meta/Schema.cc
Normal 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
467
src/fbs/meta/Schema.h
Normal 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
746
src/fbs/meta/Service.h
Normal 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> ¤tClientId() {
|
||||
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
447
src/fbs/meta/Utils.h
Normal 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
|
||||
2
src/fbs/mgmtd/CMakeLists.txt
Normal file
2
src/fbs/mgmtd/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
target_add_lib(mgmtd-fbs common)
|
||||
|
||||
7
src/fbs/mgmtd/ChainInfo.cc
Normal file
7
src/fbs/mgmtd/ChainInfo.cc
Normal 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
14
src/fbs/mgmtd/ChainInfo.h
Normal 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
49
src/fbs/mgmtd/ChainRef.h
Normal 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
|
||||
11
src/fbs/mgmtd/ChainSetting.h
Normal file
11
src/fbs/mgmtd/ChainSetting.h
Normal 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
|
||||
15
src/fbs/mgmtd/ChainTable.h
Normal file
15
src/fbs/mgmtd/ChainTable.h
Normal 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
|
||||
14
src/fbs/mgmtd/ChainTargetInfo.h
Normal file
14
src/fbs/mgmtd/ChainTargetInfo.h
Normal 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
|
||||
13
src/fbs/mgmtd/ChainTargetSetting.h
Normal file
13
src/fbs/mgmtd/ChainTargetSetting.h
Normal 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
|
||||
18
src/fbs/mgmtd/ClientSession.cc
Normal file
18
src/fbs/mgmtd/ClientSession.cc
Normal 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
|
||||
29
src/fbs/mgmtd/ClientSession.h
Normal file
29
src/fbs/mgmtd/ClientSession.h
Normal 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
|
||||
17
src/fbs/mgmtd/ClientSessionData.h
Normal file
17
src/fbs/mgmtd/ClientSessionData.h
Normal 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
|
||||
15
src/fbs/mgmtd/ConfigInfo.h
Normal file
15
src/fbs/mgmtd/ConfigInfo.h
Normal 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
|
||||
94
src/fbs/mgmtd/HeartbeatInfo.h
Normal file
94
src/fbs/mgmtd/HeartbeatInfo.h
Normal 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
|
||||
18
src/fbs/mgmtd/LocalTargetInfo.h
Normal file
18
src/fbs/mgmtd/LocalTargetInfo.h
Normal 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
|
||||
25
src/fbs/mgmtd/MgmtdLeaseInfo.h
Normal file
25
src/fbs/mgmtd/MgmtdLeaseInfo.h
Normal 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
|
||||
12
src/fbs/mgmtd/MgmtdServiceBase.h
Normal file
12
src/fbs/mgmtd/MgmtdServiceBase.h
Normal 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
|
||||
7
src/fbs/mgmtd/MgmtdServiceClient.h
Normal file
7
src/fbs/mgmtd/MgmtdServiceClient.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "MgmtdServiceBase.h"
|
||||
|
||||
namespace hf3fs::mgmtd {
|
||||
SERDE_SERVICE_CLIENT(MgmtdServiceClient, MgmtdServiceBase);
|
||||
} // namespace hf3fs::mgmtd
|
||||
28
src/fbs/mgmtd/MgmtdServiceDef.h
Normal file
28
src/fbs/mgmtd/MgmtdServiceDef.h
Normal 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"
|
||||
57
src/fbs/mgmtd/MgmtdTypes.h
Normal file
57
src/fbs/mgmtd/MgmtdTypes.h
Normal 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
|
||||
16
src/fbs/mgmtd/NodeConversion.cc
Normal file
16
src/fbs/mgmtd/NodeConversion.cc
Normal 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
|
||||
9
src/fbs/mgmtd/NodeConversion.h
Normal file
9
src/fbs/mgmtd/NodeConversion.h
Normal 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
19
src/fbs/mgmtd/NodeInfo.cc
Normal 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
71
src/fbs/mgmtd/NodeInfo.h
Normal 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
|
||||
12
src/fbs/mgmtd/NodeStatusChange.h
Normal file
12
src/fbs/mgmtd/NodeStatusChange.h
Normal 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
|
||||
18
src/fbs/mgmtd/PersistentNodeInfo.h
Normal file
18
src/fbs/mgmtd/PersistentNodeInfo.h
Normal 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
|
||||
84
src/fbs/mgmtd/RoutingInfo.cc
Normal file
84
src/fbs/mgmtd/RoutingInfo.cc
Normal 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
|
||||
49
src/fbs/mgmtd/RoutingInfo.h
Normal file
49
src/fbs/mgmtd/RoutingInfo.h
Normal 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
235
src/fbs/mgmtd/Rpc.h
Normal 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
|
||||
19
src/fbs/mgmtd/TargetInfo.h
Normal file
19
src/fbs/mgmtd/TargetInfo.h
Normal 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
|
||||
1
src/fbs/migration/CMakeLists.txt
Normal file
1
src/fbs/migration/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_add_lib(migration-fbs mgmtd-fbs core-user-fbs)
|
||||
80
src/fbs/migration/SerdeService.h
Normal file
80
src/fbs/migration/SerdeService.h
Normal 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
|
||||
1
src/fbs/monitor_collector/CMakeLists.txt
Normal file
1
src/fbs/monitor_collector/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_add_lib(MonitorCollectorService-fbs common)
|
||||
15
src/fbs/monitor_collector/MonitorCollectorService.h
Normal file
15
src/fbs/monitor_collector/MonitorCollectorService.h
Normal 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
|
||||
1
src/fbs/simple_example/CMakeLists.txt
Normal file
1
src/fbs/simple_example/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_add_lib(simple_example-fbs mgmtd-fbs core-user-fbs)
|
||||
18
src/fbs/simple_example/SerdeService.h
Normal file
18
src/fbs/simple_example/SerdeService.h
Normal 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
|
||||
1
src/fbs/storage/CMakeLists.txt
Normal file
1
src/fbs/storage/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_add_lib(storage-fbs meta-fbs mgmtd-fbs)
|
||||
109
src/fbs/storage/Common.cc
Normal file
109
src/fbs/storage/Common.cc
Normal 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
805
src/fbs/storage/Common.h
Normal 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
25
src/fbs/storage/Service.h
Normal 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
|
||||
Reference in New Issue
Block a user