[WIP] mooncake-operator#2837
Conversation
Switch HA backend from ETCD to K8S lease; add no-CUDA Dockerfile; skip git submodules in CI Docker builds; add libk8s_lease_wrapper.so to wheel packaging; add ASIO include dirs to transfer engine targets; add K8S lease operator skeleton; update pybind11 and yalantinglibs submodules; improve .dockerignore and .gitignore patterns. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…aults, fix segment display - Add vLLM PD-disaggregated inference orchestration (proxy/prefill/decode deployments) - Fix eviction config: use safe defaults (high_watermark=0.8, ratio=0.1) when CR values are 0/unset - Fix MC_SEGMENT_SIZE from 0 to 1GB (1073741824) in test job env - Fix null reference in worker-segments API handler - Add store-read-verify-job.yaml for data verification - Add Dockerfile.hostbuild for local pre-built UI images - Add test page for UI segment verification - Update .gitignore to track mooncake-operator/ui/lib/ files - Update submodules (pybind11, yalantinglibs) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…to UI ClusterRole Web UI editing of clusters (e.g. reducing worker count) failed with a 403 forbidden error because the mooncake-operator-ui ClusterRole only granted get/list/watch/create/delete verbs but not update/patch. The edit page sends a PUT request to the Kubernetes API which requires the update verb. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace nvidia/cuda base image with ubuntu:22.04 for both builder and runtime stages. Disable CUDA/EP options, remove nvlink allocator build step, use system Python 3.10 directly, and add GOPROXY for China mirror. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove all Chinese text from WebUI, replace with English equivalents - Add dedicated migration page at /clusters/[ns]/[name]/migrate with source/target worker selection, config params, and real-time progress - Add drain-worker targetIPs support to api-handler.js - Fix tsconfig target to es2015 for Set iteration support - Add operator controller and CRD changes for migration support - Add mooncake-store drain/transfer enhancements and HA supervision Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…gment reassignment, and misc improvements - mooncake-store: Add speed_mbps to drain job query response for real-time speed display; fix bandwidth throttling bug where rate check was skipped when elapsed_ms >= 1000, causing bursty throughput; calculate speed even when bandwidth is unlimited - mooncake-store: Handle stale segment reassignment in MountSegment — when a worker pod restarts or a segment is DRAINED, allow remounting by reassigning to the new client instead of returning SEGMENT_ALREADY_EXISTS - mooncake-store: Trigger segment remount after reconnecting to master in heartbeat thread (client_service.cpp) - mooncake-transfer-engine: On rePublishRpcMetaEntry SET failure, verify via GET to tolerate duplicate-key metadata server implementations - mooncake-operator CRD: Allow worker replicas to be 0 (scale-to-zero); add resource comparison to statefulSetSpecsEqual/deploymentSpecsEqual so resource changes trigger pod rollouts; reduce sample resource requests - mooncake-operator RBAC: Add pod delete verb to UI ClusterRole - mooncake-operator UI: Add Speed column to migration progress table; remove inline drain monitoring from cluster detail page (moved to dedicated migration page); add 5s auto-refresh with silent mode; add back-to-cluster links on edit page; add rimraf dev dependency Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add defensive JSON parsing in edit page to handle empty response bodies - Ensure server.js always sends a JSON error body even when headers were already sent - Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ca scaling - Bump operator replicas to 2 with pod anti-affinity for HA deployment - Add /api/operator endpoint exposing operator pods and leader election status - Add /api/operator/scale endpoint for dynamic replica adjustment (min=1) - Display Operator HA Status section on Dashboard with pod table and Leader label - Grant UI RBAC leases read + deployments patch permissions Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…implement auto-drain scale-down - Store: Add YLT_REFL for ReplicateConfig to fix "invalid rpc arg" serialization bug (struct_pack missing group_ids field) - Store: Remove drain success client eviction from ok_client_ to prevent DRAINED segment being reset to OK by remount race - Store: Add --local_buffer_size flag to mooncake_client - Operator: Implement auto-drain orchestration on worker scale-down with progress tracking in CRD status - Operator: Add PreStop hook with dynamic leader master discovery for graceful worker termination - Operator: Add dynamic leader discovery for metadata server registration - UI: Display drain job progress on cluster detail page - UI: Add standalone deployment manifest Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Clean up drain jobs where query fails (404 after master restart) and the pod no longer exists, instead of skipping cleanup - Remove drain jobs stuck at CREATED for over 10 minutes as a safety net for orphaned entries Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ter image dropdown and defaults - Operator: Force-delete worker pods after drain completes (status >= 3) to prevent pods stuck in Terminating when PreStop hook misses DRAINED signal - Operator: Force-delete terminating pods whose segment is already DRAINED - RBAC: Add pod delete/patch permission to operator manager ClusterRole - PreStop: Re-discover master leader on each poll cycle to handle HA leader changes during drain - UI: Add /api/images endpoint listing mooncake-store images from K8s nodes - UI: Replace Image text input with live dropdown fetched from /api/images - UI: Auto-generate random cluster name (adj-noun-xx) on Create page - UI: Default RDMA to disabled, segment size to 2Gi on Create page - RBAC: Add nodes get/list/watch permission to UI ClusterRole Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…rain, and fast Dockerfile deps - **Dual-leader fix**: MasterConfig reads `local_hostname` from JSON config; operator injects `POD_IP:50051` via patched.json sed; used as K8s Lease holderIdentity instead of the default "0.0.0.0:50051" - **active_clients reset**: MasterMetricManager::reset_active_clients() clears stale client count on HA leader change (previously accumulated indefinitely) - **Drain sequential scheduling**: keys_in_plan ensures same-key segments are migrated one at a time per batch, preventing duplicate Move tasks - **Fast Dockerfile runtime deps**: Add missing apt libs (libyaml-cpp0.7, libjsoncpp25, libgflags2.2, libgoogle-glog0v5, libunwind-15); COPY libk8s_lease_wrapper.so and run ldconfig for HA mode - **Operator Args sync**: Sync and compare container `Args` field in StatefulSet reconciliation to prevent unnecessary rollouts Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add WHEEL_REUSE=1 fast mode to build_wheel.sh: unpack existing wheel, replace .so/.bin, repack — skips python -m build + auditwheel (~130s → ~10s) - Fix 'python' → 'python3' in build_wheel.sh (externally-managed-env error) - Fix 'kind load' to use --name three-node-cluster (no nodes found error) - Make transfer_engine_bench copy optional (not built in all configs) - Use --break-system-packages for pip in build_wheel.sh - Auto-detect wheel reuse in dev-cycle.sh deploy-store - Install ccache for faster C++ incremental compilation Co-Authored-By: Claude <noreply@anthropic.com>
…h WebUI status Transfer engine: MultiTransport now tracks per-transport health and automatically falls back to TCP when RDMA fails consecutively. Configurable via MC_FAILOVER_THRESHOLD and MC_FAILOVER_RECOVERY_SECS env vars. Operator: New TransportFailover field in WorkerSpec configures failover per cluster. Env vars MC_FAILOVER_ENABLED/THRESHOLD/RECOVERY_SECS are injected into worker pods. WebUI: Dashboard now shows RDMA Transport Status panel with per-worker health badges and RDMA device allocation counts. Mock RDMA transport (MC_RDMA_MOCK=1) enables testing without real RDMA hardware. Fault injection via MC_RDMA_MOCK_FAIL_RATE/FAIL_AFTER. Co-Authored-By: Claude <noreply@anthropic.com>
- api-handler.js: Dynamic MC_PROTOCOL from CR's rdmaEnabled, privileged + /dev/infiniband mount for test pods when RDMA enabled - api-handler.js: rdma-status checks CR rdmaEnabled + Soft-RoCE hostPath mount instead of only device plugin resources - page.tsx: Show "Soft-RoCE" badge + mode indicator for software RDMA - resources.go: Worker pods get --protocol=rdma, hostPath, privileged - CRD: transportFailover schema - Dockerfile: Add ibverbs-providers for Soft-RoCE - .gitignore: Add CMake build artifacts, version.h, wheel, binary Co-Authored-By: Claude <noreply@anthropic.com>
- Add getTransportHealthMap() to MultiTransport/TransferEngine/Impl layer - Add /transport_health HTTP endpoint in mooncake-store returning per-transport healthy/consecutive_failures/cooling_down JSON - Enable HTTP server (--enable_http_server --http_port=9300) on workers - Sync Volumes/Ports/SecurityContext/VolumeMounts in controller reconcile - Add Ports/Volumes/SecurityContext/VolumeMounts equality checks - Update WebUI to query /transport_health on port 9300 for real runtime health data instead of inferring from resource allocation Co-Authored-By: Claude <noreply@anthropic.com>
… polling - Add benchmark API endpoints (create, status, list, delete) to api-handler.js - Add benchmark configuration & results page with PUT-only / Full Cycle workloads - Add Benchmark button to cluster detail page - Accelerate status detection: check pod container state directly instead of waiting for Job controller status update - Increase frontend polling frequency: 3s→2s (Pending), 3s→1s (Running) - Apply same polling improvements to existing Test page
…d error propagation Three operator-layer fixes addressing data loss during pod drain migration: 1. waitDrainJobs: Track FAILED (4) and CANCELED (5) drain job statuses and return an error when any drain job completes with failures, instead of silently treating all terminal statuses as success. 2. Force-delete: Only force-delete terminating pods when drain job status is SUCCEEDED (3), not on FAILED or CANCELED. Previously used >= 3 which lost data by force-killing pods whose migration had not fully succeeded. 3. orchestrateDecodeScaleDown: Collect migration errors and return a composite error instead of swallowing them. Prevents the deployment from being patched with fewer replicas when KV cache migration fails. Also fixes transfer engine cleanup ordering (destroy transports before removing RPC metadata to prevent 404 cascades) and adds store.close() to benchmark stress test scripts. Co-Authored-By: Claude <noreply@anthropic.com>
…strateWorkerScaleDown Problem: when scaling down worker replicas, orchestrateWorkerScaleDown created drain jobs for the newest pods, but the Deployment patch (triggered immediately after) caused K8s to terminate a DIFFERENT set of pods. The drained pods stayed running with their segments stuck in DRAINING state on the master, and the terminated pods were never drained. This left worker segments unable to accept new writes despite the pod being healthy. Fix: remove orchestrateWorkerScaleDown entirely. Instead, patch the Deployment first (allowing K8s to naturally select pods for termination), and rely on reconcileTerminatingWorkers to create drain jobs for the pods that K8s actually terminates. The PreStop hook polls master's segment lifecycle for DRAINED status while the operator manages drain job creation and force-deletion. This eliminates the double-path conflict where two drain mechanisms competed over different pod sets. Co-Authored-By: Claude <noreply@anthropic.com>
C++ (mooncake-store): - Fix SelectDrainTargetForKey to not fallback to existing replica segments - Fix MoveEnd to reject empty replica_ids, preventing replica count loss - Add POST /api/v1/objects/remove endpoint for batch key removal UI (mooncake-operator): - Add Terminating status badge when pod has deletionTimestamp - Disable Delete button for terminating pods - Shorten worker names to worker-1, worker-2, worker-3 format - Add Clear Data button on worker detail page - Add /clear-worker-data API endpoint with segment-key filtering - Fix worker-keys API to return all matching keys per batch Dev-cycle: - Fix kind load missing --name flag for deploy-ui and deploy-operator - Copy mooncake_master.bin from build output before Docker build - Restart all statefulsets/deployments directly by name (fix label mismatch) Co-Authored-By: Claude <noreply@anthropic.com>
…test pod from mounting segment
Three-layer fix for multi-replica (replica_num>1) data integrity:
1. Allocation strategy (allocation_strategy.h):
- Increase max_retry from names.size() to replica_num * names.size()
- Fix used_segments tracking after each successful allocation
2. Validation layer (master_service.cpp, client_service.cpp):
- HasExpectedReplicaAllocation now requires strict exact-match for
RELIABLE_MULTI_REPLICA mode, rejecting partial allocations
- Prevents silent data loss: if system can't satisfy requested replica
count, put fails with NO_AVAILABLE_HANDLE instead of silently accepting
3. UI test pod (api-handler.js):
- Set MC_SEGMENT_SIZE=0 so test pod runs as pure client without
mounting a segment, preventing replicas from being placed on a
temporary segment that disappears when the test completes
Co-Authored-By: Claude <noreply@anthropic.com>
…ULLY_UNMOUNTING segment on drain fail, fix pre-existing lease test failures Fix 1 (controller): Force-delete pod with GracePeriodSeconds(1) when drain job reaches max retries, instead of silently abandoning the pod to wait 600s PreStop timeout. Fix 2 (master_service): Set segment to GRACEFULLY_UNMOUNTING instead of OK when drain job fails. This prevents the allocator from reassigning the dying segment for new writes. Test fixes: Updated 4 pre-existing test failures caused by PutEnd now granting default_kv_lease_ttl leases. Fixed GroupedEviction, RemoveAllLeasedObject, RemoveSoftPinObject (TTL adjustments), and MoveStart (MoveEnd now correctly refuses to delete source when target already has replica). Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Resolved 16 merge conflicts: - .gitignore, docker/mooncake.Dockerfile: merged both sides - mooncake-store CMakeLists/build: kept operator build infra + upstream options - mooncake-store master_service: kept operator drain/recovery logic - mooncake-store rpc_service: kept operator metric reporting - mooncake-store master_service_test: kept operator-specific tests - mooncake-store http_metadata_server, real_client_main: took upstream improvements - mooncake-transfer-engine: merged TransportHealthMap + device transport accessors - build_wheel.sh, transport_selector_test: took upstream improvements Co-Authored-By: Claude <noreply@anthropic.com>
…UDA Docker build - Restore drain bandwidth tracking and speed_mbps fields - Restore RELIABLE_MULTI_REPLICA fix, remount after reconnect - Restore TransportHealth failover tracking and mock RDMA transport - Restore stale segment reassignment and allocation strategy fix - Include non-CUDA Dockerfile and CMake build configuration Co-Authored-By: Claude <noreply@anthropic.com>
…ping operator features - Reset master_service.cpp to mooncake/main base (removes incompatible operator code) - Reset rpc_service.cpp to mooncake/main base (fixes HTTP handler compilation) - Remove incompatible ReplicationTask custom constructor from master_service.h - Retain operator-specific additions: speed_mbps, bandwidth_mbps, blocked_unit_keys - Fix serializer.hpp → serializer.h include Co-Authored-By: Claude <noreply@anthropic.com>
…vice Co-Authored-By: Claude <noreply@anthropic.com>
…uilds - Split COPY into two stages: dependencies (cached) then source (incremental) - Add ccache with -DCMAKE_C_COMPILER_LAUNCHER + BuildKit cache mount - Exclude stale build/ directories from Docker context - Add --no-deps to pip install to avoid PyPI network timeout - Incremental build: ~130s (was ~400s) Co-Authored-By: Claude <noreply@anthropic.com>
Remove nohup.out (log file) and docker-pip-cache/*.whl (Python wheel binaries, 47MB total) from git tracking. These files are already matched by .gitignore patterns and should never have been committed. Co-Authored-By: Claude <noreply@anthropic.com>
This is a build artifact (ELF 64-bit, 8.9MB) already listed in .gitignore. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a Next.js Web UI and a Kubernetes Operator (mooncake-operator) to manage Mooncake clusters, alongside vLLM live-migration support and C++ transfer engine enhancements like mock RDMA and automatic RDMA-to-TCP failover. The review feedback is highly actionable and identifies several critical issues: a bug in the operator controller attempting to update a deleted ConfigMap, a body stream reuse error in the benchmark page, missing read timeouts in the proxy server, unhandled non-2xx HTTP status codes in the UI API handler, a failover threshold logic bug in the C++ multi-transport, an accidentally committed backup file, an in-memory session memory leak in the UI server, and missing query parameter stripping in the custom Python HTTP server.
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.
| if !configMapDataEqual(created.Data, desired.Data) { | ||
| logger.Info("created ConfigMap has wrong data, deleting and retrying with Update") | ||
| logger.Info("desired data", "data", desired.Data) | ||
| logger.Info("created data", "data", created.Data) | ||
| _ = r.Delete(ctx, &created) | ||
| existing = *desired | ||
| existing.ResourceVersion = "" | ||
| return r.Update(ctx, &existing) | ||
| } |
There was a problem hiding this comment.
In reconcileConfigMap, if the created ConfigMap has different data than desired, the code deletes the ConfigMap and then immediately calls r.Update on it. Since the resource has been deleted from the API server, r.Update will fail with a NotFound error. Instead of deleting and updating, you should directly update the existing ConfigMap's data and call r.Update.
if !configMapDataEqual(created.Data, desired.Data) {
logger.Info("created ConfigMap has wrong data, updating")
created.Data = desired.Data
return r.Update(ctx, &created)
}| let data | ||
| try { | ||
| data = await res.json() | ||
| } catch (parseErr: any) { | ||
| // Empty body or invalid JSON — try to get the raw text for debugging | ||
| const text = await res.text().catch(() => '') | ||
| throw new Error(`Invalid JSON response (${res.status}): ${parseErr.message}. Raw body (${text.length} bytes): ${text.substring(0, 200)}`) | ||
| } |
There was a problem hiding this comment.
In the Fetch API, the response body stream can only be read once. Calling res.text() in the catch block of res.json() will throw a TypeError: body stream already read because the stream has already been consumed by res.json(). To safely log or handle non-JSON error responses, you should read the response as text first, and then parse it as JSON.
let data
const text = await res.text()
try {
data = JSON.parse(text)
} catch (parseErr: any) {
throw new Error(`Invalid JSON response (${res.status}): ${parseErr.message}. Raw body (${text.length} bytes): ${text.substring(0, 200)}`)
}
| async def _proxy_http_request( | ||
| host: str, port: int, method: str, path: str, | ||
| body: dict | None = None, timeout: float = 10.0, | ||
| ) -> dict: | ||
| """Make an HTTP request to a migration endpoint.""" | ||
| try: | ||
| reader, writer = await asyncio.wait_for( | ||
| asyncio.open_connection(host, port), timeout=5.0 | ||
| ) | ||
| body_bytes = json.dumps(body).encode() if body else b"" | ||
| request_line = f"{method} {path} HTTP/1.1\r\n".encode() | ||
| headers = ( | ||
| f"Host: {host}:{port}\r\n" | ||
| f"Content-Length: {len(body_bytes)}\r\n" | ||
| f"Content-Type: application/json\r\n" | ||
| f"Connection: close\r\n\r\n" | ||
| ).encode() | ||
| writer.write(request_line + headers + body_bytes) | ||
| await writer.drain() | ||
|
|
||
| while True: | ||
| line = await reader.readuntil(b"\r\n") | ||
| if line == b"\r\n": | ||
| break | ||
| resp_body = b"" | ||
| while True: | ||
| chunk = await reader.read(4096) | ||
| if not chunk: | ||
| break | ||
| resp_body += chunk | ||
| writer.close() | ||
| await writer.wait_closed() | ||
| return json.loads(resp_body) if resp_body else {} | ||
| except Exception as e: | ||
| logger.error("Proxy HTTP request to %s:%d%s failed: %s", | ||
| host, port, path, e) | ||
| return {"error": str(e)} |
There was a problem hiding this comment.
The _proxy_http_request function defines a timeout parameter but only applies a 5-second timeout to the connection phase. The subsequent read loop has no timeout, which can cause the proxy server to hang indefinitely if the target node is unresponsive. You should wrap the entire request execution (or at least the read loop) with asyncio.wait_for using the provided timeout parameter.
async def _proxy_http_request(
host: str, port: int, method: str, path: str,
body: dict | None = None, timeout: float = 10.0,
) -> dict:
"""Make an HTTP request to a migration endpoint."""
try:
async def _run():
reader, writer = await asyncio.open_connection(host, port)
body_bytes = json.dumps(body).encode() if body else b""
request_line = f"{method} {path} HTTP/1.1\r\n".encode()
headers = (
f"Host: {host}:{port}\r\n"
f"Content-Length: {len(body_bytes)}\r\n"
f"Content-Type: application/json\r\n"
f"Connection: close\r\n\r\n"
).encode()
writer.write(request_line + headers + body_bytes)
await writer.drain()
while True:
line = await reader.readuntil(b"\r\n")
if line == b"\r\n":
break
resp_body = b""
while True:
chunk = await reader.read(4096)
if not chunk:
break
resp_body += chunk
writer.close()
await writer.wait_closed()
return json.loads(resp_body) if resp_body else {}
return await asyncio.wait_for(_run(), timeout=timeout)
except Exception as e:
logger.error("Proxy HTTP request to %s:%d%s failed: %s",
host, port, path, e)
return {"error": str(e)}| std::lock_guard<std::mutex> lock(health_mutex_); | ||
| auto it = transport_health_.find(proto_str); | ||
| if (it != transport_health_.end()) { | ||
| it->second.consecutive_failures++; | ||
| if (it->second.consecutive_failures >= failover_threshold_) { | ||
| it->second.markUnhealthy(recovery_secs_); | ||
| LOG(WARNING) << "[MultiTransport] " << proto_str | ||
| << " marked unhealthy after " | ||
| << it->second.consecutive_failures | ||
| << " consecutive failures. " | ||
| << "Cooldown for " << recovery_secs_ << "s"; | ||
| } | ||
| } else { | ||
| TransportHealth health; | ||
| health.consecutive_failures = 1; | ||
| transport_health_[proto_str] = health; | ||
| } |
There was a problem hiding this comment.
If a transport fails and its entry does not exist in transport_health_ yet, it is initialized with consecutive_failures = 1 in the else branch, but the >= failover_threshold_ check is skipped. This means that if failover_threshold_ is configured to 1, the first failure will not trigger failover, requiring a second failure instead. You can simplify and fix this by initializing the entry first or checking the threshold in both cases.
std::lock_guard<std::mutex> lock(health_mutex_);
auto &health = transport_health_[proto_str];
health.consecutive_failures++;
if (health.consecutive_failures >= failover_threshold_) {
health.markUnhealthy(recovery_secs_);
LOG(WARNING) << "[MultiTransport] " << proto_str
<< " marked unhealthy after "
<< health.consecutive_failures
<< " consecutive failures. "
<< "Cooldown for " << recovery_secs_ << "s";
}| @@ -0,0 +1,893 @@ | |||
| package controller | |||
| const SESSION_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours | ||
| const sessions = new Map() // token -> { createdAt } | ||
|
|
There was a problem hiding this comment.
Sessions are stored in an in-memory Map but are never periodically pruned. Expired sessions are only deleted when a user attempts to use them. If users log in and never return, their sessions will leak memory indefinitely. You should add a periodic setInterval to prune expired sessions.
// --- Session management ---
const SESSION_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours
const sessions = new Map() // token -> { createdAt }
// Periodically prune expired sessions to prevent memory leaks
setInterval(() => {
const now = Date.now()
for (const [token, entry] of sessions.entries()) {
if (now - entry.createdAt > SESSION_TTL_MS) {
sessions.delete(token)
}
}
}, 60 * 60 * 1000)| options.headers['Content-Length'] = Buffer.byteLength(bodyStr) | ||
| } | ||
| const req = http.request(options, (res) => { | ||
| let data = '' | ||
| res.on('data', (chunk) => data += chunk) | ||
| res.on('end', () => { | ||
| try { | ||
| resolve(JSON.parse(data)) | ||
| } catch (e) { | ||
| resolve(data) | ||
| } |
There was a problem hiding this comment.
The callMasterAPI helper does not check the HTTP response status code. If the master API returns a non-200 error (e.g., 500 or 404), the function will still resolve successfully with the error body, which can cause unexpected behavior or crashes in the caller. You should reject the promise if the status code is not in the 2xx range.
const req = http.request(options, (res) => {
let data = ''
res.on('data', (chunk) => data += chunk)
res.on('end', () => {
if (res.statusCode < 200 || res.statusCode >= 300) {
reject(new Error(`Master API returned status ${res.statusCode}: ${data}`));
return;
}
try {
resolve(JSON.parse(data))
} catch (e) {
resolve(data)
}
})
})| method, path, _ = request_line.decode().strip().split(" ", 2) | ||
|
|
There was a problem hiding this comment.
The custom HTTP server matches paths using exact matching or path.startswith, but does not strip query parameters. If a query parameter is appended (e.g., by a monitoring tool or proxy), the route matching will fail. You should strip query parameters from the path before routing.
| method, path, _ = request_line.decode().strip().split(" ", 2) | |
| request_line = await reader.readuntil(b"\r\n") | |
| method, path, _ = request_line.decode().strip().split(" ", 2) | |
| if "?" in path: | |
| path, _ = path.split("?", 1) |
Description
Module
mooncake-transfer-engine)mooncake-store)mooncake-ep)mooncake-pg)mooncake-integration)mooncake-p2p-store)mooncake-wheel)mooncake-common)mooncake-rl)Type of Change
How Has This Been Tested?
Test commands:
# Example: bash scripts/run_ci_test.shTest results:
Checklist
./scripts/code_format.shpre-commit run --all-filesand all hooks passAI Assistance Disclosure