mirror of
https://github.com/deepseek-ai/3FS
synced 2025-06-26 18:16:45 +00:00
Initial commit
This commit is contained in:
3
src/core/CMakeLists.txt
Normal file
3
src/core/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(service)
|
||||
add_subdirectory(user)
|
||||
1
src/core/README.md
Normal file
1
src/core/README.md
Normal file
@@ -0,0 +1 @@
|
||||
`core` is intended as a base component of `mgmtd`, `meta`, `storage`, and other concrete server components.
|
||||
1
src/core/app/CMakeLists.txt
Normal file
1
src/core/app/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_add_lib(core-app common mgmtd-client)
|
||||
21
src/core/app/LauncherUtils.cc
Normal file
21
src/core/app/LauncherUtils.cc
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "LauncherUtils.h"
|
||||
|
||||
#include <folly/experimental/coro/BlockingWait.h>
|
||||
|
||||
#include "common/utils/SysResource.h"
|
||||
#include "common/utils/VersionInfo.h"
|
||||
|
||||
namespace hf3fs::core::launcher {
|
||||
flat::AppInfo buildBasicAppInfo(flat::NodeId nodeId, const String &clusterId) {
|
||||
auto hostnameResult = SysResource::hostname();
|
||||
XLOGF_IF(FATAL, !hostnameResult, "Get hostname failed: {}", hostnameResult.error());
|
||||
|
||||
flat::AppInfo appInfo;
|
||||
appInfo.nodeId = nodeId;
|
||||
appInfo.clusterId = clusterId;
|
||||
appInfo.hostname = *hostnameResult;
|
||||
appInfo.pid = SysResource::pid();
|
||||
appInfo.releaseVersion = flat::ReleaseVersion::fromVersionInfo();
|
||||
return appInfo;
|
||||
}
|
||||
} // namespace hf3fs::core::launcher
|
||||
7
src/core/app/LauncherUtils.h
Normal file
7
src/core/app/LauncherUtils.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/app/AppInfo.h"
|
||||
|
||||
namespace hf3fs::core::launcher {
|
||||
flat::AppInfo buildBasicAppInfo(flat::NodeId nodeId, const String &clusterId);
|
||||
} // namespace hf3fs::core::launcher
|
||||
66
src/core/app/MgmtdClientFetcher.cc
Normal file
66
src/core/app/MgmtdClientFetcher.cc
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "MgmtdClientFetcher.h"
|
||||
|
||||
#include <folly/experimental/coro/BlockingWait.h>
|
||||
|
||||
#include "common/app/ApplicationBase.h"
|
||||
#include "stubs/common/RealStubFactory.h"
|
||||
#include "stubs/mgmtd/MgmtdServiceStub.h"
|
||||
|
||||
namespace hf3fs::core::launcher {
|
||||
MgmtdClientFetcher::MgmtdClientFetcher(String clusterId,
|
||||
const net::Client::Config &clientCfg,
|
||||
const client::MgmtdClient::Config &mgmtdClientCfg)
|
||||
: clusterId_(std::move(clusterId)),
|
||||
clientCfg_(clientCfg),
|
||||
mgmtdClientCfg_(mgmtdClientCfg) {}
|
||||
|
||||
Result<flat::ConfigInfo> MgmtdClientFetcher::loadConfigTemplate(flat::NodeType nodeType) {
|
||||
RETURN_ON_ERROR(ensureClientInited());
|
||||
return folly::coro::blockingWait([&]() -> CoTryTask<flat::ConfigInfo> {
|
||||
XLOGF(INFO, "Start to load config from mgmtd");
|
||||
auto res = co_await mgmtdClient_->getConfig(nodeType, flat::ConfigVersion(0));
|
||||
XLOGF(INFO, "Load config from mgmtd finished. res: {}", res.hasError() ? res.error() : Status::OK);
|
||||
CO_RETURN_ON_ERROR(res);
|
||||
if (!*res) {
|
||||
XLOGF(INFO, "Load empty config from mgmtd");
|
||||
co_return flat::ConfigInfo::create();
|
||||
}
|
||||
XLOGF(INFO, "Load config from mgmtd version: {}", res->value().configVersion);
|
||||
co_return res->value();
|
||||
}());
|
||||
}
|
||||
|
||||
void MgmtdClientFetcher::stopClient() {
|
||||
if (mgmtdClient_) {
|
||||
folly::coro::blockingWait(mgmtdClient_->stop());
|
||||
mgmtdClient_.reset();
|
||||
client_->stopAndJoin();
|
||||
client_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
Result<Void> MgmtdClientFetcher::ensureClientInited() {
|
||||
if (!client_) {
|
||||
auto client = std::make_unique<net::Client>(clientCfg_);
|
||||
RETURN_ON_ERROR(client->start("launcher"));
|
||||
client_ = std::move(client);
|
||||
}
|
||||
|
||||
if (!mgmtdClient_) {
|
||||
auto ctxCreator =
|
||||
// client_ may be moved to Server later so here we should capture its raw pointer.
|
||||
[client = client_.get()](net::Address addr) { return client->serdeCtx(addr); };
|
||||
auto stub = std::make_unique<stubs::RealStubFactory<mgmtd::MgmtdServiceStub>>(std::move(ctxCreator));
|
||||
|
||||
auto mgmtdClient = std::make_shared<client::MgmtdClient>(clusterId_, std::move(stub), mgmtdClientCfg_);
|
||||
|
||||
folly::coro::blockingWait(
|
||||
mgmtdClient->start(&client_->tpg().bgThreadPool().randomPick(), /*startBackground=*/false));
|
||||
auto refreshRes = folly::coro::blockingWait(mgmtdClient->refreshRoutingInfo(/*force=*/false));
|
||||
RETURN_ON_ERROR(refreshRes);
|
||||
|
||||
mgmtdClient_ = std::move(mgmtdClient);
|
||||
}
|
||||
return Void{};
|
||||
}
|
||||
} // namespace hf3fs::core::launcher
|
||||
31
src/core/app/MgmtdClientFetcher.h
Normal file
31
src/core/app/MgmtdClientFetcher.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "LauncherUtils.h"
|
||||
#include "client/mgmtd/MgmtdClient.h"
|
||||
#include "common/net/Client.h"
|
||||
|
||||
namespace hf3fs::core::launcher {
|
||||
struct MgmtdClientFetcher {
|
||||
MgmtdClientFetcher(String clusterId,
|
||||
const net::Client::Config &clientCfg,
|
||||
const client::MgmtdClient::Config &mgmtdClientCfg);
|
||||
|
||||
template <typename ConfigT>
|
||||
MgmtdClientFetcher(const ConfigT &cfg)
|
||||
: MgmtdClientFetcher(cfg.cluster_id(), cfg.client(), cfg.mgmtd_client()) {}
|
||||
|
||||
virtual ~MgmtdClientFetcher() { stopClient(); }
|
||||
|
||||
Result<flat::ConfigInfo> loadConfigTemplate(flat::NodeType nodeType);
|
||||
Result<Void> ensureClientInited();
|
||||
void stopClient();
|
||||
|
||||
virtual Result<Void> completeAppInfo(flat::AppInfo &appInfo) = 0;
|
||||
|
||||
const String clusterId_;
|
||||
const net::Client::Config &clientCfg_;
|
||||
const client::MgmtdClient::Config &mgmtdClientCfg_;
|
||||
std::unique_ptr<net::Client> client_;
|
||||
std::shared_ptr<client::MgmtdClient> mgmtdClient_;
|
||||
};
|
||||
} // namespace hf3fs::core::launcher
|
||||
12
src/core/app/ServerAppConfig.cc
Normal file
12
src/core/app/ServerAppConfig.cc
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "ServerAppConfig.h"
|
||||
|
||||
#include "common/app/ApplicationBase.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
void ServerAppConfig::init(const String &filePath, bool dump, const std::vector<config::KeyValue> &updates) {
|
||||
auto res = ApplicationBase::initConfig(*this, filePath, dump, updates);
|
||||
XLOGF_IF(FATAL, !res, "Init app config failed: {}. filePath: {}. dump: {}", res.error(), filePath, dump);
|
||||
|
||||
XLOGF_IF(FATAL, !allow_empty_node_id() && node_id() == 0, "node_id is not allowed to be 0");
|
||||
}
|
||||
} // namespace hf3fs::core
|
||||
20
src/core/app/ServerAppConfig.h
Normal file
20
src/core/app/ServerAppConfig.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/app/NodeId.h"
|
||||
#include "common/net/ib/IBDevice.h"
|
||||
#include "common/utils/ConfigBase.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
struct ServerAppConfig : public ConfigBase<ServerAppConfig> {
|
||||
CONFIG_ITEM(node_id, 0);
|
||||
CONFIG_ITEM(allow_empty_node_id, true);
|
||||
|
||||
public:
|
||||
using Base = ConfigBase<ServerAppConfig>;
|
||||
using Base::init;
|
||||
|
||||
void init(const String &filePath, bool dump, const std::vector<config::KeyValue> &updates);
|
||||
|
||||
flat::NodeId getNodeId() const { return flat::NodeId(node_id()); }
|
||||
};
|
||||
} // namespace hf3fs::core
|
||||
29
src/core/app/ServerEnv.cc
Normal file
29
src/core/app/ServerEnv.cc
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "ServerEnv.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
namespace {
|
||||
|
||||
template <typename Ptr>
|
||||
void setPtr(Ptr &src, Ptr &dst) {
|
||||
if (src.get() != dst.get()) {
|
||||
src.reset();
|
||||
src = std::move(dst);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ServerEnv::setAppInfo(flat::AppInfo appInfo) { appInfo_ = appInfo; }
|
||||
|
||||
void ServerEnv::setKvEngine(std::shared_ptr<kv::IKVEngine> engine) { setPtr(kvEngine_, engine); }
|
||||
|
||||
void ServerEnv::setMgmtdStubFactory(std::shared_ptr<MgmtdStubFactory> factory) { setPtr(mgmtdStubFactory_, factory); }
|
||||
|
||||
void ServerEnv::setUtcTimeGenerator(UtcTimeGenerator generator) { utcTimeGenerator_ = std::move(generator); }
|
||||
|
||||
void ServerEnv::setBackgroundExecutor(CPUExecutorGroup *executor) { backgroundExecutor_ = executor; }
|
||||
|
||||
void ServerEnv::setConfigUpdater(ConfigUpdater updater) { configUpdater_ = std::move(updater); }
|
||||
|
||||
void ServerEnv::setConfigValidater(ConfigValidater validater) { configValidater_ = std::move(validater); }
|
||||
|
||||
} // namespace hf3fs::core
|
||||
53
src/core/app/ServerEnv.h
Normal file
53
src/core/app/ServerEnv.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/app/AppInfo.h"
|
||||
#include "common/kv/IKVEngine.h"
|
||||
#include "common/utils/CPUExecutorGroup.h"
|
||||
#include "stubs/common/IStubFactory.h"
|
||||
#include "stubs/mgmtd/IMgmtdServiceStub.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
class ServerEnv {
|
||||
public:
|
||||
const flat::AppInfo &appInfo() const { return appInfo_; }
|
||||
|
||||
void setAppInfo(flat::AppInfo appInfo);
|
||||
|
||||
const std::shared_ptr<kv::IKVEngine> &kvEngine() const { return kvEngine_; }
|
||||
|
||||
void setKvEngine(std::shared_ptr<kv::IKVEngine> engine);
|
||||
|
||||
using MgmtdStubFactory = stubs::IStubFactory<mgmtd::IMgmtdServiceStub>;
|
||||
const std::shared_ptr<MgmtdStubFactory> &mgmtdStubFactory() const { return mgmtdStubFactory_; }
|
||||
|
||||
void setMgmtdStubFactory(std::shared_ptr<MgmtdStubFactory> factory);
|
||||
|
||||
using UtcTimeGenerator = std::function<UtcTime()>;
|
||||
const UtcTimeGenerator &utcTimeGenerator() const { return utcTimeGenerator_; }
|
||||
|
||||
void setUtcTimeGenerator(UtcTimeGenerator generator);
|
||||
|
||||
CPUExecutorGroup *backgroundExecutor() const { return backgroundExecutor_; }
|
||||
|
||||
void setBackgroundExecutor(CPUExecutorGroup *executor);
|
||||
|
||||
using ConfigUpdater = std::function<Result<Void>(const String &, const String &)>;
|
||||
const ConfigUpdater &configUpdater() const { return configUpdater_; }
|
||||
|
||||
void setConfigUpdater(ConfigUpdater updater);
|
||||
|
||||
using ConfigValidater = std::function<Result<Void>(const String &, const String &)>;
|
||||
const ConfigValidater &configValidater() const { return configValidater_; }
|
||||
|
||||
void setConfigValidater(ConfigValidater validater);
|
||||
|
||||
private:
|
||||
flat::AppInfo appInfo_;
|
||||
std::shared_ptr<kv::IKVEngine> kvEngine_;
|
||||
std::shared_ptr<MgmtdStubFactory> mgmtdStubFactory_;
|
||||
UtcTimeGenerator utcTimeGenerator_ = &UtcClock::now;
|
||||
CPUExecutorGroup *backgroundExecutor_ = nullptr;
|
||||
ConfigUpdater configUpdater_;
|
||||
ConfigValidater configValidater_;
|
||||
};
|
||||
} // namespace hf3fs::core
|
||||
12
src/core/app/ServerLauncher.cc
Normal file
12
src/core/app/ServerLauncher.cc
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "ServerLauncher.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
Result<Void> ServerLauncherBase::parseFlags(int *argc, char ***argv) {
|
||||
static constexpr std::string_view appConfigPrefix = "--app_config.";
|
||||
static constexpr std::string_view launcherConfigPrefix = "--launcher_config.";
|
||||
RETURN_ON_ERROR(ApplicationBase::parseFlags(appConfigPrefix, argc, argv, appConfigFlags_));
|
||||
RETURN_ON_ERROR(ApplicationBase::parseFlags(launcherConfigPrefix, argc, argv, launcherConfigFlags_));
|
||||
return Void{};
|
||||
}
|
||||
|
||||
} // namespace hf3fs::core
|
||||
79
src/core/app/ServerLauncher.h
Normal file
79
src/core/app/ServerLauncher.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "LauncherUtils.h"
|
||||
#include "common/app/ApplicationBase.h"
|
||||
#include "common/utils/ConstructLog.h"
|
||||
#include "fbs/mgmtd/ConfigInfo.h"
|
||||
|
||||
DECLARE_string(app_cfg);
|
||||
DECLARE_bool(dump_default_app_cfg);
|
||||
|
||||
DECLARE_string(launcher_cfg);
|
||||
DECLARE_bool(dump_default_launcher_cfg);
|
||||
|
||||
namespace hf3fs::core {
|
||||
class ServerLauncherBase {
|
||||
public:
|
||||
Result<Void> parseFlags(int *argc, char ***argv);
|
||||
|
||||
protected:
|
||||
ApplicationBase::ConfigFlags appConfigFlags_;
|
||||
ApplicationBase::ConfigFlags launcherConfigFlags_;
|
||||
};
|
||||
|
||||
template <typename Server>
|
||||
class ServerLauncher : public ServerLauncherBase {
|
||||
public:
|
||||
using AppConfig = typename Server::AppConfig;
|
||||
using LauncherConfig = typename Server::LauncherConfig;
|
||||
using RemoteConfigFetcher = typename Server::RemoteConfigFetcher;
|
||||
static constexpr auto kNodeType = Server::kNodeType;
|
||||
|
||||
ServerLauncher() = default;
|
||||
|
||||
Result<Void> init() {
|
||||
appConfig_.init(FLAGS_app_cfg, FLAGS_dump_default_app_cfg, appConfigFlags_);
|
||||
launcherConfig_.init(FLAGS_launcher_cfg, FLAGS_dump_default_launcher_cfg, launcherConfigFlags_);
|
||||
|
||||
XLOGF(INFO, "Full AppConfig:\n{}", appConfig_.toString());
|
||||
XLOGF(INFO, "Full LauncherConfig:\n{}", launcherConfig_.toString());
|
||||
|
||||
auto ibResult = net::IBManager::start(launcherConfig_.ib_devices());
|
||||
XLOGF_IF(FATAL, !ibResult, "Failed to start IBManager: {}", ibResult.error());
|
||||
XLOGF(INFO, "IBDevice inited");
|
||||
|
||||
fetcher_ = std::make_unique<RemoteConfigFetcher>(launcherConfig_);
|
||||
return Void{};
|
||||
}
|
||||
|
||||
Result<std::pair<String, String>> loadConfigTemplate() {
|
||||
auto res = fetcher_->loadConfigTemplate(kNodeType);
|
||||
RETURN_ON_ERROR(res);
|
||||
return std::make_pair(res->content, res->genUpdateDesc());
|
||||
}
|
||||
|
||||
Result<flat::AppInfo> loadAppInfo() {
|
||||
auto appInfo = launcher::buildBasicAppInfo(appConfig_.getNodeId(), launcherConfig_.cluster_id());
|
||||
RETURN_ON_ERROR(fetcher_->completeAppInfo(appInfo));
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
Result<Void> startServer(Server &server, const flat::AppInfo &appInfo) {
|
||||
if constexpr (requires { fetcher_->startServer(server, appInfo); }) {
|
||||
return fetcher_->startServer(server, appInfo);
|
||||
} else {
|
||||
return server.start(appInfo);
|
||||
}
|
||||
}
|
||||
|
||||
const auto &appConfig() const { return appConfig_; }
|
||||
|
||||
const auto &launcherConfig() const { return launcherConfig_; }
|
||||
|
||||
private:
|
||||
AppConfig appConfig_;
|
||||
LauncherConfig launcherConfig_;
|
||||
|
||||
std::unique_ptr<RemoteConfigFetcher> fetcher_;
|
||||
};
|
||||
} // namespace hf3fs::core
|
||||
15
src/core/app/ServerLauncherConfig.cc
Normal file
15
src/core/app/ServerLauncherConfig.cc
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "ServerLauncherConfig.h"
|
||||
|
||||
#include "common/app/ApplicationBase.h"
|
||||
#include "common/app/Utils.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
void ServerLauncherConfig::init(const String &filePath, bool dump, const std::vector<config::KeyValue> &updates) {
|
||||
app_detail::initConfigFromFile(*this, filePath, dump, updates);
|
||||
|
||||
auto rv = flat::ReleaseVersion::fromVersionInfo();
|
||||
if (!allow_dev_version() && !rv.getIsReleaseVersion()) {
|
||||
XLOGF(FATAL, "Dev version is not allowed: {}", rv);
|
||||
}
|
||||
}
|
||||
} // namespace hf3fs::core
|
||||
22
src/core/app/ServerLauncherConfig.h
Normal file
22
src/core/app/ServerLauncherConfig.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "client/mgmtd/MgmtdClient.h"
|
||||
#include "common/app/NodeId.h"
|
||||
#include "common/net/Client.h"
|
||||
#include "common/utils/ConfigBase.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
struct ServerLauncherConfig : public ConfigBase<ServerLauncherConfig> {
|
||||
CONFIG_ITEM(cluster_id, "");
|
||||
CONFIG_OBJ(ib_devices, net::IBDevice::Config);
|
||||
CONFIG_OBJ(client, net::Client::Config);
|
||||
CONFIG_OBJ(mgmtd_client, client::MgmtdClient::Config);
|
||||
CONFIG_ITEM(allow_dev_version, true);
|
||||
|
||||
public:
|
||||
using Base = ConfigBase<ServerLauncherConfig>;
|
||||
using Base::init;
|
||||
|
||||
void init(const String &filePath, bool dump, const std::vector<config::KeyValue> &updates);
|
||||
};
|
||||
} // namespace hf3fs::core
|
||||
18
src/core/app/ServerMgmtdClientFetcher.cc
Normal file
18
src/core/app/ServerMgmtdClientFetcher.cc
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "ServerMgmtdClientFetcher.h"
|
||||
|
||||
#include <folly/experimental/coro/BlockingWait.h>
|
||||
|
||||
namespace hf3fs::core::launcher {
|
||||
Result<Void> ServerMgmtdClientFetcher::completeAppInfo(flat::AppInfo &appInfo) {
|
||||
RETURN_ON_ERROR(ensureClientInited());
|
||||
return folly::coro::blockingWait([&]() -> CoTryTask<void> {
|
||||
auto routingInfo = mgmtdClient_->getRoutingInfo();
|
||||
if (!routingInfo || !routingInfo->raw()) co_return makeError(MgmtdClientCode::kRoutingInfoNotReady);
|
||||
const auto *ni = routingInfo->raw()->getNode(appInfo.nodeId);
|
||||
if (ni) {
|
||||
appInfo.tags = ni->tags;
|
||||
}
|
||||
co_return Void{};
|
||||
}());
|
||||
}
|
||||
} // namespace hf3fs::core::launcher
|
||||
10
src/core/app/ServerMgmtdClientFetcher.h
Normal file
10
src/core/app/ServerMgmtdClientFetcher.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "MgmtdClientFetcher.h"
|
||||
|
||||
namespace hf3fs::core::launcher {
|
||||
struct ServerMgmtdClientFetcher : public MgmtdClientFetcher {
|
||||
using MgmtdClientFetcher::MgmtdClientFetcher;
|
||||
Result<Void> completeAppInfo(flat::AppInfo &appInfo) final;
|
||||
};
|
||||
} // namespace hf3fs::core::launcher
|
||||
1
src/core/service/CMakeLists.txt
Normal file
1
src/core/service/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_add_lib(core-service core-service-fbs)
|
||||
14
src/core/service/CoreService.cc
Normal file
14
src/core/service/CoreService.cc
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "CoreService.h"
|
||||
|
||||
#include "core/service/ops/Include.h"
|
||||
#include "core/utils/runOp.h"
|
||||
|
||||
#define DEFINE_SERDE_SERVICE_METHOD_FULL(svc, name, Name, id, reqtype, rsptype) \
|
||||
CoTryTask<rsptype> svc##Service::name(serde::CallContext &ctx, const reqtype &req) { \
|
||||
Name##Operation op(std::move(req)); \
|
||||
CO_INVOKE_OP_INFO(op, ctx.peer()); \
|
||||
}
|
||||
|
||||
namespace hf3fs::core {
|
||||
#include "fbs/core/service/CoreServiceDef.h"
|
||||
} // namespace hf3fs::core
|
||||
14
src/core/service/CoreService.h
Normal file
14
src/core/service/CoreService.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/serde/CallContext.h"
|
||||
#include "fbs/core/service/CoreServiceBase.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
class CoreService : public serde::ServiceWrapper<CoreService, CoreServiceBase> {
|
||||
public:
|
||||
#define DEFINE_SERDE_SERVICE_METHOD_FULL(svc, name, Name, id, reqtype, rsptype) \
|
||||
CoTryTask<rsptype> name(serde::CallContext &ctx, const reqtype &req);
|
||||
|
||||
#include "fbs/core/service/CoreServiceDef.h"
|
||||
};
|
||||
} // namespace hf3fs::core
|
||||
19
src/core/service/ops/EchoOperation.h
Normal file
19
src/core/service/ops/EchoOperation.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/utils/ServiceOperation.h"
|
||||
#include "fbs/core/service/Rpc.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
|
||||
struct EchoOperation : ServiceOperationWithMetric<"CoreService", "Echo", "op"> {
|
||||
EchoMessage req;
|
||||
|
||||
explicit EchoOperation(EchoMessage r)
|
||||
: req(std::move(r)) {}
|
||||
|
||||
String toStringImpl() const final { return "Echo"; }
|
||||
|
||||
CoTryTask<EchoMessage> handle() { co_return req; }
|
||||
};
|
||||
|
||||
} // namespace hf3fs::core
|
||||
24
src/core/service/ops/GetConfigOperation.h
Normal file
24
src/core/service/ops/GetConfigOperation.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/app/ApplicationBase.h"
|
||||
#include "core/utils/ServiceOperation.h"
|
||||
#include "fbs/core/service/Rpc.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
|
||||
struct GetConfigOperation : ServiceOperationWithMetric<"CoreService", "GetConfig", "op"> {
|
||||
GetConfigReq req;
|
||||
|
||||
explicit GetConfigOperation(GetConfigReq r)
|
||||
: req(std::move(r)) {}
|
||||
|
||||
String toStringImpl() const final { return "GetConfig"; }
|
||||
|
||||
CoTryTask<GetConfigRsp> handle() {
|
||||
auto cfg = ApplicationBase::getConfigString(req.configKey);
|
||||
CO_RETURN_ON_ERROR(cfg);
|
||||
co_return GetConfigRsp::create(std::move(*cfg));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace hf3fs::core
|
||||
24
src/core/service/ops/GetLastConfigUpdateRecordOperation.h
Normal file
24
src/core/service/ops/GetLastConfigUpdateRecordOperation.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/app/ApplicationBase.h"
|
||||
#include "core/utils/ServiceOperation.h"
|
||||
#include "fbs/core/service/Rpc.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
|
||||
struct GetLastConfigUpdateRecordOperation
|
||||
: ServiceOperationWithMetric<"CoreService", "GetLastConfigUpdateRecord", "op"> {
|
||||
GetLastConfigUpdateRecordReq req;
|
||||
|
||||
explicit GetLastConfigUpdateRecordOperation(GetLastConfigUpdateRecordReq r)
|
||||
: req(std::move(r)) {}
|
||||
|
||||
String toStringImpl() const final { return "GetLastConfigUpdateRecord"; }
|
||||
|
||||
CoTryTask<GetLastConfigUpdateRecordRsp> handle() {
|
||||
auto res = ApplicationBase::getLastConfigUpdateRecord();
|
||||
co_return GetLastConfigUpdateRecordRsp::create(std::move(res));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace hf3fs::core
|
||||
24
src/core/service/ops/HotUpdateConfigOperation.h
Normal file
24
src/core/service/ops/HotUpdateConfigOperation.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/app/ApplicationBase.h"
|
||||
#include "core/utils/ServiceOperation.h"
|
||||
#include "fbs/core/service/Rpc.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
|
||||
struct HotUpdateConfigOperation : ServiceOperationWithMetric<"CoreService", "HotUpdateConfig", "op"> {
|
||||
HotUpdateConfigReq req;
|
||||
|
||||
explicit HotUpdateConfigOperation(HotUpdateConfigReq r)
|
||||
: req(std::move(r)) {}
|
||||
|
||||
String toStringImpl() const final { return "HotUpdateConfig"; }
|
||||
|
||||
CoTryTask<HotUpdateConfigRsp> handle() {
|
||||
auto res = ApplicationBase::hotUpdateConfig(req.update, req.render);
|
||||
CO_RETURN_ON_ERROR(res);
|
||||
co_return HotUpdateConfigRsp::create();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace hf3fs::core
|
||||
8
src/core/service/ops/Include.h
Normal file
8
src/core/service/ops/Include.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "EchoOperation.h"
|
||||
#include "GetConfigOperation.h"
|
||||
#include "GetLastConfigUpdateRecordOperation.h"
|
||||
#include "HotUpdateConfigOperation.h"
|
||||
#include "RenderConfigOperation.h"
|
||||
#include "ShutdownOperation.h"
|
||||
29
src/core/service/ops/RenderConfigOperation.h
Normal file
29
src/core/service/ops/RenderConfigOperation.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/app/ApplicationBase.h"
|
||||
#include "core/utils/ServiceOperation.h"
|
||||
#include "fbs/core/service/Rpc.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
|
||||
struct RenderConfigOperation : ServiceOperationWithMetric<"CoreService", "RenderConfig", "op"> {
|
||||
RenderConfigReq req;
|
||||
|
||||
explicit RenderConfigOperation(RenderConfigReq r)
|
||||
: req(std::move(r)) {}
|
||||
|
||||
String toStringImpl() const final { return "RenderConfig"; }
|
||||
|
||||
CoTryTask<RenderConfigRsp> handle() {
|
||||
auto res = ApplicationBase::renderConfig(req.configTemplate, req.testUpdate, req.isHotUpdate);
|
||||
CO_RETURN_ON_ERROR(res);
|
||||
auto &[content, updateRes] = *res;
|
||||
if (updateRes) {
|
||||
co_return RenderConfigRsp::create(std::move(content), Status(StatusCode::kOK), std::move(*updateRes));
|
||||
} else {
|
||||
co_return RenderConfigRsp::create(std::move(content), std::move(updateRes.error()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace hf3fs::core
|
||||
24
src/core/service/ops/ShutdownOperation.h
Normal file
24
src/core/service/ops/ShutdownOperation.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/app/ApplicationBase.h"
|
||||
#include "common/utils/suicide.h"
|
||||
#include "core/utils/ServiceOperation.h"
|
||||
#include "fbs/core/service/Rpc.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
|
||||
struct ShutdownOperation : ServiceOperationWithMetric<"CoreService", "Shutdown", "op"> {
|
||||
ShutdownReq req_;
|
||||
|
||||
explicit ShutdownOperation(ShutdownReq req)
|
||||
: req_(std::move(req)) {}
|
||||
|
||||
String toStringImpl() const final { return "Shutdown"; }
|
||||
|
||||
CoTryTask<ShutdownRsp> handle() {
|
||||
CO_RETURN_ON_ERROR(suicide(req_.graceful));
|
||||
co_return ShutdownRsp::create();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace hf3fs::core
|
||||
1
src/core/user/CMakeLists.txt
Normal file
1
src/core/user/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_add_lib(core-user core-user-fbs fdb)
|
||||
120
src/core/user/UserCache.h
Normal file
120
src/core/user/UserCache.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/utils/ConfigBase.h"
|
||||
#include "common/utils/LockManager.h"
|
||||
#include "common/utils/RobinHood.h"
|
||||
#include "common/utils/UtcTime.h"
|
||||
#include "fbs/core/user/User.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
using flat::Gid;
|
||||
using flat::Uid;
|
||||
using flat::UserAttr;
|
||||
using flat::UserInfo;
|
||||
|
||||
class UserCache {
|
||||
public:
|
||||
struct Config : ConfigBase<Config> {
|
||||
CONFIG_ITEM(buckets, 127u, ConfigCheckers::isPositivePrime<uint32_t>);
|
||||
CONFIG_HOT_UPDATED_ITEM(exist_ttl, 5_min);
|
||||
CONFIG_HOT_UPDATED_ITEM(inexist_ttl, 10_s);
|
||||
};
|
||||
|
||||
explicit UserCache(const Config &cfg)
|
||||
: cfg_(cfg),
|
||||
lockManager_(cfg.buckets()),
|
||||
buckets_(cfg.buckets()) {}
|
||||
|
||||
struct GetResult {
|
||||
std::optional<UserAttr> attr;
|
||||
bool marked = false;
|
||||
|
||||
static GetResult unmarkedNotExist() { return {std::nullopt, false}; }
|
||||
static GetResult markedNotExist() { return {std::nullopt, true}; }
|
||||
static GetResult exist(UserAttr attr) { return {std::move(attr), false}; }
|
||||
};
|
||||
|
||||
GetResult get(Uid id) {
|
||||
auto lock = lockManager_.lock(id);
|
||||
auto now = SteadyClock::now();
|
||||
auto &bucket = buckets_[lockManager_.idx(id)];
|
||||
|
||||
auto it = bucket.find(id);
|
||||
if (it == bucket.end()) return GetResult::unmarkedNotExist();
|
||||
|
||||
auto &entry = it->second;
|
||||
if (expired(entry, now)) {
|
||||
bucket.erase(it);
|
||||
return GetResult::unmarkedNotExist();
|
||||
}
|
||||
|
||||
if (entry.attr)
|
||||
return GetResult::exist(*entry.attr);
|
||||
else
|
||||
return GetResult::markedNotExist();
|
||||
}
|
||||
|
||||
void set(Uid id, std::optional<UserAttr> attr) {
|
||||
auto lock = lockManager_.lock(id);
|
||||
auto &bucket = buckets_[lockManager_.idx(id)];
|
||||
auto &entry = bucket[id];
|
||||
auto now = SteadyClock::now();
|
||||
entry.setTs = now;
|
||||
entry.attr = std::move(attr);
|
||||
}
|
||||
|
||||
void clear(Uid id) {
|
||||
auto lock = lockManager_.lock(id);
|
||||
auto &bucket = buckets_[lockManager_.idx(id)];
|
||||
bucket.erase(id);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (uint32_t i = 0; i < lockManager_.numBuckets(); ++i) {
|
||||
auto lock = lockManager_.lock_at(i);
|
||||
auto &bucket = buckets_[i];
|
||||
bucket.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void clearRetired() {
|
||||
std::vector<Uid> uids;
|
||||
for (uint32_t i = 0; i < lockManager_.numBuckets(); ++i) {
|
||||
auto lock = lockManager_.lock_at(i);
|
||||
auto &bucket = buckets_[i];
|
||||
auto now = SteadyClock::now();
|
||||
for (const auto &[id, entry] : bucket) {
|
||||
if (expired(entry, now)) uids.push_back(id);
|
||||
}
|
||||
}
|
||||
if (!uids.empty()) {
|
||||
for (uint32_t i = 0; i < lockManager_.numBuckets(); ++i) {
|
||||
auto lock = lockManager_.lock_at(i);
|
||||
auto &bucket = buckets_[i];
|
||||
auto now = SteadyClock::now();
|
||||
for (auto id : uids) {
|
||||
auto it = bucket.find(id);
|
||||
if (expired(it->second, now)) bucket.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
std::optional<UserAttr> attr;
|
||||
SteadyTime setTs;
|
||||
};
|
||||
|
||||
bool expired(const Entry &entry, SteadyTime now) const {
|
||||
auto timeout = entry.attr ? cfg_.exist_ttl().asUs() : cfg_.inexist_ttl().asUs();
|
||||
return now - entry.setTs >= timeout;
|
||||
}
|
||||
|
||||
using Bucket = robin_hood::unordered_node_map<Uid, Entry>;
|
||||
|
||||
const Config &cfg_;
|
||||
UniqueLockManager lockManager_;
|
||||
std::vector<Bucket> buckets_;
|
||||
};
|
||||
} // namespace hf3fs::core
|
||||
180
src/core/user/UserStore.cc
Normal file
180
src/core/user/UserStore.cc
Normal file
@@ -0,0 +1,180 @@
|
||||
#include "UserStore.h"
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <folly/logging/xlog.h>
|
||||
|
||||
#include "UserToken.h"
|
||||
#include "common/kv/KeyPrefix.h"
|
||||
#include "common/serde/Serde.h"
|
||||
#include "common/utils/Result.h"
|
||||
#include "common/utils/SerDeser.h"
|
||||
#include "core/user/UserCache.h"
|
||||
#include "fdb/FDBTransaction.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
namespace {
|
||||
String packUid(Uid uid) {
|
||||
static constexpr auto prefix = kv::KeyPrefix::User;
|
||||
String buf;
|
||||
buf.reserve(sizeof(prefix) + sizeof(Uid::UnderlyingType));
|
||||
Serializer ser(buf);
|
||||
ser.put(prefix);
|
||||
ser.put(uid.toUnderType());
|
||||
return buf;
|
||||
}
|
||||
|
||||
Result<Void> ensureTokenMatchUid(std::string_view token, flat::Uid uid) {
|
||||
auto newUid = decodeUidFromUserToken(token);
|
||||
RETURN_ON_ERROR(newUid);
|
||||
if (*newUid != uid) {
|
||||
return makeError(StatusCode::kInvalidFormat, "Could not set token from other uid");
|
||||
}
|
||||
return Void{};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CoTryTask<std::optional<UserAttr>> UserStore::getUser(IReadOnlyTransaction &txn, Uid uid) {
|
||||
auto key = packUid(uid);
|
||||
auto getResult = co_await txn.snapshotGet(key);
|
||||
CO_RETURN_ON_ERROR(getResult);
|
||||
if (!getResult->has_value()) {
|
||||
co_return std::nullopt;
|
||||
}
|
||||
auto value = **getResult;
|
||||
XLOGF(DBG,
|
||||
"key value {:02x} {:02x}",
|
||||
fmt::join(key.data(), key.data(), ","),
|
||||
fmt::join(value.data(), value.data(), ","));
|
||||
UserAttr attr;
|
||||
CO_RETURN_ON_ERROR(serde::deserialize(attr, **getResult));
|
||||
attr.uid = uid; // old versions of UserAttr didn't persist uid
|
||||
co_return attr;
|
||||
}
|
||||
|
||||
CoTryTask<UserAttr> UserStore::addUser(IReadWriteTransaction &txn,
|
||||
Uid uid,
|
||||
String name,
|
||||
std::vector<Gid> groups,
|
||||
bool isRootUser,
|
||||
bool isAdmin,
|
||||
std::string_view token) {
|
||||
auto key = packUid(uid);
|
||||
auto getResult = co_await txn.snapshotGet(key);
|
||||
CO_RETURN_ON_ERROR(getResult);
|
||||
if (getResult->has_value()) co_return makeError(MetaCode::kExists);
|
||||
|
||||
UserAttr attr;
|
||||
attr.name = std::move(name);
|
||||
attr.uid = uid;
|
||||
attr.gid = flat::Gid(uid);
|
||||
attr.groups = std::move(groups);
|
||||
attr.root = isRootUser;
|
||||
attr.admin = isAdmin;
|
||||
|
||||
if (!token.empty()) {
|
||||
CO_RETURN_ON_ERROR(ensureTokenMatchUid(token, uid));
|
||||
attr.token = token;
|
||||
} else {
|
||||
auto tokenResult = co_await encodeUserToken(uid, txn);
|
||||
CO_RETURN_ON_ERROR(tokenResult);
|
||||
attr.token = *tokenResult;
|
||||
}
|
||||
|
||||
CO_RETURN_ON_ERROR(co_await txn.set(key, serde::serialize(attr)));
|
||||
co_return attr;
|
||||
}
|
||||
|
||||
CoTryTask<void> UserStore::removeUser(IReadWriteTransaction &txn, Uid uid) {
|
||||
auto fetchResult = co_await getUser(txn, uid);
|
||||
CO_RETURN_ON_ERROR(fetchResult);
|
||||
if (!fetchResult->has_value()) {
|
||||
co_return makeError(MetaCode::kNotFound, fmt::format("Uid {} not found", uid.toUnderType()));
|
||||
}
|
||||
auto key = packUid(uid);
|
||||
co_return co_await txn.clear(key);
|
||||
}
|
||||
|
||||
CoTryTask<UserAttr> UserStore::setUserAttr(IReadWriteTransaction &txn,
|
||||
Uid uid,
|
||||
std::string_view newName,
|
||||
const std::vector<Gid> *gids,
|
||||
std::optional<bool> isRootUser,
|
||||
std::optional<bool> isAdmin) {
|
||||
auto fetchResult = co_await getUser(txn, uid);
|
||||
CO_RETURN_ON_ERROR(fetchResult);
|
||||
if (!fetchResult->has_value()) {
|
||||
co_return makeError(MetaCode::kNotFound, fmt::format("Uid {} not found", uid.toUnderType()));
|
||||
}
|
||||
|
||||
const auto &user = **fetchResult;
|
||||
auto newUser = user;
|
||||
|
||||
if (!newName.empty()) {
|
||||
newUser.name = newName;
|
||||
}
|
||||
|
||||
if (gids) {
|
||||
newUser.groups = *gids;
|
||||
}
|
||||
if (isRootUser) {
|
||||
newUser.root = *isRootUser;
|
||||
}
|
||||
if (isAdmin) {
|
||||
newUser.admin = *isAdmin;
|
||||
}
|
||||
if (newUser != user) {
|
||||
CO_RETURN_ON_ERROR(co_await txn.set(packUid(uid), serde::serialize(newUser)));
|
||||
}
|
||||
co_return newUser;
|
||||
}
|
||||
|
||||
CoTryTask<UserAttr> UserStore::setUserToken(IReadWriteTransaction &txn,
|
||||
Uid uid,
|
||||
std::string_view newToken,
|
||||
bool invalidateExistedToken,
|
||||
size_t maxTokens,
|
||||
Duration tokenLifetimeExtendLength) {
|
||||
auto fetchResult = co_await getUser(txn, uid);
|
||||
CO_RETURN_ON_ERROR(fetchResult);
|
||||
if (!fetchResult->has_value()) {
|
||||
co_return makeError(MetaCode::kNotFound, fmt::format("Uid {} not found", uid.toUnderType()));
|
||||
}
|
||||
|
||||
const auto &user = **fetchResult;
|
||||
auto newUser = user;
|
||||
|
||||
String newTokenStr(newToken);
|
||||
if (!newTokenStr.empty()) {
|
||||
CO_RETURN_ON_ERROR(ensureTokenMatchUid(newTokenStr, uid));
|
||||
} else {
|
||||
auto tokenResult = co_await encodeUserToken(uid, txn);
|
||||
CO_RETURN_ON_ERROR(tokenResult);
|
||||
newTokenStr = std::move(*tokenResult);
|
||||
}
|
||||
CO_RETURN_ON_ERROR(
|
||||
newUser.addNewToken(newTokenStr, UtcClock::now(), invalidateExistedToken, maxTokens, tokenLifetimeExtendLength));
|
||||
if (newUser != user) {
|
||||
CO_RETURN_ON_ERROR(co_await txn.set(packUid(uid), serde::serialize(newUser)));
|
||||
}
|
||||
co_return newUser;
|
||||
}
|
||||
|
||||
CoTryTask<std::vector<UserAttr>> UserStore::listUsers(IReadOnlyTransaction &txn) {
|
||||
auto listRes = co_await kv::TransactionHelper::listByPrefix(txn, kv::toStr(kv::KeyPrefix::User), {});
|
||||
CO_RETURN_ON_ERROR(listRes);
|
||||
|
||||
std::vector<UserAttr> res;
|
||||
for (const auto &kv : *listRes) {
|
||||
kv::KeyPrefix keyPrefix;
|
||||
Uid::UnderlyingType uid;
|
||||
CO_RETURN_ON_ERROR(Deserializer::deserRawArgs(kv.key, keyPrefix, uid));
|
||||
|
||||
UserAttr attr;
|
||||
CO_RETURN_ON_ERROR(serde::deserialize(attr, kv.value));
|
||||
attr.uid = Uid(uid);
|
||||
res.push_back(std::move(attr));
|
||||
}
|
||||
co_return res;
|
||||
}
|
||||
|
||||
} // namespace hf3fs::core
|
||||
47
src/core/user/UserStore.h
Normal file
47
src/core/user/UserStore.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/kv/ITransaction.h"
|
||||
#include "core/user/UserCache.h"
|
||||
#include "fbs/core/user/User.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
using flat::Gid;
|
||||
using flat::Uid;
|
||||
using flat::UserAttr;
|
||||
using flat::UserInfo;
|
||||
using hf3fs::kv::IReadOnlyTransaction;
|
||||
using hf3fs::kv::IReadWriteTransaction;
|
||||
|
||||
class UserStore {
|
||||
public:
|
||||
UserStore() = default;
|
||||
|
||||
CoTryTask<std::optional<UserAttr>> getUser(IReadOnlyTransaction &txn, Uid uid);
|
||||
|
||||
CoTryTask<UserAttr> addUser(IReadWriteTransaction &txn,
|
||||
Uid uid,
|
||||
String name,
|
||||
std::vector<Gid> groups,
|
||||
bool isRootUser,
|
||||
bool isAdmin = false,
|
||||
std::string_view token = {});
|
||||
|
||||
CoTryTask<void> removeUser(IReadWriteTransaction &txn, Uid uid);
|
||||
|
||||
CoTryTask<UserAttr> setUserAttr(IReadWriteTransaction &txn,
|
||||
Uid uid,
|
||||
std::string_view newName,
|
||||
const std::vector<Gid> *gids,
|
||||
std::optional<bool> isRootUser,
|
||||
std::optional<bool> isAdmin);
|
||||
|
||||
CoTryTask<UserAttr> setUserToken(IReadWriteTransaction &txn,
|
||||
Uid uid,
|
||||
std::string_view newToken,
|
||||
bool invalidateExistedToken,
|
||||
size_t maxTokens,
|
||||
Duration tokenLifetimeExtendLength);
|
||||
|
||||
CoTryTask<std::vector<UserAttr>> listUsers(IReadOnlyTransaction &txn);
|
||||
};
|
||||
} // namespace hf3fs::core
|
||||
83
src/core/user/UserStoreEx.cc
Normal file
83
src/core/user/UserStoreEx.cc
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "UserStoreEx.h"
|
||||
|
||||
#include "UserToken.h"
|
||||
#include "common/kv/IKVEngine.h"
|
||||
#include "common/kv/WithTransaction.h"
|
||||
#include "fdb/FDBRetryStrategy.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
UserStoreEx::UserStoreEx(kv::IKVEngine &kvEngine,
|
||||
const kv::TransactionRetry &transactionRetry,
|
||||
const UserCache::Config &cacheCfg)
|
||||
: kvEngine_(kvEngine),
|
||||
transactionRetry_(transactionRetry),
|
||||
userCache_(cacheCfg) {}
|
||||
|
||||
CoTryTask<flat::UserAttr> UserStoreEx::getUser(std::string_view token) {
|
||||
auto uid = decodeUidFromUserToken(token);
|
||||
if (UNLIKELY(uid.hasError())) {
|
||||
co_return makeError(StatusCode::kAuthenticationFail, "Token decode failed");
|
||||
}
|
||||
auto user = co_await getUser(*uid);
|
||||
CO_RETURN_ON_ERROR(user);
|
||||
if (auto res = user->validateToken(token, UtcClock::now()); res.hasError()) {
|
||||
XLOGF(ERR, "Token validate failed: {}, uid {}", res.error(), *uid);
|
||||
co_return makeError(StatusCode::kAuthenticationFail, "Token validate failed");
|
||||
}
|
||||
co_return *user;
|
||||
}
|
||||
|
||||
CoTryTask<void> UserStoreEx::authenticate(flat::UserInfo &userInfo) {
|
||||
std::vector<Gid> gids;
|
||||
auto userFromToken = co_await getUser(userInfo.token);
|
||||
CO_RETURN_ON_ERROR(userFromToken);
|
||||
|
||||
if (userFromToken->root) {
|
||||
XLOGF(DBG, "Root user {}, uid {}, euid {}", userFromToken->name, userFromToken->uid, userInfo.uid);
|
||||
if (userInfo.uid != userFromToken->uid) {
|
||||
// root users can act as anyone
|
||||
userFromToken = co_await getUser(userInfo.uid);
|
||||
CO_RETURN_ON_ERROR(userFromToken);
|
||||
}
|
||||
} else {
|
||||
if (userInfo.uid != userFromToken->uid || userInfo.gid != userFromToken->gid) {
|
||||
auto msg = fmt::format("User not match, userInfo {}, (uid {}, gid {})",
|
||||
userInfo,
|
||||
userFromToken->uid.toUnderType(),
|
||||
userFromToken->gid.toUnderType());
|
||||
XLOGF(ERR, "{}", msg);
|
||||
co_return makeError(StatusCode::kAuthenticationFail, std::move(msg));
|
||||
}
|
||||
}
|
||||
|
||||
userInfo.groups = std::move(userFromToken->groups);
|
||||
co_return Void{};
|
||||
}
|
||||
|
||||
CoTryTask<flat::UserAttr> UserStoreEx::getUser(flat::Uid uid) {
|
||||
auto getResult = userCache_.get(uid);
|
||||
if (getResult.attr) {
|
||||
co_return *getResult.attr;
|
||||
}
|
||||
if (getResult.marked) {
|
||||
co_return makeError(StatusCode::kAuthenticationFail, fmt::format("Uid {} not found", uid.toUnderType()));
|
||||
}
|
||||
auto strategy = kv::FDBRetryStrategy(
|
||||
{transactionRetry_.max_backoff(), transactionRetry_.max_retry_count(), /*retryMaybeCommitted=*/true});
|
||||
auto result = co_await kv::WithTransaction(strategy).run(
|
||||
kvEngine_.createReadonlyTransaction(),
|
||||
[&](kv::IReadOnlyTransaction &txn) -> CoTryTask<std::optional<UserAttr>> {
|
||||
co_return co_await userStore_.getUser(txn, uid);
|
||||
});
|
||||
if (result.hasError()) {
|
||||
CO_RETURN_ERROR(result);
|
||||
} else {
|
||||
userCache_.set(uid, *result);
|
||||
if (result->has_value()) {
|
||||
co_return **result;
|
||||
} else {
|
||||
co_return makeError(StatusCode::kAuthenticationFail, fmt::format("Uid {} not found", uid.toUnderType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace hf3fs::core
|
||||
26
src/core/user/UserStoreEx.h
Normal file
26
src/core/user/UserStoreEx.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "UserCache.h"
|
||||
#include "UserStore.h"
|
||||
#include "common/kv/IKVEngine.h"
|
||||
#include "common/kv/TransactionRetry.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
|
||||
class UserStoreEx {
|
||||
public:
|
||||
UserStoreEx(kv::IKVEngine &kvEngine, const kv::TransactionRetry &transactionRetry, const UserCache::Config &cacheCfg);
|
||||
|
||||
CoTryTask<void> authenticate(flat::UserInfo &userInfo);
|
||||
CoTryTask<flat::UserAttr> getUser(std::string_view token);
|
||||
CoTryTask<flat::UserAttr> getUser(flat::Uid uid);
|
||||
|
||||
UserCache &cache() { return userCache_; }
|
||||
|
||||
private:
|
||||
kv::IKVEngine &kvEngine_;
|
||||
const kv::TransactionRetry &transactionRetry_;
|
||||
UserStore userStore_;
|
||||
UserCache userCache_;
|
||||
};
|
||||
} // namespace hf3fs::core
|
||||
100
src/core/user/UserToken.cc
Normal file
100
src/core/user/UserToken.cc
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "UserToken.h"
|
||||
|
||||
#include <folly/Random.h>
|
||||
#include <folly/base64.h>
|
||||
#include <folly/hash/Checksum.h>
|
||||
#include <folly/lang/Bits.h>
|
||||
|
||||
#include "fdb/FDBTransaction.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
namespace {
|
||||
const uint16_t magicNum = 0xF1DB;
|
||||
// clang-format off
|
||||
constexpr std::array<uint8_t, 18> mapping = {
|
||||
12, 6, 0,
|
||||
13, 7, 1,
|
||||
14, 8, 2,
|
||||
15, 9, 3,
|
||||
16, 10, 4,
|
||||
17, 11, 5,
|
||||
};
|
||||
// clang-format on
|
||||
} // namespace
|
||||
|
||||
String encodeUserToken(uint32_t uid, uint64_t randomv) {
|
||||
static_assert(folly::Endian::order == folly::Endian::Order::LITTLE);
|
||||
|
||||
std::array<uint8_t, 18> tmp;
|
||||
auto *p = tmp.data();
|
||||
|
||||
static_assert(sizeof(magicNum) == 2);
|
||||
std::memcpy(p, &magicNum, sizeof(magicNum));
|
||||
p += sizeof(magicNum);
|
||||
|
||||
static_assert(sizeof(uid) == 4);
|
||||
std::memcpy(p, &uid, sizeof(uid));
|
||||
p += sizeof(uid);
|
||||
|
||||
static_assert(sizeof(randomv) == 8);
|
||||
std::memcpy(p, &randomv, sizeof(randomv));
|
||||
p += sizeof(randomv);
|
||||
|
||||
auto crc = folly::crc32(tmp.data(), 14, 0);
|
||||
static_assert(sizeof(crc) == 4);
|
||||
std::memcpy(p, &crc, sizeof(crc));
|
||||
|
||||
std::array<char, 18> buffer;
|
||||
for (size_t i = 0; i < 18; ++i) {
|
||||
buffer[mapping[i]] = tmp[i];
|
||||
}
|
||||
return folly::base64Encode(std::string_view(buffer.data(), buffer.size()));
|
||||
}
|
||||
|
||||
Result<std::pair<uint32_t, uint64_t>> decodeUserToken(std::string_view token) {
|
||||
try {
|
||||
auto decoded = folly::base64Decode(token);
|
||||
if (decoded.size() != 18) return makeError(StatusCode::kInvalidFormat, "Decode token fail: invalid format");
|
||||
|
||||
std::array<uint8_t, 18> tmp;
|
||||
for (size_t i = 0; i < 18; ++i) {
|
||||
tmp[i] = decoded[mapping[i]];
|
||||
}
|
||||
|
||||
uint16_t mn = 0;
|
||||
uint32_t uid = 0, crc = 0;
|
||||
uint64_t timestamp = 0;
|
||||
auto *p = tmp.data();
|
||||
std::memcpy(&mn, p, sizeof(mn));
|
||||
p += sizeof(mn);
|
||||
std::memcpy(&uid, p, sizeof(uid));
|
||||
p += sizeof(uid);
|
||||
std::memcpy(×tamp, p, sizeof(timestamp));
|
||||
p += sizeof(timestamp);
|
||||
std::memcpy(&crc, p, sizeof(crc));
|
||||
|
||||
if (mn != magicNum) {
|
||||
return makeError(StatusCode::kInvalidFormat, "Decode token fail: invalid format");
|
||||
}
|
||||
|
||||
auto expectedCrc = folly::crc32(tmp.data(), 14, 0);
|
||||
if (crc != expectedCrc) {
|
||||
return makeError(StatusCode::kInvalidFormat, "Decode token fail: invalid crc");
|
||||
}
|
||||
return std::make_pair(uid, timestamp);
|
||||
} catch (const folly::base64_decode_error &e) {
|
||||
return makeError(StatusCode::kInvalidFormat, "Decode token fail: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
Result<flat::Uid> decodeUidFromUserToken(std::string_view token) {
|
||||
auto r = decodeUserToken(token);
|
||||
RETURN_ON_ERROR(r);
|
||||
auto [uid, ts] = *r;
|
||||
return flat::Uid{uid};
|
||||
}
|
||||
|
||||
CoTryTask<String> encodeUserToken(uint32_t uid, kv::IReadOnlyTransaction &txn) {
|
||||
co_return encodeUserToken(uid, folly::Random::secureRand64());
|
||||
}
|
||||
} // namespace hf3fs::core
|
||||
14
src/core/user/UserToken.h
Normal file
14
src/core/user/UserToken.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/kv/ITransaction.h"
|
||||
#include "common/utils/Coroutine.h"
|
||||
#include "common/utils/Result.h"
|
||||
#include "fbs/core/user/User.h"
|
||||
|
||||
namespace hf3fs::core {
|
||||
String encodeUserToken(uint32_t uid, uint64_t timestamp);
|
||||
CoTryTask<String> encodeUserToken(uint32_t uid, kv::IReadOnlyTransaction &txn);
|
||||
|
||||
Result<std::pair<uint32_t, uint64_t>> decodeUserToken(std::string_view token);
|
||||
Result<flat::Uid> decodeUidFromUserToken(std::string_view token);
|
||||
} // namespace hf3fs::core
|
||||
101
src/core/utils/ServiceOperation.h
Normal file
101
src/core/utils/ServiceOperation.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <folly/experimental/symbolizer/Symbolizer.h>
|
||||
#include <folly/logging/xlog.h>
|
||||
|
||||
#include "common/monitor/Recorder.h"
|
||||
#include "common/utils/NameWrapper.h"
|
||||
#include "common/utils/String.h"
|
||||
|
||||
#define RETURN_AND_LOG_OP_ERR(...) RETURN_AND_LOG_OP_ERR_IMPL(return, __VA_ARGS__)
|
||||
#define CO_RETURN_AND_LOG_OP_ERR(...) RETURN_AND_LOG_OP_ERR_IMPL(co_return, __VA_ARGS__)
|
||||
|
||||
#define RETURN_AND_LOG_OP_ERR_IMPL(RETURN, op, code, s, ...) \
|
||||
do { \
|
||||
auto _msg = fmt::format(s __VA_OPT__(, ) __VA_ARGS__); \
|
||||
LOG_OP_ERR((op), "error: {} {}", StatusCode::toString(code), _msg); \
|
||||
RETURN makeError(code, _msg); \
|
||||
} while (false)
|
||||
|
||||
#define LOG_OP_DBG(...) LOG_OP_IMPL(DBG, __VA_ARGS__)
|
||||
#define LOG_OP_INFO(...) LOG_OP_IMPL(INFO, __VA_ARGS__)
|
||||
#define LOG_OP_WARN(...) LOG_OP_IMPL(WARN, __VA_ARGS__)
|
||||
#define LOG_OP_ERR(...) LOG_OP_IMPL(ERR, __VA_ARGS__)
|
||||
#define LOG_OP_CRITICAL(...) LOG_OP_IMPL(CRITICAL, __VA_ARGS__)
|
||||
#define LOG_OP_FATAL(op, fmt, ...) \
|
||||
XLOGF(FATAL, \
|
||||
"{} " fmt ". stack trace:\n{}", \
|
||||
(op).toString() __VA_OPT__(, ) __VA_ARGS__, \
|
||||
folly::symbolizer::getStackTraceStr())
|
||||
|
||||
#define LOG_OP_IMPL(LEVEL, op, fmt, ...) XLOGF(LEVEL, "{} " fmt, (op).toString() __VA_OPT__(, ) __VA_ARGS__)
|
||||
|
||||
namespace hf3fs::core {
|
||||
struct ServiceOperation {
|
||||
virtual ~ServiceOperation() = default;
|
||||
|
||||
String toString() const {
|
||||
if (!cache) {
|
||||
cache = fmt::format("[{}Op {} No.{}]", serviceNameImpl(), toStringImpl(), reqId);
|
||||
}
|
||||
return *cache;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_base_of_v<ServiceOperation, T>) T &as() {
|
||||
auto *p = dynamic_cast<T *>(this);
|
||||
XLOG_IF(FATAL, !p, "invalid dynamic cast from ServiceOperation to {}", typeid(T).name());
|
||||
return *p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_base_of_v<ServiceOperation, T>) const T &as() const {
|
||||
auto *p = dynamic_cast<const T *>(this);
|
||||
XLOG_IF(FATAL, !p, "invalid dynamic cast from ServiceOperation to {}", typeid(T).name());
|
||||
return *p;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual String serviceNameImpl() const = 0;
|
||||
virtual String toStringImpl() const = 0;
|
||||
|
||||
static int64_t nextReqId() {
|
||||
static std::atomic<int64_t> id{1};
|
||||
return id.fetch_add(1, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
const int64_t reqId = nextReqId();
|
||||
mutable std::optional<String> cache;
|
||||
};
|
||||
|
||||
template <NameWrapper ServiceName, NameWrapper OpName, NameWrapper MetricNamePrefix>
|
||||
struct ServiceOperationWithMetric : ServiceOperation {
|
||||
static constexpr std::string_view serviceName = ServiceName;
|
||||
static constexpr std::string_view opName = OpName;
|
||||
static constexpr std::string_view metricNamePrefix = MetricNamePrefix;
|
||||
|
||||
static_assert(!serviceName.empty());
|
||||
static_assert(!opName.empty());
|
||||
static_assert(!metricNamePrefix.empty());
|
||||
|
||||
static const monitor::TagSet &getTagSet() {
|
||||
static auto tagSet = [] {
|
||||
monitor::TagSet ts;
|
||||
ts.addTag("instance", String(opName));
|
||||
return ts;
|
||||
}();
|
||||
return tagSet;
|
||||
}
|
||||
|
||||
static monitor::SimpleOperationRecorder::Guard startRecord() {
|
||||
static monitor::SimpleOperationRecorder recorder(fmt::format("{}.{}", serviceName, metricNamePrefix),
|
||||
getTagSet(),
|
||||
/*recordErrorCode=*/true);
|
||||
return recorder.record();
|
||||
}
|
||||
|
||||
String serviceNameImpl() const final { return String(serviceName); }
|
||||
};
|
||||
|
||||
} // namespace hf3fs::core
|
||||
20
src/core/utils/runOp.h
Normal file
20
src/core/utils/runOp.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/utils/UtcTime.h"
|
||||
|
||||
#define CO_INVOKE_OP(NORMAL_PATH_LOG_LEVEL, op, peer, ...) \
|
||||
LOG_OP_##NORMAL_PATH_LOG_LEVEL(op, "start execution. from:{}", peer); \
|
||||
auto guard = op.startRecord(); \
|
||||
auto result = co_await op.handle(__VA_ARGS__); \
|
||||
if (result.hasError()) { \
|
||||
guard.reportWithCode(result.error().code()); \
|
||||
LOG_OP_ERR(op, "failed: {}. latency: {}", result.error(), *guard.latency()); \
|
||||
CO_RETURN_ERROR(result); \
|
||||
} else { \
|
||||
guard.reportWithCode(StatusCode::kOK); \
|
||||
LOG_OP_##NORMAL_PATH_LOG_LEVEL(op, "succeeded. latency: {}", *guard.latency()); \
|
||||
co_return result; \
|
||||
}
|
||||
|
||||
#define CO_INVOKE_OP_INFO(...) CO_INVOKE_OP(INFO, __VA_ARGS__)
|
||||
#define CO_INVOKE_OP_DBG(...) CO_INVOKE_OP(DBG, __VA_ARGS__)
|
||||
Reference in New Issue
Block a user