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
+123 -58
View File
@@ -1,13 +1,23 @@
mod common;
use gitks::pb::archive_service_server::ArchiveService;
use gitks::pb::pack_service_server::PackService;
use gitks::pb::*;
#[test]
fn test_get_archive_tar() {
let (_dir, gb) = common::setup_bare_repo();
let chunks = gb
.get_archive(ArchiveRequest {
repository: None,
fn hdr(name: &str) -> RepositoryHeader {
RepositoryHeader {
relative_path: name.into(),
..Default::default()
}
}
#[tokio::test]
async fn test_get_archive_tar() {
let (dir, gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let chunks = svc
.get_archive(tonic::Request::new(ArchiveRequest {
repository: Some(hdr("test-repo")),
treeish: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -17,20 +27,24 @@ fn test_get_archive_tar() {
format: archive_options::Format::ArchiveFormatTar as i32,
..Default::default()
}),
})
.expect("get_archive tar");
}))
.await
.unwrap()
.into_inner();
let chunks: Vec<_> = tokio_stream::StreamExt::collect(chunks).await;
assert!(!chunks.is_empty(), "should produce archive data");
let total_size: usize = chunks.iter().map(|c| c.data.len()).sum();
let total_size: usize = chunks.iter().map(|c| c.as_ref().unwrap().data.len()).sum();
assert!(total_size > 0, "archive should not be empty");
}
#[test]
fn test_get_archive_zip() {
let (_dir, gb) = common::setup_bare_repo();
let chunks = gb
.get_archive(ArchiveRequest {
repository: None,
#[tokio::test]
async fn test_get_archive_zip() {
let (dir, gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let chunks = svc
.get_archive(tonic::Request::new(ArchiveRequest {
repository: Some(hdr("test-repo")),
treeish: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -40,23 +54,27 @@ fn test_get_archive_zip() {
format: archive_options::Format::ArchiveFormatZip as i32,
..Default::default()
}),
})
.expect("get_archive zip");
}))
.await
.unwrap()
.into_inner();
let chunks: Vec<_> = tokio_stream::StreamExt::collect(chunks).await;
assert!(!chunks.is_empty());
let data = &chunks[0].data;
let data = &chunks[0].as_ref().unwrap().data;
assert!(
data.starts_with(b"PK"),
"zip archive should start with PK magic bytes"
);
}
#[test]
fn test_list_archive_entries() {
let (_dir, gb) = common::setup_bare_repo();
let result = gb
.list_archive_entries(ListArchiveEntriesRequest {
repository: None,
#[tokio::test]
async fn test_list_archive_entries() {
let (dir, gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let result = svc
.list_archive_entries(tonic::Request::new(ListArchiveEntriesRequest {
repository: Some(hdr("test-repo")),
treeish: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -64,8 +82,10 @@ fn test_list_archive_entries() {
}),
pathspec: vec![],
pagination: None,
})
.expect("list_archive_entries");
}))
.await
.unwrap()
.into_inner();
assert!(!result.entries.is_empty(), "should list entries");
let paths: Vec<&str> = result.entries.iter().map(|e| e.path.as_str()).collect();
@@ -76,12 +96,13 @@ fn test_list_archive_entries() {
);
}
#[test]
fn test_get_archive_with_prefix() {
let (_dir, gb) = common::setup_bare_repo();
let chunks = gb
.get_archive(ArchiveRequest {
repository: None,
#[tokio::test]
async fn test_get_archive_with_prefix() {
let (dir, gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let chunks = svc
.get_archive(tonic::Request::new(ArchiveRequest {
repository: Some(hdr("test-repo")),
treeish: Some(ObjectSelector {
selector: Some(object_selector::Selector::Revision(ObjectName {
revision: "main".into(),
@@ -92,29 +113,68 @@ fn test_get_archive_with_prefix() {
prefix: "project/".into(),
..Default::default()
}),
})
.expect("get_archive with prefix");
}))
.await
.unwrap()
.into_inner();
let chunks: Vec<_> = tokio_stream::StreamExt::collect(chunks).await;
assert!(!chunks.is_empty());
}
#[test]
fn test_fsck_clean_repo() {
let (_dir, gb) = common::setup_bare_repo();
let result = gb
.fsck(FsckRequest {
repository: None,
#[tokio::test]
async fn test_fsck_clean_repo() {
let (dir, gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let result = svc
.fsck(tonic::Request::new(FsckRequest {
repository: Some(hdr("test-repo")),
strict: false,
connectivity_only: false,
})
.expect("fsck");
}))
.await
.unwrap()
.into_inner();
assert!(result.ok);
assert!(result.errors.is_empty());
}
#[test]
fn test_list_packfiles() {
let (_dir, gb) = common::setup_bare_repo();
#[tokio::test]
async fn test_fsck_strict() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let result = svc
.fsck(tonic::Request::new(FsckRequest {
repository: Some(hdr("test-repo")),
strict: true,
connectivity_only: false,
}))
.await
.unwrap()
.into_inner();
assert!(result.ok, "strict fsck should pass on clean repo");
}
#[tokio::test]
async fn test_fsck_connectivity_only() {
let (dir, _gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let result = svc
.fsck(tonic::Request::new(FsckRequest {
repository: Some(hdr("test-repo")),
strict: false,
connectivity_only: true,
}))
.await
.unwrap()
.into_inner();
assert!(result.ok, "connectivity-only fsck should pass");
}
#[tokio::test]
async fn test_list_packfiles() {
let (dir, gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
duct::cmd(
"git",
@@ -128,12 +188,14 @@ fn test_list_packfiles() {
.run()
.expect("git gc");
let result = gb
.list_packfiles(ListPackfilesRequest {
repository: None,
let result = svc
.list_packfiles(tonic::Request::new(ListPackfilesRequest {
repository: Some(hdr("test-repo")),
pagination: None,
})
.expect("list_packfiles");
}))
.await
.unwrap()
.into_inner();
assert!(
!result.packfiles.is_empty(),
@@ -145,16 +207,19 @@ fn test_list_packfiles() {
}
}
#[test]
fn test_advertise_refs() {
let (_dir, gb) = common::setup_bare_repo();
let result = gb
.advertise_refs(AdvertiseRefsRequest {
repository: None,
#[tokio::test]
async fn test_advertise_refs() {
let (dir, gb) = common::setup_bare_repo();
let svc = common::setup_service(dir.path());
let result = svc
.advertise_refs(tonic::Request::new(AdvertiseRefsRequest {
repository: Some(hdr("test-repo")),
protocol: None,
service: String::new(),
})
.expect("advertise_refs");
}))
.await
.unwrap()
.into_inner();
assert!(!result.references.is_empty(), "should have refs");
let ref_names: Vec<&str> = result.references.iter().map(|r| r.name.as_str()).collect();