1use super::Database;
4use crate::DbError;
5use coven_foundation::progress::ProgressReport;
6
7impl Database {
8 pub async fn snapshot_slot_bytes(
15 &self,
16 audience: coven_merge::Audience,
17 after: u64,
18 ) -> Result<u64, DbError> {
19 self.call(move |inner| {
20 inner.access.with_committed(|db| {
21 let too_large = || DbError::TooLarge {
22 field: "snapshot slot bytes", actual: u64::MAX, maximum: u64::MAX - 1,
23 };
24 let mut bytes = 0u64;
25 db.for_each("SELECT s.size FROM _coven_write_slot_audiences a JOIN _coven_write_slots s ON s.slot=a.slot WHERE a.audience=?1 AND a.slot>?2",
26 rusqlite::params![audience, after.to_be_bytes()], |row| {
27 let size = u64::try_from(row.get::<_, i64>(0)?).map_err(|_negative| DbError::DamagedDatabase)?;
28 bytes = bytes.checked_add(size).ok_or_else(too_large)?;
29 Ok::<_, DbError>(())
30 })?;
31 if audience == coven_merge::Audience::Store {
32 let records: i64 = db.query_row("SELECT coalesce(sum(length(record)),0) FROM _coven_slot_records WHERE slot>?1",
33 [after.to_be_bytes()], |row| row.get(0))?;
34 bytes = u64::try_from(records).ok().and_then(|records| bytes.checked_add(records)).ok_or_else(too_large)?;
35 }
36 Ok(bytes)
37 })
38 }).await
39 }
40
41 pub async fn waiting_snapshot_headers(
44 &self,
45 ) -> Result<Vec<coven_format::write_stream::WriteHeaderFrame>, DbError> {
46 self.call(move |inner| {
47 inner.access.with_committed(|db| {
48 let mut headers = Vec::new();
49 crate::upload::each_plaintext(db, |header, _| {
50 headers.push(header);
51 Ok(())
52 })?;
53 Ok(headers)
54 })
55 })
56 .await
57 }
58
59 pub async fn validate_snapshot(
67 &self,
68 id: coven_format::admin::SnapshotId,
69 staging: crate::SnapshotStaging,
70 author: coven_foundation::id_source::MemberId,
71 authorship: std::sync::Arc<dyn crate::Authorship>,
72 ) -> Result<
73 std::collections::BTreeSet<(
74 coven_foundation::id_source::DeviceId,
75 coven_foundation::id_source::ObjectId,
76 )>,
77 DbError,
78 > {
79 if !staging.staged_by(self) {
80 return Err(
81 super::input_database::staging::refuse(vec![staging], STAGED_ELSEWHERE).await
82 );
83 }
84 let mut stagings = [staging];
85 self.call(move |inner| {
86 inner.inputs.read_staged(&mut stagings, |inputs, [staging]| {
87 let body = |error: DbError| crate::SnapshotError::Body {
89 snapshot: id.clone(),
90 error: Box::new(error),
91 staged: None,
92 };
93 let prepared = staging.prepare(inputs, authorship.as_ref()).map_err(&body)?;
94 inner.access.with_writer(|writer| {
95 writer.discarded_transaction(|writer| {
96 let schema = &inner.access.write_schema;
97 crate::snapshot_state::create_tables(writer)?;
98 crate::snapshot_install::create_tables(writer, schema)?;
99 let mut progress = ProgressReport::unwatched(inner.clock.clone());
102 crate::snapshot_load::begin_installing(schema, 1, &mut progress);
103 crate::snapshot_load::read(
104 writer,
105 schema,
106 prepared,
107 Some(author),
108 &mut progress,
109 )
110 .map_err(&body)?;
111 super::file_retention::snapshot_references(writer, schema, &id.audience)
112 })
113 })
114 })
115 })
116 .await
117 }
118
119 pub async fn write_snapshot<B, F, E>(
128 &self,
129 id: coven_format::admin::SnapshotId,
130 begin: B,
131 emit: F,
132 ) -> Result<u32, crate::SnapshotWriteError<E>>
133 where
134 B: FnOnce(&coven_format::snapshot::SnapshotHeader) -> Result<(), E> + Send + 'static,
135 F: FnMut(Vec<u8>) -> Result<(), E> + Send + 'static,
136 E: coven_foundation::task::CarriesBug + Send + 'static,
137 {
138 self.call(move |inner| {
139 inner.access.with_committed(|reader| {
140 if crate::raise_capture::provisional(reader)?
143 .iter()
144 .any(|(audience, _, _)| *audience == id.audience)
145 {
146 return Err(DbError::RaiseWaiting.into());
147 }
148 crate::write_schema::prepare_evaluators(reader, &inner.access.write_schema)?;
149 crate::snapshot_write::write(reader, &inner.access.write_schema, id, begin, emit)
150 })
151 })
152 .await
153 }
154
155 pub async fn breaking_version(&self) -> Result<u32, DbError> {
158 self.call(move |inner| {
159 let reader = inner.access.readers.acquire_reader();
160 reader.with_reader(crate::raise_capture::breaking_version)
161 })
162 .await
163 }
164
165 pub async fn raise_captures(&self) -> Result<Vec<crate::RaiseCapture>, DbError> {
168 self.call(move |inner| {
169 let reader = inner.access.readers.acquire_reader();
170 reader.with_reader(|reader| crate::raise_capture::captures(reader, inner.access.device))
171 })
172 .await
173 }
174
175 pub async fn prepare_raise_capture(
181 &self,
182 audience: coven_merge::Audience,
183 version: u32,
184 ) -> Result<Option<crate::RaiseCapture>, DbError> {
185 self.call(move |inner| {
186 let device = inner.access.device;
187 inner.access.with_writer(|writer| {
188 writer.transaction(|writer| {
189 crate::raise_capture::prepare(
190 writer,
191 &inner.access.write_schema,
192 device,
193 &audience,
194 version,
195 )
196 })
197 })
198 })
199 .await
200 }
201
202 pub async fn unconfirmed_own_prefix(
207 &self,
208 through: coven_merge::WriteId,
209 ) -> Result<Option<coven_merge::WriteId>, DbError> {
210 self.call(move |inner| {
211 let reader = inner.access.readers.acquire_reader();
212 reader.with_reader(|reader| crate::upload::unconfirmed(reader, through))
213 })
214 .await
215 }
216
217 pub async fn write_raise_snapshot<B, F, E>(
220 &self,
221 id: coven_format::admin::SnapshotId,
222 version: u32,
223 begin: B,
224 emit: F,
225 ) -> Result<u32, crate::SnapshotWriteError<E>>
226 where
227 B: FnOnce(&coven_format::snapshot::SnapshotHeader) -> Result<(), E> + Send + 'static,
228 F: FnMut(Vec<u8>) -> Result<(), E> + Send + 'static,
229 E: coven_foundation::task::CarriesBug + Send + 'static,
230 {
231 self.call(move |inner| {
232 inner.access.with_committed(|reader| {
233 crate::write_schema::prepare_evaluators(reader, &inner.access.write_schema)?;
234 crate::raise_capture::write(
235 reader,
236 &inner.access.write_schema,
237 id,
238 version,
239 begin,
240 emit,
241 )
242 })
243 })
244 .await
245 }
246
247 pub async fn discard_raise_capture(
249 &self,
250 audience: coven_merge::Audience,
251 version: u32,
252 ) -> Result<(), DbError> {
253 self.call(move |inner| {
254 inner.access.with_writer(|writer| {
255 writer
256 .transaction(|writer| crate::raise_capture::discard(writer, &audience, version))
257 })
258 })
259 .await
260 }
261
262 pub async fn load_snapshots<P: crate::PendingValue, W>(
281 &self,
282 reload: crate::SnapshotReload<crate::SnapshotStaging, crate::DownloadedWriteStream<W>>,
283 progress: Option<ProgressReport<crate::LoadStep>>,
284 ) -> Result<Vec<crate::LoadedSnapshot>, DbError>
285 where
286 W: std::io::Read + Send + 'static,
287 {
288 let mut stagings = Vec::new();
290 let sources: Vec<crate::SnapshotSource<usize>> = reload
291 .snapshots
292 .into_iter()
293 .map(|source| match source {
294 crate::SnapshotSource::Stored { id, input, author } => {
295 stagings.push(input);
296 crate::SnapshotSource::Stored { id, input: stagings.len() - 1, author }
297 }
298 crate::SnapshotSource::Empty(audience) => crate::SnapshotSource::Empty(audience),
299 crate::SnapshotSource::Current(audience) => {
300 crate::SnapshotSource::Current(audience)
301 }
302 })
303 .collect();
304 if !stagings.iter().all(|staging| staging.staged_by(self)) {
305 return Err(super::input_database::staging::refuse(stagings, STAGED_ELSEWHERE).await);
306 }
307 let authorship = reload.authorship;
308 self.call(move |inner| {
309 let started = inner.clock.elapsed();
310 let mut progress =
311 progress.unwrap_or_else(|| ProgressReport::unwatched(inner.clock.clone()));
312 inner.inputs.read_staged(&mut stagings, |inputs, stagings| {
313 let snapshots = sources
314 .into_iter()
315 .map(|source| {
316 Ok(match source {
317 crate::SnapshotSource::Stored { id, input, author } => {
318 let input = stagings[input]
319 .prepare(inputs, authorship.as_ref())
320 .map_err(|error| crate::SnapshotError::Body {
321 snapshot: id.clone(),
322 error: Box::new(error),
323 staged: None,
324 })?;
325 crate::SnapshotSource::Stored { id, input, author }
326 }
327 crate::SnapshotSource::Empty(audience) => {
328 crate::SnapshotSource::Empty(audience)
329 }
330 crate::SnapshotSource::Current(audience) => {
331 crate::SnapshotSource::Current(audience)
332 }
333 })
334 })
335 .collect::<Result<_, DbError>>()?;
336 let reload = crate::SnapshotReload {
337 slots: reload
338 .slots
339 .into_iter()
340 .map(|slot| slot.prepare(inputs))
341 .collect::<Result<_, _>>()?,
342 snapshots,
343 target: reload.target,
344 state: reload.state,
345 provider_access: reload.provider_access,
346 cutoffs: reload.cutoffs,
347 boundaries: reload.boundaries,
348 operation: reload.operation,
349 authorship,
350 };
351 let prepared = inner.clock.elapsed();
352 let reader = inner.access.readers.acquire_reader();
355 let loaded = inner.with_files("load a snapshot", |writer, files| {
356 reader.with_reader(|reader| {
357 reader.read_transaction(|| {
358 crate::write_schema::prepare_evaluators(
359 reader,
360 &inner.access.write_schema,
361 )?;
362 crate::snapshot_load::load::<P>(
363 writer,
364 reader,
365 &inner.access.write_schema,
366 reload,
367 files,
368 &mut progress,
369 )
370 })
371 })
372 });
373 tracing::info!(
374 preparing = ?(prepared - started),
375 installing = ?(inner.clock.elapsed() - prepared),
376 "installed snapshots"
377 );
378 loaded
379 })
380 })
381 .await
382 }
383}
384
385const STAGED_ELSEWHERE: DbError =
388 DbError::Invariant { what: "a snapshot is loaded by the store that staged it" };