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