Skip to content

[Store] feat: expose client metrics HTTP config to Python#2822

Merged
ykwd merged 3 commits into
kvcache-ai:mainfrom
Lin-z-w:feat/client-python-metrics-api
Jul 13, 2026
Merged

[Store] feat: expose client metrics HTTP config to Python#2822
ykwd merged 3 commits into
kvcache-ai:mainfrom
Lin-z-w:feat/client-python-metrics-api

Conversation

@Lin-z-w

@Lin-z-w Lin-z-w commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Description

Expose the Mooncake Store client-side health and metrics HTTP endpoint through the Python-facing client setup APIs and the packaged store service configuration.

This change migrates the client Python metrics exposure behavior from Mooncake-taas PR5 and includes the relevant PR9 compatibility fix. It adds:

  • Python and pybind setup arguments for enable_client_http_server and client_http_port.
  • MooncakeConfig JSON/env support via MOONCAKE_ENABLE_CLIENT_HTTP_SERVER and MOONCAKE_CLIENT_HTTP_PORT.
  • Store service propagation of the new config fields into MooncakeDistributedStore.setup.
  • Client HTTP endpoint tests for /health, /metrics, and /metrics/summary, including disabled-metrics and port-conflict behavior.
  • Additional metrics latency buckets for the long-tail ranges.
  • Documentation for deployment and observability usage.

The client HTTP endpoint remains disabled by default, so existing Python and SGLang integrations keep their current behavior unless they opt in.

Module

  • Transfer Engine (mooncake-transfer-engine)
  • Mooncake Store (mooncake-store)
  • Mooncake EP (mooncake-ep)
  • Mooncake PG (mooncake-pg)
  • Integration (mooncake-integration)
  • P2P Store (mooncake-p2p-store)
  • Python Wheel (mooncake-wheel)
  • Common (mooncake-common)
  • Mooncake RL (mooncake-rl)
  • CI/CD
  • Docs
  • Other

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Breaking change
  • Documentation update
  • Performance improvement
  • Other

How Has This Been Tested?

Test commands:

git diff --check

SKIP=mooncake-code-format pre-commit run --files docs/source/deployment/mooncake-store-deployment-guide.md docs/source/getting_started/observability.md mooncake-integration/store/store_py.cpp mooncake-store/include/client_metric.h mooncake-store/include/dummy_client.h mooncake-store/include/pyclient.h mooncake-store/include/real_client.h mooncake-store/include/types.h mooncake-store/src/real_client.cpp mooncake-store/src/real_client_main.cpp mooncake-store/tests/client_metrics_test.cpp mooncake-wheel/mooncake/mooncake_config.py mooncake-wheel/mooncake/mooncake_store_service.py mooncake-wheel/tests/test_mooncake_config.py mooncake-wheel/tests/test_mooncake_store_service_api.py

cmake --build build --target client_metrics_test store
build/mooncake-store/tests/client_metrics_test

PYTHONPATH=/home/lzw/Mooncake/mooncake-wheel python3 -m unittest mooncake-wheel/tests/test_mooncake_config.py mooncake-wheel.tests.test_mooncake_store_service_api.StoreServiceApiTest.test_start_store_service_passes_tenant_id_to_setup mooncake-wheel.tests.test_mooncake_store_service_api.StoreServiceApiTest.test_cli_config_can_override_tenant_id

PATH=/home/lzw/Mooncake/docs/.venv/bin:$PATH make html

Test results:

  • Unit tests pass
  • Integration tests pass (if applicable)
  • Manual testing done (describe below)

Manual validation:

  • client_metrics_test passed: 12 tests.
  • Python unittest passed: 37 tests.
  • Sphinx make html passed with only sandbox-network intersphinx inventory warnings.
  • mooncake-code-format was run through the pre-commit hook and formatted the touched C++ files. It was skipped on the final targeted pre-commit/commit because the hook compares against local origin/main, which was older than the rebased upstream/main and attempted to format unrelated upstream files.

Checklist

  • I have performed a self-review of my own code
  • I have formatted my code using ./scripts/code_format.sh
  • I have run pre-commit run --all-files and all hooks pass
  • I have updated the documentation (if applicable)
  • I have added tests to prove my changes are effective
  • For changes >500 LOC: I have filed an RFC issue

AI Assistance Disclosure

  • No AI tools were used
  • AI tools were used (specify below)

