1use crate::storage::manager::RecordWriteBytes;
17use std::sync::Arc;
19use super::{RecoveryEntry, RecoverySnapshot};
23use crate::storage::manager::{
24 ENTRY_OVERHEAD, Inner, Operation, Pending, Target, encoded_size, recovery_preparation_bytes, recovery_resident_bytes,
25};
26use crate::storage::{ReadLimits, RecordInput, StorageCursor, StorageManager, StoredRecord};
27use crate::{Error, Result};
28use harmos_domain::state::Position;
29
30pub struct Permit {
39 context: crate::execution::Context,
40 inner: Arc<Inner>,
41 bytes: usize,
42 target: Target,
43 first_record: StorageCursor,
44 records: Vec<StoredRecord>,
45 record_bytes: usize,
46 capture_bytes: usize,
47 payload: Vec<u8>,
48 active: bool,
49 snapshot: bool,
50}
51
52impl<C> StorageManager<C> {
59 #[tracing::instrument(name = "storage.state.admission.reserve_transition", level = "debug", skip_all)]
60 pub async fn reserve_transition(&self, payload_bytes: usize, records: Option<ReadLimits>) -> Result<Permit> {
61 self.reserve_recovery(payload_bytes, records, false).await
62 }
63
64 #[tracing::instrument(name = "storage.state.admission.reserve_snapshot", level = "debug", skip_all)]
65 pub async fn reserve_snapshot(&self, payload_bytes: usize) -> Result<Permit> {
66 self.reserve_recovery(payload_bytes, None, true).await
67 }
68
69 #[tracing::instrument(name = "storage.state.admission.reserve_recovery", level = "debug", skip_all)]
70 async fn reserve_recovery(&self, payload_bytes: usize, records: Option<ReadLimits>, snapshot: bool) -> Result<Permit> {
71 let recovery_bytes = recovery_preparation_bytes(payload_bytes, self.inner.recovery.history(), self.inner.primary.history())?;
72 let bytes = recovery_bytes;
73 let batches = 1 + usize::from(records.is_some());
74 if batches > self.inner.settings.request.max_pending_write_batches.get() {
75 return Err(Error::InvalidInput { field: "managed capture", reason: "requires two queue slots" });
76 }
77 if bytes > self.inner.settings.request.max_pending_write_bytes.get() {
78 return Err(Error::InvalidInput { field: "managed recovery", reason: "group exceeds memory budget" });
79 }
80 let mut changed = self.inner.changed.subscribe();
81 loop {
82 {
83 let mut state = self.inner.state.lock().unwrap();
84 if !snapshot && recovery_bytes > state.journal_limit {
85 return Err(Error::InvalidInput { field: "managed recovery", reason: "group exceeds recovery read bound" });
86 }
87 if state.status.closing {
88 return Err(Error::Closing { domain: "storage" });
89 }
90 if !state.ready || !state.status.running {
91 return Err(Error::NotReady { domain: "storage" });
92 }
93 if let Some(source) = &state.status.failure {
94 return Err(Error::Persistence { source: source.clone() });
95 }
96 state.collect();
97 if state.reserved == 0
98 && state.finishing.is_none()
99 && state.status.pending_bytes <= self.inner.settings.request.max_pending_write_bytes.get() - bytes
100 && state.status.pending_batches <= self.inner.settings.request.max_pending_write_batches.get() - batches
101 {
102 state
103 .next
104 .checked_add(batches as u64)
105 .ok_or(Error::InvalidInput { field: "managed capture", reason: "queue order overflow" })?;
106 let mut first_record = state.status.accepted.clone();
107 if let Some(limits) = records {
108 first_record
109 .sequence
110 .checked_add(limits.rows.get() as u64)
111 .ok_or(Error::InvalidInput { field: "managed capture", reason: "position overflow" })?;
112 first_record.sequence += 1;
113 }
114 let target = Target {
115 index: state
116 .next
117 .checked_add(1)
118 .ok_or(Error::InvalidInput { field: "managed recovery", reason: "queue order overflow" })?,
119 sequence: state
120 .status
121 .recovery_accepted
122 .sequence
123 .checked_add(u64::from(!snapshot))
124 .ok_or(Error::InvalidInput { field: "managed recovery", reason: "position overflow" })?,
125 };
126 state.reserved = bytes;
127 state.reserved_batches = batches;
128 state.collect();
129 return Ok(Permit {
130 context: crate::execution::Context::current(),
131 inner: self.inner.clone(),
132 bytes: recovery_bytes,
133 target,
134 first_record,
135 records: Vec::new(),
136 record_bytes: 0,
137 capture_bytes: 0,
138 payload: Vec::new(),
139 active: true,
140 snapshot,
141 });
142 }
143 self.inner.relieve_pressure(&mut state, bytes, batches);
144 }
145 changed.changed().await.map_err(|_| Error::NotReady { domain: "storage" })?;
146 }
147 }
148
149 #[tracing::instrument(name = "storage.state.admission.settle_transition", level = "debug", skip_all)]
150 pub async fn settle_transition(&self) {
151 let mut changed = self.inner.changed.subscribe();
152 loop {
153 if self.inner.state.lock().unwrap().reserved == 0 {
154 return;
155 }
156 if changed.changed().await.is_err() {
157 return;
158 }
159 }
160 }
161}
162
163impl Permit {
164 pub fn snapshot_payload(&mut self, payload: Vec<u8>) -> Result<()> {
165 let context = self.context.clone();
166 context.sync(|| {
167 let _stage = tracing::debug_span!("storage.state.snapshot_payload").entered();
168 debug_assert!(self.snapshot);
169 let bytes = recovery_resident_bytes(payload.capacity(), self.inner.recovery.history(), self.inner.primary.history())?;
170 if bytes > self.bytes {
171 return Err(Error::InvalidInput { field: "state snapshot", reason: "payload exceeds reservation" });
172 }
173 self.payload = payload;
174 self.bytes = bytes;
175 let mut state = self.inner.state.lock().unwrap();
176 state.reserved = bytes;
177 state.collect();
178 self.inner.notify();
179 Ok(())
180 })
181 }
182
183 pub fn projected(&self) -> &[StoredRecord] {
184 &self.records
185 }
186
187 #[tracing::instrument(name = "storage.state.admission.push", level = "debug", skip_all)]
188 pub async fn push(&mut self, record: RecordInput, limits: ReadLimits) -> Result<()> {
189 let context = self.context.clone();
190 let span = context.sync(|| tracing::debug_span!("storage.state.project"));
191 let future = async {
192 let invalid = || Error::InvalidInput { field: "record projection", reason: "capture limit exceeded" };
193 if self.records.len() >= limits.rows.get() {
194 return Err(invalid());
195 }
196 let capture = encoded_size(&record.borrowed())?
197 .checked_add(encoded_size(&self.first_record.history)?)
198 .and_then(|bytes| bytes.checked_add(ENTRY_OVERHEAD))
199 .ok_or_else(invalid)?;
200 let capture_total = self.capture_bytes.checked_add(capture).ok_or_else(invalid)?;
201 let bytes = record
205 .resident_bytes(&self.first_record.history)?
206 .checked_add(std::mem::size_of::<StoredRecord>() * 3)
207 .ok_or_else(invalid)?;
208 let total = self.record_bytes.checked_add(bytes).ok_or_else(invalid)?;
209 if capture_total > limits.bytes.get()
212 || self.bytes.checked_add(total).is_none_or(|total| total > self.inner.settings.request.max_pending_write_bytes.get())
213 {
214 return Err(invalid());
215 }
216 let mut position = self.first_record.clone();
217 position.sequence = position.sequence.checked_add(self.records.len() as u64).ok_or_else(invalid)?;
218 let capacity = self.inner.settings.request.max_pending_write_bytes.get();
219 self.inner
220 .admit_when(|state| {
221 super::super::capacity::admitting(state, super::super::capacity::Pool::Write)?;
222 state.collect();
223 if state.status.pending_bytes <= capacity - bytes {
224 state.reserved += bytes;
225 state.collect();
226 return Ok(Some(()));
227 }
228 let held = state.status.pending_bytes.saturating_sub(state.queue.bytes()).saturating_sub(state.scratch_bytes);
232 if held > capacity - bytes {
233 return Err(invalid());
234 }
235 self.inner.relieve_pressure(state, bytes, 0);
238 Ok(None)
239 })
240 .await?;
241 self.records.push(record.stored(position));
242 self.record_bytes = total;
243 self.capture_bytes = capture_total;
244 Ok(())
245 };
246 context.scope(tracing::Instrument::instrument(future, span)).await
247 }
248
249 pub fn encode(&mut self, value: &impl serde::Serialize) -> Result<()> {
250 let context = self.context.clone();
251 context.sync(|| {
252 let _stage = tracing::debug_span!("storage.state.encode").entered();
253 self.payload =
254 serde_json::to_vec(value).map_err(|_| Error::InvalidInput { field: "managed recovery", reason: "cannot encode group" })?;
255 let bytes = recovery_resident_bytes(self.payload.capacity(), self.inner.recovery.history(), self.inner.primary.history())?;
259 if bytes > self.bytes {
260 return Err(Error::InvalidInput { field: "managed recovery", reason: "encoded allocation exceeds reservation" });
261 }
262 self.bytes = bytes;
263 let mut state = self.inner.state.lock().unwrap();
264 state.reserved = self
265 .bytes
266 .checked_add(self.record_bytes)
267 .ok_or(Error::InvalidInput { field: "managed recovery", reason: "size overflow" })?;
268 state.reserved_batches = 1 + usize::from(!self.records.is_empty());
269 state.collect();
270 self.inner.notify();
271 Ok(())
272 })
273 }
274
275 pub fn commit(mut self, frontier: Position) {
276 let context = self.context.clone();
277 context.sync(|| {
278 let _stage = tracing::debug_span!("storage.state.commit").entered();
279 let mut state = self.inner.state.lock().unwrap();
280 let required_data = if self.snapshot {
281 state.journal_required_data.clone()
282 } else {
283 self.records.last().map_or_else(|| state.status.saved.clone(), |record| record.position.clone())
284 };
285 if required_data.sequence > state.journal_required_data.sequence {
286 state.journal_required_data = required_data.clone();
287 }
288 if let Some(last) = self.records.last() {
289 state.status.accepted = last.position.clone();
290 state.data_accepted = Target { index: self.target.index, sequence: last.position.sequence };
291 state.queue.push_back(Arc::new(Pending::new(
292 &self.inner,
293 None,
294 self.target.index,
295 self.record_bytes,
296 Operation::Records(crate::StorageBatch::new(std::mem::take(&mut self.records))),
297 )));
298 self.target.index += 1; }
300 let position = crate::storage::RecoveryPosition {
301 history: state.status.recovery_accepted.history.clone(),
302 sequence: self.target.sequence,
303 };
304 state.status.recovery_accepted = position.clone();
305 state.recovery_accepted = self.target;
306 state.next = self.target.index;
307 state.queue.push_back(Arc::new(Pending::new(
308 &self.inner,
309 (!self.snapshot).then_some(frontier),
310 self.target.index,
311 self.bytes,
312 if self.snapshot {
313 Operation::Snapshot(crate::StorageBatch::new(vec![RecoverySnapshot {
314 required_data: required_data.clone(),
315 through: position,
316 payload: std::mem::take(&mut self.payload),
317 }]))
318 } else {
319 Operation::Recovery(crate::StorageBatch::new(vec![RecoveryEntry {
320 required_data,
321 position,
322 payload: std::mem::take(&mut self.payload),
323 }]))
324 },
325 )));
326 state.reserved = 0;
327 state.reserved_batches = 0;
328 self.active = false;
329 state.collect();
330 self.inner.notify();
331 })
332 }
333}
334
335impl Drop for Permit {
336 fn drop(&mut self) {
337 if self.active {
338 let mut state = self.inner.state.lock().unwrap();
339 state.reserved = 0;
340 state.reserved_batches = 0;
341 state.collect();
342 self.inner.notify();
343 }
344 }
345}
346
347