hawdb_core/error/
resource.rs1use std::fmt::{Display, Formatter};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum ResourceBudgetKind {
9 MemoryBytes,
10 SpillBytes,
11 SpillRuns,
12 MetadataEntries,
13 WorkUnits,
14}
15
16impl ResourceBudgetKind {
17 pub const fn as_str(self) -> &'static str {
18 match self {
19 Self::MemoryBytes => "memory_bytes",
20 Self::SpillBytes => "spill_bytes",
21 Self::SpillRuns => "spill_runs",
22 Self::MetadataEntries => "metadata_entries",
23 Self::WorkUnits => "work_units",
24 }
25 }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum ResourceBudgetScope {
31 Account,
32 Query,
33 Operator,
34 SharedPool,
35 FileSystem,
36}
37
38impl ResourceBudgetScope {
39 pub const fn as_str(self) -> &'static str {
40 match self {
41 Self::Account => "account",
42 Self::Query => "query",
43 Self::Operator => "operator",
44 Self::SharedPool => "shared_pool",
45 Self::FileSystem => "file_system",
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct ResourceBudgetError {
63 pub resource: ResourceBudgetKind,
64 pub scope: ResourceBudgetScope,
65 pub owner: String,
66 pub memory_class: Option<&'static str>,
67 pub requested: u64,
68 pub used: u64,
69 pub budget: u64,
70 pub budget_option: &'static str,
71 pub message: String,
73}
74
75impl Display for ResourceBudgetError {
76 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
77 formatter.write_str(&self.message)
78 }
79}
80
81impl std::error::Error for ResourceBudgetError {}
82
83pub(super) fn find_resource_budget_error(
84 error: &(dyn std::error::Error + 'static),
85) -> Option<ResourceBudgetError> {
86 let mut current = Some(error);
87 while let Some(error) = current {
88 if let Some(resource) = error.downcast_ref::<ResourceBudgetError>() {
89 return Some(resource.clone());
90 }
91 if let Some(error) = error.downcast_ref::<std::io::Error>()
93 && let Some(inner) = error.get_ref()
94 && let Some(resource) = find_resource_budget_error(inner)
95 {
96 return Some(resource);
97 }
98 current = error.source();
99 }
100 None
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::HawDBError;
107
108 #[test]
109 fn resource_metadata_survives_nested_io_and_storage_contexts() {
110 let cause = ResourceBudgetError {
111 resource: ResourceBudgetKind::MemoryBytes,
112 scope: ResourceBudgetScope::Account,
113 owner: "source".into(),
114 memory_class: Some("external_read"),
115 requested: 8,
116 used: 4,
117 budget: 10,
118 budget_option: "source grant",
119 message: "source budget refused".into(),
120 };
121 let expected = HawDBError::from(cause.clone());
122 for inner in [
123 std::io::Error::other(cause),
124 std::io::Error::other(expected.clone()),
125 ] {
126 let nested = std::io::Error::other(std::io::Error::other(inner));
127 assert_eq!(
128 HawDBError::from_storage_error_with_context(nested, "read node"),
129 expected
130 );
131 }
132 assert!(matches!(
133 HawDBError::from_storage_error(std::io::Error::other("ordinary I/O failure")),
134 HawDBError::Storage(_)
135 ));
136 }
137}