This is an automated email from the ASF dual-hosted git repository.
hubcio pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git
The following commit(s) were added to refs/heads/master by this push:
new 92aee83c4 fix(server): skip NUMA memory binding on a single-node host
(#4243)
92aee83c4 is described below
commit 92aee83c40f91fcc955e50ad5aaf8f3f6e07bfc9
Author: Justin Mclean <[email protected]>
AuthorDate: Mon Sep 21 16:27:52 2026 +1000
fix(server): skip NUMA memory binding on a single-node host (#4243)
---
core/server/src/shard_allocator.rs | 44 +++++++++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)
diff --git a/core/server/src/shard_allocator.rs
b/core/server/src/shard_allocator.rs
index 606e64881..667b0fb36 100644
--- a/core/server/src/shard_allocator.rs
+++ b/core/server/src/shard_allocator.rs
@@ -100,7 +100,11 @@ impl NumaTopology {
pub fn detect() -> Result<Self, ShardingError> {
let topology =
Topology::new().map_err(|e| ShardingError::TopologyDetection {
msg: e.to_string() })?;
+ Self::from_topology(topology)
+ }
+ /// Split from [`Self::detect`] so tests can pass a synthetic topology.
+ fn from_topology(topology: Topology) -> Result<Self, ShardingError> {
let numa_nodes: Vec<_> =
topology.objects_with_type(NUMANode).collect();
let node_count = numa_nodes.len();
@@ -481,6 +485,9 @@ impl ShardAllocator {
numa.cores_per_node
};
+ // Binding to the only node gains nothing, and some VMs reject it.
+ let pin_memory = topology.node_count > 1;
+
let mut shard_infos = Vec::new();
let node_cpus: Vec<Vec<usize>> = nodes
@@ -511,7 +518,7 @@ impl ShardAllocator {
for cpu_id in cores_to_use {
shard_infos.push(ShardInfo {
cpu_set: HashSet::from([cpu_id]),
- numa_node: Some(node_id),
+ numa_node: pin_memory.then_some(node_id),
});
}
}
@@ -600,6 +607,41 @@ mod tests {
}
}
+ fn synthetic_topology(description: &str) -> NumaTopology {
+ let topology = hwlocality::topology::builder::TopologyBuilder::new()
+ .from_synthetic(description)
+ .expect("valid synthetic description")
+ .build()
+ .expect("synthetic topology builds");
+ NumaTopology::from_topology(topology).expect("synthetic topology has
NUMA nodes")
+ }
+
+ #[test]
+ fn a_single_numa_node_is_not_bound() {
+ let topology = synthetic_topology("node:1 core:4 pu:1");
+ let shards =
+ ShardAllocator::compute_numa_assignments(&topology,
&NumaConfig::default()).unwrap();
+
+ assert_eq!(shards.len(), 4);
+ assert!(
+ shards.iter().all(|shard| shard.numa_node.is_none()),
+ "a single-node topology must not pin shard memory"
+ );
+ }
+
+ #[test]
+ fn several_numa_nodes_are_still_bound() {
+ let topology = synthetic_topology("node:2 core:2 pu:1");
+ let shards =
+ ShardAllocator::compute_numa_assignments(&topology,
&NumaConfig::default()).unwrap();
+
+ assert_eq!(shards.len(), 4);
+ assert!(
+ shards.iter().all(|shard| shard.numa_node.is_some()),
+ "a multi-node topology must keep pinning shard memory"
+ );
+ }
+
#[test]
fn bind_cpu_with_empty_set_is_a_no_op() {
let shard = ShardInfo {