feat(server): add tracing spans and caching to archive and blame services

- Add tracing spans with repo labels for archive and blame operations
- Implement caching for archive list entries when using OID selectors
- Implement caching for blame operations when using OID selectors
- Add detailed
This commit is contained in:
zhenyi
2026-06-04 15:33:16 +08:00
parent 729604f13b
commit cc202d6d1f
41 changed files with 2400 additions and 1067 deletions
+172 -120
View File
@@ -1,13 +1,23 @@
mod common;
use gitks::pb::commit_service_server::CommitService;
use gitks::pb::tree_service_server::TreeService;
use gitks::pb::*;
#[test]
fn test_get_commit_with_author() {
let (_dir, gb) = common::setup_bare_repo();
let commit = gb
.get_commit(GetCommitRequest {
repository: None,
fn hdr() -> RepositoryHeader {
RepositoryHeader {
relative_path: "test-repo".into(),
..Default::default()
}
}
#[tokio::test]
async fn test_get_commit_with_author() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let commit = svc
.get_commit(tonic::Request::new(GetCommitRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -15,8 +25,10 @@ fn test_get_commit_with_author() {
}),
include_stats: false,
include_raw: false,
})
.expect("get_commit");
}))
.await
.unwrap()
.into_inner();
assert!(commit.author.is_some(), "author must be populated");
let author = commit.author.as_ref().unwrap();
@@ -43,12 +55,13 @@ fn test_get_commit_with_author() {
);
}
#[test]
fn test_get_commit_subject_body() {
let (_dir, gb) = common::setup_bare_repo();
let commit = gb
.get_commit(GetCommitRequest {
repository: None,
#[tokio::test]
async fn test_get_commit_subject_body() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let commit = svc
.get_commit(tonic::Request::new(GetCommitRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main~2".into(),
@@ -56,8 +69,10 @@ fn test_get_commit_subject_body() {
}),
include_stats: false,
include_raw: false,
})
.expect("get_commit");
}))
.await
.unwrap()
.into_inner();
assert_eq!(commit.subject, "second commit");
assert!(!commit.message.is_empty());
@@ -65,12 +80,13 @@ fn test_get_commit_subject_body() {
assert!(!commit.parent_oids.is_empty());
}
#[test]
fn test_get_commit_with_raw() {
let (_dir, gb) = common::setup_bare_repo();
let commit = gb
.get_commit(GetCommitRequest {
repository: None,
#[tokio::test]
async fn test_get_commit_with_raw() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let commit = svc
.get_commit(tonic::Request::new(GetCommitRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -78,8 +94,10 @@ fn test_get_commit_with_raw() {
}),
include_stats: false,
include_raw: true,
})
.expect("get_commit with raw");
}))
.await
.unwrap()
.into_inner();
assert!(
!commit.raw.is_empty(),
@@ -90,12 +108,13 @@ fn test_get_commit_with_raw() {
assert!(raw_str.contains("author"), "raw should contain author line");
}
#[test]
fn test_list_commits_with_pagination() {
let (_dir, gb) = common::setup_bare_repo();
let page1 = gb
.list_commits(ListCommitsRequest {
repository: None,
#[tokio::test]
async fn test_list_commits_with_pagination() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let page1 = svc
.list_commits(tonic::Request::new(ListCommitsRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -113,15 +132,17 @@ fn test_list_commits_with_pagination() {
page_size: 2,
page_token: String::new(),
}),
})
.expect("list_commits page 1");
}))
.await
.unwrap()
.into_inner();
assert_eq!(page1.commits.len(), 2);
let pi = page1.page_info.unwrap();
assert!(pi.has_next_page);
let page2 = gb
.list_commits(ListCommitsRequest {
repository: None,
let page2 = svc
.list_commits(tonic::Request::new(ListCommitsRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -139,18 +160,21 @@ fn test_list_commits_with_pagination() {
page_size: 2,
page_token: pi.next_page_token,
}),
})
.expect("list_commits page 2");
}))
.await
.unwrap()
.into_inner();
assert!(!page2.commits.is_empty());
assert_ne!(page1.commits[0].oid, page2.commits[0].oid);
}
#[test]
fn test_get_commit_ancestors_pagination() {
let (_dir, gb) = common::setup_bare_repo();
let page1 = gb
.get_commit_ancestors(GetCommitAncestorsRequest {
repository: None,
#[tokio::test]
async fn test_get_commit_ancestors_pagination() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let page1 = svc
.get_commit_ancestors(tonic::Request::new(GetCommitAncestorsRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -161,8 +185,10 @@ fn test_get_commit_ancestors_pagination() {
page_size: 2,
page_token: String::new(),
}),
})
.expect("ancestors page 1");
}))
.await
.unwrap()
.into_inner();
assert_eq!(page1.commits.len(), 2);
let pi = page1.page_info.unwrap();
@@ -172,9 +198,9 @@ fn test_get_commit_ancestors_pagination() {
"next_page_token must be set"
);
let page2 = gb
.get_commit_ancestors(GetCommitAncestorsRequest {
repository: None,
let page2 = svc
.get_commit_ancestors(tonic::Request::new(GetCommitAncestorsRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -185,8 +211,10 @@ fn test_get_commit_ancestors_pagination() {
page_size: 2,
page_token: pi.next_page_token,
}),
})
.expect("ancestors page 2");
}))
.await
.unwrap()
.into_inner();
assert!(!page2.commits.is_empty(), "page 2 should have commits");
assert_ne!(
@@ -195,12 +223,13 @@ fn test_get_commit_ancestors_pagination() {
);
}
#[test]
fn test_compare_commits() {
let (_dir, gb) = common::setup_bare_repo();
let result = gb
.compare_commits(CompareCommitsRequest {
repository: None,
#[tokio::test]
async fn test_compare_commits() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let result = svc
.compare_commits(tonic::Request::new(CompareCommitsRequest {
repository: Some(hdr()),
base: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "feature".into(),
@@ -217,8 +246,10 @@ fn test_compare_commits() {
page_size: 100,
page_token: String::new(),
}),
})
.expect("compare_commits");
}))
.await
.unwrap()
.into_inner();
assert!(!result.commits.is_empty());
assert!(result.merge_base.is_some());
@@ -226,13 +257,14 @@ fn test_compare_commits() {
assert!(stats.additions > 0);
}
#[test]
fn test_create_commit_and_cherry_pick() {
let (_dir, gb) = common::setup_bare_repo();
#[tokio::test]
async fn test_create_commit_and_cherry_pick() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let created = gb
.create_commit(CreateCommitRequest {
repository: None,
let created = svc
.create_commit(tonic::Request::new(CreateCommitRequest {
repository: Some(hdr()),
branch: "feature".into(),
message: "cherry-pick source".into(),
author: Some(Signature {
@@ -265,8 +297,10 @@ fn test_create_commit_and_cherry_pick() {
}),
force: false,
trailers: vec![],
})
.expect("create_commit for cherry-pick source");
}))
.await
.unwrap()
.into_inner();
let source_oid = created
.commit
@@ -278,9 +312,9 @@ fn test_create_commit_and_cherry_pick() {
.hex
.clone();
let cp_result = gb
.cherry_pick_commit(CherryPickCommitRequest {
repository: None,
let cp_result = svc
.cherry_pick_commit(tonic::Request::new(CherryPickCommitRequest {
repository: Some(hdr()),
commit: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: source_oid.clone(),
@@ -296,15 +330,17 @@ fn test_create_commit_and_cherry_pick() {
}),
message: String::new(),
mainline: 0,
})
.expect("cherry_pick_commit");
}))
.await
.unwrap()
.into_inner();
let cp_commit = cp_result.commit.unwrap();
assert_eq!(cp_commit.subject, "cherry-pick source");
let blob = gb
.get_blob(GetBlobRequest {
repository: None,
let blob = svc
.get_blob(tonic::Request::new(GetBlobRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -313,14 +349,17 @@ fn test_create_commit_and_cherry_pick() {
path: "cp_file.txt".into(),
oid: None,
max_bytes: 0,
})
.expect("get_blob after cherry-pick");
}))
.await
.unwrap()
.into_inner();
assert_eq!(blob.data, b"cherry pick me");
}
#[test]
fn test_cherry_pick_root_commit() {
let (dir, gb) = common::setup_bare_repo();
#[tokio::test]
async fn test_cherry_pick_root_commit() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let work_dir = dir.path().join("work");
common::run(&work_dir, &["checkout", "--orphan", "root-source"]);
@@ -338,8 +377,8 @@ fn test_cherry_pick_root_commit() {
.stdout;
let root_oid = String::from_utf8(root_oid).unwrap().trim().to_string();
gb.cherry_pick_commit(CherryPickCommitRequest {
repository: None,
svc.cherry_pick_commit(tonic::Request::new(CherryPickCommitRequest {
repository: Some(hdr()),
commit: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: root_oid,
@@ -349,12 +388,13 @@ fn test_cherry_pick_root_commit() {
committer: None,
message: String::new(),
mainline: 0,
})
.expect("cherry_pick_commit root");
}))
.await
.unwrap();
let blob = gb
.get_blob(GetBlobRequest {
repository: None,
let blob = svc
.get_blob(tonic::Request::new(GetBlobRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "feature".into(),
@@ -363,18 +403,21 @@ fn test_cherry_pick_root_commit() {
path: "root_only.txt".into(),
oid: None,
max_bytes: 0,
})
.expect("get root file after cherry-pick");
}))
.await
.unwrap()
.into_inner();
assert_eq!(blob.data, b"from root\n");
}
#[test]
fn test_revert_commit() {
let (_dir, gb) = common::setup_bare_repo();
#[tokio::test]
async fn test_revert_commit() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let created = gb
.create_commit(CreateCommitRequest {
repository: None,
let created = svc
.create_commit(tonic::Request::new(CreateCommitRequest {
repository: Some(hdr()),
branch: "main".into(),
message: "to be reverted".into(),
author: None,
@@ -395,8 +438,10 @@ fn test_revert_commit() {
}),
force: false,
trailers: vec![],
})
.expect("create_commit");
}))
.await
.unwrap()
.into_inner();
let to_revert = created
.commit
@@ -408,9 +453,9 @@ fn test_revert_commit() {
.hex
.clone();
let revert_result = gb
.revert_commit(RevertCommitRequest {
repository: None,
let revert_result = svc
.revert_commit(tonic::Request::new(RevertCommitRequest {
repository: Some(hdr()),
commit: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: to_revert,
@@ -419,8 +464,10 @@ fn test_revert_commit() {
branch: "main".into(),
committer: None,
message: String::new(),
})
.expect("revert_commit");
}))
.await
.unwrap()
.into_inner();
let revert_commit = revert_result.commit.unwrap();
assert!(
@@ -429,29 +476,32 @@ fn test_revert_commit() {
revert_commit.subject
);
let blob_result = gb.get_blob(GetBlobRequest {
repository: None,
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
})),
}),
path: "revert_me.txt".into(),
oid: None,
max_bytes: 0,
});
let blob_result = svc
.get_blob(tonic::Request::new(GetBlobRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
})),
}),
path: "revert_me.txt".into(),
oid: None,
max_bytes: 0,
}))
.await;
assert!(
blob_result.is_err(),
"revert_me.txt should be deleted after revert"
);
}
#[test]
fn test_oid_binary_encoding() {
let (_dir, gb) = common::setup_bare_repo();
let commit = gb
.get_commit(GetCommitRequest {
repository: None,
#[tokio::test]
async fn test_oid_binary_encoding() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let commit = svc
.get_commit(tonic::Request::new(GetCommitRequest {
repository: Some(hdr()),
revision: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -459,8 +509,10 @@ fn test_oid_binary_encoding() {
}),
include_stats: false,
include_raw: false,
})
.expect("get_commit");
}))
.await
.unwrap()
.into_inner();
let oid = commit.oid.unwrap();
assert_eq!(oid.value.len(), 20);
assert_eq!(oid.hex.len(), 40);