feat(memdb×libmemdb): memdb 服务化落地——RPC 服务端 + 构建/测试/部署闭环

- libmemdb: 补全 flush() 惰性 TTL 驱逐;修复 explicit 误用/头文件缺失/
  时间戳碰撞误删;size()/get_db() 前 flush 消除脏读;三处双重哈希查找
  合并;index_keys 去冗余字段;update(refresh_ttl) 改原地重索引,
  免 remove+insert 的拷贝与重复 flush
- memdb: 重写 RPC 服务端(libmsg RpcServer, :8011, 9 路由, mutex 保护);
  补全 Makefile/_set.sh/_run.sh 与 --instance/--log-level CLI;
  SIGINT/SIGTERM 优雅退出;注册 services.yaml
- 测试: libmemdb 单元测试 22 例 + memdb RPC 集成测试 12 场景(含并发)
- 清理: 删除误合入的 db2/create_sqlite3.sql 旧版(scripts/ 下为权威版)
This commit is contained in:
qing committed 2026-08-15 09:27:12 +08:00
1 parent a18a7de1ca
commit 26de527a09
10 files changed
+1016 -116

No files matched your search

+172 -51
View File
@@ -1,78 +1,199 @@
#include <us/RpcServer.hpp>
#include <us/RpcClient.hpp>
#include <boost/json.hpp>
#include <us/memdb.hpp>
#include <string>
#include <boost/program_options.hpp>
#include <atomic>
#include <csignal>
#include <cstdint>
#include <iostream>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
namespace json = boost::json;
struct ClientConfig {
std::string connected_host = "127.0.0.1";
int connected_port = 8005;
std::string service_name = "db2";
double timeout = 10.0;
bool retry_on_refused = true;
};
namespace po = boost::program_options;
struct ServerConfig {
std::string listen_host = "127.0.0.1";
int listen_port = 8011;
int workers = 4;
std::string db_url = "sqlite:///database.db";
std::string listen_host = "127.0.0.1";
int listen_port = 8011;
int workers = 4;
int max_time = 600; // 默认 TTL 10 分钟
};
static std::string get_db_path(const std::string url) {
const std::string prefix = "sqlite:///";
if (url.rfind(prefix, 0) == 0) {
return url.substr(prefix.size());
}
return url;
static std::atomic<bool> g_running{true};
static void signal_handler(int) {
g_running = false;
}
class DBConnection {
class MemdbServer {
private:
ClientConfig __cfg;
ServerConfig __cfg;
Memdb<std::string, std::string> __db;
pmc::net::RpcServer __rpc;
std::mutex __mtx;
static const std::string& _require_string(const json::object& req, const std::string& field) {
auto it = req.find(field);
if (it == req.end()) {
throw std::runtime_error("missing required field: " + field);
}
if (!it->value().is_string()) {
throw std::runtime_error("field '" + field + "' must be a string");
}
return it->value().as_string();
}
public:
explicit DBConnection(const ClientConfig& cfg) :
__cfg(cfg) {}
~DBConnection() = default;
explicit MemdbServer(const ServerConfig& cfg)
: __cfg(cfg)
, __db(cfg.max_time)
, __rpc(cfg.listen_host, cfg.listen_port,
{.workers = cfg.workers})
{
_register_routes();
}
std::string get_from_sql(const std::string& sql_name) {
pmc::net::RpcClient client(__cfg.connected_host, __cfg.connected_port, __cfg.service_name, __cfg.timeout, __cfg.retry_on_refused);
void start() { __rpc.start(); }
void stop() { __rpc.stop(); }
try {
auto data = client.call("execute", {{"action", "select"}, {"table", "memdb"}, {"where", {"sql_name", sql_name}}});
} catch (const std::exception& err) {
std::cerr << "[memdb] Error: 连接失败: " << err.what() << std::endl;
std::exit(1);
}
private:
void _register_routes() {
// ---- 健康检查 ----
__rpc.add_route("ping", [](const json::object&) -> json::value {
return "pong";
});
// ---- set: 插入键值对 ----
__rpc.add_route("set", [this](const json::object& req) -> json::value {
const auto& key = _require_string(req, "key");
const auto& val = _require_string(req, "value");
std::lock_guard<std::mutex> lock(__mtx);
return __db.insert(key, val);
});
// ---- get: 查询键 ----
__rpc.add_route("get", [this](const json::object& req) -> json::value {
const auto& key = _require_string(req, "key");
std::lock_guard<std::mutex> lock(__mtx);
auto result = __db.search(key);
if (result.has_value()) {
return json::string(*result);
}
return nullptr;
});
// ---- del: 删除键 ----
__rpc.add_route("del", [this](const json::object& req) -> json::value {
const auto& key = _require_string(req, "key");
std::lock_guard<std::mutex> lock(__mtx);
return __db.remove(key);
});
// ---- update: 更新键值(可选刷新 TTL)----
__rpc.add_route("update", [this](const json::object& req) -> json::value {
const auto& key = _require_string(req, "key");
const auto& val = _require_string(req, "value");
bool refresh_ttl = false;
if (auto it = req.find("refresh_ttl"); it != req.end() && it->value().is_bool()) {
refresh_ttl = it->value().as_bool();
}
std::lock_guard<std::mutex> lock(__mtx);
return __db.update(key, val, refresh_ttl);
});
// ---- exists: 检查键是否存在 ----
__rpc.add_route("exists", [this](const json::object& req) -> json::value {
const auto& key = _require_string(req, "key");
std::lock_guard<std::mutex> lock(__mtx);
return __db.search(key).has_value();
});
// ---- size: 当前条目数 ----
__rpc.add_route("size", [this](const json::object&) -> json::value {
std::lock_guard<std::mutex> lock(__mtx);
return static_cast<int64_t>(__db.size());
});
// ---- show: 导出所有条目 ----
__rpc.add_route("show", [this](const json::object&) -> json::value {
std::lock_guard<std::mutex> lock(__mtx);
json::array result;
for (const auto& col : __db.get_db()) {
json::object entry;
entry["key"] = json::string(col.key);
entry["value"] = json::string(col.value);
result.push_back(std::move(entry));
}
return result;
});
// ---- expire: 更新值并重置 TTL(等价于 del + set)----
__rpc.add_route("expire", [this](const json::object& req) -> json::value {
const auto& key = _require_string(req, "key");
const auto& val = _require_string(req, "value");
std::lock_guard<std::mutex> lock(__mtx);
__db.remove(key);
return __db.insert(key, val);
});
}
};
class MemdbServer {
int main(int argc, char** argv) {
std::signal(SIGINT, signal_handler);
std::signal(SIGTERM, signal_handler);
ServerConfig cfg;
private:
ServerConfig __cfg;
po::options_description desc("memdb options");
desc.add_options()
("help,h", "display help")
("instance", po::value<std::string>()->default_value("memdb"), "instance name")
("log-level", po::value<std::string>()->default_value("info"), "log level")
("host", po::value<std::string>()->default_value("127.0.0.1"), "listen host")
("port", po::value<int>()->default_value(8011), "listen port")
("workers", po::value<int>()->default_value(4), "worker threads")
("max-time", po::value<int>()->default_value(600), "default TTL (seconds)");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
public:
explicit MemdbServer(const ServerConfig& cfg) :
__cfg(cfg) {}
void start() {
//TODO 编写
if (vm.count("help")) {
std::cout << desc << std::endl;
return 0;
}
};
cfg.listen_host = vm["host"].as<std::string>();
cfg.listen_port = vm["port"].as<int>();
cfg.workers = vm["workers"].as<int>();
cfg.max_time = vm["max-time"].as<int>();
auto instance = vm["instance"].as<std::string>();
auto log_level = vm["log-level"].as<std::string>();
MemdbServer server(cfg);
std::cout << "[memdb:" << instance << "] log-level=" << log_level
<< " listening on " << cfg.listen_host
<< ":" << cfg.listen_port << std::endl;
std::thread server_thread([&server]() {
server.start();
});
while (g_running) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "\n[memdb:" << instance << "] shutting down..." << std::endl;
server.stop();
if (server_thread.joinable()) {
server_thread.join();
}
return 0;
}