use std::borrow::Cow;
use crate::plan::{JoinSide, NodeId};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct FailureContext {
pub memory: Option<MemoryFailure>,
pub boundary: Option<ResourceBoundary>,
pub allocation_source: Option<AllocationSource>,
pub graph_cache_scope_peak_bytes: Option<u64>,
pub cleanup_deadline_elapsed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemoryFailure {
pub boundary: Cow<'static, str>,
pub operation: Cow<'static, str>,
pub usage: Option<MemoryUsage>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MemoryUsage {
pub limit_bytes: u64,
pub peak_bytes: u64,
pub peak_aligned_bytes: u64,
pub current_bytes: u64,
pub current_aligned_bytes: u64,
pub failed_allocation_count: u64,
pub largest_failed_allocation_bytes: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResourceBoundary {
pub memory_boundary: &'static str,
pub stage: &'static str,
pub node_id: Option<NodeId>,
pub side: Option<JoinSide>,
pub resume: Option<&'static str>,
}
impl ResourceBoundary {
pub const fn new(memory_boundary: &'static str, node_id: NodeId) -> Self {
Self { memory_boundary, stage: memory_boundary, node_id: Some(node_id), side: None, resume: None }
}
#[must_use]
pub const fn with_stage(mut self, stage: &'static str) -> Self {
self.stage = stage;
self
}
#[must_use]
pub const fn with_side(mut self, side: JoinSide) -> Self {
self.side = Some(side);
self
}
#[must_use]
pub const fn with_resume(mut self, resume: &'static str) -> Self {
self.resume = Some(resume);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AllocationSource {
Unknown,
RmmOutOfMemory,
RmmBadAlloc,
StdBadAlloc,
}
impl AllocationSource {
pub const fn as_str(self) -> &'static str {
match self {
Self::Unknown => "unknown",
Self::RmmOutOfMemory => "rmm_out_of_memory",
Self::RmmBadAlloc => "rmm_bad_alloc",
Self::StdBadAlloc => "std_bad_alloc",
}
}
}
const MEMORY_ENFORCEMENT: &str = "query_domain_member";
const MEMORY_STATUS: &str = "exceeded_or_lower_layer_rejected";
impl FailureContext {
pub(super) fn projection(&self) -> Vec<(&'static str, Cow<'_, str>)> {
let mut facts = Vec::new();
if let Some(memory) = &self.memory {
facts.push(("budget_boundary", Cow::Borrowed(memory.boundary.as_ref())));
facts.push(("budget_operation", Cow::Borrowed(memory.operation.as_ref())));
facts.push(("budget_enforcement", Cow::Borrowed(MEMORY_ENFORCEMENT)));
facts.push(("budget_status", Cow::Borrowed(MEMORY_STATUS)));
if let Some(usage) = memory.usage {
facts.extend(
[
("budget_bytes", usage.limit_bytes),
("budget_peak_bytes", usage.peak_bytes),
("budget_peak_aligned_bytes", usage.peak_aligned_bytes),
("budget_current_bytes", usage.current_bytes),
("budget_current_aligned_bytes", usage.current_aligned_bytes),
("budget_failed_allocation_count", usage.failed_allocation_count),
("budget_largest_failed_allocation_bytes", usage.largest_failed_allocation_bytes),
]
.map(|(key, value)| (key, Cow::Owned(value.to_string()))),
);
}
}
if let Some(boundary) = &self.boundary {
facts.push(("memory_boundary", Cow::Borrowed(boundary.memory_boundary)));
facts.push(("stage", Cow::Borrowed(boundary.stage)));
if let Some(node_id) = boundary.node_id {
facts.push(("node_id", Cow::Owned(node_id.to_string())));
}
if let Some(side) = boundary.side {
facts.push(("side", Cow::Owned(side.to_string())));
}
if let Some(resume) = boundary.resume {
facts.push(("resume_boundary", Cow::Borrowed(resume)));
}
}
if let Some(source) = self.allocation_source {
facts.push(("cugraph_allocation_source", Cow::Borrowed(source.as_str())));
}
facts
}
pub(super) fn projects(&self, key: &str) -> bool {
self.projection().iter().any(|(projected, _)| *projected == key)
}
}