Skip to content

Commit 6377b2b

Browse files
jordanhunt22Convex, Inc.
authored andcommitted
Add document retention check to previous_revisions (#24014)
Enforce document retention in `previous_revisions`. This is a follow-up on [this](get-convex/convex#23587) PR. GitOrigin-RevId: f2219dd6f48c6f6ab2c46cedbccec691f4069612
1 parent 2558a81 commit 6377b2b

File tree

4 files changed

+41
-26
lines changed

4 files changed

+41
-26
lines changed

crates/common/src/persistence.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ pub trait PersistenceReader: Send + Sync + 'static {
353353
async fn previous_revisions(
354354
&self,
355355
ids: BTreeSet<(InternalDocumentId, Timestamp)>,
356+
retention_validator: Arc<dyn RetentionValidator>,
356357
) -> anyhow::Result<
357358
BTreeMap<(InternalDocumentId, Timestamp), (Timestamp, Option<ResolvedDocument>)>,
358359
>;
@@ -553,7 +554,9 @@ impl RepeatablePersistence {
553554
// Reading documents <ts, so ts-1 needs to be repeatable.
554555
anyhow::ensure!(*ts <= self.upper_bound.succ()?);
555556
}
556-
self.reader.previous_revisions(ids).await
557+
self.reader
558+
.previous_revisions(ids, self.retention_validator.clone())
559+
.await
557560
}
558561

559562
pub fn read_snapshot(&self, at: RepeatableTimestamp) -> anyhow::Result<PersistenceSnapshot> {

crates/common/src/testing/persistence_test_suite.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1643,7 +1643,9 @@ pub async fn persistence_previous_revisions<P: Persistence>(p: Arc<P>) -> anyhow
16431643
(id8, 3, 1, true),
16441644
];
16451645
assert_eq!(
1646-
reader.previous_revisions(queries).await?,
1646+
reader
1647+
.previous_revisions(queries, Arc::new(NoopRetentionValidator))
1648+
.await?,
16471649
expected
16481650
.into_iter()
16491651
.map(|(id, ts, prev_ts, exists)| (

crates/common/src/testing/test_persistence.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ impl PersistenceReader for TestPersistence {
278278
async fn previous_revisions(
279279
&self,
280280
ids: BTreeSet<(InternalDocumentId, Timestamp)>,
281+
_retention_validator: Arc<dyn RetentionValidator>,
281282
) -> anyhow::Result<
282283
BTreeMap<(InternalDocumentId, Timestamp), (Timestamp, Option<ResolvedDocument>)>,
283284
> {

crates/sqlite/src/lib.rs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![feature(coroutines)]
55

66
use std::{
7+
cmp,
78
collections::{
89
BTreeMap,
910
BTreeSet,
@@ -533,36 +534,44 @@ impl PersistenceReader for SqlitePersistence {
533534
async fn previous_revisions(
534535
&self,
535536
ids: BTreeSet<(InternalDocumentId, Timestamp)>,
537+
retention_validator: Arc<dyn RetentionValidator>,
536538
) -> anyhow::Result<
537539
BTreeMap<(InternalDocumentId, Timestamp), (Timestamp, Option<ResolvedDocument>)>,
538540
> {
539-
let inner = self.inner.lock();
540541
let mut out = BTreeMap::new();
541-
for (id, ts) in ids {
542-
let mut stmt = inner.connection.prepare(PREV_REV_QUERY)?;
543-
let internal_id = id.internal_id();
544-
let params = params![&id.table().0[..], &internal_id[..], &u64::from(ts)];
545-
let mut row_iter = stmt.query_map(params, load_document_row)?;
546-
if let Some(row) = row_iter.next() {
547-
let (id, prev_ts, table, json_value, deleted) = row?;
548-
let id = InternalId::try_from(id)?;
549-
let table = TableId(table.try_into()?);
550-
let prev_ts = Timestamp::try_from(prev_ts)?;
551-
let document_id = table.id(id);
552-
let document = if !deleted {
553-
let json_value = json_value.ok_or_else(|| {
554-
anyhow::anyhow!("Unexpected NULL json_value at {} {}", id, prev_ts)
555-
})?;
556-
let json_value: serde_json::Value = serde_json::from_str(&json_value)?;
557-
let value: ConvexValue = json_value.try_into()?;
558-
let document = ResolvedDocument::from_database(table, value)?;
559-
Some(document)
560-
} else {
561-
None
562-
};
563-
out.insert((document_id, ts), (prev_ts, document));
542+
let mut min_ts = Timestamp::MAX;
543+
{
544+
let inner = self.inner.lock();
545+
for (id, ts) in ids {
546+
let mut stmt = inner.connection.prepare(PREV_REV_QUERY)?;
547+
let internal_id = id.internal_id();
548+
let params = params![&id.table().0[..], &internal_id[..], &u64::from(ts)];
549+
let mut row_iter = stmt.query_map(params, load_document_row)?;
550+
if let Some(row) = row_iter.next() {
551+
let (id, prev_ts, table, json_value, deleted) = row?;
552+
let id = InternalId::try_from(id)?;
553+
let table = TableId(table.try_into()?);
554+
let prev_ts = Timestamp::try_from(prev_ts)?;
555+
let document_id = table.id(id);
556+
let document = if !deleted {
557+
let json_value = json_value.ok_or_else(|| {
558+
anyhow::anyhow!("Unexpected NULL json_value at {} {}", id, prev_ts)
559+
})?;
560+
let json_value: serde_json::Value = serde_json::from_str(&json_value)?;
561+
let value: ConvexValue = json_value.try_into()?;
562+
let document = ResolvedDocument::from_database(table, value)?;
563+
Some(document)
564+
} else {
565+
None
566+
};
567+
min_ts = cmp::min(ts, min_ts);
568+
out.insert((document_id, ts), (prev_ts, document));
569+
}
564570
}
565571
}
572+
retention_validator
573+
.validate_document_snapshot(min_ts)
574+
.await?;
566575
Ok(out)
567576
}
568577

0 commit comments

Comments
 (0)