From 985b0af3f6230a0d36e0b413f42dc9a3c1522fe6 Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Thu, 8 Feb 2024 16:52:05 +0000 Subject: [PATCH] radicle: implement caching for issues and patches Implement caching for the `Issue` and `Patch` COB. This is achieved by having the `Transaction::initial` and `Transaction::commit` methods take a cache which can update the newly created/updated object. The `Store::remove` method also takes the cache for removing the object from the cache, as well as the repository. This meant that the `*Mut` types for the respective COBs are required to carry a cache alongside the repository store. Identities will not be cached, and so this is always set to `NoCache` -- which performs no caching, and always succeeds. To perform cache reading for issues and patches, the `cache::Issues` and `cache::Patches` traits are introduced. They capture the minimal amount of methods that are needed for the `rad issue` and `rad patch` CLI commands. The implementor for each of these traits is there respective `Cache` types, which combines their backing repository store and the SQLite database. They both provide convenience methods: - `create` (and `draft` for patches) - `get_mut` - `remove` which can be used instead of their repository store counterparts. All uses of the repository stores (where needed) are replaced with `Cache` types. Signed-off-by: Fintan Halpenny X-Clacks-Overhead: GNU Terry Pratchett --- radicle-cli/src/commands/clone.rs | 7 +- radicle-cli/src/commands/inbox.rs | 12 +- radicle-cli/src/commands/issue.rs | 45 +- radicle-cli/src/commands/patch.rs | 45 +- radicle-cli/src/commands/patch/archive.rs | 3 +- radicle-cli/src/commands/patch/assign.rs | 2 +- radicle-cli/src/commands/patch/checkout.rs | 7 +- radicle-cli/src/commands/patch/comment.rs | 10 +- radicle-cli/src/commands/patch/delete.rs | 3 +- radicle-cli/src/commands/patch/diff.rs | 3 +- radicle-cli/src/commands/patch/edit.rs | 8 +- radicle-cli/src/commands/patch/label.rs | 2 +- radicle-cli/src/commands/patch/list.rs | 16 +- radicle-cli/src/commands/patch/ready.rs | 3 +- radicle-cli/src/commands/patch/redact.rs | 8 +- radicle-cli/src/commands/patch/review.rs | 5 +- radicle-cli/src/commands/patch/show.rs | 2 +- radicle-cli/src/commands/patch/update.rs | 8 +- radicle-cli/src/commands/stats.rs | 8 +- radicle-cli/tests/commands.rs | 6 +- radicle-httpd/src/api.rs | 6 +- radicle-httpd/src/api/error.rs | 13 + radicle-httpd/src/api/v1/delegates.rs | 9 +- radicle-httpd/src/api/v1/projects.rs | 34 +- radicle-httpd/src/test.rs | 10 +- radicle-node/src/runtime.rs | 7 +- radicle-node/src/test/environment.rs | 5 +- radicle-node/src/tests.rs | 2 +- radicle-node/src/worker.rs | 19 +- radicle-node/src/worker/fetch.rs | 95 +- radicle-node/src/worker/fetch/error.rs | 28 +- radicle-remote-helper/src/lib.rs | 2 +- radicle-remote-helper/src/list.rs | 20 +- radicle-remote-helper/src/push.rs | 67 +- radicle-tools/src/rad-merge.rs | 4 +- radicle/src/cob.rs | 25 +- radicle/src/cob/cache.rs | 53 +- radicle/src/cob/issue.rs | 175 ++-- radicle/src/cob/issue/cache.rs | 685 +++++++++++++++ radicle/src/cob/patch.rs | 382 ++++++-- radicle/src/cob/patch/cache.rs | 974 +++++++++++++++++++++ radicle/src/cob/store.rs | 23 +- radicle/src/profile.rs | 69 +- radicle/src/storage.rs | 11 + 44 files changed, 2632 insertions(+), 289 deletions(-) create mode 100644 radicle/src/cob/issue/cache.rs create mode 100644 radicle/src/cob/patch/cache.rs diff --git a/radicle-cli/src/commands/clone.rs b/radicle-cli/src/commands/clone.rs index f46fe015..8e5bb00a 100644 --- a/radicle-cli/src/commands/clone.rs +++ b/radicle-cli/src/commands/clone.rs @@ -5,9 +5,10 @@ use std::str::FromStr; use std::time; use anyhow::anyhow; +use radicle::issue::cache::Issues as _; +use radicle::patch::cache::Patches as _; use thiserror::Error; -use radicle::cob; use radicle::git::raw; use radicle::identity::doc; use radicle::identity::doc::{DocError, RepoId}; @@ -186,8 +187,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { info.push([term::format::bold(proj.name()).into()]); info.push([term::format::italic(proj.description()).into()]); - let issues = cob::issue::Issues::open(&repo)?.counts()?; - let patches = cob::patch::Patches::open(&repo)?.counts()?; + let issues = profile.issues(&repo)?.counts()?; + let patches = profile.patches(&repo)?.counts()?; info.push([term::Line::spaced([ term::format::tertiary(issues.open).into(), diff --git a/radicle-cli/src/commands/inbox.rs b/radicle-cli/src/commands/inbox.rs index 2997765c..620584c0 100644 --- a/radicle-cli/src/commands/inbox.rs +++ b/radicle-cli/src/commands/inbox.rs @@ -6,10 +6,10 @@ use anyhow::anyhow; use localtime::LocalTime; use radicle::identity::Identity; -use radicle::issue::Issues; +use radicle::issue::cache::Issues as _; use radicle::node::notifications; use radicle::node::notifications::*; -use radicle::patch::Patches; +use radicle::patch::cache::Patches as _; use radicle::prelude::{Profile, RepoId}; use radicle::storage::{ReadRepository, ReadStorage}; use radicle::{cob, Storage}; @@ -233,8 +233,8 @@ where let (_, head) = repo.head()?; let doc = repo.identity_doc()?; let proj = doc.project()?; - let issues = Issues::open(&repo)?; - let patches = Patches::open(&repo)?; + let issues = profile.issues(&repo)?; + let patches = profile.patches(&repo)?; let mut notifs = notifs.by_repo(&rid, sort_by.field)?.collect::>(); if !sort_by.reverse { @@ -406,13 +406,13 @@ fn show( match n.kind { NotificationKind::Cob { type_name, id } if type_name == *cob::issue::TYPENAME => { - let issues = Issues::open(&repo)?; + let issues = profile.issues(&repo)?; let issue = issues.get(&id)?.unwrap(); term::issue::show(&issue, &id, term::issue::Format::default(), profile)?; } NotificationKind::Cob { type_name, id } if type_name == *cob::patch::TYPENAME => { - let patches = Patches::open(&repo)?; + let patches = profile.patches(&repo)?; let patch = patches.get(&id)?.unwrap(); term::patch::show(&patch, &id, false, &repo, None, profile)?; diff --git a/radicle-cli/src/commands/issue.rs b/radicle-cli/src/commands/issue.rs index 8554ca6f..cacb394c 100644 --- a/radicle-cli/src/commands/issue.rs +++ b/radicle-cli/src/commands/issue.rs @@ -6,13 +6,14 @@ use anyhow::{anyhow, Context as _}; use radicle::cob::common::{Label, Reaction}; use radicle::cob::issue; -use radicle::cob::issue::{CloseReason, Issues, State}; +use radicle::cob::issue::{CloseReason, State}; use radicle::cob::thread; use radicle::crypto::Signer; +use radicle::issue::cache::Issues as _; use radicle::prelude::Did; use radicle::profile; use radicle::storage; -use radicle::storage::{WriteRepository, WriteStorage}; +use radicle::storage::{ReadRepository, WriteRepository, WriteStorage}; use radicle::Profile; use radicle::{cob, Node}; @@ -425,7 +426,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { | Operation::Label { .. } ); - let mut issues = Issues::open(&repo)?; + let mut issues = profile.issues_mut(&repo)?; match options.op { Operation::Edit { @@ -546,7 +547,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { issue.label(labels, &signer)?; } Operation::List { assigned, state } => { - list(&issues, &assigned, &state, &profile)?; + list(issues, &assigned, &state, &profile)?; } Operation::Delete { id } => { let id = id.resolve(&repo.backend)?; @@ -562,13 +563,16 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> { Ok(()) } -fn list( - issues: &Issues, +fn list( + cache: C, assigned: &Option, state: &Option, profile: &profile::Profile, -) -> anyhow::Result<()> { - if issues.is_empty()? { +) -> anyhow::Result<()> +where + C: issue::cache::Issues, +{ + if cache.is_empty()? { term::print(term::format::italic("Nothing to show.")); return Ok(()); } @@ -580,7 +584,8 @@ fn list( }; let mut all = Vec::new(); - for result in issues.all()? { + let issues = cache.list()?; + for result in issues { let Ok((id, issue)) = result else { // Skip issues that failed to load. continue; @@ -664,16 +669,20 @@ fn list( Ok(()) } -fn open( +fn open( title: Option, description: Option, labels: Vec