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
+61 -17
View File
@@ -1,6 +1,7 @@
mod archive;
mod blame;
mod branch;
mod cache;
mod commit;
mod diff;
mod merge;
@@ -23,11 +24,23 @@ use crate::pb::{
#[derive(Clone)]
pub struct GitksService {
/// 所有仓库的根路径前缀
pub(crate) repo_prefix: PathBuf,
/// Root prefix path for all repositories
pub repo_prefix: PathBuf,
}
impl GitksService {
fn repo_label(&self, header: Option<&crate::pb::RepositoryHeader>) -> String {
header
.and_then(|h| {
if h.relative_path.is_empty() {
None
} else {
Some(h.relative_path.clone())
}
})
.unwrap_or_else(|| "unknown".into())
}
pub(crate) fn resolve(
&self,
header: Option<&crate::pb::RepositoryHeader>,
@@ -35,7 +48,12 @@ impl GitksService {
let header =
header.ok_or_else(|| tonic::Status::invalid_argument("repository is required"))?;
let header = self.prefixed_header(header);
GitBare::from_repository_header(&header).map_err(into_status)
let gb = GitBare::from_repository_header(&header).map_err(into_status)?;
tracing::debug!(
repo = %gb.bare_dir.display(),
"resolved repository"
);
Ok(gb)
}
pub(crate) fn resolve_for_init(
@@ -49,7 +67,7 @@ impl GitksService {
return Err(tonic::Status::invalid_argument("relative_path is required"));
}
let candidate = self.repo_prefix.join(relative_path);
// 路径穿越检查
// Path traversal check
let canonical = candidate
.canonicalize()
.unwrap_or_else(|_| candidate.clone());
@@ -65,11 +83,8 @@ impl GitksService {
Ok(canonical)
}
/// 将客户端传入的 header 注入 repo_prefix 作为 storage_path
fn prefixed_header(
&self,
header: &crate::pb::RepositoryHeader,
) -> crate::pb::RepositoryHeader {
/// Inject repo_prefix as storage_path into the client-provided header
fn prefixed_header(&self, header: &crate::pb::RepositoryHeader) -> crate::pb::RepositoryHeader {
crate::pb::RepositoryHeader {
storage_path: self.repo_prefix.to_string_lossy().into_owned(),
relative_path: header.relative_path.clone(),
@@ -115,20 +130,49 @@ pub(crate) fn git_cmd(gb: &GitBare, args: &[&str]) -> Result<std::process::Outpu
gb.bare_dir.to_string_lossy().into_owned(),
];
full_args.extend(args.iter().map(|s| s.to_string()));
std::process::Command::new("git")
tracing::debug!(
repo = %gb.bare_dir.display(),
args = %full_args.iter().skip(2).cloned().collect::<Vec<_>>().join(" "),
"spawning git subprocess"
);
let result = std::process::Command::new("git")
.args(&full_args)
.output()
.map_err(|e| tonic::Status::internal(e.to_string()))
.map_err(|e| {
tracing::error!(
repo = %gb.bare_dir.display(),
error = %e,
"failed to spawn git subprocess"
);
tonic::Status::internal(e.to_string())
})?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
tracing::warn!(
repo = %gb.bare_dir.display(),
status = ?result.status.code(),
stderr = %stderr.trim(),
"git subprocess exited with non-zero status"
);
}
Ok(result)
}
pub async fn serve(
addr: std::net::SocketAddr,
repo_prefix: PathBuf,
) -> Result<(), tonic::transport::Error> {
let span = tracing::info_span!("gitks.server", %addr);
let _enter = span.enter();
let svc = GitksService { repo_prefix };
tonic::transport::Server::builder()
.add_service(repository_service_server::RepositoryServiceServer::new(svc.clone()))
.add_service(archive_service_server::ArchiveServiceServer::new(svc.clone()))
tracing::info!("registering gRPC services");
let server = tonic::transport::Server::builder()
.add_service(repository_service_server::RepositoryServiceServer::new(
svc.clone(),
))
.add_service(archive_service_server::ArchiveServiceServer::new(
svc.clone(),
))
.add_service(blame_service_server::BlameServiceServer::new(svc.clone()))
.add_service(branch_service_server::BranchServiceServer::new(svc.clone()))
.add_service(commit_service_server::CommitServiceServer::new(svc.clone()))
@@ -136,7 +180,7 @@ pub async fn serve(
.add_service(merge_service_server::MergeServiceServer::new(svc.clone()))
.add_service(pack_service_server::PackServiceServer::new(svc.clone()))
.add_service(tag_service_server::TagServiceServer::new(svc.clone()))
.add_service(tree_service_server::TreeServiceServer::new(svc))
.serve(addr)
.await
.add_service(tree_service_server::TreeServiceServer::new(svc));
tracing::info!("server ready, starting to accept connections");
server.serve(addr).await
}