AI assistance: OpenAI Codex was used to inspect the codebase, port the changes, resolve rebase conflicts, run validation, and draft this PR description. I reviewed and edited the generated code before submission.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces client-side HTTP health and metrics endpoints (/health, /metrics, and /metrics/summary) for Mooncake clients, updating the C++ core, Python bindings, configuration parsing, documentation, and tests. The feedback suggests enhancing the robustness of the configuration parsing in real_client.cpp by implementing a trim helper function to strip leading and trailing whitespace from boolean and integer configuration values, preventing parsing failures or unintended fallbacks.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +1000 to +1022
inline bool get_config_bool(const ConfigDict &config, const std::string &key,
bool default_value) {
auto it = config.find(key);
if (it == config.end()) {
return default_value;
}

std::string value = it->second;
std::transform(value.begin(), value.end(), value.begin(),
[](unsigned char c) { return std::tolower(c); });
if (value == "true" || value == "1" || value == "yes" || value == "on" ||
value == "enable") {
return true;
}
if (value == "false" || value == "0" || value == "no" || value == "off" ||
value == "disable") {
return false;
}

LOG(WARNING) << "Invalid boolean value for config key '" << key
<< "': " << it->second << ", using default: " << default_value;
return default_value;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Configuration values parsed from environment variables or external files can often contain leading or trailing whitespace (e.g., "true " or " 1"). Since get_config_bool performs exact string matching, any leading or trailing whitespace will cause the check to fail and silently fall back to the default value. Introducing a helper function trim to strip whitespace before processing makes the configuration parsing much more robust.

inline std::string trim(const std::string &s) {
    auto start = s.find_first_not_of(" \t\r\n");
    if (start == std::string::npos) return "";
    auto end = s.find_last_not_of(" \t\r\n");
    return s.substr(start, end - start + 1);
}

inline bool get_config_bool(const ConfigDict &config, const std::string &key,
                            bool default_value) {
    auto it = config.find(key);
    if (it == config.end()) {
        return default_value;
    }

    std::string value = trim(it->second);
    std::transform(value.begin(), value.end(), value.begin(),
                   [](unsigned char c) { return std::tolower(c); });
    if (value == "true" || value == "1" || value == "yes" || value == "on" ||
        value == "enable") {
        return true;
    }
    if (value == "false" || value == "0" || value == "no" || value == "off" ||
        value == "disable") {
        return false;
    }

    LOG(WARNING) << "Invalid boolean value for config key '" << key
                 << "': " << it->second << ", using default: " << default_value;
    return default_value;
}

Comment on lines +1024 to +1046
inline std::optional<int> get_config_int(const ConfigDict &config,
const std::string &key,
int default_value) {
auto it = config.find(key);
if (it == config.end()) {
return default_value;
}

try {
size_t pos = 0;
int value = std::stoi(it->second, &pos);
if (pos != it->second.size()) {
LOG(ERROR) << "Invalid integer value for config key '" << key
<< "': " << it->second;
return std::nullopt;
}
return value;
} catch (const std::exception &) {
LOG(ERROR) << "Invalid integer value for config key '" << key
<< "': " << it->second;
return std::nullopt;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the trim helper function to strip any leading or trailing whitespace from the integer string before passing it to std::stoi. This prevents parsing failures due to trailing spaces (which would otherwise fail the pos != it->second.size() check).

inline std::optional<int> get_config_int(const ConfigDict &config,
                                         const std::string &key,
                                         int default_value) {
    auto it = config.find(key);
    if (it == config.end()) {
        return default_value;
    }

    try {
        std::string trimmed = trim(it->second);
        size_t pos = 0;
        int value = std::stoi(trimmed, &pos);
        if (pos != trimmed.size()) {
            LOG(ERROR) << "Invalid integer value for config key '" << key
                       << "': " << it->second;
            return std::nullopt;
        }
        return value;
    } catch (const std::exception &) {
        LOG(ERROR) << "Invalid integer value for config key '" << key
                   << "': " << it->second;
        return std::nullopt;
    }
}

@codecov-commenter

codecov-commenter commented Jul 9, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 90.29126% with 20 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mooncake-store/src/real_client.cpp 80.00% 13 Missing ⚠️
mooncake-integration/store/store_py.cpp 0.00% 4 Missing ⚠️
mooncake-store/tests/client_metrics_test.cpp 98.52% 2 Missing ⚠️
mooncake-store/src/real_client_main.cpp 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

return default_value;
}

inline std::optional<int> get_config_int(const ConfigDict &config,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we already have a ConfigDict type, would it make sense to turn it into a proper class? Then these helper functions could become public methods, rather than living in real_client.cpp.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. ConfigDict is currently a public alias used by RealClient::setup_internal and existing C++ callers. Turning it into a class would broaden the scope of this PR and could introduce source and ABI compatibility concerns. Since these parsing rules are specific to client setup, I prefer to keep the helpers local in this PR. We can consider introducing a typed ClientConfig separately if we want to formalize the configuration API.

Comment thread mooncake-store/src/real_client.cpp Outdated
try {
std::string value = trim(it->second);
size_t pos = 0;
int parsed_value = std::stoi(value, &pos);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: std::from_chars is a more modern alternative and doesn’t throw exceptions.

Here's a some example.

auto [p, ec] = std::from_chars(s.data(), s.data() + s.size(), x);

if (ec == std::errc{}) {
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. I updated get_config_int to use std::from_chars, checking both the error code and that the entire trimmed value was consumed. I also added regression coverage for trailing characters and out-of-range integer values in commit 72a2b0d.

@Lin-z-w Lin-z-w requested a review from Aionw July 13, 2026 07:08
@ykwd ykwd merged commit 159df02 into kvcache-ai:main Jul 13, 2026
50 of 52 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants