mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-30 16:50:47 +08:00
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 <fintan.halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
committed by
cloudhead
parent
67e46952b6
commit
02fb9397e8
@@ -9,6 +9,8 @@ use axum::http::Method;
|
||||
use axum::response::{IntoResponse, Json};
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use radicle::issue::cache::Issues as _;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tokio::sync::RwLock;
|
||||
@@ -60,8 +62,8 @@ impl Context {
|
||||
|
||||
let payload = doc.project()?;
|
||||
let delegates = doc.delegates;
|
||||
let issues = issue::Issues::open(&repo)?.counts()?;
|
||||
let patches = patch::Patches::open(&repo)?.counts()?;
|
||||
let issues = self.profile.issues(&repo)?.counts()?;
|
||||
let patches = self.profile.patches(&repo)?.counts()?;
|
||||
let db = &self.profile.database()?;
|
||||
let seeding = db.count(&id).unwrap_or_default();
|
||||
|
||||
|
||||
@@ -30,6 +30,14 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
Storage(#[from] radicle::storage::Error),
|
||||
|
||||
/// Cob cache error.
|
||||
#[error(transparent)]
|
||||
CobCache(#[from] radicle::cob::cache::Error),
|
||||
|
||||
/// Cob issue cache error.
|
||||
#[error(transparent)]
|
||||
CacheIssue(#[from] radicle::cob::issue::cache::Error),
|
||||
|
||||
/// Cob issue error.
|
||||
#[error(transparent)]
|
||||
CobIssue(#[from] radicle::cob::issue::Error),
|
||||
@@ -38,6 +46,10 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
CobPatch(#[from] radicle::cob::patch::Error),
|
||||
|
||||
/// Cob patch cache error.
|
||||
#[error(transparent)]
|
||||
CachePatch(#[from] radicle::cob::patch::cache::Error),
|
||||
|
||||
/// Cob store error.
|
||||
#[error(transparent)]
|
||||
CobStore(#[from] radicle::cob::store::Error),
|
||||
@@ -123,6 +135,7 @@ impl IntoResponse for Error {
|
||||
Error::BadRequest(msg) => (StatusCode::BAD_REQUEST, Some(msg)),
|
||||
other => {
|
||||
tracing::error!("Error: {message}");
|
||||
tracing::debug!("Error Debug: {:?}", other);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, Some(other.to_string()))
|
||||
|
||||
@@ -5,10 +5,10 @@ use axum::response::IntoResponse;
|
||||
use axum::routing::get;
|
||||
use axum::{Json, Router};
|
||||
|
||||
use radicle::cob::issue::Issues;
|
||||
use radicle::cob::patch::Patches;
|
||||
use radicle::identity::{Did, Visibility};
|
||||
use radicle::issue::cache::Issues as _;
|
||||
use radicle::node::routing::Store;
|
||||
use radicle::patch::cache::Patches as _;
|
||||
use radicle::storage::{ReadRepository, ReadStorage};
|
||||
|
||||
use crate::api::error::Error;
|
||||
@@ -44,7 +44,6 @@ async fn delegates_projects_handler(
|
||||
let storage = &ctx.profile.storage;
|
||||
let db = &ctx.profile.database()?;
|
||||
let pinned = &ctx.profile.config.web.pinned;
|
||||
|
||||
let mut projects = match show {
|
||||
ProjectQuery::All => storage
|
||||
.repositories()?
|
||||
@@ -73,13 +72,13 @@ async fn delegates_projects_handler(
|
||||
let Ok(payload) = id.doc.project() else {
|
||||
return None;
|
||||
};
|
||||
let Ok(issues) = Issues::open(&repo) else {
|
||||
let Ok(issues) = ctx.profile.issues(&repo) else {
|
||||
return None;
|
||||
};
|
||||
let Ok(issues) = issues.counts() else {
|
||||
return None;
|
||||
};
|
||||
let Ok(patches) = Patches::open(&repo) else {
|
||||
let Ok(patches) = ctx.profile.patches(&repo) else {
|
||||
return None;
|
||||
};
|
||||
let Ok(patches) = patches.counts() else {
|
||||
|
||||
@@ -14,7 +14,10 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tower_http::set_header::SetResponseHeaderLayer;
|
||||
|
||||
use radicle::cob::{issue, patch, resolve_embed, Embed, Label, Uri};
|
||||
use radicle::cob::{
|
||||
issue, issue::cache::Issues as _, patch, patch::cache::Patches as _, resolve_embed, Embed,
|
||||
Label, Uri,
|
||||
};
|
||||
use radicle::identity::{Did, RepoId, Visibility};
|
||||
use radicle::node::routing::Store;
|
||||
use radicle::node::{AliasStore, Node, NodeId};
|
||||
@@ -126,13 +129,13 @@ async fn project_root_handler(
|
||||
let Ok(payload) = info.doc.project() else {
|
||||
return None;
|
||||
};
|
||||
let Ok(issues) = issue::Issues::open(&repo) else {
|
||||
let Ok(issues) = ctx.profile.issues(&repo) else {
|
||||
return None;
|
||||
};
|
||||
let Ok(issues) = issues.counts() else {
|
||||
return None;
|
||||
};
|
||||
let Ok(patches) = patch::Patches::open(&repo) else {
|
||||
let Ok(patches) = ctx.profile.patches(&repo) else {
|
||||
return None;
|
||||
};
|
||||
let Ok(patches) = patches.counts() else {
|
||||
@@ -587,9 +590,9 @@ async fn issues_handler(
|
||||
let state = state.unwrap_or_default();
|
||||
let storage = &ctx.profile.storage;
|
||||
let repo = storage.repository(project)?;
|
||||
let issues = issue::Issues::open(&repo)?;
|
||||
let issues = ctx.profile.issues(&repo)?;
|
||||
let mut issues: Vec<_> = issues
|
||||
.all()?
|
||||
.list()?
|
||||
.filter_map(|r| {
|
||||
let (id, issue) = r.ok()?;
|
||||
(state.matches(issue.state())).then_some((id, issue))
|
||||
@@ -639,7 +642,7 @@ async fn issue_create_handler(
|
||||
.filter_map(|embed| resolve_embed(&repo, embed))
|
||||
.collect();
|
||||
|
||||
let mut issues = issue::Issues::open(&repo)?;
|
||||
let mut issues = ctx.profile.issues_mut(&repo)?;
|
||||
let issue = issues
|
||||
.create(
|
||||
issue.title,
|
||||
@@ -672,7 +675,7 @@ async fn issue_update_handler(
|
||||
let storage = &ctx.profile.storage;
|
||||
let signer = ctx.profile.signer()?;
|
||||
let repo = storage.repository(project)?;
|
||||
let mut issues = issue::Issues::open(&repo)?;
|
||||
let mut issues = ctx.profile.issues_mut(&repo)?;
|
||||
let mut issue = issues.get_mut(&issue_id.into())?;
|
||||
|
||||
let id = match action {
|
||||
@@ -723,7 +726,9 @@ async fn issue_handler(
|
||||
) -> impl IntoResponse {
|
||||
let storage = &ctx.profile.storage;
|
||||
let repo = storage.repository(project)?;
|
||||
let issue = issue::Issues::open(&repo)?
|
||||
let issue = ctx
|
||||
.profile
|
||||
.issues(&repo)?
|
||||
.get(&issue_id.into())?
|
||||
.ok_or(Error::NotFound)?;
|
||||
let aliases = ctx.profile.aliases();
|
||||
@@ -756,7 +761,7 @@ async fn patch_create_handler(
|
||||
.signer()
|
||||
.map_err(|_| Error::Auth("Unauthorized"))?;
|
||||
let repo = storage.repository(project)?;
|
||||
let mut patches = patch::Patches::open(&repo)?;
|
||||
let mut patches = ctx.profile.patches_mut(&repo)?;
|
||||
let base_oid = repo.raw().merge_base(*patch.target, *patch.oid)?;
|
||||
|
||||
let patch = patches
|
||||
@@ -795,7 +800,7 @@ async fn patch_update_handler(
|
||||
.signer()
|
||||
.map_err(|_| Error::Auth("Unauthorized"))?;
|
||||
let repo = storage.repository(project)?;
|
||||
let mut patches = patch::Patches::open(&repo)?;
|
||||
let mut patches = ctx.profile.patches_mut(&repo)?;
|
||||
let mut patch = patches.get_mut(&patch_id.into())?;
|
||||
let id = match action {
|
||||
patch::Action::Edit { title, target } => patch.edit(title, target, &signer)?,
|
||||
@@ -940,9 +945,9 @@ async fn patches_handler(
|
||||
let state = state.unwrap_or_default();
|
||||
let storage = &ctx.profile.storage;
|
||||
let repo = storage.repository(project)?;
|
||||
let patches = patch::Patches::open(&repo)?;
|
||||
let patches = ctx.profile.patches(&repo)?;
|
||||
let mut patches = patches
|
||||
.all()?
|
||||
.list()?
|
||||
.filter_map(|r| {
|
||||
let (id, patch) = r.ok()?;
|
||||
(state.matches(patch.state())).then_some((id, patch))
|
||||
@@ -968,9 +973,8 @@ async fn patch_handler(
|
||||
) -> impl IntoResponse {
|
||||
let storage = &ctx.profile.storage;
|
||||
let repo = storage.repository(project)?;
|
||||
let patch = patch::Patches::open(&repo)?
|
||||
.get(&patch_id.into())?
|
||||
.ok_or(Error::NotFound)?;
|
||||
let patches = ctx.profile.patches(&repo)?;
|
||||
let patch = patches.get(&patch_id.into())?.ok_or(Error::NotFound)?;
|
||||
let aliases = ctx.profile.aliases();
|
||||
|
||||
Ok::<_, Error>(Json(api::json::patch(
|
||||
|
||||
@@ -11,18 +11,16 @@ use serde_json::Value;
|
||||
use time::OffsetDateTime;
|
||||
use tower::ServiceExt;
|
||||
|
||||
use radicle::cob::issue::Issues;
|
||||
use radicle::cob::patch::{MergeTarget, Patches};
|
||||
use radicle::cob::patch::MergeTarget;
|
||||
use radicle::crypto::ssh::keystore::MemorySigner;
|
||||
use radicle::crypto::ssh::Keystore;
|
||||
use radicle::crypto::{KeyPair, Seed, Signer};
|
||||
use radicle::git::{raw as git2, RefString};
|
||||
use radicle::identity::Visibility;
|
||||
use radicle::node;
|
||||
use radicle::profile;
|
||||
use radicle::profile::Home;
|
||||
use radicle::storage::ReadStorage;
|
||||
use radicle::Storage;
|
||||
use radicle::{node, profile};
|
||||
use radicle_crypto::test::signer::MockSigner;
|
||||
|
||||
use crate::api::{auth, Context};
|
||||
@@ -231,7 +229,7 @@ fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G
|
||||
|
||||
let storage = &profile.storage;
|
||||
let repo = storage.repository(rid).unwrap();
|
||||
let mut issues = Issues::open(&repo).unwrap();
|
||||
let mut issues = profile.issues_mut(&repo).unwrap();
|
||||
let issue = issues
|
||||
.create(
|
||||
"Issue #1".to_string(),
|
||||
@@ -245,7 +243,7 @@ fn seed_with_signer<G: Signer>(dir: &Path, profile: radicle::Profile, signer: &G
|
||||
tracing::debug!(target: "test", "Contributor issue: {}", issue.id());
|
||||
|
||||
// eq. rad patch open
|
||||
let mut patches = Patches::open(&repo).unwrap();
|
||||
let mut patches = profile.patches_mut(&repo).unwrap();
|
||||
let oid = radicle::git::Oid::from_str(HEAD).unwrap();
|
||||
let base = radicle::git::Oid::from_str(PARENT).unwrap();
|
||||
let patch = patches
|
||||
|
||||
Reference in New Issue
Block a user