Skip to content

Commit 0c3db2d

Browse files
committed
fix(capacity): preserve CPU advice when GPU data is unknown
1 parent 86b4fce commit 0c3db2d

3 files changed

Lines changed: 45 additions & 4 deletions

File tree

‎.github/workflows/ci_test.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ jobs:
200200
--test stack_shadow \
201201
--test stack_shard \
202202
--test inode_footprints_snapshot \
203+
--test planning_capacity \
203204
--test stack_shard_destinations \
204205
--test stack_admit \
205206
--test admission_forecast_lifecycle \

‎src/planning_capacity.rs‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,19 @@ fn resource_pair(
149149
}
150150

151151
impl Node {
152-
fn resources(&self) -> Option<(Resources, Resources)> {
152+
fn resources(&self, require_gpu: bool) -> Option<(Resources, Resources, bool)> {
153153
let cpu = resource_pair(self.cpus_total, self.cpus_allocatable, self.cpus_allocated)?;
154154
let mem = resource_pair(
155155
self.memory_mib_total,
156156
self.memory_mib_allocatable,
157157
self.memory_mib_allocated,
158158
)?;
159-
let gpu = resource_pair(self.gpus_total, self.gpus_allocatable, self.gpus_allocated)?;
159+
let gpu = resource_pair(self.gpus_total, self.gpus_allocatable, self.gpus_allocated);
160+
if require_gpu && gpu.is_none() {
161+
return None;
162+
}
163+
let gpu_known = gpu.is_some();
164+
let gpu = gpu.unwrap_or((0, 0));
160165
Some((
161166
Resources {
162167
cpus: cpu.0,
@@ -168,6 +173,7 @@ impl Node {
168173
memory_mib: mem.1,
169174
gpus: gpu.1,
170175
},
176+
gpu_known,
171177
))
172178
}
173179

@@ -278,6 +284,7 @@ pub fn inspect(
278284
let mut free_slots = 0u64;
279285
let mut idle_nodes = 0u64;
280286
let mut free = Resources::default();
287+
let mut gpu_unknown_nodes = 0u64;
281288
let mut excluded: BTreeMap<String, u64> = BTreeMap::new();
282289
let mut states: BTreeMap<String, u64> = BTreeMap::new();
283290
for node in &snapshot.nodes {
@@ -331,7 +338,7 @@ pub fn inspect(
331338
continue;
332339
}
333340
};
334-
let Some((configured, remaining)) = node.resources() else {
341+
let Some((configured, remaining, gpu_known)) = node.resources(request.gpus > 0) else {
335342
*excluded
336343
.entry("resources_unknown_or_inconsistent".into())
337344
.or_default() += 1;
@@ -356,6 +363,9 @@ pub fn inspect(
356363
.or_default() += 1;
357364
continue;
358365
}
366+
if !gpu_known {
367+
gpu_unknown_nodes += 1;
368+
}
359369
free.add(remaining)?;
360370
let slots = remaining.slots(request);
361371
if slots > 0 {
@@ -429,6 +439,8 @@ pub fn inspect(
429439
"request":request,"partition_target_nodes":partition_target_nodes,
430440
"feature_eligible_nodes":eligible,"queue_fitting_nodes":queue_fitting,
431441
"idle_nodes":idle_nodes,"free_fitting_nodes":free_fitting,"free_slots":free_slots,
432-
"known_free_resources":free,"state_counts":states,"excluded":excluded,"pending":pending}),
442+
"known_free_resources":{"cpus":free.cpus,"memory_mib":free.memory_mib,
443+
"gpus":if gpu_unknown_nodes == 0 {Some(free.gpus)} else {None}},
444+
"gpu_capacity_unknown_nodes":gpu_unknown_nodes,"state_counts":states,"excluded":excluded,"pending":pending}),
433445
)
434446
}

‎tests/planning_capacity.rs‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,31 @@ fn malformed_constraints_duplicates_and_inconsistent_resources_are_not_accepted(
172172
value["cpus_allocated"] = json!(17);
173173
assert_eq!(report(&snapshot(vec![value]))["free_slots"], 0);
174174
}
175+
176+
#[test]
177+
fn cpu_requests_do_not_require_gpu_observations() {
178+
let mut n = node("cpu-only", &["IDLE"]);
179+
n["gpus_allocatable"] = Value::Null;
180+
n["gpus_allocated"] = Value::Null;
181+
let mut req = request();
182+
req.gpus = 0;
183+
req.gpu_type = None;
184+
let value = inspect(
185+
&serde_json::to_vec(&snapshot(vec![n])).unwrap(),
186+
&req,
187+
"2026-01-01T12:00:10Z",
188+
60,
189+
)
190+
.unwrap();
191+
assert_eq!(value["free_slots"], 2);
192+
assert_eq!(value["gpu_capacity_unknown_nodes"], 1);
193+
assert!(value["known_free_resources"]["gpus"].is_null());
194+
req.memory_mib = 0;
195+
assert!(inspect(
196+
&serde_json::to_vec(&snapshot(vec![])).unwrap(),
197+
&req,
198+
"2026-01-01T12:00:10Z",
199+
60
200+
)
201+
.is_err());
202+
}

0 commit comments

Comments
 (0)