This commit is contained in:
Name committed 2026-04-28 08:47:33 -06:00
1 parent a7ef49a3cb
commit 6e82dad7cd
2 files changed
+95 -77

No files matched your search

+4 -77
View File
@@ -664,60 +664,6 @@ def planner_circuit_identity_key(circuit):
return circuit_id
def load_minor_state_from_queuing_structure(path=None):
if path is None:
path = get_queuing_structure_path()
data = _load_json_dict(path)
network = data.get("Network")
if not isinstance(network, dict):
return {"sites": {}, "circuits": {}}
sites = {}
circuits = {}
def walk(node_map, trail=()):
for node_name, node in sorted(node_map.items()):
if not isinstance(node, dict):
continue
node_path = trail + (node_name,)
site_key = "/".join(node_path)
parent_path = "/".join(trail)
queue = _parse_int_token(node.get("cpuNum"))
class_minor = _parse_int_token(node.get("classMinor"))
class_major = _parse_int_token(node.get("classMajor"))
up_class_major = _parse_int_token(node.get("up_classMajor"))
if queue is not None and class_minor is not None:
sites[site_key] = {
"class_minor": class_minor,
"queue": queue + 1,
"parent_path": parent_path,
"class_major": class_major,
"up_class_major": up_class_major,
}
if isinstance(node.get("circuits"), list):
for circuit in node.get("circuits", []):
if not isinstance(circuit, dict):
continue
circuit_id = circuit.get("circuitID")
circuit_minor = _parse_int_token(circuit.get("classMinor"))
if queue is None or circuit_id is None or circuit_minor is None:
continue
circuits[str(circuit_id)] = {
"class_minor": circuit_minor,
"queue": queue + 1,
"parent_node": circuit.get("ParentNode", node_name),
"class_major": _parse_int_token(circuit.get("classMajor")),
"up_class_major": _parse_int_token(circuit.get("up_classMajor")),
}
children = node.get("children")
if isinstance(children, dict):
walk(children, node_path)
walk(network)
return {"sites": sites, "circuits": circuits}
def calculateR2q(maxRateInMbps):
# So we've learned that r2q defaults to 10, and is used to calculate quantum. Quantum is rateInBytes/r2q by
# default. This default gives errors at high rates, and tc clamps the quantum to 200000. Setting a high quantum
@@ -2029,9 +1975,8 @@ def refreshShapers():
f"Failed to save planner state at {state_path}: {e}", stacklevel=2
)
# Seed persisted site/circuit minor assignments. When planner state is absent,
# fall back to the previous queuing structure so the first run after an upgrade
# can preserve existing class IDs.
# Load persisted top-level planner state. Full reloads rebuild TC, so
# site/circuit class minors are runtime artifacts and are not reused below.
try:
state # noqa: B018
except NameError:
@@ -2039,24 +1984,6 @@ def refreshShapers():
state_path = get_planner_state_path()
if not isinstance(state, dict) or len(state.keys()) == 0:
state = load_planner_state(state_path, None)
try:
circuit_state_from_disk = state.get("circuits", {}) if isinstance(state, dict) else {}
except Exception:
circuit_state_from_disk = {}
try:
site_state_from_disk = state.get("sites", {}) if isinstance(state, dict) else {}
except Exception:
site_state_from_disk = {}
if not isinstance(circuit_state_from_disk, dict):
circuit_state_from_disk = {}
if not isinstance(site_state_from_disk, dict):
site_state_from_disk = {}
if not circuit_state_from_disk or not site_state_from_disk:
fallback_minor_state = load_minor_state_from_queuing_structure()
if not site_state_from_disk:
site_state_from_disk = fallback_minor_state.get("sites", {}) or {}
if not circuit_state_from_disk:
circuit_state_from_disk = fallback_minor_state.get("circuits", {}) or {}
circuit_state_updated = {}
site_state_updated = {}
planner_site_inputs = []
@@ -2126,8 +2053,8 @@ def refreshShapers():
identity_plan = plan_class_identities(
planner_site_inputs,
planner_circuit_groups,
site_state=site_state_from_disk,
circuit_state=circuit_state_from_disk,
site_state={},
circuit_state={},
stick_offset=stickOffset,
circuit_padding=CIRCUIT_PADDING,
)
+91
View File
@@ -834,6 +834,97 @@ mod tests {
assert_ne!(result.sites[0].class_minor, 0x29);
}
#[test]
fn class_identity_planner_compacts_when_full_reload_ignores_saved_minors() {
const FRAGMENTED_SITE_MINOR: u16 = 0xff00;
const FRAGMENTED_CHILD_MINOR: u16 = 0xff02;
const FRAGMENTED_CIRCUIT_MINOR: u16 = 0xff10;
const CIRCUIT_PADDING: u32 = 8;
let site_inputs = vec![
SiteIdentityInput {
site_key: "CpueQueue0/site-a".to_string(),
parent_path: "CpueQueue0".to_string(),
queue: 1,
has_children: true,
},
SiteIdentityInput {
site_key: "CpueQueue0/site-a/child".to_string(),
parent_path: "CpueQueue0/site-a".to_string(),
queue: 1,
has_children: false,
},
];
let circuit_groups = vec![CircuitIdentityGroupInput {
parent_node: "child".to_string(),
queue: 1,
circuit_ids: vec!["c1".to_string(), "c2".to_string()],
}];
let fragmented_sites = BTreeMap::from([
(
"CpueQueue0/site-a".to_string(),
PlannerSiteIdentityState {
class_minor: FRAGMENTED_SITE_MINOR,
queue: 1,
parent_path: "CpueQueue0".to_string(),
class_major: 1,
up_class_major: 0x41,
},
),
(
"CpueQueue0/site-a/child".to_string(),
PlannerSiteIdentityState {
class_minor: FRAGMENTED_CHILD_MINOR,
queue: 1,
parent_path: "CpueQueue0/site-a".to_string(),
class_major: 1,
up_class_major: 0x41,
},
),
]);
let fragmented_circuits = BTreeMap::from([(
"c1".to_string(),
PlannerCircuitIdentityState {
class_minor: FRAGMENTED_CIRCUIT_MINOR,
queue: 1,
parent_node: "child".to_string(),
class_major: 1,
up_class_major: 0x41,
},
)]);
let sticky_result = plan_class_identities(
&site_inputs,
&circuit_groups,
&fragmented_sites,
&fragmented_circuits,
0x40,
CIRCUIT_PADDING,
);
assert!(sticky_result.last_used_minor_by_queue[&1] > u32::from(FRAGMENTED_CIRCUIT_MINOR));
let full_reload_result = plan_class_identities(
&site_inputs,
&circuit_groups,
&BTreeMap::new(),
&BTreeMap::new(),
0x40,
CIRCUIT_PADDING,
);
let expected_last_minor = u32::from(full_reload_result.circuits[1].class_minor)
.saturating_add(1)
.saturating_add(CIRCUIT_PADDING);
assert_eq!(full_reload_result.sites[0].class_minor, 3);
assert_eq!(full_reload_result.sites[1].class_minor, 5);
assert_eq!(full_reload_result.circuits[0].class_minor, 6);
assert_eq!(full_reload_result.circuits[1].class_minor, 7);
assert_eq!(
full_reload_result.last_used_minor_by_queue[&1],
expected_last_minor
);
}
#[test]
fn class_identity_planner_runtime_constraints_prefer_runtime_band() {
let site_inputs = vec![SiteIdentityInput {