mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2026-10-25 22:30:05 -05:00
Fix lazy queue memory preflight warnings
This commit is contained in:
1 parent
b2a801aeb0
commit
ddf78a0f16
8 files changed
+81
-11
No files matched your search
@@ -2860,6 +2860,10 @@ def refreshShapers():
|
||||
},
|
||||
"TC_QDISC_CAPACITY",
|
||||
)
|
||||
elif qdiscBudgetEstimate.get("memory_warning_only"):
|
||||
preflightSummary = qdiscBudgetEstimate.get("summary") or ""
|
||||
logging.warning(preflightSummary)
|
||||
print("WARNING: " + preflightSummary)
|
||||
|
||||
# Save queuingStructure
|
||||
queuingStructure = {}
|
||||
|
||||
@@ -1693,6 +1693,8 @@ pub struct BakeryPreflightSnapshot {
|
||||
pub memory_guard_min_available_bytes: u64,
|
||||
/// Whether the memory preflight passed.
|
||||
pub memory_ok: bool,
|
||||
/// Whether the memory-only failure is an allowed lazy-queue warning.
|
||||
pub memory_warning_only: bool,
|
||||
/// Per-interface planned qdisc counts.
|
||||
pub interfaces: Vec<BakeryCapacityInterfaceSnapshot>,
|
||||
}
|
||||
@@ -2459,18 +2461,27 @@ fn refresh_live_capacity_snapshot(config: &Config, force: bool) {
|
||||
/// This function is not pure: it updates retained in-memory Bakery telemetry state.
|
||||
pub fn record_qdisc_preflight_snapshot(snapshot: BakeryPreflightSnapshot) {
|
||||
let ok = snapshot.ok;
|
||||
let memory_warning_only = snapshot.memory_warning_only;
|
||||
let summary = snapshot.message.clone();
|
||||
{
|
||||
let mut state = telemetry_state().write();
|
||||
state.preflight = Some(snapshot);
|
||||
}
|
||||
push_bakery_event(
|
||||
if ok {
|
||||
if memory_warning_only {
|
||||
"preflight_warning"
|
||||
} else if ok {
|
||||
"preflight_ok"
|
||||
} else {
|
||||
"preflight_blocked"
|
||||
},
|
||||
if ok { "info" } else { "warning" },
|
||||
if memory_warning_only {
|
||||
"warning"
|
||||
} else if ok {
|
||||
"info"
|
||||
} else {
|
||||
"warning"
|
||||
},
|
||||
summary,
|
||||
);
|
||||
}
|
||||
@@ -2571,15 +2582,17 @@ pub struct QdiscBudgetEstimate {
|
||||
pub memory_guard_min_available_bytes: u64,
|
||||
/// Whether the memory preflight passed.
|
||||
pub memory_ok: bool,
|
||||
/// Whether lazy queue mode makes a memory-only preflight failure non-blocking.
|
||||
pub memory_warning_only: bool,
|
||||
}
|
||||
|
||||
impl QdiscBudgetEstimate {
|
||||
/// Returns `true` when all planned per-interface counts fit within the safe budget.
|
||||
/// Returns `true` when the qdisc count fits and memory preflight passes or is a lazy-queue warning.
|
||||
pub fn ok(&self) -> bool {
|
||||
self.interfaces
|
||||
.values()
|
||||
.all(|count| *count <= self.safe_budget)
|
||||
&& self.memory_ok
|
||||
&& (self.memory_ok || self.memory_warning_only)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2723,6 +2736,14 @@ pub fn estimate_full_reload_auto_qdisc_budget(
|
||||
.saturating_sub(memory_guard_min_available_bytes)
|
||||
>= estimated_total_memory_bytes
|
||||
});
|
||||
let qdisc_counts_fit = interfaces
|
||||
.values()
|
||||
.all(|count| *count <= SAFE_QDISC_BUDGET_PER_INTERFACE);
|
||||
let memory_warning_only = lazy_queue_memory_preflight_is_warning(
|
||||
config.queues.lazy_queues.as_ref(),
|
||||
qdisc_counts_fit,
|
||||
memory_ok,
|
||||
);
|
||||
|
||||
QdiscBudgetEstimate {
|
||||
interfaces,
|
||||
@@ -2733,9 +2754,23 @@ pub fn estimate_full_reload_auto_qdisc_budget(
|
||||
memory_snapshot,
|
||||
memory_guard_min_available_bytes,
|
||||
memory_ok,
|
||||
memory_warning_only,
|
||||
}
|
||||
}
|
||||
|
||||
fn lazy_queue_memory_preflight_is_warning(
|
||||
lazy_queue_mode: Option<&LazyQueueMode>,
|
||||
qdisc_counts_fit: bool,
|
||||
memory_ok: bool,
|
||||
) -> bool {
|
||||
qdisc_counts_fit
|
||||
&& !memory_ok
|
||||
&& matches!(
|
||||
lazy_queue_mode,
|
||||
Some(LazyQueueMode::Htb | LazyQueueMode::Full)
|
||||
)
|
||||
}
|
||||
|
||||
fn desired_shaping_tree_active(config: &Arc<Config>) -> bool {
|
||||
!config.queues.queue_mode.is_observe()
|
||||
}
|
||||
@@ -14356,6 +14391,25 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memory_warning_only_allows_preflight_to_continue() {
|
||||
let mut estimate = QdiscBudgetEstimate {
|
||||
interfaces: BTreeMap::from([("eth0".to_string(), 1)]),
|
||||
interface_details: BTreeMap::new(),
|
||||
safe_budget: SAFE_QDISC_BUDGET_PER_INTERFACE,
|
||||
hard_limit: HARD_QDISC_HANDLE_LIMIT_PER_INTERFACE,
|
||||
estimated_total_memory_bytes: 1,
|
||||
memory_snapshot: None,
|
||||
memory_guard_min_available_bytes: BAKERY_MEMORY_GUARD_MIN_AVAILABLE_BYTES,
|
||||
memory_ok: false,
|
||||
memory_warning_only: true,
|
||||
};
|
||||
|
||||
assert!(estimate.ok());
|
||||
estimate.memory_warning_only = false;
|
||||
assert!(!estimate.ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_qdisc_memory_estimate_deduplicates_qdisc_identity() {
|
||||
let commands = vec![
|
||||
|
||||
@@ -3646,11 +3646,15 @@ impl Bakery {
|
||||
.interfaces
|
||||
.values()
|
||||
.all(|count| *count <= estimate.safe_budget);
|
||||
let preflight_status = match (qdisc_counts_fit, estimate.memory_ok) {
|
||||
(true, true) => "fits preflight",
|
||||
(false, true) => "exceeds qdisc-count preflight",
|
||||
(true, false) => "passed qdisc-count preflight but failed memory preflight",
|
||||
(false, false) => "exceeds qdisc-count preflight and failed memory preflight",
|
||||
let preflight_status = if estimate.memory_warning_only {
|
||||
"passed qdisc-count preflight; memory preflight is a non-blocking lazy-queue warning"
|
||||
} else {
|
||||
match (qdisc_counts_fit, estimate.memory_ok) {
|
||||
(true, true) => "fits preflight",
|
||||
(false, true) => "exceeds qdisc-count preflight",
|
||||
(true, false) => "passed qdisc-count preflight but failed memory preflight",
|
||||
(false, false) => "exceeds qdisc-count preflight and failed memory preflight",
|
||||
}
|
||||
};
|
||||
let memory_summary = if let Some(snapshot) = estimate.memory_snapshot.as_ref() {
|
||||
let required_available_bytes = estimate
|
||||
@@ -3738,6 +3742,7 @@ impl Bakery {
|
||||
estimate.estimated_total_memory_bytes,
|
||||
)?;
|
||||
result.set_item("memory_ok", estimate.memory_ok)?;
|
||||
result.set_item("memory_warning_only", estimate.memory_warning_only)?;
|
||||
result.set_item(
|
||||
"memory_guard_min_available_bytes",
|
||||
estimate.memory_guard_min_available_bytes,
|
||||
|
||||
@@ -907,6 +907,7 @@ fn handle_bus_requests(requests: &[BusRequest], responses: &mut Vec<BusResponse>
|
||||
memory_available_bytes: *memory_available_bytes,
|
||||
memory_guard_min_available_bytes: *memory_guard_min_available_bytes,
|
||||
memory_ok: *memory_ok,
|
||||
memory_warning_only: *ok && !*memory_ok,
|
||||
interfaces: interfaces
|
||||
.iter()
|
||||
.map(|entry| lqos_bakery::BakeryCapacityInterfaceSnapshot {
|
||||
|
||||
@@ -99,6 +99,9 @@ function classifyEvent(entry) {
|
||||
} else if (event === "preflight_ok") {
|
||||
outcome = "Passed";
|
||||
outcomeClass = "bg-success-subtle text-success border border-success-subtle";
|
||||
} else if (event === "preflight_warning") {
|
||||
outcome = "Memory Warning";
|
||||
outcomeClass = "bg-warning-subtle text-warning border border-warning-subtle";
|
||||
} else if (event === "preflight_blocked") {
|
||||
outcome = "Blocked";
|
||||
outcomeClass = "bg-warning-subtle text-warning border border-warning-subtle";
|
||||
|
||||
@@ -37,6 +37,9 @@ function bakeryMemoryBadge(preflight) {
|
||||
if (preflight.memoryOk) {
|
||||
return mkBadge("Memory OK", "bg-success-subtle text-success border border-success-subtle", preflight.message || "");
|
||||
}
|
||||
if (preflight.ok) {
|
||||
return mkBadge("Memory Warning", "bg-warning-subtle text-warning border border-warning-subtle", preflight.message || "");
|
||||
}
|
||||
return mkBadge("Memory Guard", "bg-danger-subtle text-danger border border-danger-subtle", preflight.message || "");
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user