From 9db69a40a62442a1c6959cdda553fe7a57105709 Mon Sep 17 00:00:00 2001 From: cloudhead Date: Fri, 9 Aug 2024 10:48:46 +0200 Subject: [PATCH] radicle: Remove cache entries before caching In case of a bug or failed cache update, there may be objects in the cache that are no longer in storage. When doing a full re-cache, remove all existing entries from the repo first. --- radicle/src/cob/cache.rs | 6 ++++++ radicle/src/cob/issue.rs | 5 +++++ radicle/src/cob/issue/cache.rs | 23 +++++++++++++++++++- radicle/src/cob/patch.rs | 5 +++++ radicle/src/cob/patch/cache.rs | 39 ++++++++++++++++++++++++++++------ 5 files changed, 70 insertions(+), 8 deletions(-) diff --git a/radicle/src/cob/cache.rs b/radicle/src/cob/cache.rs index ed440aff..ae4fdc2e 100644 --- a/radicle/src/cob/cache.rs +++ b/radicle/src/cob/cache.rs @@ -203,6 +203,8 @@ pub trait Remove { /// /// This assumes that the `id` is unique across repositories. fn remove(&mut self, id: &ObjectId) -> Result; + /// Delete all entries from a repo. + fn remove_all(&mut self, rid: &RepoId) -> Result; } /// An in-memory cache for storing COB objects. @@ -270,6 +272,10 @@ impl Remove for NoCache { fn remove(&mut self, _id: &ObjectId) -> Result { Ok(()) } + + fn remove_all(&mut self, _rid: &RepoId) -> Result { + Ok(()) + } } /// Track the progress of cache writes when transferring the diff --git a/radicle/src/cob/issue.rs b/radicle/src/cob/issue.rs index 5ac665cb..dbdf453a 100644 --- a/radicle/src/cob/issue.rs +++ b/radicle/src/cob/issue.rs @@ -72,6 +72,11 @@ pub enum Error { #[source] err: Box, }, + #[error("failed to remove issues from cache: {err}")] + CacheRemoveAll { + #[source] + err: Box, + }, } /// Reason why an issue was closed. diff --git a/radicle/src/cob/issue/cache.rs b/radicle/src/cob/issue/cache.rs index b5272dbc..8b0eb7fb 100644 --- a/radicle/src/cob/issue/cache.rs +++ b/radicle/src/cob/issue/cache.rs @@ -151,8 +151,13 @@ impl<'a, R, C> Cache, C> { ) -> Result<(), super::Error> where R: ReadRepository + cob::Store, - C: Update, + C: Update + Remove, { + // Start by clearing the cache. This will get rid of issues that are cached but + // no longer exist in storage. + self.remove_all(&self.rid()) + .map_err(|e| super::Error::CacheRemoveAll { err: e.into() })?; + let issues = self.store.all()?; let mut progress = cache::WriteAllProgress::new(issues.len()); for issue in self.store.all()? { @@ -266,6 +271,10 @@ where fn remove(&mut self, id: &ObjectId) -> Result { self.cache.remove(id) } + + fn remove_all(&mut self, rid: &RepoId) -> Result { + self.cache.remove_all(rid) + } } #[derive(Debug, Error)] @@ -321,6 +330,18 @@ impl Remove for StoreWriter { Ok(db.change_count() > 0) }) } + + fn remove_all(&mut self, rid: &RepoId) -> Result { + let mut stmt = self.db.prepare( + "DELETE FROM issues + WHERE repo = ?1", + )?; + + stmt.bind((1, rid))?; + stmt.next()?; + + Ok(self.db.change_count() > 0) + } } pub struct NoCacheIter<'a> { diff --git a/radicle/src/cob/patch.rs b/radicle/src/cob/patch.rs index d30c47a3..ea1f0072 100644 --- a/radicle/src/cob/patch.rs +++ b/radicle/src/cob/patch.rs @@ -145,6 +145,11 @@ pub enum Error { #[source] err: Box, }, + #[error("failed to remove patches from cache: {err}")] + CacheRemoveAll { + #[source] + err: Box, + }, } /// Patch operation. diff --git a/radicle/src/cob/patch/cache.rs b/radicle/src/cob/patch/cache.rs index 0c4f3c3a..72ef3700 100644 --- a/radicle/src/cob/patch/cache.rs +++ b/radicle/src/cob/patch/cache.rs @@ -218,8 +218,13 @@ impl<'a, R, C> Cache, C> { ) -> Result<(), super::Error> where R: ReadRepository + cob::Store, - C: Update, + C: Update + Remove, { + // Start by clearing the cache. This will get rid of patches that are cached but + // no longer exist in storage. + self.remove_all(&self.rid()) + .map_err(|e| super::Error::CacheRemoveAll { err: e.into() })?; + let patches = self.store.all()?; let mut progress = cache::WriteAllProgress::new(patches.len()); for patch in self.store.all()? { @@ -333,6 +338,10 @@ where fn remove(&mut self, id: &ObjectId) -> Result { self.cache.remove(id) } + + fn remove_all(&mut self, rid: &RepoId) -> Result { + self.cache.remove_all(rid) + } } #[derive(Debug, Error)] @@ -388,6 +397,18 @@ impl Remove for StoreWriter { Ok(db.change_count() > 0) }) } + + fn remove_all(&mut self, rid: &RepoId) -> Result { + let mut stmt = self.db.prepare( + "DELETE FROM patches + WHERE repo = ?1", + )?; + + stmt.bind((1, rid))?; + stmt.next()?; + + Ok(self.db.change_count() > 0) + } } #[derive(Debug, Error)] @@ -395,7 +416,9 @@ pub enum Error { #[error("object `{1}` of type `{0}` was not found")] NotFound(TypeName, ObjectId), #[error(transparent)] - Object(#[from] cob::object::ParseObjectId), + ObjectId(#[from] cob::object::ParseObjectId), + #[error("object {0} failed to parse: {1}")] + Object(ObjectId, serde_json::Error), #[error(transparent)] Json(#[from] serde_json::Error), #[error(transparent)] @@ -413,7 +436,8 @@ pub struct PatchesIter<'a> { impl<'a> PatchesIter<'a> { fn parse_row(row: sql::Row) -> Result<(PatchId, Patch), Error> { let id = PatchId::from_str(row.read::<&str, _>("id"))?; - let patch = serde_json::from_str::(row.read::<&str, _>("patch"))?; + let patch = serde_json::from_str::(row.read::<&str, _>("patch")) + .map_err(|e| Error::Object(id, e))?; Ok((id, patch)) } } @@ -558,21 +582,21 @@ mod query { rid: &RepoId, id: &PatchId, ) -> Result, Error> { - let id = sql::Value::String(id.to_string()); + let key = sql::Value::String(id.to_string()); let mut stmt = db.prepare( "SELECT patch FROM patches WHERE id = ?1 AND repo = ?2", )?; - stmt.bind((1, id))?; + stmt.bind((1, key))?; stmt.bind((2, rid))?; match stmt.into_iter().next().transpose()? { None => Ok(None), Some(row) => { let patch = row.read::<&str, _>("patch"); - let patch = serde_json::from_str(patch)?; + let patch = serde_json::from_str(patch).map_err(|e| Error::Object(*id, e))?; Ok(Some(patch)) } } @@ -598,7 +622,8 @@ mod query { None => Ok(None), Some(row) => { let id = PatchId::from_str(row.read::<&str, _>("id"))?; - let patch = serde_json::from_str::(row.read::<&str, _>("patch"))?; + let patch = serde_json::from_str::(row.read::<&str, _>("patch")) + .map_err(|e| Error::Object(id, e))?; let revision = serde_json::from_str::(row.read::<&str, _>("revision"))?; Ok(Some(ByRevision { id,