use std::collections::HashMap;
use std::sync::Arc;
#[cfg(any(test, feature = "gpu-tests"))]
use std::sync::Barrier;
use onnx_runtime_cuda_memory::release::MappedBlock;
use onnx_runtime_cuda_memory::virtual_memory::{PhysicalHandlePool, PhysicalLocation};
use onnx_runtime_cuda_memory::vmm_allocator::CudaVmmAllocator;
use onnx_runtime_ep_api::{ExpertWeightGroup, ResidencyDecision, ResidencyPlan};
use onnx_runtime_ir::ValueId;
use onnx_runtime_loader::WeightRegionCatalog;
#[cfg(any(test, feature = "gpu-tests"))]
use crate::granule_transition::transition_granule_range_with_phase8_faults;
use crate::granule_transition::{TransitionOutcome, transition_granule_range, verify_safe_point};
use crate::runtime::CudaRuntime;
pub const COARSE_RESIDENCY_ENABLE_ENV: &str = "ONNX_GENAI_WEIGHT_OFFLOAD_COARSE_RESIDENCY_ENABLE";
#[cfg(any(test, feature = "gpu-tests"))]
pub struct RollbackSafePointInterlock {
reached: Barrier,
resume: Barrier,
}
#[cfg(any(test, feature = "gpu-tests"))]
impl RollbackSafePointInterlock {
pub fn new() -> Arc<Self> {
Arc::new(Self {
reached: Barrier::new(2),
resume: Barrier::new(2),
})
}
pub fn wait_until_forward_failure(&self) {
self.reached.wait();
}
pub fn resume_rollback(&self) {
self.resume.wait();
}
fn block_before_rollback(&self) {
self.reached.wait();
self.resume.wait();
}
}
#[deprecated(note = "use COARSE_RESIDENCY_ENABLE_ENV")]
pub const COARSE_RESIDENCY_PROFILE_ENV: &str = COARSE_RESIDENCY_ENABLE_ENV;
pub fn coarse_residency_profile_enabled() -> bool {
matches!(
std::env::var(COARSE_RESIDENCY_ENABLE_ENV)
.ok()
.as_deref()
.map(str::trim)
.map(str::to_ascii_lowercase)
.as_deref(),
Some("1") | Some("true") | Some("on")
)
}
#[derive(Debug, Clone)]
struct CommittedRange {
offset: usize,
len: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HostResidentRange {
pub value: ValueId,
pub offset: usize,
pub len: usize,
}
#[derive(Debug, Default, Clone)]
struct ValueProgress {
committed: Vec<CommittedRange>,
}
#[derive(Debug, Clone)]
pub struct RollbackFailure {
pub value: ValueId,
pub range: (usize, usize),
pub detail: String,
pub committed_count: Option<usize>,
pub poisoned_range: Option<(usize, usize)>,
pub quarantined: Vec<MappedBlock>,
}
pub type FatalProgress = (ValueId, usize, Option<(usize, usize)>);
#[derive(Debug, Default)]
pub struct BoundaryApplicationOutcome {
pub policy_name: &'static str,
pub values_inspected: usize,
pub values_touched: usize,
pub hot_expert_count: usize,
pub cold_expert_count: usize,
pub device_bytes_released: u64,
pub host_bytes_committed: u64,
pub transition_time_ms: f64,
pub failure_count: usize,
pub rollback_count: usize,
pub fallback_reason: Option<String>,
pub per_value_fallbacks: Vec<(ValueId, String)>,
pub committed_values: Vec<ValueId>,
pub quarantined: Vec<(ValueId, Vec<MappedBlock>)>,
pub fatal_progress: Vec<FatalProgress>,
pub rollback_failures: Vec<RollbackFailure>,
pub host_resident_ranges: Vec<HostResidentRange>,
}
fn append_unpoisoned_suffix(
ranges: &mut Vec<HostResidentRange>,
value: ValueId,
range: &CommittedRange,
committed_count: usize,
granularity: usize,
poisoned_range: Option<(usize, usize)>,
) {
let committed_bytes = committed_count.saturating_mul(granularity).min(range.len);
let start = range.offset.saturating_add(committed_bytes);
let end = range.offset.saturating_add(range.len);
if start >= end {
return;
}
let Some((poison_offset, poison_len)) = poisoned_range else {
ranges.push(HostResidentRange {
value,
offset: start,
len: end - start,
});
return;
};
let poison_end = poison_offset.saturating_add(poison_len);
if poison_offset > start {
let prefix_end = poison_offset.min(end);
if prefix_end > start {
ranges.push(HostResidentRange {
value,
offset: start,
len: prefix_end - start,
});
}
}
let suffix_start = poison_end.max(start);
if suffix_start < end {
ranges.push(HostResidentRange {
value,
offset: suffix_start,
len: end - suffix_start,
});
}
}
fn check_same_device(
allocator: &CudaVmmAllocator,
device_pool: &PhysicalHandlePool,
host_pool: &PhysicalHandlePool,
device_ordinal: i32,
) -> Result<(), String> {
let allocator_device = allocator.device_key();
let expected = onnx_runtime_memory_governor::DeviceKey::device(device_ordinal as u32);
if allocator_device != expected {
return Err(format!(
"allocator device_key {allocator_device:?} does not match requested device_ordinal {device_ordinal} (expected {expected:?})"
));
}
if device_pool.device_ordinal_pub() != device_ordinal {
return Err(format!(
"device_pool device_ordinal {} does not match requested device_ordinal {device_ordinal}",
device_pool.device_ordinal_pub()
));
}
if host_pool.device_ordinal_pub() != device_ordinal {
return Err(format!(
"host_pool device_ordinal {} does not match requested device_ordinal {device_ordinal}",
host_pool.device_ordinal_pub()
));
}
Ok(())
}
fn cold_ranges_for(
catalog: &WeightRegionCatalog,
hot: &std::collections::HashSet<usize>,
granularity: usize,
) -> Result<Vec<(usize, usize)>, String> {
let total_experts = catalog.layout().experts;
let mut cold_ranges: Vec<(usize, usize)> = Vec::new();
for expert in 0..total_experts {
if hot.contains(&expert) {
continue;
}
let range = catalog
.relative_range(expert)
.ok_or_else(|| format!("expert {expert} has no relative_range"))?;
let offset = range.start;
let len = range.end.saturating_sub(range.start);
if len == 0 {
continue;
}
if offset % granularity != 0 || len % granularity != 0 {
return Err(format!(
"expert {expert} range {offset}..{} is not granule-aligned (granularity={granularity})",
range.end
));
}
cold_ranges.push((offset, len));
}
cold_ranges.sort_by_key(|&(off, _)| off);
let mut merged: Vec<(usize, usize)> = Vec::with_capacity(cold_ranges.len());
for (off, len) in cold_ranges {
match merged.last_mut() {
Some(last) if last.0 + last.1 == off => last.1 += len,
_ => merged.push((off, len)),
}
}
Ok(merged)
}
#[allow(clippy::too_many_arguments)]
pub fn apply_residency_plan_at_boundary(
runtime: &Arc<CudaRuntime>,
residency: &crate::weight_paging::CudaWeightResidency,
plan: &ResidencyPlan,
catalogs: &HashMap<ValueId, WeightRegionCatalog>,
allocators: &HashMap<ValueId, Arc<CudaVmmAllocator>>,
device_pool: &Arc<PhysicalHandlePool>,
host_pool: &Arc<PhysicalHandlePool>,
device_count: usize,
device_ordinal: i32,
expert_groups: &[ExpertWeightGroup],
) -> BoundaryApplicationOutcome {
apply_residency_plan_at_boundary_inner(
coarse_residency_profile_enabled(),
runtime,
residency,
plan,
catalogs,
allocators,
device_pool,
host_pool,
device_count,
device_ordinal,
expert_groups,
#[cfg(any(test, feature = "gpu-tests"))]
None,
#[cfg(any(test, feature = "gpu-tests"))]
None,
)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn apply_resolved_residency_plan_at_boundary(
runtime: &Arc<CudaRuntime>,
residency: &crate::weight_paging::CudaWeightResidency,
plan: &ResidencyPlan,
catalogs: &HashMap<ValueId, WeightRegionCatalog>,
allocators: &HashMap<ValueId, Arc<CudaVmmAllocator>>,
device_pool: &Arc<PhysicalHandlePool>,
host_pool: &Arc<PhysicalHandlePool>,
device_count: usize,
device_ordinal: i32,
expert_groups: &[ExpertWeightGroup],
) -> BoundaryApplicationOutcome {
apply_residency_plan_at_boundary_inner(
true,
runtime,
residency,
plan,
catalogs,
allocators,
device_pool,
host_pool,
device_count,
device_ordinal,
expert_groups,
#[cfg(any(test, feature = "gpu-tests"))]
None,
#[cfg(any(test, feature = "gpu-tests"))]
None,
)
}
#[cfg(any(test, feature = "gpu-tests"))]
#[allow(clippy::too_many_arguments)]
pub fn apply_residency_plan_at_boundary_with_phase8_faults(
runtime: &Arc<CudaRuntime>,
residency: &crate::weight_paging::CudaWeightResidency,
plan: &ResidencyPlan,
catalogs: &HashMap<ValueId, WeightRegionCatalog>,
allocators: &HashMap<ValueId, Arc<CudaVmmAllocator>>,
device_pool: &Arc<PhysicalHandlePool>,
host_pool: &Arc<PhysicalHandlePool>,
device_count: usize,
device_ordinal: i32,
expert_groups: &[ExpertWeightGroup],
phase8_faults: HashMap<ValueId, Arc<onnx_runtime_cuda_memory::release::DriverFaultPlan>>,
) -> BoundaryApplicationOutcome {
apply_residency_plan_at_boundary_inner(
coarse_residency_profile_enabled(),
runtime,
residency,
plan,
catalogs,
allocators,
device_pool,
host_pool,
device_count,
device_ordinal,
expert_groups,
Some(phase8_faults),
None,
)
}
#[cfg(any(test, feature = "gpu-tests"))]
#[allow(clippy::too_many_arguments)]
pub fn apply_residency_plan_at_boundary_with_rollback_interlock(
runtime: &Arc<CudaRuntime>,
residency: &crate::weight_paging::CudaWeightResidency,
plan: &ResidencyPlan,
catalogs: &HashMap<ValueId, WeightRegionCatalog>,
allocators: &HashMap<ValueId, Arc<CudaVmmAllocator>>,
device_pool: &Arc<PhysicalHandlePool>,
host_pool: &Arc<PhysicalHandlePool>,
device_count: usize,
device_ordinal: i32,
expert_groups: &[ExpertWeightGroup],
phase8_faults: HashMap<ValueId, Arc<onnx_runtime_cuda_memory::release::DriverFaultPlan>>,
rollback_interlock: Arc<RollbackSafePointInterlock>,
) -> BoundaryApplicationOutcome {
apply_residency_plan_at_boundary_inner(
true,
runtime,
residency,
plan,
catalogs,
allocators,
device_pool,
host_pool,
device_count,
device_ordinal,
expert_groups,
Some(phase8_faults),
Some(rollback_interlock),
)
}
#[allow(clippy::too_many_arguments)]
fn apply_residency_plan_at_boundary_inner(
route_residency_enabled: bool,
runtime: &Arc<CudaRuntime>,
residency: &crate::weight_paging::CudaWeightResidency,
plan: &ResidencyPlan,
catalogs: &HashMap<ValueId, WeightRegionCatalog>,
allocators: &HashMap<ValueId, Arc<CudaVmmAllocator>>,
device_pool: &Arc<PhysicalHandlePool>,
host_pool: &Arc<PhysicalHandlePool>,
device_count: usize,
device_ordinal: i32,
expert_groups: &[ExpertWeightGroup],
#[cfg(any(test, feature = "gpu-tests"))] phase8_faults: Option<
HashMap<ValueId, Arc<onnx_runtime_cuda_memory::release::DriverFaultPlan>>,
>,
#[cfg(any(test, feature = "gpu-tests"))] rollback_interlock: Option<
Arc<RollbackSafePointInterlock>,
>,
) -> BoundaryApplicationOutcome {
let mut outcome = BoundaryApplicationOutcome {
policy_name: plan.policy_name(),
..Default::default()
};
if !route_residency_enabled {
outcome.fallback_reason = Some("feature gate disabled".to_string());
return outcome;
}
if let Err(err) = onnx_runtime_cuda_memory::capability::host_numa_capability(device_ordinal) {
outcome.fallback_reason = Some(format!("host-numa capability unavailable: {err}"));
return outcome;
}
let sp = residency.resize_safe_point(device_count);
let verified = match verify_safe_point(sp) {
Ok(v) => v,
Err(reason) => {
outcome.fallback_reason = Some(format!("resize safe-point not clear: {reason}"));
return outcome;
}
};
let granularity = device_pool.granularity().max(host_pool.granularity());
let mut value_to_group: HashMap<ValueId, usize> = HashMap::new();
let mut group_failed: std::collections::HashSet<usize> = std::collections::HashSet::new();
let mut group_fail_reason: HashMap<usize, String> = HashMap::new();
for (idx, group) in expert_groups.iter().enumerate() {
let present: Vec<ValueId> = group
.members
.iter()
.copied()
.filter(|v| catalogs.contains_key(v))
.collect();
if present.len() < 2 {
continue;
}
for &value in &present {
value_to_group.insert(value, idx);
}
let mut has_per_expert = false;
let mut has_non_per_expert = false;
for &value in &present {
match plan.decision(value) {
Some(ResidencyDecision::PerExpertCandidate { .. }) => has_per_expert = true,
_ => has_non_per_expert = true,
}
}
if has_per_expert && has_non_per_expert {
group_failed.insert(idx);
group_fail_reason.entry(idx).or_insert_with(|| {
"expert-group members disagree on decision shape: at least one present member \
is PerExpertCandidate while another present member is WholeBankResident or \
absent from this plan"
.to_string()
});
}
}
struct Eligible {
value: ValueId,
hot: std::collections::HashSet<usize>,
total_experts: usize,
hot_count: usize,
cold_count: usize,
merged_ranges: Vec<(usize, usize)>,
}
let mut eligible: Vec<Eligible> = Vec::new();
let mut per_value_precheck: Vec<(ValueId, Result<Eligible, String>)> = Vec::new();
for value in plan.ordered_values() {
outcome.values_inspected += 1;
let decision = match plan.decision(value) {
Some(d) => d,
None => continue,
};
let experts = match decision {
ResidencyDecision::PerExpertCandidate { experts } => experts,
ResidencyDecision::WholeBankResident { .. } => continue,
};
let precheck = (|| -> Result<Eligible, String> {
let catalog = catalogs
.get(&value)
.ok_or_else(|| "no catalog available".to_string())?;
if !catalog.is_pageable() {
return Err("catalog not pageable".to_string());
}
let allocator = allocators
.get(&value)
.ok_or_else(|| "no VMM allocator for value".to_string())?;
check_same_device(allocator, device_pool, host_pool, device_ordinal)?;
let total_experts = catalog.layout().experts;
let hot: std::collections::HashSet<usize> = experts.iter().copied().collect();
let merged = cold_ranges_for(catalog, &hot, granularity)?;
let hot_count = hot.len().min(total_experts);
let cold_count = total_experts.saturating_sub(hot_count);
Ok(Eligible {
value,
hot,
total_experts,
hot_count,
cold_count,
merged_ranges: merged,
})
})();
if let Err(reason) = &precheck
&& let Some(&group_idx) = value_to_group.get(&value)
{
group_failed.insert(group_idx);
group_fail_reason
.entry(group_idx)
.or_insert_with(|| format!("group member {value:?} failed: {reason}"));
}
per_value_precheck.push((value, precheck));
}
let mut group_hot: HashMap<usize, (std::collections::HashSet<usize>, usize)> = HashMap::new();
for (value, precheck) in &per_value_precheck {
let Some(&group_idx) = value_to_group.get(value) else {
continue;
};
if group_failed.contains(&group_idx) {
continue;
}
let Ok(candidate) = precheck else { continue };
match group_hot.get(&group_idx) {
None => {
group_hot.insert(group_idx, (candidate.hot.clone(), candidate.total_experts));
}
Some((canonical_hot, canonical_total_experts)) => {
if candidate.total_experts != *canonical_total_experts {
group_failed.insert(group_idx);
group_fail_reason.entry(group_idx).or_insert_with(|| {
format!(
"expert-group members disagree on expert_count domain ({canonical_total_experts} vs {}, value {value:?})",
candidate.total_experts
)
});
} else if candidate.hot != *canonical_hot {
group_failed.insert(group_idx);
group_fail_reason.entry(group_idx).or_insert_with(|| {
format!(
"expert-group members disagree on hot-expert partition (value {value:?})"
)
});
}
}
}
}
for (value, precheck) in per_value_precheck {
if let Some(&group_idx) = value_to_group.get(&value)
&& group_failed.contains(&group_idx)
{
let reason = group_fail_reason
.get(&group_idx)
.cloned()
.unwrap_or_else(|| "expert-group member failed".to_string());
outcome
.per_value_fallbacks
.push((value, format!("expert-group fallback: {reason}")));
continue;
}
match precheck {
Ok(mut e) => {
if let Some(&group_idx) = value_to_group.get(&value)
&& let Some((canonical_hot, _)) = group_hot.get(&group_idx)
{
let catalog = catalogs.get(&value).expect("validated present above");
match cold_ranges_for(catalog, canonical_hot, granularity) {
Ok(merged) => e.merged_ranges = merged,
Err(reason) => {
outcome.per_value_fallbacks.push((value, reason));
continue;
}
}
}
eligible.push(e);
}
Err(reason) => outcome.per_value_fallbacks.push((value, reason)),
}
}
let mut progress: HashMap<ValueId, ValueProgress> = HashMap::new();
let mut rollback_required = false;
'outer: for e in &eligible {
let value = e.value;
outcome.hot_expert_count += e.hot_count;
outcome.cold_expert_count += e.cold_count;
let entry = progress.entry(value).or_default();
for (offset, len) in &e.merged_ranges {
let allocator = allocators.get(&value).expect("validated present above");
let node = onnx_runtime_cuda_memory::capability::host_numa_capability(device_ordinal)
.map(|c| c.host_numa_id)
.unwrap_or(0);
let start = std::time::Instant::now();
#[cfg(any(test, feature = "gpu-tests"))]
let value_fault = phase8_faults.as_ref().and_then(|m| m.get(&value).cloned());
let result = allocator.with_reservation_mut(|reservation, backing| {
#[cfg(any(test, feature = "gpu-tests"))]
if let Some(fault_plan) = value_fault.clone() {
return transition_granule_range_with_phase8_faults(
runtime,
reservation,
backing,
*offset,
*len,
PhysicalLocation::HostNuma { node },
device_pool,
host_pool,
&verified,
|| residency.resize_safe_point(device_count),
fault_plan,
);
}
transition_granule_range(
runtime,
reservation,
backing,
*offset,
*len,
PhysicalLocation::HostNuma { node },
device_pool,
host_pool,
&verified,
|| residency.resize_safe_point(device_count),
)
});
outcome.transition_time_ms += start.elapsed().as_secs_f64() * 1000.0;
match result {
TransitionOutcome::Committed {
granules: _,
new_owned_bytes,
old_released_bytes,
} => {
outcome.host_bytes_committed += new_owned_bytes;
outcome.device_bytes_released += old_released_bytes;
entry.committed.push(CommittedRange {
offset: *offset,
len: *len,
});
}
TransitionOutcome::Rejected { reason } => {
outcome.failure_count += 1;
outcome
.per_value_fallbacks
.push((value, format!("transition rejected: {reason}")));
rollback_required = true;
break 'outer;
}
TransitionOutcome::RolledBack { fault } => {
outcome.failure_count += 1;
outcome
.per_value_fallbacks
.push((value, format!("transition rolled back: {fault:?}")));
rollback_required = true;
break 'outer;
}
TransitionOutcome::Fatal {
transition_fault,
quarantined,
committed_count,
poisoned_range,
..
} => {
let committed_len = committed_count.saturating_mul(granularity).min(*len);
if committed_len > 0 {
entry.committed.push(CommittedRange {
offset: *offset,
len: committed_len,
});
}
outcome.quarantined.push((value, quarantined));
outcome
.fatal_progress
.push((value, committed_count, poisoned_range));
outcome
.per_value_fallbacks
.push((value, format!("fatal transition: {transition_fault:?}")));
rollback_required = true;
break 'outer;
}
}
}
}
if rollback_required {
#[cfg(any(test, feature = "gpu-tests"))]
if let Some(interlock) = rollback_interlock {
interlock.block_before_rollback();
}
let recheck_sp = residency.resize_safe_point(device_count);
let rollback_sp = match verify_safe_point(recheck_sp) {
Ok(v) => v,
Err(reason) => {
outcome.fallback_reason = Some(format!(
"transition failure + safe-point lost during rollback: {reason}"
));
for (&value, prog) in &progress {
outcome
.host_resident_ranges
.extend(prog.committed.iter().map(|range| HostResidentRange {
value,
offset: range.offset,
len: range.len,
}));
}
outcome
.host_resident_ranges
.sort_by_key(|range| (range.value.0, range.offset, range.len));
outcome.committed_values = outcome
.host_resident_ranges
.iter()
.map(|range| range.value)
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
outcome
.committed_values
.sort_unstable_by_key(|value| value.0);
outcome.values_touched = outcome.committed_values.len();
return outcome;
}
};
for (value, prog) in &progress {
if prog.committed.is_empty() {
continue;
}
let allocator = match allocators.get(value) {
Some(a) => a,
None => continue,
};
let mut all_ok = true;
for range in &prog.committed {
#[cfg(any(test, feature = "gpu-tests"))]
let value_fault = phase8_faults.as_ref().and_then(|m| m.get(value).cloned());
let result = allocator.with_reservation_mut(|reservation, backing| {
#[cfg(any(test, feature = "gpu-tests"))]
if let Some(fault_plan) = value_fault.clone() {
return transition_granule_range_with_phase8_faults(
runtime,
reservation,
backing,
range.offset,
range.len,
PhysicalLocation::Device {
ordinal: device_ordinal,
},
host_pool,
device_pool,
&rollback_sp,
|| residency.resize_safe_point(device_count),
fault_plan,
);
}
transition_granule_range(
runtime,
reservation,
backing,
range.offset,
range.len,
PhysicalLocation::Device {
ordinal: device_ordinal,
},
host_pool,
device_pool,
&rollback_sp,
|| residency.resize_safe_point(device_count),
)
});
match result {
TransitionOutcome::Committed { .. } => {}
TransitionOutcome::Rejected { reason } => {
all_ok = false;
outcome.host_resident_ranges.push(HostResidentRange {
value: *value,
offset: range.offset,
len: range.len,
});
outcome.rollback_failures.push(RollbackFailure {
value: *value,
range: (range.offset, range.len),
detail: format!("reverse transition rejected: {reason}"),
committed_count: None,
poisoned_range: None,
quarantined: Vec::new(),
});
}
TransitionOutcome::RolledBack { fault } => {
all_ok = false;
outcome.host_resident_ranges.push(HostResidentRange {
value: *value,
offset: range.offset,
len: range.len,
});
outcome.rollback_failures.push(RollbackFailure {
value: *value,
range: (range.offset, range.len),
detail: format!("reverse transition rolled back: {fault:?}"),
committed_count: None,
poisoned_range: None,
quarantined: Vec::new(),
});
}
TransitionOutcome::Fatal {
transition_fault,
quarantined,
committed_count,
poisoned_range,
..
} => {
all_ok = false;
append_unpoisoned_suffix(
&mut outcome.host_resident_ranges,
*value,
range,
committed_count,
granularity,
poisoned_range,
);
outcome.quarantined.push((*value, quarantined.clone()));
outcome.rollback_failures.push(RollbackFailure {
value: *value,
range: (range.offset, range.len),
detail: format!("reverse transition fatal: {transition_fault:?}"),
committed_count: Some(committed_count),
poisoned_range,
quarantined,
});
}
}
}
if all_ok {
outcome.rollback_count += 1;
}
}
if outcome.fallback_reason.is_none() {
outcome.fallback_reason =
Some("transition failure; atomic rollback attempted".to_string());
}
} else {
for (&value, prog) in &progress {
outcome
.host_resident_ranges
.extend(prog.committed.iter().map(|range| HostResidentRange {
value,
offset: range.offset,
len: range.len,
}));
}
}
outcome
.host_resident_ranges
.sort_by_key(|range| (range.value.0, range.offset, range.len));
outcome.committed_values = outcome
.host_resident_ranges
.iter()
.map(|range| range.value)
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
outcome
.committed_values
.sort_unstable_by_key(|value| value.0);
outcome.values_touched = outcome.committed_values.len();
outcome
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn feature_gate_default_off() {
let expected = matches!(
std::env::var(COARSE_RESIDENCY_ENABLE_ENV)
.ok()
.as_deref()
.map(str::trim)
.map(str::to_ascii_lowercase)
.as_deref(),
Some("1") | Some("true") | Some("on")
);
assert_eq!(coarse_residency_profile_enabled(), expected);
}
#[test]
fn boundary_outcome_default_is_noop_shape() {
let outcome = BoundaryApplicationOutcome::default();
assert_eq!(outcome.values_touched, 0);
assert_eq!(outcome.hot_expert_count, 0);
assert_eq!(outcome.cold_expert_count, 0);
assert_eq!(outcome.host_bytes_committed, 0);
assert!(outcome.per_value_fallbacks.is_empty());
assert!(outcome.committed_values.is_empty());
assert!(outcome.quarantined.is_empty());
assert!(outcome.fatal_progress.is_empty());
assert!(outcome.rollback_failures.is_empty());
}
#[test]
#[allow(deprecated)]
fn deprecated_env_alias_matches_new_name() {
assert_eq!(COARSE_RESIDENCY_PROFILE_ENV, COARSE_RESIDENCY_ENABLE_ENV);
}
}