harmos_storage/manager/
recovery.rs1use super::*;
22
23impl<C> StorageManager<C> {
38 #[tracing::instrument(name = "storage.manager.recovery.write_recovery", level = "debug", skip_all)]
39 pub async fn write_recovery(&self, payloads: Vec<Vec<u8>>) -> Result<RecoveryPosition> {
40 self.check_raw_recovery()?;
41 if payloads.is_empty() {
42 return Ok(self.status().recovery_accepted);
43 }
44 let required_data = self.cursor_saved();
45 let bytes = payloads.iter().try_fold(0usize, |bytes, payload| {
46 bytes.checked_add(recovery_encoded_bound(payload.len(), self.inner.recovery.history(), self.inner.primary.history())?).ok_or(
47 Error::InvalidInput {
48 field: "storage admission",
49 reason: "size overflow",
50 },
51 )
52 })?;
53 let sequence = self
54 .admit(Domain::Recovery, bytes, payloads.len(), |first| {
55 Ok(Operation::Recovery(crate::StorageBatch::new(
56 payloads
57 .into_iter()
58 .enumerate()
59 .map(|(index, payload)| RecoveryEntry {
60 required_data: required_data.clone(),
61 position: self.recovery_position(first + index as u64),
62 payload,
63 })
64 .collect(),
65 )))
66 })
67 .await?;
68 Ok(self.recovery_position(sequence))
69 }
70
71 #[tracing::instrument(name = "storage.manager.recovery.write_snapshot", level = "debug", skip_all)]
72 pub async fn write_snapshot(&self, snapshot: RecoverySnapshot) -> Result<()> {
73 self.check_raw_recovery()?;
74 self.inner.recovery.history().check(&snapshot.through.history)?;
75 self.check_state_dependency(&snapshot.required_data)?;
76 let bytes = recovery_encoded_bound(snapshot.payload.len(), &snapshot.through.history, &snapshot.required_data.history)?;
77 self.admit(Domain::Recovery, bytes, 0, |_| Ok(Operation::Snapshot(StorageBatch::new(vec![snapshot])))).await?;
78 Ok(())
79 }
80
81 #[tracing::instrument(name = "storage.manager.recovery.flush", level = "debug", skip_all)]
82 pub async fn flush(&self) -> Result<StorageCursor> {
83 let through = self.cursor_accepted();
84 self.flush_until(&through).await
85 }
86
87 #[tracing::instrument(name = "storage.manager.recovery.flush_until", level = "debug", skip_all)]
88 pub async fn flush_until(&self, through: &StorageCursor) -> Result<StorageCursor> {
89 let files = self.capture_files(Some(through))?;
90 self.flush_prepared(through, &files).await
91 }
92
93 #[tracing::instrument(name = "storage.manager.recovery.flush_prepared", level = "debug", skip_all)]
94 pub async fn flush_prepared(&self, through: &StorageCursor, files: &Option<crate::FileSave>) -> Result<StorageCursor> {
95 self.flush_domain(Domain::Data, Some(through)).await?;
96 if files.is_some() {
97 let _lifecycle = tracing::Instrument::instrument(self.inner.lifecycle.write(), tracing::trace_span!("storage.lock.wait")).await;
98 if self.inner.state.lock().unwrap().status.closed {
99 return Err(Error::Closing { domain: "storage" });
100 }
101 self.persist_files(files).await?;
102 }
103 Ok(self.cursor_saved())
104 }
105
106 #[tracing::instrument(name = "storage.manager.recovery.flush_recovery", level = "debug", skip_all)]
107 pub async fn flush_recovery(&self) -> Result<RecoveryPosition> {
108 self.flush_domain(Domain::Recovery, None).await?;
109 Ok(self.status().recovery_saved)
110 }
111
112 #[tracing::instrument(name = "storage.manager.recovery.read_recovery", level = "debug", skip_all)]
113 pub async fn read_recovery(
114 &self, after: &RecoveryPosition, through: &RecoveryPosition, limits: ReadLimits,
115 ) -> Result<crate::ReadBatch<RecoveryPage>> {
116 let _lifecycle = tracing::Instrument::instrument(self.inner.lifecycle.read(), tracing::trace_span!("storage.lock.wait")).await;
117 let status = self.status();
118 if !status.recovery_saved.covers(through)? {
119 return Err(Error::InvalidInput {
120 field: "recovery range",
121 reason: "frontier is not durable",
122 });
123 }
124 let page = self.inner.bounded("recovery.read_entries", self.inner.recovery.read_entries(after, through, limits)).await?;
125 page.check(after, through, limits, |entry| encoded_size(entry).unwrap_or(usize::MAX))?;
126 for entry in &page.entries {
127 self.check_state_dependency(&entry.required_data)?;
128 }
129 Ok(crate::ReadBatch::new(page))
130 }
131
132 #[tracing::instrument(name = "storage.manager.recovery.read_snapshot", level = "debug", skip_all)]
133 pub async fn read_snapshot(&self, max_bytes: std::num::NonZeroUsize) -> Result<Option<crate::ReadBatch<RecoverySnapshot>>> {
134 let _lifecycle = tracing::Instrument::instrument(self.inner.lifecycle.read(), tracing::trace_span!("storage.lock.wait")).await;
135 let snapshot = self.inner.bounded("recovery.read_snapshot", self.inner.recovery.read_snapshot(max_bytes)).await?;
136 if let Some(snapshot) = &snapshot
137 && (!self.status().recovery_saved.covers(&snapshot.through)?
138 || !self.status().saved.covers(&snapshot.required_data)?
139 || encoded_size(snapshot)? > max_bytes.get())
140 {
141 return Err(Error::InvalidInput {
142 field: "recovery snapshot",
143 reason: "invalid saved boundary or byte size",
144 });
145 }
146 Ok(snapshot.map(crate::ReadBatch::new))
147 }
148}
149
150