Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
2004e55
feat(umbp): add RESP/Redis master metadata store backend (Phase 1)
isytwu Jul 2, 2026
fac23e7
fix(umbp): stop master crashing when the metadata store throws
isytwu Jul 2, 2026
b632302
fix(rdma): capture errno immediately on ibverbs endpoint creation fai…
isytwu Jul 2, 2026
b7ebd51
build(umbp): make local RESP launcher apt-free
isytwu Jul 2, 2026
f8c60c7
perf(umbp): fold RouteGet lease/access bump into one HSET
isytwu Jul 2, 2026
826a60f
test(umbp): multi-process external-master mode for Redis-backend perf…
isytwu Jul 7, 2026
433b438
feat(umbp): shard Redis block keyspace for RouteGet/Exists (opt-in, d…
isytwu Jul 7, 2026
6a32f36
feat(umbp): multi-endpoint Redis fan-out for horizontal scaling
isytwu Jul 7, 2026
c45e1b7
perf(umbp): concurrent multi-endpoint read fan-out via worker pool
isytwu Jul 9, 2026
75bda3e
feat(umbp): tolerate a down shard instance in multi-endpoint reads
isytwu Jul 9, 2026
f21849c
feat(umbp): export per-op store latency (mori_umbp_store_op_latency_s…
isytwu Jul 10, 2026
944eaaa
feat(umbp): tolerate a down shard instance in multi-endpoint writes
isytwu Jul 10, 2026
5cb6a73
test(umbp): advertise routable node_address for the bench RDMA engine
isytwu Jul 10, 2026
2a2016d
build(umbp): vendor redis-plus-plus (1.3.15) as the Redis-backend cli…
isytwu Jul 10, 2026
b214c43
build(umbp): run RESP stores as independent containers (single/dragon…
isytwu Jul 10, 2026
af3078d
feat(umbp): fail-fast readiness probe + mode validation for the Redis…
isytwu Jul 10, 2026
9a33cff
docs(umbp): record locked Redis-backend decisions (HA, redis++, conta…
isytwu Jul 10, 2026
9c1103d
refactor(umbp): extract IRespClient seam so the store is client-agnostic
isytwu Jul 10, 2026
3576ac8
feat(umbp): add redis-plus-plus cluster client behind the IRespClient…
isytwu Jul 10, 2026
98bc1e3
feat(umbp): wire Redis Cluster mode into the store + factory
isytwu Jul 10, 2026
cbb6274
test(umbp): run the store conformance suite against a real Redis Cluster
isytwu Jul 10, 2026
9cfc623
docs(umbp): Redis Cluster mode is implemented and verified
isytwu Jul 10, 2026
bed2b11
perf(umbp): EVALSHA in the cluster client (small win + correctness)
isytwu Jul 12, 2026
84d804e
feat(umbp): auto-size cluster block shards to ~2x the master count
isytwu Jul 12, 2026
ff4c296
perf(umbp): balanced per-master shard placement for Redis Cluster
isytwu Jul 12, 2026
ad8f046
test(umbp): cover cluster slot math + balanced-placement conformance
isytwu Jul 12, 2026
7b3bcc8
test(umbp): full-link proxy bench supports cluster / multi-endpoint b…
isytwu Jul 12, 2026
339e0a2
perf(umbp): parallelize Redis-metadata READ hot path on Dragonfly
isytwu Jul 12, 2026
25a815e
refactor(umbp): dedupe cluster ConnectionOptions; nudge unsharded Dra…
isytwu Jul 13, 2026
ba4c157
umbp/redis: cache EVALSHA by script identity, tolerate cluster node f…
isytwu Jul 13, 2026
3bd7b3b
umbp/redis: factor shared RESP client helpers into resp_detail.h
isytwu Jul 13, 2026
444f113
umbp/redis: extract sharded block-read fan-out to a unit-testable header
isytwu Jul 13, 2026
bf696f7
docs(redis): add §8 caveats for durability, read=write, control plane…
isytwu Jul 14, 2026
650619b
umbp/redis: implement external-KV registry + hit counts on the contro…
isytwu Jul 14, 2026
61ea7b9
umbp/redis: enumerate eviction candidates via reverse-index scan (no …
isytwu Jul 14, 2026
5b877f0
test(umbp/redis): cover external-KV, hit counts, GC, and eviction in …
isytwu Jul 14, 2026
b07c720
docs(umbp/redis): mark external-KV / hit / eviction (#5) delivered in…
isytwu Jul 14, 2026
e3abe41
test(umbp/redis): add external-KV hot-path benchmarks across backends
isytwu Jul 14, 2026
a8b5a0d
umbp/redis: shard the external-KV hot path + cut match/register Lua cost
isytwu Jul 14, 2026
1711156
test(umbp): fix multi-process fetch/both teardown reset in kvevent bench
isytwu Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@
path = 3rdparty/spdk
url = https://github.com/spdk/spdk.git
shallow = true
[submodule "3rdparty/redis-plus-plus"]
path = 3rdparty/redis-plus-plus
url = https://github.com/sewenew/redis-plus-plus.git
1 change: 1 addition & 0 deletions 3rdparty/redis-plus-plus
Submodule redis-plus-plus added at a63ac4
6 changes: 6 additions & 0 deletions include/mori/metrics/prometheus_metrics_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ class MetricsServer {
void observe(std::string_view name, std::string_view help, const std::vector<double>& bounds,
double value);

// Record one histogram observation into a LABELED series (e.g.
// {op=...,backend=...}). Same first-write-wins bucket layout as the unlabeled
// observe(); reuses the labeled-histogram storage that observeAggregated uses.
void observe(std::string_view name, std::string_view help, const Labels& labels,
const std::vector<double>& bounds, double value);

// Merge a pre-aggregated histogram batch into a labeled series.
// `bucket_counts` is CUMULATIVE (bucket_counts[i] = #obs with value <=
// bounds[i]) and is added per-bucket into the stored series; `count` and
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ def build_extension(self, ext: Extension) -> None:
build_tests = os.environ.get("BUILD_TESTS", "OFF")
build_umbp = "ON" if build_umbp_enabled else "OFF"
build_umbp_spdk = "ON" if build_umbp_spdk_enabled else "OFF"
use_redis_backend = os.environ.get("USE_REDIS_BACKEND", "OFF")
build_xla_ffi_ops = os.environ.get("BUILD_XLA_FFI_OPS", "OFF")
with_mpi = (
"ON"
Expand Down Expand Up @@ -411,6 +412,7 @@ def build_extension(self, ext: Extension) -> None:
f"-DBUILD_BENCHMARK={build_benchmark}",
f"-DBUILD_TESTS={build_tests}",
f"-DBUILD_UMBP={build_umbp}",
f"-DUSE_REDIS_BACKEND={use_redis_backend}",
f"-DUSE_SPDK={build_umbp_spdk}",
f"-DWITH_MPI={with_mpi}",
"-DBUILD_TORCH_BOOTSTRAP=OFF",
Expand Down
29 changes: 21 additions & 8 deletions src/application/transport/rdma/providers/ibverbs/ibverbs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,25 +170,32 @@ RdmaEndpoint IBVerbsDeviceContext::CreateRdmaEndpoint(const RdmaEndpointConfig&
// TODO: we need to add more options in config, include min cqe num for ib_create_cq
endpoint.ibvHandle.compCh = config.withCompChannel ? ibv_create_comp_channel(context) : nullptr;
if (config.withCompChannel && !endpoint.ibvHandle.compCh) {
MORI_APP_ERROR("ibv_create_comp_channel failed: errno={} ({}); dev={}", errno, strerror(errno),
// Capture errno immediately: any intervening libc call before we read it
// (e.g. isatty() inside the logging sink when stderr is not a tty, which
// sets ENOTTY) would otherwise overwrite the real failure code.
const int err = errno;
MORI_APP_ERROR("ibv_create_comp_channel failed: errno={} ({}); dev={}", err, strerror(err),
GetRdmaDevice()->Name());
throw std::runtime_error("ibv_create_comp_channel failed: " + std::string(strerror(errno)));
throw std::runtime_error("ibv_create_comp_channel failed: " + std::string(strerror(err)));
}

endpoint.ibvHandle.cq =
ibv_create_cq(context, config.maxCqeNum, NULL, endpoint.ibvHandle.compCh, 0);
if (!endpoint.ibvHandle.cq) {
// Capture errno before the lock/log path can overwrite it (see the note at
// the comp-channel site).
const int err = errno;
size_t cqPoolSize = 0;
{
std::lock_guard<std::mutex> lock(poolMu);
cqPoolSize = cqPool.size();
}
MORI_APP_ERROR(
"ibv_create_cq failed: errno={} ({}); dev={} max_cqe={} dev_max_cqe={} cqs_in_pool={}",
errno, strerror(errno), GetRdmaDevice()->Name(), config.maxCqeNum,
deviceAttr->orig_attr.max_cqe, cqPoolSize);
"ibv_create_cq failed: errno={} ({}); dev={} max_cqe={} dev_max_cqe={} cqs_in_pool={}", err,
strerror(err), GetRdmaDevice()->Name(), config.maxCqeNum, deviceAttr->orig_attr.max_cqe,
cqPoolSize);
if (endpoint.ibvHandle.compCh) ibv_destroy_comp_channel(endpoint.ibvHandle.compCh);
throw std::runtime_error("ibv_create_cq failed: " + std::string(strerror(errno)));
throw std::runtime_error("ibv_create_cq failed: " + std::string(strerror(err)));
}

// TODO: should also manage the lifecycle of completion channel && srq
Expand Down Expand Up @@ -216,6 +223,12 @@ RdmaEndpoint IBVerbsDeviceContext::CreateRdmaEndpoint(const RdmaEndpointConfig&
.qp_type = IBV_QPT_RC};
endpoint.ibvHandle.qp = ibv_create_qp(pd, &qpAttr);
if (!endpoint.ibvHandle.qp) {
// Capture errno immediately. Otherwise the lock/log path below overwrites
// it before we read it: spdlog's console sink calls isatty(stderr), which
// sets errno=ENOTTY when stderr is not a terminal, masking the real error
// (commonly EINVAL when the requested QP capacity — max_send_wr x per-WQE
// size from sge+inline — exceeds the device's per-QP work-queue budget).
const int err = errno;
size_t qpPoolSize = 0;
{
std::lock_guard<std::mutex> lock(poolMu);
Expand All @@ -224,13 +237,13 @@ RdmaEndpoint IBVerbsDeviceContext::CreateRdmaEndpoint(const RdmaEndpointConfig&
MORI_APP_ERROR(
"ibv_create_qp failed: errno={} ({}); dev={} port={} max_send_wr={} max_recv_wr={} "
"max_send_sge={} max_cqe={} dev_caps(max_qp_wr={} max_qp={} max_cqe={}) qps_in_pool={}",
errno, strerror(errno), GetRdmaDevice()->Name(), config.portId, qpAttr.cap.max_send_wr,
err, strerror(err), GetRdmaDevice()->Name(), config.portId, qpAttr.cap.max_send_wr,
qpAttr.cap.max_recv_wr, qpAttr.cap.max_send_sge, config.maxCqeNum,
deviceAttr->orig_attr.max_qp_wr, deviceAttr->orig_attr.max_qp,
deviceAttr->orig_attr.max_cqe, qpPoolSize);
ibv_destroy_cq(endpoint.ibvHandle.cq);
if (endpoint.ibvHandle.compCh) ibv_destroy_comp_channel(endpoint.ibvHandle.compCh);
throw std::runtime_error("ibv_create_qp failed: " + std::string(strerror(errno)));
throw std::runtime_error("ibv_create_qp failed: " + std::string(strerror(err)));
}
endpoint.handle.qpn = endpoint.ibvHandle.qp->qp_num;

Expand Down
19 changes: 19 additions & 0 deletions src/metrics/prometheus_metrics_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,25 @@ void MetricsServer::observe(std::string_view name, std::string_view help,
static std::string FormatLabels(
const mori::metrics::MetricsServer::Labels& labels); // forward decl

void MetricsServer::observe(std::string_view name, std::string_view help, const Labels& labels,
const std::vector<double>& bounds, double value) {
auto label_str = FormatLabels(labels);
std::lock_guard<std::mutex> lk(mutex_);
auto& family = labeled_histograms_[std::string(name)];
family.help = help;
auto& s = family.series[label_str];
if (s.bounds.empty()) {
s.bounds = bounds;
s.bucket_counts.assign(bounds.size(), 0u);
}
// Increment all cumulative buckets whose upper bound >= value.
for (std::size_t i = 0; i < s.bounds.size(); ++i) {
if (value <= s.bounds[i]) s.bucket_counts[i]++;
}
s.count++;
s.sum += value;
}

void MetricsServer::observeAggregated(std::string_view name, std::string_view help,
const Labels& labels, const std::vector<double>& bounds,
const std::vector<uint64_t>& bucket_counts, uint64_t count,
Expand Down
48 changes: 48 additions & 0 deletions src/umbp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ add_library(
${UMBP_PEER_PROTO_SRCS}
${UMBP_PEER_GRPC_SRCS}
distributed/master/in_memory_master_metadata_store.cpp
distributed/master/master_metadata_store_factory.cpp
distributed/master/master_server.cpp
distributed/master/master_client.cpp
distributed/master/rpc_latency_timer.cpp
Expand Down Expand Up @@ -347,6 +348,53 @@ target_link_libraries(

target_compile_features(umbp_common PUBLIC cxx_std_17)

# ---------- Optional Redis / RESP master metadata backend --------------------
# Makes the master stateless by storing all metadata in an external RESP store
# (Redis / Dragonfly / Valkey). OFF by default so existing builds are
# unaffected; enable with -DUSE_REDIS_BACKEND=ON. The RESP client seam is built
# on hiredis (RespClient isolates the client library from the store).
option(USE_REDIS_BACKEND
"Build the RESP-compatible Redis master metadata backend" OFF)
if(USE_REDIS_BACKEND)
find_path(HIREDIS_INCLUDE_DIR hiredis/hiredis.h REQUIRED)
find_library(HIREDIS_LIB NAMES hiredis REQUIRED)
message(
STATUS
"UMBP Redis backend ENABLED (hiredis lib: ${HIREDIS_LIB}, include: ${HIREDIS_INCLUDE_DIR})"
)

# redis-plus-plus (3rdparty submodule) — the cluster/sentinel/pooling-capable
# client the Redis backend standardizes on. Built from source so any build
# image works without an extra system package; only the client library needs
# a submodule (hiredis stays the system lib). Static + PIC (umbp_common is
# linked into the shared pybind module), C++17, tests off. The store code does
# not depend on it yet (that migration is a later step); building it here
# de-risks the dependency (toolchain, -Werror, link) before the seam rebase.
set(_REDISPP_DIR "${CMAKE_SOURCE_DIR}/3rdparty/redis-plus-plus")
if(NOT EXISTS "${_REDISPP_DIR}/CMakeLists.txt")
message(
FATAL_ERROR
"USE_REDIS_BACKEND=ON but 3rdparty/redis-plus-plus is empty; run "
"'git submodule update --init --recursive 3rdparty/redis-plus-plus'")
endif()
set(REDIS_PLUS_PLUS_BUILD_TEST OFF CACHE BOOL "" FORCE)
set(REDIS_PLUS_PLUS_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(REDIS_PLUS_PLUS_BUILD_STATIC ON CACHE BOOL "" FORCE)
set(REDIS_PLUS_PLUS_CXX_STANDARD 17 CACHE STRING "" FORCE)
add_subdirectory("${_REDISPP_DIR}"
"${CMAKE_BINARY_DIR}/3rdparty/redis-plus-plus")

target_sources(
umbp_common PRIVATE distributed/master/redis/resp_client.cpp
distributed/master/redis/resp_cluster_client.cpp
distributed/master/redis_master_metadata_store.cpp)
target_include_directories(umbp_common PRIVATE ${HIREDIS_INCLUDE_DIR})
# redis++_static carries its own (and hiredis') include dirs; link it before
# hiredis so the static archive's hiredis symbols resolve.
target_link_libraries(umbp_common PUBLIC redis++_static ${HIREDIS_LIB})
target_compile_definitions(umbp_common PUBLIC USE_REDIS_BACKEND)
endif()

if(MORI_UMBP_TESTING)
target_compile_definitions(umbp_common PUBLIC MORI_UMBP_TESTING)
endif()
Expand Down
8 changes: 7 additions & 1 deletion src/umbp/distributed/master/eviction_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,13 @@ void EvictionManager::EvictionLoop() {
[this] { return !running_.load(std::memory_order_relaxed); });
}
if (!running_.load(std::memory_order_relaxed)) break;
RunOnce();
// A metadata-store hiccup (e.g. RESP transport error) must not kill the
// eviction thread; log and retry on the next tick.
try {
RunOnce();
} catch (const std::exception& e) {
MORI_UMBP_ERROR("[EvictionManager] metadata-store error: {}", e.what());
}
}
}

Expand Down
Loading