1use super::{
4 attach_collects_with_budget, reconstruct, Lmpg, QueryError, QuerySnapshot, SearchRequest,
5};
6
7#[must_use]
14pub fn effective_budget(work_budget: Option<usize>, config: u64) -> Option<u64> {
15 match work_budget {
16 Some(bytes) => Some(u64::try_from(bytes).unwrap_or(u64::MAX)),
17 None => (config != 0).then_some(config),
18 }
19}
20
21pub(super) fn charge_record(
29 budget: Option<u64>,
30 charged: &mut u64,
31 reservation: Option<&mut rushdb_storage::resources::ByteReservation>,
32 record: &serde_json::Value,
33) -> Result<(), QueryError> {
34 if let Some(limit) = budget {
35 let bytes =
36 serde_json::to_vec(record).map_or(0, |v| u64::try_from(v.len()).unwrap_or(u64::MAX));
37 *charged = charged.saturating_add(bytes);
38 if let Some(reservation) = reservation {
39 let _ = reservation.resize(usize::try_from(*charged).unwrap_or(usize::MAX));
40 }
41 if *charged > limit {
42 return Err(QueryError::ResourceLimit(format!(
43 "query working set exceeded {limit} bytes"
44 )));
45 }
46 }
47 Ok(())
48}
49
50pub(super) fn reconstruct_page_with_budget(
54 lmpg: &Lmpg,
55 req: &SearchRequest,
56 is_cancelled: &dyn Fn() -> bool,
57 snap: &QuerySnapshot,
58 page_roots: Vec<u64>,
59 budget: Option<u64>,
60) -> Result<Vec<serde_json::Value>, QueryError> {
61 let mut charged: u64 = 0;
62 let mut reservation = lmpg
65 .store
66 .graph_phase_budget()
67 .and_then(|phase_budget| phase_budget.try_reserve(0).ok());
68 let mut session = reconstruct::ReconstructSession::new(snap, lmpg.tenant);
69 let mut records = Vec::with_capacity(page_roots.len());
70 for ord in page_roots {
71 if is_cancelled() {
72 return Err(QueryError::Cancelled);
73 }
74 let Some(json) = session.reconstruct_record(ord)? else {
75 continue;
76 };
77 charge_record(budget, &mut charged, reservation.as_mut(), &json)?;
78 let remaining = budget
82 .map(|limit| usize::try_from(limit.saturating_sub(charged)).unwrap_or(usize::MAX));
83 let record = attach_collects_with_budget(
84 lmpg,
85 snap,
86 ord,
87 json,
88 &req.collects,
89 remaining,
90 is_cancelled,
91 )?;
92 charge_record(budget, &mut charged, reservation.as_mut(), &record)?;
95 records.push(record);
96 }
97 Ok(records)
98}