fix(core): survive heterogeneous NUMA topologies in RDMA fetch#386
Open
xiaguan wants to merge 1 commit into
Open
fix(core): survive heterogeneous NUMA topologies in RDMA fetch#386xiaguan wants to merge 1 commit into
xiaguan wants to merge 1 commit into
Conversation
Machines with fewer NUMA nodes than their peers broke the fetch path in two ways: 1. Eviction storm: allocating for a NUMA node with no local pool made reclaim loop until the read cache was empty (largest_free stays 0 for a missing pool), so every failed fetch wiped the entire resident cache and flooded MetaServer with removals. StorageEngine::allocate now rejects requests that exceed what the target pool could hold when empty (missing pool or size above per-shard capacity) before any reclaim, with an error log and a pool_alloc_unsatisfiable counter. 2. Fetch failure: the remote slot's numa_node was used verbatim as a key into local pools, so blocks advertised on nodes this machine does not have failed allocation and aborted the whole batch. The fetch path now resolves advertised NUMA nodes onto local topology: local nodes are kept, foreign ids spread deterministically by id, UNKNOWN slots round-robin by slot position, and with no NUMA pools everything collapses to UNKNOWN. Rebuilt SealedBlocks carry the resolved local node, so re-serving advertises real topology. Remapped slots are counted in the fetch summary log and a rdma_fetch_numa_remapped counter. Also fixes the duplicate `warn` import that broke non-rdma builds of pegaflow-core. The allocate guard only covers provably unsatisfiable requests; fragmentation or inflight-pinned memory can still trigger deep reclaim. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feifei-111
approved these changes
Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
In a cluster mixing machines with different NUMA node counts (e.g. 2-node and 6-node hosts), the RDMA fetch path broke down in two compounding ways when a small-topology machine fetched blocks from a large-topology peer:
Eviction storm. A remote slot advertising a NUMA node with no local pool made
StorageEngine::allocateenter its reclaim loop withlargest_free_allocation_for_node() == 0forever — the loop's only remaining exit was an empty read cache. Every failed fetch attempt evicted the machine's entire resident cache and flooded the MetaServer removal queue, which then cascaded into cluster-wide cache churn.Guaranteed fetch failure. The advertised
numa_nodewas used verbatim as a key into local pinned pools, sofailed to allocate slab for NUMAxaborted the whole batch. On heterogeneous clusters, cross-topology fetch could never succeed.Observed in production; the slab-allocation failures and full-cache eviction storms were confirmed live before p2p was disabled as a stopgap.
Fix
Guard (storm killer):
StorageEngine::allocatenow rejects requests that are provably unsatisfiable — larger than what the target pool could hold when completely empty (missing NUMA pool → 0, or size above per-shard capacity) — before any reclaim, with an error log and a newpegaflow_pool_alloc_unsatisfiablecounter. The capacity bound is a construction-time constant read (allocatable_bytes), no allocator locks on the hot path.Root cause (topology resolution): the fetch path now treats the advertised NUMA node as a placement hint, not a pool key.
resolve_block_numasrewrites each slot onto local topology right afterQueryBlocksForTransfer:UNKNOWN(no hint) → round-robin by slot position, so a batch spreads across pools instead of collapsing onto one node;UNKNOWN.Rebuilt
SealedBlocks carry the resolved local node, so re-serving fetched blocks advertises real topology to peers (and resolution is idempotent on the next hop). Remap counts appear in the existingRDMA fetch summarylog line (numa_remapped=N) and a newpegaflow_rdma_fetch_numa_remappedcounter — expected steady state on heterogeneous clusters, so no per-fetch warn.Also fixes the duplicate
warnimport instorage/mod.rsthat brokepegaflow-corebuilds without therdmafeature.Scope note
The guard closes the deterministic full-cache wipe (allocation that can never succeed). Reclaim can still evict deeply when a request is satisfiable in principle but the pool is fragmented or pinned by inflight/still-referenced blocks — out of scope here.
Tests (TDD)
All new tests were written first and observed failing on the old code:
alloc_for_missing_numa_pool_fails_fast_without_evicting— the incident scenario: NUMA pool only for node 0, allocation for node 3 must fail without touching resident cache (previously wiped it).alloc_larger_than_node_capacity_fails_fast_without_evicting— oversized request, same contract.resolve_block_numas_maps_foreign_nodes_onto_local_topology— keep/remap/idempotency.resolve_block_numas_spreads_unknown_slots_across_local_nodes— UNKNOWN batches must not pile onto one node.resolve_block_numas_without_local_pools_collapses_to_unknown— global-allocator receiver.Verified:
cargo test -p pegaflow-corepasses withcuda-13,rdma(128) andcuda-13without rdma (123, previously did not compile); workspace clippy clean on both combos.🤖 Generated with Claude Code