[Store] add c++ http metadata server in mooncake master (#766)

* [Store] config: start http server from master

Signed-off-by: Teng Ma <sima.mt@alibaba-inc.com>

* [Refactor] Use coro http server for metadata

* fix cmake

* fix merge

* add doc and test

* clear

---------

Signed-off-by: Teng Ma <sima.mt@alibaba-inc.com>
This commit is contained in:
Teng Ma 2025-08-25 13:56:37 +08:00 committed by GitHub
parent 2833d8be8b
commit f76c92295b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 333 additions and 2 deletions

View File

@ -535,6 +535,35 @@ After enabling the persistence feature:
#### 3FS USRBIO Plugin
If you need to use 3FS's native API (USRBIO) to achieve high-performance persistent file reads and writes, you can refer to the configuration instructions in this document [3FS USRBIO Plugin](/mooncake-store/src/hf3fs/README.md).
### Builtin Metadata Server
Mooncake Store provides a built-in HTTP metadata server as an alternative to etcd for storing cluster metadata. This feature is particularly useful for development environments or scenarios where etcd is not available.
#### Configuration Parameters
The HTTP metadata server can be configured using the following parameters:
- **`enable_http_metadata_server`** (boolean, default: `false`): Enables the built-in HTTP metadata server instead of using etcd. When set to `true`, the master service will start an embedded HTTP server that handles metadata operations.
- **`http_metadata_server_port`** (integer, default: `8080`): Specifies the TCP port on which the HTTP metadata server will listen for incoming connections. This port must be available and not conflict with other services.
- **`http_metadata_server_host`** (string, default: `"0.0.0.0"`): Specifies the host address for the HTTP metadata server to bind to. Use `"0.0.0.0"` to listen on all available network interfaces, or specify a specific IP address for security purposes.
#### Usage Example
To start the master service with the HTTP metadata server enabled:
```bash
./build/mooncake-store/src/mooncake_master \
--enable_http_metadata_server=true \
--http_metadata_server_port=8080 \
--http_metadata_server_host=0.0.0.0
```
When enabled, the HTTP metadata server will start automatically and provide metadata services for the Mooncake Store cluster. This eliminates the need for an external etcd deployment, simplifying the setup process for development and testing environments.
Note that the HTTP metadata server is designed for single-node deployments and does not provide the high availability features that etcd offers. For production environments requiring high availability, etcd is still the recommended choice.
## Mooncake Store Python API
**Complete Python API Documentation**: [https://kvcache-ai.github.io/Mooncake/mooncake-store-api/python-binding.html](https://kvcache-ai.github.io/Mooncake/mooncake-store-api/python-binding.html)

View File

@ -537,6 +537,35 @@ struct ReplicateConfig {
#### 3FS USRBIO 插件
如需通过3FS原生接口USRBIO实现高性能持久化文件读写请参阅本文档的配置说明。[3FS USRBIO 插件配置](/mooncake-store/src/hf3fs/README.md)。
### 内置元数据服务器
Mooncake Store 提供了内置的 HTTP 元数据服务器作为 etcd 的替代方案,用于存储集群元数据。此功能特别适用于开发环境或 etcd 不可用的场景。
#### 配置参数
HTTP 元数据服务器可通过以下参数进行配置:
- **`enable_http_metadata_server`**(布尔值,默认值:`false`):启用内置的 HTTP 元数据服务器以替代 etcd。当设置为 `true` 时,主服务将启动一个嵌入式 HTTP 服务器来处理元数据操作。
- **`http_metadata_server_port`**(整型,默认值:`8080`):指定 HTTP 元数据服务器监听的 TCP 端口。该端口必须可用且不能与其他服务冲突。
- **`http_metadata_server_host`**(字符串,默认值:`"0.0.0.0"`):指定 HTTP 元数据服务器绑定的主机地址。使用 `"0.0.0.0"` 可监听所有可用网络接口,或指定特定 IP 地址以提高安全性。
#### 使用示例
要使用启用了 HTTP 元数据服务器的主服务,请运行:
```bash
./build/mooncake-store/src/mooncake_master \
--enable_http_metadata_server=true \
--http_metadata_server_port=8080 \
--http_metadata_server_host=0.0.0.0
```
启用后HTTP 元数据服务器将自动启动并为 Mooncake Store 集群提供元数据服务。这消除了对外部 etcd 部署的需求,简化了开发和测试环境的设置过程。
请注意HTTP 元数据服务器专为单节点部署设计,不提供 etcd 所具备的高可用性功能。对于需要高可用性的生产环境,仍推荐使用 etcd。
## Mooncake Store Python API
**完整的 Python API 文档**: [https://kvcache-ai.github.io/Mooncake/mooncake-store-api/python-binding.html](https://kvcache-ai.github.io/Mooncake/mooncake-store-api/python-binding.html)

View File

@ -0,0 +1,54 @@
#ifndef MOONCAKE_HTTP_METADATA_SERVER_H
#define MOONCAKE_HTTP_METADATA_SERVER_H
#include <string>
#include <unordered_map>
#include <mutex>
#include <ylt/coro_http/coro_http_server.hpp>
namespace mooncake {
enum class KVPoll {
Failed = 0,
Bootstrapping = 1,
WaitingForInput = 2,
Transferring = 3,
Success = 4
};
class HttpMetadataServer {
public:
HttpMetadataServer(uint16_t port, const std::string& host = "0.0.0.0");
~HttpMetadataServer();
// Start the HTTP metadata server
bool start();
// Stop the HTTP metadata server
void stop();
// Poll the server status
KVPoll poll() const;
// Check if the server is running
bool is_running() const { return running_; }
// Non-copyable
HttpMetadataServer(const HttpMetadataServer&) = delete;
HttpMetadataServer& operator=(const HttpMetadataServer&) = delete;
private:
void init_server();
uint16_t port_;
std::string host_;
std::unique_ptr<coro_http::coro_http_server> server_;
std::unordered_map<std::string, std::string> store_;
mutable std::mutex store_mutex_;
bool running_;
};
} // namespace mooncake
#endif // MOONCAKE_HTTP_METADATA_SERVER_H

View File

@ -30,6 +30,11 @@ struct MasterConfig {
std::string cluster_id;
std::string root_fs_dir;
std::string memory_allocator;
// HTTP metadata server configuration
bool enable_http_metadata_server;
uint32_t http_metadata_server_port;
std::string http_metadata_server_host;
};
class MasterServiceSupervisorConfig {

View File

@ -23,6 +23,7 @@ set(MOONCAKE_STORE_SOURCES
posix_file.cpp
client_buffer.cpp
pybind_client.cpp
http_metadata_server.cpp
)
set(EXTRA_LIBS "")

View File

@ -0,0 +1,122 @@
#include "http_metadata_server.h"
#include <ylt/coro_http/coro_http_server.hpp>
#include <glog/logging.h>
#include <mutex>
#include <string>
namespace mooncake {
HttpMetadataServer::HttpMetadataServer(uint16_t port, const std::string& host)
: port_(port),
host_(host),
server_(std::make_unique<coro_http::coro_http_server>(4, port)),
running_(false) {
init_server();
}
HttpMetadataServer::~HttpMetadataServer() { stop(); }
void HttpMetadataServer::init_server() {
using namespace coro_http;
// GET /metadata?key=<key>
server_->set_http_handler<GET>(
"/metadata", [this](coro_http_request& req, coro_http_response& resp) {
auto key = req.get_query_value("key");
if (key.empty()) {
resp.set_status_and_content(status_type::bad_request,
"Missing key parameter");
return;
}
std::lock_guard<std::mutex> lock(store_mutex_);
auto it = store_.find(std::string(key));
if (it == store_.end()) {
resp.set_status_and_content(status_type::not_found,
"metadata not found");
return;
}
resp.add_header("Content-Type", "application/json");
resp.set_status_and_content(status_type::ok, it->second);
});
// PUT /metadata?key=<key>
server_->set_http_handler<PUT>(
"/metadata", [this](coro_http_request& req, coro_http_response& resp) {
auto key = req.get_query_value("key");
if (key.empty()) {
resp.set_status_and_content(status_type::bad_request,
"Missing key parameter");
return;
}
std::string body(req.get_body());
{
std::lock_guard<std::mutex> lock(store_mutex_);
store_[std::string(key)] = body;
}
resp.set_status_and_content(status_type::ok, "metadata updated");
});
// DELETE /metadata?key=<key>
server_->set_http_handler<coro_http::http_method::DEL>(
"/metadata", [this](coro_http_request& req, coro_http_response& resp) {
auto key = req.get_query_value("key");
if (key.empty()) {
resp.set_status_and_content(status_type::bad_request,
"Missing key parameter");
return;
}
std::lock_guard<std::mutex> lock(store_mutex_);
auto it = store_.find(std::string(key));
if (it == store_.end()) {
resp.set_status_and_content(status_type::not_found,
"metadata not found");
return;
}
store_.erase(it);
resp.set_status_and_content(status_type::ok, "metadata deleted");
});
// Health check endpoint
server_->set_http_handler<GET>(
"/health", [](coro_http_request& req, coro_http_response& resp) {
resp.set_status_and_content(status_type::ok, "OK");
});
}
bool HttpMetadataServer::start() {
if (running_) {
return true;
}
server_->async_start();
running_ = true;
LOG(INFO) << "HTTP metadata server started on " << host_ << ":" << port_;
return true;
}
void HttpMetadataServer::stop() {
if (!running_) {
return;
}
server_->stop();
running_ = false;
LOG(INFO) << "HTTP metadata server stopped";
}
KVPoll HttpMetadataServer::poll() const {
if (!running_) {
return KVPoll::Failed;
}
return KVPoll::Success;
}
} // namespace mooncake

View File

@ -1,12 +1,14 @@
#include <gflags/gflags.h>
#include <chrono> // For std::chrono
#include <memory> // For std::unique_ptr
#include <thread> // For std::thread
#include <ylt/coro_rpc/coro_rpc_server.hpp>
#include <ylt/easylog/record.hpp>
#include "default_config.h"
#include "ha_helper.h"
#include "http_metadata_server.h"
#include "rpc_service.h"
#include "types.h"
@ -75,6 +77,12 @@ DEFINE_string(cluster_id, mooncake::DEFAULT_CLUSTER_ID,
DEFINE_string(memory_allocator, "offset",
"Memory allocator for global segments, cachelib | offset");
DEFINE_bool(enable_http_metadata_server, false,
"Enable HTTP metadata server instead of etcd");
DEFINE_int32(http_metadata_server_port, 8080,
"Port for HTTP metadata server to listen on");
DEFINE_string(http_metadata_server_host, "0.0.0.0",
"Host for HTTP metadata server to bind to");
void InitMasterConf(const mooncake::DefaultConfig& default_config,
mooncake::MasterConfig& master_config) {
@ -125,6 +133,15 @@ void InitMasterConf(const mooncake::DefaultConfig& default_config,
default_config.GetString("memory_allocator",
&master_config.memory_allocator,
FLAGS_memory_allocator);
default_config.GetBool("enable_http_metadata_server",
&master_config.enable_http_metadata_server,
FLAGS_enable_http_metadata_server);
default_config.GetUInt32("http_metadata_server_port",
&master_config.http_metadata_server_port,
FLAGS_http_metadata_server_port);
default_config.GetString("http_metadata_server_host",
&master_config.http_metadata_server_host,
FLAGS_http_metadata_server_host);
}
void LoadConfigFromCmdline(mooncake::MasterConfig& master_config,
@ -258,6 +275,48 @@ void LoadConfigFromCmdline(mooncake::MasterConfig& master_config,
!conf_set) {
master_config.memory_allocator = FLAGS_memory_allocator;
}
if ((google::GetCommandLineFlagInfo("enable_http_metadata_server", &info) &&
!info.is_default) ||
!conf_set) {
master_config.enable_http_metadata_server =
FLAGS_enable_http_metadata_server;
}
if ((google::GetCommandLineFlagInfo("http_metadata_server_port", &info) &&
!info.is_default) ||
!conf_set) {
master_config.http_metadata_server_port =
FLAGS_http_metadata_server_port;
}
if ((google::GetCommandLineFlagInfo("http_metadata_server_host", &info) &&
!info.is_default) ||
!conf_set) {
master_config.http_metadata_server_host =
FLAGS_http_metadata_server_host;
}
}
// Function to start HTTP metadata server
std::unique_ptr<mooncake::HttpMetadataServer> StartHttpMetadataServer(
int port, const std::string& host) {
LOG(INFO) << "Starting C++ HTTP metadata server on " << host << ":" << port;
try {
auto server =
std::make_unique<mooncake::HttpMetadataServer>(port, host);
server->start();
// Check if server started successfully
if (server->is_running()) {
LOG(INFO) << "C++ HTTP metadata server started successfully";
return server;
} else {
LOG(ERROR) << "Failed to start C++ HTTP metadata server";
return nullptr;
}
} catch (const std::exception& e) {
LOG(ERROR) << "Failed to start C++ HTTP metadata server: " << e.what();
return nullptr;
}
}
int main(int argc, char* argv[]) {
@ -321,7 +380,29 @@ int main(int argc, char* argv[]) {
<< master_config.rpc_enable_tcp_no_delay
<< ", cluster_id=" << master_config.cluster_id
<< ", root_fs_dir=" << master_config.root_fs_dir
<< ", memory_allocator=" << master_config.memory_allocator;
<< ", memory_allocator=" << master_config.memory_allocator
<< ", enable_http_metadata_server="
<< master_config.enable_http_metadata_server
<< ", http_metadata_server_port="
<< master_config.http_metadata_server_port
<< ", http_metadata_server_host="
<< master_config.http_metadata_server_host;
// Start HTTP metadata server if enabled
std::unique_ptr<mooncake::HttpMetadataServer> http_metadata_server;
if (master_config.enable_http_metadata_server) {
http_metadata_server =
StartHttpMetadataServer(master_config.http_metadata_server_port,
master_config.http_metadata_server_host);
if (!http_metadata_server) {
LOG(FATAL) << "Failed to start HTTP metadata server";
return 1;
}
// Give the server some time to start
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (master_config.enable_ha) {
mooncake::MasterServiceSupervisor supervisor(
@ -341,4 +422,4 @@ int main(int argc, char* argv[]) {
mooncake::RegisterRpcService(server, wrapped_master_service);
return server.start();
}
}
}

View File

@ -56,5 +56,15 @@ fi
echo "Running CLI entry point tests..."
python test_cli.py
killall mooncake_http_metadata_server
killall mooncake_master
mooncake_master --default_kv_lease_ttl=500 --enable_http_metadata_server=true &
MASTER_PID=$!
sleep 1
MC_METADATA_SERVER=http://127.0.0.1:8080/metadata DEFAULT_KV_LEASE_TTL=500 python test_distributed_object_store.py
sleep 1
kill $MASTER_PID || true
echo "All tests completed successfully!"
cd ../..