feat(cluster): implement distributed clustering with etcd coordination
- Integrate etcd-client for distributed coordination and leader election - Add remote client macros with proper formatting for all services - Implement RequestMetrics for tracking RPC performance and errors - Add rate limiting mechanism across all service endpoints - Create ElectionRequest and ElectionResult message types for leader election - Add role management with primary/replica switching capabilities - Implement health checker with automatic failover detection - Add repository count metrics for cluster monitoring - Update Cargo.toml with etcd-client and dashmap dependencies - Modify RepoEntry to include read_only flag for replica handling - Implement should_accept_election logic to prevent duplicate elections - Add RoleChangedEvent handling for cluster role updates
This commit is contained in:
+23
-3
@@ -3,7 +3,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, cache, into_status};
|
||||
|
||||
remote_client!(remote_archive_client, ArchiveServiceClient<tonic::transport::Channel>, "archive");
|
||||
remote_client!(
|
||||
remote_archive_client,
|
||||
ArchiveServiceClient<tonic::transport::Channel>,
|
||||
"archive"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl archive_service_server::ArchiveService for GitksService {
|
||||
@@ -14,7 +18,9 @@ impl archive_service_server::ArchiveService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ArchiveRequest>,
|
||||
) -> Result<tonic::Response<Self::GetArchiveStream>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.ArchiveService/GetArchive");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("archive.get_archive", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -24,16 +30,22 @@ impl archive_service_server::ArchiveService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_archive_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
let resp = client.get_archive(inner).await?;
|
||||
let stream = super::bridge_server_stream(resp.into_inner());
|
||||
return Ok(tonic::Response::new(stream));
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let stream = gb.get_archive_stream(inner)?;
|
||||
tracing::info!(%repo, "archive streaming started");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(stream))
|
||||
}
|
||||
|
||||
@@ -41,7 +53,9 @@ impl archive_service_server::ArchiveService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ListArchiveEntriesRequest>,
|
||||
) -> Result<tonic::Response<ListArchiveEntriesResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.ArchiveService/ListArchiveEntries");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("archive.list_archive_entries", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -51,11 +65,16 @@ impl archive_service_server::ArchiveService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_archive_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.list_archive_entries(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.treeish) {
|
||||
cache::cached_response("archive.list_archive_entries", &inner, || {
|
||||
@@ -65,6 +84,7 @@ impl archive_service_server::ArchiveService for GitksService {
|
||||
gb.list_archive_entries(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.entries.len(), "list_archive_entries done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+23
-3
@@ -3,7 +3,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, cache, into_status, into_stream};
|
||||
|
||||
remote_client!(remote_blame_client, BlameServiceClient<tonic::transport::Channel>, "blame");
|
||||
remote_client!(
|
||||
remote_blame_client,
|
||||
BlameServiceClient<tonic::transport::Channel>,
|
||||
"blame"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl blame_service_server::BlameService for GitksService {
|
||||
@@ -14,7 +18,9 @@ impl blame_service_server::BlameService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<BlameRequest>,
|
||||
) -> Result<tonic::Response<BlameResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BlameService/Blame");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let path = inner.path.clone();
|
||||
let span = tracing::info_span!("blame.blame", %repo, %path);
|
||||
@@ -25,11 +31,16 @@ impl blame_service_server::BlameService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_blame_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.blame(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("blame.blame", &inner, || {
|
||||
@@ -39,6 +50,7 @@ impl blame_service_server::BlameService for GitksService {
|
||||
gb.blame(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, %path, hunks = resp.hunks.len(), "blame done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -46,7 +58,9 @@ impl blame_service_server::BlameService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<BlameRequest>,
|
||||
) -> Result<tonic::Response<Self::StreamBlameStream>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BlameService/StreamBlame");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let path = inner.path.clone();
|
||||
let span = tracing::info_span!("blame.stream_blame", %repo, %path);
|
||||
@@ -57,13 +71,18 @@ impl blame_service_server::BlameService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_blame_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
let resp = client.stream_blame(inner).await?;
|
||||
let stream = super::bridge_server_stream(resp.into_inner());
|
||||
return Ok(tonic::Response::new(stream));
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("blame.blame", &inner, || {
|
||||
@@ -72,6 +91,7 @@ impl blame_service_server::BlameService for GitksService {
|
||||
} else {
|
||||
gb.blame(inner).map_err(into_status)?
|
||||
};
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(into_stream(resp.hunks)))
|
||||
}
|
||||
}
|
||||
|
||||
+77
-9
@@ -3,7 +3,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status};
|
||||
|
||||
remote_client!(remote_branch_client, BranchServiceClient<tonic::transport::Channel>, "branch");
|
||||
remote_client!(
|
||||
remote_branch_client,
|
||||
BranchServiceClient<tonic::transport::Channel>,
|
||||
"branch"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl branch_service_server::BranchService for GitksService {
|
||||
@@ -11,7 +15,9 @@ impl branch_service_server::BranchService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ListBranchesRequest>,
|
||||
) -> Result<tonic::Response<ListBranchesResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BranchService/ListBranches");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("branch.list_branches", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -21,14 +27,20 @@ impl branch_service_server::BranchService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_branch_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.list_branches(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.list_branches(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, count = resp.branches.len(), "list_branches done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -36,7 +48,9 @@ impl branch_service_server::BranchService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetBranchRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BranchService/GetBranch");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.get_branch", %repo, %name);
|
||||
@@ -47,13 +61,19 @@ impl branch_service_server::BranchService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_branch_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_branch(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.get_branch(inner).map_err(into_status)?;
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -61,7 +81,9 @@ impl branch_service_server::BranchService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<CreateBranchRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BranchService/CreateBranch");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.create_branch", %repo, %name);
|
||||
@@ -72,15 +94,21 @@ impl branch_service_server::BranchService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_branch_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.create_branch(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.create_branch(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "branch created");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", name), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -88,7 +116,9 @@ impl branch_service_server::BranchService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<DeleteBranchRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BranchService/DeleteBranch");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.delete_branch", %repo, %name);
|
||||
@@ -99,15 +129,21 @@ impl branch_service_server::BranchService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_branch_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.delete_branch(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
gb.delete_branch(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "branch deleted");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", name), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
@@ -115,7 +151,9 @@ impl branch_service_server::BranchService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<RenameBranchRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BranchService/RenameBranch");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let old = inner.old_name.clone();
|
||||
let new = inner.new_name.clone();
|
||||
@@ -127,15 +165,21 @@ impl branch_service_server::BranchService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_branch_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.rename_branch(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.rename_branch(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, old = %old, new = %new, "branch renamed");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", new), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -143,7 +187,9 @@ impl branch_service_server::BranchService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<UpdateBranchTargetRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BranchService/UpdateBranchTarget");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.update_branch_target", %repo, %name);
|
||||
@@ -154,15 +200,21 @@ impl branch_service_server::BranchService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_branch_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.update_branch_target(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.update_branch_target(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "branch target updated");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", name), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -170,7 +222,9 @@ impl branch_service_server::BranchService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<SetBranchUpstreamRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BranchService/SetBranchUpstream");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.set_branch_upstream", %repo, %name);
|
||||
@@ -181,15 +235,21 @@ impl branch_service_server::BranchService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_branch_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.set_branch_upstream(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.set_branch_upstream(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "branch upstream set");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", name), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -197,7 +257,9 @@ impl branch_service_server::BranchService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<CompareBranchRequest>,
|
||||
) -> Result<tonic::Response<CompareBranchResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.BranchService/CompareBranch");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let source = inner.source_branch.clone();
|
||||
let target = inner.target_branch.clone();
|
||||
@@ -209,14 +271,20 @@ impl branch_service_server::BranchService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_branch_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.compare_branch(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.compare_branch(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %source, %target, ahead = resp.ahead_by, behind = resp.behind_by, "branch compared");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+68
-8
@@ -3,7 +3,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, cache, into_status};
|
||||
|
||||
remote_client!(remote_commit_client, CommitServiceClient<tonic::transport::Channel>, "commit");
|
||||
remote_client!(
|
||||
remote_commit_client,
|
||||
CommitServiceClient<tonic::transport::Channel>,
|
||||
"commit"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl commit_service_server::CommitService for GitksService {
|
||||
@@ -11,7 +15,9 @@ impl commit_service_server::CommitService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ListCommitsRequest>,
|
||||
) -> Result<tonic::Response<ListCommitsResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/ListCommits");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("commit.list_commits", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -21,11 +27,16 @@ impl commit_service_server::CommitService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_commit_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.list_commits(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if !inner.all && cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("commit.list_commits", &inner, || {
|
||||
@@ -35,6 +46,7 @@ impl commit_service_server::CommitService for GitksService {
|
||||
gb.list_commits(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.commits.len(), total = resp.page_info.as_ref().map(|p| p.total_count).unwrap_or(0), "list_commits done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -42,7 +54,9 @@ impl commit_service_server::CommitService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetCommitRequest>,
|
||||
) -> Result<tonic::Response<Commit>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/GetCommit");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("commit.get_commit", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -52,11 +66,16 @@ impl commit_service_server::CommitService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_commit_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_commit(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("commit.get_commit", &inner, || {
|
||||
@@ -65,6 +84,7 @@ impl commit_service_server::CommitService for GitksService {
|
||||
} else {
|
||||
gb.get_commit(inner).map_err(into_status)?
|
||||
};
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -72,7 +92,9 @@ impl commit_service_server::CommitService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetCommitAncestorsRequest>,
|
||||
) -> Result<tonic::Response<GetCommitAncestorsResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/GetCommitAncestors");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("commit.get_commit_ancestors", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -82,11 +104,16 @@ impl commit_service_server::CommitService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_commit_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_commit_ancestors(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("commit.get_commit_ancestors", &inner, || {
|
||||
@@ -96,6 +123,7 @@ impl commit_service_server::CommitService for GitksService {
|
||||
gb.get_commit_ancestors(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.commits.len(), "get_commit_ancestors done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -103,7 +131,9 @@ impl commit_service_server::CommitService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<CreateCommitRequest>,
|
||||
) -> Result<tonic::Response<CreateCommitResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CreateCommit");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let branch = inner.branch.clone();
|
||||
let span = tracing::info_span!("commit.create_commit", %repo, %branch);
|
||||
@@ -114,11 +144,16 @@ impl commit_service_server::CommitService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_commit_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.create_commit(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.create_commit(inner).map_err(into_status)?;
|
||||
let commit_hex = resp
|
||||
@@ -128,6 +163,7 @@ impl commit_service_server::CommitService for GitksService {
|
||||
.unwrap_or("?");
|
||||
tracing::info!(%repo, %branch, %commit_hex, "commit created");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", branch), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -135,7 +171,9 @@ impl commit_service_server::CommitService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<RevertCommitRequest>,
|
||||
) -> Result<tonic::Response<CreateCommitResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/RevertCommit");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let branch = inner.branch.clone();
|
||||
let span = tracing::info_span!("commit.revert_commit", %repo, %branch);
|
||||
@@ -146,15 +184,21 @@ impl commit_service_server::CommitService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_commit_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.revert_commit(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.revert_commit(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %branch, "commit reverted");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", branch), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -162,7 +206,9 @@ impl commit_service_server::CommitService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<CherryPickCommitRequest>,
|
||||
) -> Result<tonic::Response<CreateCommitResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CherryPickCommit");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let branch = inner.branch.clone();
|
||||
let span = tracing::info_span!("commit.cherry_pick_commit", %repo, %branch);
|
||||
@@ -173,15 +219,21 @@ impl commit_service_server::CommitService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_commit_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.cherry_pick_commit(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.cherry_pick_commit(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %branch, "commit cherry-picked");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", branch), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -189,7 +241,9 @@ impl commit_service_server::CommitService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<CompareCommitsRequest>,
|
||||
) -> Result<tonic::Response<CompareCommitsResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CompareCommits");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("commit.compare_commits", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -199,11 +253,16 @@ impl commit_service_server::CommitService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_commit_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.compare_commits(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selectors_are_oid(&inner.base, &inner.head) {
|
||||
cache::cached_response("commit.compare_commits", &inner, || {
|
||||
@@ -213,6 +272,7 @@ impl commit_service_server::CommitService for GitksService {
|
||||
gb.compare_commits(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.commits.len(), "compare_commits done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+41
-5
@@ -3,7 +3,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, cache, into_status, into_stream};
|
||||
|
||||
remote_client!(remote_diff_client, DiffServiceClient<tonic::transport::Channel>, "diff");
|
||||
remote_client!(
|
||||
remote_diff_client,
|
||||
DiffServiceClient<tonic::transport::Channel>,
|
||||
"diff"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl diff_service_server::DiffService for GitksService {
|
||||
@@ -14,7 +18,9 @@ impl diff_service_server::DiffService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetDiffRequest>,
|
||||
) -> Result<tonic::Response<GetDiffResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.DiffService/GetDiff");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("diff.get_diff", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -24,11 +30,16 @@ impl diff_service_server::DiffService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_diff_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_diff(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selectors_are_oid(&inner.base, &inner.head) {
|
||||
cache::cached_response("diff.get_diff", &inner, || {
|
||||
@@ -38,6 +49,7 @@ impl diff_service_server::DiffService for GitksService {
|
||||
gb.get_diff(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, files = resp.files.len(), overflow = resp.overflow, "get_diff done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -45,7 +57,9 @@ impl diff_service_server::DiffService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetCommitDiffRequest>,
|
||||
) -> Result<tonic::Response<GetDiffResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.DiffService/GetCommitDiff");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("diff.get_commit_diff", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -55,11 +69,16 @@ impl diff_service_server::DiffService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_diff_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_commit_diff(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.commit) {
|
||||
cache::cached_response("diff.get_commit_diff", &inner, || {
|
||||
@@ -69,6 +88,7 @@ impl diff_service_server::DiffService for GitksService {
|
||||
gb.get_commit_diff(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, files = resp.files.len(), "get_commit_diff done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -76,7 +96,9 @@ impl diff_service_server::DiffService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetPatchRequest>,
|
||||
) -> Result<tonic::Response<Self::GetPatchStream>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.DiffService/GetPatch");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("diff.get_patch", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -86,13 +108,18 @@ impl diff_service_server::DiffService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_diff_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
let resp = client.get_patch(inner).await?;
|
||||
let stream = super::bridge_server_stream(resp.into_inner());
|
||||
return Ok(tonic::Response::new(stream));
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let items = if cache::selectors_are_oid(&inner.base, &inner.head) {
|
||||
cache::cached_vec_response("diff.get_patch", &inner, || {
|
||||
@@ -101,6 +128,7 @@ impl diff_service_server::DiffService for GitksService {
|
||||
} else {
|
||||
gb.get_patch(inner).map_err(into_status)?
|
||||
};
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(into_stream(items)))
|
||||
}
|
||||
|
||||
@@ -108,7 +136,9 @@ impl diff_service_server::DiffService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetDiffStatsRequest>,
|
||||
) -> Result<tonic::Response<DiffStats>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.DiffService/GetDiffStats");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("diff.get_diff_stats", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -118,11 +148,16 @@ impl diff_service_server::DiffService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_diff_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_diff_stats(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selectors_are_oid(&inner.base, &inner.head) {
|
||||
cache::cached_response("diff.get_diff_stats", &inner, || {
|
||||
@@ -131,6 +166,7 @@ impl diff_service_server::DiffService for GitksService {
|
||||
} else {
|
||||
gb.get_diff_stats(inner).map_err(into_status)?
|
||||
};
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+50
-6
@@ -3,7 +3,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status};
|
||||
|
||||
remote_client!(remote_merge_client, MergeServiceClient<tonic::transport::Channel>, "merge");
|
||||
remote_client!(
|
||||
remote_merge_client,
|
||||
MergeServiceClient<tonic::transport::Channel>,
|
||||
"merge"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl merge_service_server::MergeService for GitksService {
|
||||
@@ -11,7 +15,9 @@ impl merge_service_server::MergeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<CheckMergeRequest>,
|
||||
) -> Result<tonic::Response<MergeResult>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.MergeService/CheckMerge");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("merge.check_merge", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -21,14 +27,20 @@ impl merge_service_server::MergeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_merge_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.check_merge(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.check_merge(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, status = resp.status, "check_merge done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -36,7 +48,9 @@ impl merge_service_server::MergeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<MergeRequest>,
|
||||
) -> Result<tonic::Response<MergeResult>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.MergeService/Merge");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let target = inner.target_branch.clone();
|
||||
let span = tracing::info_span!("merge.merge", %repo, %target);
|
||||
@@ -47,15 +61,21 @@ impl merge_service_server::MergeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_merge_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.merge(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.merge(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %target, status = resp.status, "merge done");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", target), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -63,7 +83,9 @@ impl merge_service_server::MergeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ListMergeConflictsRequest>,
|
||||
) -> Result<tonic::Response<ListMergeConflictsResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.MergeService/ListMergeConflicts");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("merge.list_merge_conflicts", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -73,14 +95,20 @@ impl merge_service_server::MergeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_merge_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.list_merge_conflicts(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.list_merge_conflicts(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, conflicts = resp.conflicts.len(), "list_merge_conflicts done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -88,7 +116,9 @@ impl merge_service_server::MergeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ResolveMergeConflictsRequest>,
|
||||
) -> Result<tonic::Response<MergeResult>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.MergeService/ResolveMergeConflicts");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let target = inner.target_branch.clone();
|
||||
let span = tracing::info_span!("merge.resolve_merge_conflicts", %repo, %target);
|
||||
@@ -99,15 +129,21 @@ impl merge_service_server::MergeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_merge_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.resolve_merge_conflicts(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.resolve_merge_conflicts(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %target, status = resp.status, "merge conflicts resolved");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", target), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -115,7 +151,9 @@ impl merge_service_server::MergeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<RebaseRequest>,
|
||||
) -> Result<tonic::Response<RebaseResult>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.MergeService/Rebase");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let branch = inner.branch.clone();
|
||||
let span = tracing::info_span!("merge.rebase", %repo, %branch);
|
||||
@@ -126,15 +164,21 @@ impl merge_service_server::MergeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_merge_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.rebase(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.rebase(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %branch, status = resp.status, "rebase done");
|
||||
self.notify_ref_update(&repo, &format!("refs/heads/{}", branch), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+47
-1
@@ -62,6 +62,9 @@ pub struct GitksService {
|
||||
pub repo_prefix: PathBuf,
|
||||
pub node_actor: Option<ActorRef<GitNodeMessage>>,
|
||||
pub grpc_addr: String,
|
||||
pub disk_cache: Option<crate::disk_cache::DiskCache>,
|
||||
pub pack_cache: Option<crate::pack_cache::PackCache>,
|
||||
pub hook_manager: Option<crate::hooks::HookManager>,
|
||||
}
|
||||
|
||||
impl GitksService {
|
||||
@@ -70,6 +73,9 @@ impl GitksService {
|
||||
repo_prefix,
|
||||
node_actor: None,
|
||||
grpc_addr: String::new(),
|
||||
disk_cache: None,
|
||||
pack_cache: None,
|
||||
hook_manager: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +84,21 @@ impl GitksService {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_disk_cache(mut self, dc: crate::disk_cache::DiskCache) -> Self {
|
||||
self.disk_cache = Some(dc);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_pack_cache(mut self, pc: crate::pack_cache::PackCache) -> Self {
|
||||
self.pack_cache = Some(pc);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_hook_manager(mut self, hm: crate::hooks::HookManager) -> Self {
|
||||
self.hook_manager = Some(hm);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_grpc_addr(mut self, grpc_addr: String) -> Self {
|
||||
self.grpc_addr = grpc_addr;
|
||||
self
|
||||
@@ -156,6 +177,26 @@ impl GitksService {
|
||||
.unwrap_or_else(|| "unknown".into())
|
||||
}
|
||||
|
||||
/// Get the relative path from a repository header, if any.
|
||||
pub(crate) fn repo_relative_path<'a>(&self, header: Option<&'a crate::pb::RepositoryHeader>) -> Option<&'a str> {
|
||||
header.and_then(|h| {
|
||||
if h.relative_path.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(h.relative_path.as_str())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Acquire a rate-limit permit for the repository in this request.
|
||||
/// Returns a guard that releases the permit on drop.
|
||||
pub(crate) async fn acquire_rate_limit(
|
||||
&self,
|
||||
header: Option<&crate::pb::RepositoryHeader>,
|
||||
) -> Result<Option<crate::rate_limit::RateLimitGuard>, tonic::Status> {
|
||||
crate::rate_limit::acquire_or_reject(self.repo_relative_path(header)).await
|
||||
}
|
||||
|
||||
pub(crate) fn resolve(
|
||||
&self,
|
||||
header: Option<&crate::pb::RepositoryHeader>,
|
||||
@@ -241,9 +282,14 @@ impl GitksService {
|
||||
old_oid: &str,
|
||||
new_oid: &str,
|
||||
) {
|
||||
// Invalidate caches that depend on this repository
|
||||
// Invalidate moka caches
|
||||
crate::server::cache::invalidate_repo(relative_path);
|
||||
|
||||
// Invalidate disk cache
|
||||
if let Some(ref pc) = self.pack_cache {
|
||||
pc.invalidate_repo(relative_path);
|
||||
}
|
||||
|
||||
if let Some(ref actor) = self.node_actor {
|
||||
let event = crate::actor::message::RefUpdateEvent {
|
||||
relative_path: relative_path.to_string(),
|
||||
|
||||
+162
-8
@@ -6,7 +6,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status};
|
||||
|
||||
remote_client!(remote_pack_client, PackServiceClient<tonic::transport::Channel>, "pack");
|
||||
remote_client!(
|
||||
remote_pack_client,
|
||||
PackServiceClient<tonic::transport::Channel>,
|
||||
"pack"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl pack_service_server::PackService for GitksService {
|
||||
@@ -18,7 +22,9 @@ impl pack_service_server::PackService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<AdvertiseRefsRequest>,
|
||||
) -> Result<tonic::Response<AdvertiseRefsResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.PackService/AdvertiseRefs");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.advertise_refs", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -28,14 +34,37 @@ impl pack_service_server::PackService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_pack_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.advertise_refs(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
if let Some(ref pc) = self.pack_cache {
|
||||
let protocol = inner.service.clone();
|
||||
if let Ok(digest) = pc.disk_cache().compute_info_refs_key(&repo, &protocol) {
|
||||
if let Some(cached) = pc.lookup_info_refs::<AdvertiseRefsResponse>(&digest) {
|
||||
tracing::info!(%repo, refs = cached.references.len(), "advertise_refs done (cached)");
|
||||
m.record("ok");
|
||||
return Ok(tonic::Response::new(cached));
|
||||
}
|
||||
let resp = gb.advertise_refs(inner).map_err(into_status)?;
|
||||
pc.store_info_refs(&digest, &resp);
|
||||
tracing::info!(%repo, refs = resp.references.len(), "advertise_refs done (written to cache)");
|
||||
m.record("ok");
|
||||
return Ok(tonic::Response::new(resp));
|
||||
}
|
||||
}
|
||||
|
||||
let resp = gb.advertise_refs(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, refs = resp.references.len(), "advertise_refs done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -43,11 +72,13 @@ impl pack_service_server::PackService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<tonic::Streaming<UploadPackRequest>>,
|
||||
) -> Result<tonic::Response<Self::UploadPackStream>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.PackService/UploadPack");
|
||||
let mut stream = request.into_inner();
|
||||
let first = stream
|
||||
.next()
|
||||
.await
|
||||
.ok_or_else(|| tonic::Status::invalid_argument("empty upload-pack stream"))??;
|
||||
let _rate = self.acquire_rate_limit(first.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(first.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.upload_pack", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -57,6 +88,7 @@ impl pack_service_server::PackService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_pack_client(self, first.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(16);
|
||||
let _ = tx.send(first).await;
|
||||
tokio::spawn(async move {
|
||||
@@ -78,9 +110,13 @@ impl pack_service_server::PackService for GitksService {
|
||||
let out = super::bridge_server_stream(resp.into_inner());
|
||||
return Ok(tonic::Response::new(out));
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
tracing::info!(%repo, "upload-pack streaming started");
|
||||
|
||||
@@ -97,6 +133,7 @@ impl pack_service_server::PackService for GitksService {
|
||||
});
|
||||
|
||||
let result = gb.upload_pack(ReceiverStream::new(rx)).await?;
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(result))
|
||||
}
|
||||
|
||||
@@ -104,11 +141,13 @@ impl pack_service_server::PackService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<tonic::Streaming<ReceivePackRequest>>,
|
||||
) -> Result<tonic::Response<Self::ReceivePackStream>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.PackService/ReceivePack");
|
||||
let mut stream = request.into_inner();
|
||||
let first = stream
|
||||
.next()
|
||||
.await
|
||||
.ok_or_else(|| tonic::Status::invalid_argument("empty receive-pack stream"))??;
|
||||
let _rate = self.acquire_rate_limit(first.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(first.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.receive_pack", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -118,6 +157,7 @@ impl pack_service_server::PackService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_pack_client(self, first.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(16);
|
||||
let _ = tx.send(first).await;
|
||||
tokio::spawn(async move {
|
||||
@@ -139,9 +179,13 @@ impl pack_service_server::PackService for GitksService {
|
||||
let out = super::bridge_server_stream(resp.into_inner());
|
||||
return Ok(tonic::Response::new(out));
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
tracing::info!(%repo, "receive-pack streaming started");
|
||||
|
||||
@@ -158,6 +202,7 @@ impl pack_service_server::PackService for GitksService {
|
||||
});
|
||||
|
||||
let result = gb.receive_pack(ReceiverStream::new(rx)).await?;
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(result))
|
||||
}
|
||||
|
||||
@@ -165,7 +210,9 @@ impl pack_service_server::PackService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<PackObjectsRequest>,
|
||||
) -> Result<tonic::Response<Self::PackObjectsStream>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.PackService/PackObjects");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.pack_objects", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -175,16 +222,97 @@ impl pack_service_server::PackService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_pack_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
let resp = client.pack_objects(inner).await?;
|
||||
let stream = super::bridge_server_stream(resp.into_inner());
|
||||
return Ok(tonic::Response::new(stream));
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
if let Some(ref pc) = self.pack_cache
|
||||
&& let Some(opts) = inner.options.as_ref()
|
||||
{
|
||||
let wants_hex: Vec<String> = opts.wants.iter().map(|w| w.hex.clone()).collect();
|
||||
let haves_hex: Vec<String> = opts.haves.iter().map(|h| h.hex.clone()).collect();
|
||||
if let Ok(digest) = pc.disk_cache().compute_pack_objects_key(
|
||||
&repo,
|
||||
&wants_hex,
|
||||
&haves_hex,
|
||||
opts.thin_pack,
|
||||
opts.use_bitmaps,
|
||||
opts.delta_base_offset,
|
||||
) {
|
||||
if let Some(file) = pc
|
||||
.disk_cache()
|
||||
.open_stream_read(crate::pack_cache::PACK_CACHE_NAMESPACE, &digest)
|
||||
.ok()
|
||||
.flatten()
|
||||
{
|
||||
tracing::info!(%repo, digest = %digest, "pack-objects cache hit, streaming from disk");
|
||||
m.record("ok");
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(16);
|
||||
tokio::spawn(async move {
|
||||
let result = tokio::task::spawn_blocking(move || {
|
||||
use std::io::Read;
|
||||
let mut file = file;
|
||||
let mut buf = vec![0u8; 65536];
|
||||
let mut chunks = Vec::new();
|
||||
loop {
|
||||
match file.read(&mut buf) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => chunks.push(Ok(PackfileChunk {
|
||||
data: buf[..n].to_vec(),
|
||||
})),
|
||||
Err(e) => {
|
||||
chunks.push(Err(tonic::Status::internal(format!(
|
||||
"cache read error: {e}"
|
||||
))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
chunks
|
||||
})
|
||||
.await;
|
||||
match result {
|
||||
Ok(chunks) => {
|
||||
for chunk in chunks {
|
||||
if tx.send(chunk).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = tx
|
||||
.send(Err(tonic::Status::internal(format!(
|
||||
"cache read task failed: {e}"
|
||||
))))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
});
|
||||
return Ok(tonic::Response::new(ReceiverStream::new(rx)));
|
||||
}
|
||||
|
||||
// Cache miss: execute pack-objects and tee to cache
|
||||
tracing::info!(%repo, digest = %digest, "pack-objects cache miss");
|
||||
let stream = gb.pack_objects(inner).await?;
|
||||
let tee_stream = pc.tee_pack_stream(&digest, stream);
|
||||
m.record("ok");
|
||||
return Ok(tonic::Response::new(tee_stream));
|
||||
}
|
||||
}
|
||||
|
||||
let stream = gb.pack_objects(inner).await?;
|
||||
tracing::info!(%repo, "pack-objects streaming started");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(stream))
|
||||
}
|
||||
|
||||
@@ -192,11 +320,15 @@ impl pack_service_server::PackService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<tonic::Streaming<IndexPackRequest>>,
|
||||
) -> Result<tonic::Response<IndexPackResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.PackService/IndexPack");
|
||||
let mut stream = request.into_inner();
|
||||
let mut inputs = Vec::new();
|
||||
while let Some(msg) = stream.next().await {
|
||||
inputs.push(msg?);
|
||||
}
|
||||
let _rate = self
|
||||
.acquire_rate_limit(inputs.first().and_then(|r: &IndexPackRequest| r.repository.as_ref()))
|
||||
.await?;
|
||||
let repo = self.repo_label(inputs.first().and_then(|r| r.repository.as_ref()));
|
||||
let span = tracing::info_span!("pack.index_pack", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -210,14 +342,20 @@ impl pack_service_server::PackService for GitksService {
|
||||
)
|
||||
.await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.index_pack(tokio_stream::iter(inputs)).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.index_pack(inputs).map_err(into_status)?;
|
||||
tracing::info!(%repo, objects = resp.object_count, "index_pack done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -225,7 +363,9 @@ impl pack_service_server::PackService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ListPackfilesRequest>,
|
||||
) -> Result<tonic::Response<ListPackfilesResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.PackService/ListPackfiles");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.list_packfiles", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -235,14 +375,20 @@ impl pack_service_server::PackService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_pack_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.list_packfiles(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.list_packfiles(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, count = resp.packfiles.len(), "list_packfiles done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -250,7 +396,9 @@ impl pack_service_server::PackService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<FsckRequest>,
|
||||
) -> Result<tonic::Response<FsckResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.PackService/Fsck");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.fsck", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -260,14 +408,20 @@ impl pack_service_server::PackService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_pack_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.fsck(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.fsck(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, ok = resp.ok, errors = resp.errors.len(), warnings = resp.warnings.len(), "fsck done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+287
-2
@@ -2,8 +2,13 @@ use crate::pb::repository_service_client::RepositoryServiceClient;
|
||||
use crate::pb::*;
|
||||
|
||||
use super::{GitksService, git_cmd, into_status, repository_maint};
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
|
||||
remote_client!(remote_repository_client, RepositoryServiceClient<tonic::transport::Channel>, "repository");
|
||||
remote_client!(
|
||||
remote_repository_client,
|
||||
RepositoryServiceClient<tonic::transport::Channel>,
|
||||
"repository"
|
||||
);
|
||||
|
||||
fn default_branch_name(gb: &crate::bare::GitBare) -> String {
|
||||
git_cmd(gb, &["symbolic-ref", "HEAD"])
|
||||
@@ -23,7 +28,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetRepositoryRequest>,
|
||||
) -> Result<tonic::Response<Repository>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.RepositoryService/GetRepository");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.get_repository", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -33,14 +40,20 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_repository_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_repository(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let bare = gb.bare_dir.join("HEAD").exists();
|
||||
let object_format = gb.object_format();
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(Repository {
|
||||
header: inner.repository,
|
||||
bare,
|
||||
@@ -54,15 +67,21 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<InitRepositoryRequest>,
|
||||
) -> Result<tonic::Response<Repository>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.RepositoryService/InitRepository");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.init_repository", %repo);
|
||||
let _enter = span.enter();
|
||||
let bare_dir = self.resolve_for_init(inner.repository.as_ref())?;
|
||||
let gb = crate::bare::GitBare::new(bare_dir);
|
||||
gb.init_repository(inner.bare).map_err(into_status)?;
|
||||
if let Some(ref hm) = self.hook_manager {
|
||||
hm.install_hooks(&gb.bare_dir).map_err(into_status)?;
|
||||
}
|
||||
tracing::info!(%repo, bare = inner.bare, "repository initialized");
|
||||
self.notify_ref_update(&repo, "HEAD", "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(Repository {
|
||||
header: inner.repository,
|
||||
bare: inner.bare,
|
||||
@@ -74,7 +93,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<DeleteRepositoryRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.RepositoryService/DeleteRepository");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.delete_repository", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -83,12 +104,15 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
&& let Some(mut client) =
|
||||
remote_repository_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.delete_repository(inner).await;
|
||||
}
|
||||
tracing::warn!(%repo, path = %bare_dir.display(), "deleting repository");
|
||||
std::fs::remove_dir_all(&bare_dir).map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
tracing::info!(%repo, "repository deleted");
|
||||
self.notify_ref_update(&repo, "", "", "");
|
||||
crate::rate_limit::remove_repository(&repo);
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
@@ -421,4 +445,265 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
tracing::info!(%repo, ok = resp.ok, "commit-graph write done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
// ── Hooks Management ────────────────────────────────────────────
|
||||
|
||||
async fn list_hooks(
|
||||
&self,
|
||||
request: tonic::Request<ListHooksRequest>,
|
||||
) -> Result<tonic::Response<ListHooksResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let hook_mgr = self.hook_manager.as_ref();
|
||||
let hooks = if let Some(hm) = hook_mgr {
|
||||
hm.list_hooks(&gb.bare_dir)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let resp = ListHooksResponse {
|
||||
hooks: hooks
|
||||
.into_iter()
|
||||
.map(|h| crate::pb::HookInfo {
|
||||
hook_type: h.hook_type,
|
||||
level: h.level.to_string(),
|
||||
path: h.path,
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
async fn set_custom_hook(
|
||||
&self,
|
||||
request: tonic::Request<SetCustomHookRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let hook_mgr = self.hook_manager.as_ref();
|
||||
if let Some(hm) = hook_mgr {
|
||||
hm.set_custom_hook(&gb.bare_dir, &inner.hook_name, &inner.content)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
} else {
|
||||
return Err(tonic::Status::failed_precondition("hooks not enabled"));
|
||||
}
|
||||
tracing::info!(repo = %gb.bare_dir.display(), hook = %inner.hook_name, "custom hook set");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
async fn remove_custom_hook(
|
||||
&self,
|
||||
request: tonic::Request<RemoveCustomHookRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let hook_mgr = self.hook_manager.as_ref();
|
||||
if let Some(hm) = hook_mgr {
|
||||
hm.remove_custom_hook(&gb.bare_dir, &inner.hook_name)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
} else {
|
||||
return Err(tonic::Status::failed_precondition("hooks not enabled"));
|
||||
}
|
||||
tracing::info!(repo = %gb.bare_dir.display(), hook = %inner.hook_name, "custom hook removed");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
// ── Snapshot Operations ──────────────────────────────────────────
|
||||
|
||||
async fn create_snapshot(
|
||||
&self,
|
||||
request: tonic::Request<CreateSnapshotRequest>,
|
||||
) -> Result<tonic::Response<CreateSnapshotResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.create_snapshot", %repo);
|
||||
let _enter = span.enter();
|
||||
|
||||
let storage = crate::snapshot::storage::LocalSnapshotStorage::new(
|
||||
self.repo_prefix.join("+gitks-snapshots"),
|
||||
);
|
||||
let snapshot_id = crate::snapshot::ops::create_and_store_snapshot(&gb, &repo, &storage)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
let head_oid = crate::snapshot::ops::get_head_oid_internal(&gb)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
use crate::snapshot::storage::SnapshotStorageBackend;
|
||||
let actual_size = storage
|
||||
.read_snapshot(&snapshot_id)
|
||||
.map(|d| d.len() as u64)
|
||||
.unwrap_or(0);
|
||||
|
||||
tracing::info!(%repo, snapshot_id = %snapshot_id, size_bytes = actual_size, "snapshot created");
|
||||
Ok(tonic::Response::new(CreateSnapshotResponse {
|
||||
snapshot_id,
|
||||
size_bytes: actual_size,
|
||||
head_oid,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn restore_snapshot(
|
||||
&self,
|
||||
request: tonic::Request<RestoreSnapshotRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let target_repo = self.repo_label(inner.target_repository.as_ref());
|
||||
let span = tracing::info_span!("repo.restore_snapshot", %target_repo);
|
||||
let _enter = span.enter();
|
||||
|
||||
let storage = crate::snapshot::storage::LocalSnapshotStorage::new(
|
||||
self.repo_prefix.join("+gitks-snapshots"),
|
||||
);
|
||||
|
||||
let target_path = self.resolve_for_init(inner.target_repository.as_ref())?;
|
||||
|
||||
crate::snapshot::ops::restore_from_storage(&target_path, &inner.snapshot_id, &storage)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
tracing::info!(%target_repo, snapshot_id = %inner.snapshot_id, "snapshot restored");
|
||||
self.notify_ref_update(&target_repo, "HEAD", "", "");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
async fn list_snapshots(
|
||||
&self,
|
||||
request: tonic::Request<ListSnapshotsRequest>,
|
||||
) -> Result<tonic::Response<ListSnapshotsResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
|
||||
let storage = crate::snapshot::storage::LocalSnapshotStorage::new(
|
||||
self.repo_prefix.join("+gitks-snapshots"),
|
||||
);
|
||||
use crate::snapshot::storage::SnapshotStorageBackend;
|
||||
let snapshots = storage
|
||||
.list_snapshots(&repo)
|
||||
.map_err(tonic::Status::internal)?;
|
||||
|
||||
let resp = ListSnapshotsResponse {
|
||||
snapshots: snapshots
|
||||
.into_iter()
|
||||
.map(|s| crate::pb::SnapshotInfo {
|
||||
snapshot_id: s.snapshot_id,
|
||||
relative_path: s.relative_path,
|
||||
size_bytes: s.size_bytes,
|
||||
created_at: s.created_at,
|
||||
head_oid: s.head_oid,
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
async fn delete_snapshot(
|
||||
&self,
|
||||
request: tonic::Request<DeleteSnapshotRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
|
||||
let storage = crate::snapshot::storage::LocalSnapshotStorage::new(
|
||||
self.repo_prefix.join("+gitks-snapshots"),
|
||||
);
|
||||
use crate::snapshot::storage::SnapshotStorageBackend;
|
||||
storage
|
||||
.delete_snapshot(&inner.snapshot_id)
|
||||
.map_err(tonic::Status::internal)?;
|
||||
|
||||
tracing::info!(snapshot_id = %inner.snapshot_id, "snapshot deleted");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
// ── Repository Move ──────────────────────────────────────────────
|
||||
|
||||
type FetchRepositoryDataStream =
|
||||
ReceiverStream<Result<FetchRepositoryDataResponse, tonic::Status>>;
|
||||
|
||||
async fn move_repository(
|
||||
&self,
|
||||
request: tonic::Request<MoveRepositoryRequest>,
|
||||
) -> Result<tonic::Response<MoveRepositoryResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let source_repo = self.repo_label(inner.source_repository.as_ref());
|
||||
let span = tracing::info_span!("repo.move_repository", %source_repo);
|
||||
let _enter = span.enter();
|
||||
|
||||
let gb = self.resolve(inner.source_repository.as_ref())?;
|
||||
|
||||
let bundle_data = crate::snapshot::ops::create_snapshot(&gb)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
let target_path = self.resolve_for_init(inner.target_repository.as_ref())?;
|
||||
|
||||
let target_gb = crate::bare::GitBare::new(target_path.clone());
|
||||
target_gb
|
||||
.init_repository(true)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
crate::snapshot::ops::restore_snapshot(&target_path, &bundle_data)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
if let Some(ref hm) = self.hook_manager {
|
||||
hm.install_hooks(&target_path)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
}
|
||||
|
||||
let source_path = gb.bare_dir.clone();
|
||||
std::fs::remove_dir_all(&source_path)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
self.notify_ref_update(&source_repo, "HEAD", "", "");
|
||||
|
||||
tracing::info!(source = %source_repo, "repository moved successfully");
|
||||
Ok(tonic::Response::new(MoveRepositoryResponse {
|
||||
state: MoveRepositoryState::MoveStateCompleted as i32,
|
||||
error_message: String::new(),
|
||||
}))
|
||||
}
|
||||
|
||||
async fn fetch_repository_data(
|
||||
&self,
|
||||
request: tonic::Request<FetchRepositoryDataRequest>,
|
||||
) -> Result<tonic::Response<Self::FetchRepositoryDataStream>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.fetch_repository_data", %repo);
|
||||
let _enter = span.enter();
|
||||
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let bundle_data = crate::snapshot::ops::create_snapshot(&gb)
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(16);
|
||||
tokio::spawn(async move {
|
||||
const CHUNK_SIZE: usize = 65536;
|
||||
let total = bundle_data.len();
|
||||
if total == 0 {
|
||||
let _ = tx
|
||||
.send(Ok(FetchRepositoryDataResponse {
|
||||
data: vec![],
|
||||
done: true,
|
||||
}))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
for offset in (0..total).step_by(CHUNK_SIZE) {
|
||||
let end = (offset + CHUNK_SIZE).min(total);
|
||||
let chunk_data = bundle_data[offset..end].to_vec();
|
||||
let is_done = end >= total;
|
||||
if tx
|
||||
.send(Ok(FetchRepositoryDataResponse {
|
||||
data: chunk_data,
|
||||
done: is_done,
|
||||
}))
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(tonic::Response::new(ReceiverStream::new(rx)))
|
||||
}
|
||||
}
|
||||
|
||||
+50
-6
@@ -3,7 +3,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status};
|
||||
|
||||
remote_client!(remote_tag_client, TagServiceClient<tonic::transport::Channel>, "tag");
|
||||
remote_client!(
|
||||
remote_tag_client,
|
||||
TagServiceClient<tonic::transport::Channel>,
|
||||
"tag"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl tag_service_server::TagService for GitksService {
|
||||
@@ -11,7 +15,9 @@ impl tag_service_server::TagService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ListTagsRequest>,
|
||||
) -> Result<tonic::Response<ListTagsResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TagService/ListTags");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tag.list_tags", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -21,14 +27,20 @@ impl tag_service_server::TagService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tag_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.list_tags(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.list_tags(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, count = resp.tags.len(), "list_tags done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -36,7 +48,9 @@ impl tag_service_server::TagService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetTagRequest>,
|
||||
) -> Result<tonic::Response<Tag>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TagService/GetTag");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("tag.get_tag", %repo, %name);
|
||||
@@ -47,13 +61,19 @@ impl tag_service_server::TagService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tag_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_tag(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.get_tag(inner).map_err(into_status)?;
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -61,7 +81,9 @@ impl tag_service_server::TagService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<CreateTagRequest>,
|
||||
) -> Result<tonic::Response<Tag>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TagService/CreateTag");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("tag.create_tag", %repo, %name);
|
||||
@@ -72,15 +94,21 @@ impl tag_service_server::TagService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tag_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.create_tag(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.create_tag(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "tag created");
|
||||
self.notify_ref_update(&repo, &format!("refs/tags/{}", name), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -88,7 +116,9 @@ impl tag_service_server::TagService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<DeleteTagRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TagService/DeleteTag");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("tag.delete_tag", %repo, %name);
|
||||
@@ -99,15 +129,21 @@ impl tag_service_server::TagService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tag_client(self, inner.repository.as_ref(), true).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.delete_tag(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
gb.delete_tag(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "tag deleted");
|
||||
self.notify_ref_update(&repo, &format!("refs/tags/{}", name), "", "");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
@@ -115,7 +151,9 @@ impl tag_service_server::TagService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<VerifyTagRequest>,
|
||||
) -> Result<tonic::Response<VerifiedSignature>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TagService/VerifyTag");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("tag.verify_tag", %repo, %name);
|
||||
@@ -126,14 +164,20 @@ impl tag_service_server::TagService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tag_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.verify_tag(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = gb.verify_tag(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, verified = resp.verified, "tag verified");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+59
-7
@@ -3,7 +3,11 @@ use crate::pb::*;
|
||||
|
||||
use super::{GitksService, cache, into_status, into_stream};
|
||||
|
||||
remote_client!(remote_tree_client, TreeServiceClient<tonic::transport::Channel>, "tree");
|
||||
remote_client!(
|
||||
remote_tree_client,
|
||||
TreeServiceClient<tonic::transport::Channel>,
|
||||
"tree"
|
||||
);
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl tree_service_server::TreeService for GitksService {
|
||||
@@ -14,7 +18,9 @@ impl tree_service_server::TreeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<ListTreeRequest>,
|
||||
) -> Result<tonic::Response<ListTreeResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TreeService/ListTree");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.list_tree", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -24,11 +30,16 @@ impl tree_service_server::TreeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tree_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.list_tree(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.list_tree", &inner, || {
|
||||
@@ -38,6 +49,7 @@ impl tree_service_server::TreeService for GitksService {
|
||||
gb.list_tree(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.entries.len(), "list_tree done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -45,7 +57,9 @@ impl tree_service_server::TreeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetTreeRequest>,
|
||||
) -> Result<tonic::Response<Tree>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TreeService/GetTree");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.get_tree", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -55,11 +69,16 @@ impl tree_service_server::TreeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tree_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_tree(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.get_tree", &inner, || {
|
||||
@@ -68,6 +87,7 @@ impl tree_service_server::TreeService for GitksService {
|
||||
} else {
|
||||
gb.get_tree(inner).map_err(into_status)?
|
||||
};
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -75,7 +95,9 @@ impl tree_service_server::TreeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetBlobRequest>,
|
||||
) -> Result<tonic::Response<Blob>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TreeService/GetBlob");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let path = inner.path.clone();
|
||||
let span = tracing::info_span!("tree.get_blob", %repo, %path);
|
||||
@@ -86,11 +108,16 @@ impl tree_service_server::TreeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tree_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_blob(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.get_blob", &inner, || {
|
||||
@@ -99,6 +126,7 @@ impl tree_service_server::TreeService for GitksService {
|
||||
} else {
|
||||
gb.get_blob(inner).map_err(into_status)?
|
||||
};
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -106,7 +134,9 @@ impl tree_service_server::TreeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetRawBlobRequest>,
|
||||
) -> Result<tonic::Response<Self::GetRawBlobStream>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TreeService/GetRawBlob");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.get_raw_blob", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -116,13 +146,18 @@ impl tree_service_server::TreeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tree_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
let resp = client.get_raw_blob(inner).await?;
|
||||
let stream = super::bridge_server_stream(resp.into_inner());
|
||||
return Ok(tonic::Response::new(stream));
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let items = if inner.oid.is_some() {
|
||||
cache::cached_vec_response("tree.get_raw_blob", &inner, || {
|
||||
@@ -135,6 +170,7 @@ impl tree_service_server::TreeService for GitksService {
|
||||
} else {
|
||||
gb.get_raw_blob(inner).map_err(into_status)?
|
||||
};
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(into_stream(items)))
|
||||
}
|
||||
|
||||
@@ -142,7 +178,9 @@ impl tree_service_server::TreeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<GetFileMetadataRequest>,
|
||||
) -> Result<tonic::Response<FileMetadata>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TreeService/GetFileMetadata");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.get_file_metadata", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -152,11 +190,16 @@ impl tree_service_server::TreeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tree_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.get_file_metadata(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.get_file_metadata", &inner, || {
|
||||
@@ -165,6 +208,7 @@ impl tree_service_server::TreeService for GitksService {
|
||||
} else {
|
||||
gb.get_file_metadata(inner).map_err(into_status)?
|
||||
};
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -172,7 +216,9 @@ impl tree_service_server::TreeService for GitksService {
|
||||
&self,
|
||||
request: tonic::Request<FindFilesRequest>,
|
||||
) -> Result<tonic::Response<FindFilesResponse>, tonic::Status> {
|
||||
let m = crate::metrics::RequestMetrics::new("gitks.TreeService/FindFiles");
|
||||
let inner = request.into_inner();
|
||||
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.find_files", %repo);
|
||||
let _enter = span.enter();
|
||||
@@ -182,11 +228,16 @@ impl tree_service_server::TreeService for GitksService {
|
||||
if let Some(mut client) =
|
||||
remote_tree_client(self, inner.repository.as_ref(), false).await?
|
||||
{
|
||||
m.record("ok");
|
||||
return client.find_files(inner).await;
|
||||
}
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => {
|
||||
crate::metrics::record_rpc_error(&m, &err);
|
||||
return Err(err);
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.find_files", &inner, || {
|
||||
@@ -196,6 +247,7 @@ impl tree_service_server::TreeService for GitksService {
|
||||
gb.find_files(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.files.len(), "find_files done");
|
||||
m.record("ok");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user