feat(cluster): implement Raft consensus with tracing and HTTP support

- Add Raft log and snapshot mechanisms for distributed consensus
- Integrate hyper HTTP server and client libraries for network communication
- Enhance tracing capabilities with structured logging and spans
- Add dependency tracking for new consensus-related crates
- Implement snapshot storage with serialization and persistence
- Add remote repository synchronization via Raft commands
- Include comprehensive tracing instrumentation across services
This commit is contained in:
zhenyi
2026-06-10 18:33:42 +08:00
parent 0207cde234
commit c32a7cad2f
10 changed files with 453 additions and 6 deletions
Generated
+90
View File
@@ -372,6 +372,15 @@ dependencies = [
"parking_lot_core", "parking_lot_core",
] ]
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@@ -686,12 +695,17 @@ name = "gitks"
version = "1.0.0" version = "1.0.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes",
"crc32fast",
"dashmap", "dashmap",
"dotenvy", "dotenvy",
"duct", "duct",
"etcd-client", "etcd-client",
"gix", "gix",
"gix-archive", "gix-archive",
"http-body-util",
"hyper",
"hyper-util",
"moka", "moka",
"prost", "prost",
"prost-types", "prost-types",
@@ -710,6 +724,7 @@ dependencies = [
"tonic-prost", "tonic-prost",
"tonic-prost-build", "tonic-prost-build",
"tracing", "tracing",
"tracing-appender",
"tracing-subscriber", "tracing-subscriber",
] ]
@@ -2039,6 +2054,12 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "num-conv"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.4" version = "1.21.4"
@@ -2136,6 +2157,12 @@ dependencies = [
"portable-atomic", "portable-atomic",
] ]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.21" version = "0.2.21"
@@ -2791,6 +2818,12 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "symlink"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.117" version = "2.0.117"
@@ -2867,6 +2900,37 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "time"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.11.0" version = "1.11.0"
@@ -3081,6 +3145,19 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-appender"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c"
dependencies = [
"crossbeam-channel",
"symlink",
"thiserror",
"time",
"tracing-subscriber",
]
[[package]] [[package]]
name = "tracing-attributes" name = "tracing-attributes"
version = "0.1.31" version = "0.1.31"
@@ -3113,6 +3190,16 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-serde"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
dependencies = [
"serde",
"tracing-core",
]
[[package]] [[package]]
name = "tracing-subscriber" name = "tracing-subscriber"
version = "0.3.23" version = "0.3.23"
@@ -3123,12 +3210,15 @@ dependencies = [
"nu-ansi-term", "nu-ansi-term",
"once_cell", "once_cell",
"regex-automata", "regex-automata",
"serde",
"serde_json",
"sharded-slab", "sharded-slab",
"smallvec", "smallvec",
"thread_local", "thread_local",
"tracing", "tracing",
"tracing-core", "tracing-core",
"tracing-log", "tracing-log",
"tracing-serde",
] ]
[[package]] [[package]]
+7 -1
View File
@@ -24,6 +24,8 @@ gix = { version = "0.84", default-features = false, features = ["serde", "blame"
gix-archive = { version = "0.33", features = ["sha256","sha1","document-features"] } gix-archive = { version = "0.33", features = ["sha256","sha1","document-features"] }
duct = { version = "1", features = [] } duct = { version = "1", features = [] }
tracing = { version = "0.1", features = ["log"] } tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tracing-appender = "0.2"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "process", "io-util", "sync", "net"] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "process", "io-util", "sync", "net"] }
tokio-stream = { version = "0.1", features = ["full"] } tokio-stream = { version = "0.1", features = ["full"] }
tokio-util = "0.7" tokio-util = "0.7"
@@ -34,12 +36,16 @@ tonic = { version = "0.14", features = ["transport"] }
tonic-prost = "0.14" tonic-prost = "0.14"
tempfile = "3" tempfile = "3"
dotenvy = "0.15" dotenvy = "0.15"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
ractor = { version = "0.15.13", features = ["cluster","tokio_runtime","monitors","message_span_propogation","async-trait"]} ractor = { version = "0.15.13", features = ["cluster","tokio_runtime","monitors","message_span_propogation","async-trait"]}
ractor_cluster = { version = "0.15.13", features = ["async-trait"] } ractor_cluster = { version = "0.15.13", features = ["async-trait"] }
async-trait = "0.1.89" async-trait = "0.1.89"
etcd-client = { version = "0.18.0", features = ["tls"] } etcd-client = { version = "0.18.0", features = ["tls"] }
dashmap = "6" dashmap = "6"
hyper = { version = "1", features = ["server", "http1"] }
hyper-util = { version = "0.1", features = ["tokio"] }
http-body-util = "0.1"
bytes = "1"
crc32fast = "1"
[[bin]] [[bin]]
name = "gitks" name = "gitks"
path = "main.rs" path = "main.rs"
+10 -4
View File
@@ -1,15 +1,21 @@
pub mod handler; pub mod handler;
pub mod message; pub mod message;
pub mod raft_log;
pub mod server; pub mod server;
pub mod snapshot;
pub mod sync; pub mod sync;
pub use handler::find_primary_in_cluster; pub use handler::find_primary_in_cluster;
pub use handler::{ pub use handler::{
GitNodeActor, GitNodeArgs, RepoEntry, broadcast_ref_update, broadcast_role_changed, broadcast_append_entries, broadcast_ref_update, broadcast_role_changed,
get_category_members, get_cluster_nodes, list_all_groups, route_group_for, start_node_actor, get_category_members, get_cluster_nodes, is_leader_lease_valid, list_all_groups,
renew_leader_lease, route_group_for, start_node_actor, GitNodeActor, GitNodeArgs, RepoEntry,
}; };
pub use message::{ pub use message::{
ElectionRequest, ElectionResult, GitNodeMessage, NodeHealth, ROLE_PRIMARY, ROLE_REPLICA, AppendEntriesRequest, AppendEntriesResponse, ElectionRequest, ElectionResult,
RefUpdateEvent, RepoActorMessage, RoleChangedEvent, RouteDecision, GitNodeMessage, NodeHealth, ReadIndexRequest, ReadIndexResponse, RefUpdateEvent,
RepoActorMessage, RoleChangedEvent, RouteDecision, SerializedRaftEntry,
ROLE_PRIMARY, ROLE_REPLICA, RAFT_MSG_VERSION,
}; };
pub use raft_log::{Command as RaftCommand, LogEntry as RaftLogEntry, RaftLog};
pub use server::init_actor_cluster; pub use server::init_actor_cluster;
+3 -1
View File
@@ -2,14 +2,16 @@ use crate::actor::handler::start_node_actor;
use crate::actor::message::GitNodeMessage; use crate::actor::message::GitNodeMessage;
use crate::server::GitksService; use crate::server::GitksService;
use ractor::ActorRef; use ractor::ActorRef;
use std::path::PathBuf;
pub async fn init_actor_cluster( pub async fn init_actor_cluster(
service: GitksService, service: GitksService,
storage_name: String, storage_name: String,
grpc_addr: String, grpc_addr: String,
data_dir: PathBuf,
) -> Result<(ActorRef<GitNodeMessage>, tokio::task::JoinHandle<()>), ractor::SpawnErr> { ) -> Result<(ActorRef<GitNodeMessage>, tokio::task::JoinHandle<()>), ractor::SpawnErr> {
tracing::info!(storage_name = %storage_name, grpc_addr = %grpc_addr, "initializing actor cluster"); tracing::info!(storage_name = %storage_name, grpc_addr = %grpc_addr, "initializing actor cluster");
let result = start_node_actor(service, storage_name.clone(), grpc_addr).await?; let result = start_node_actor(service, storage_name.clone(), grpc_addr, data_dir).await?;
tracing::info!(storage_name = %storage_name, "actor cluster ready"); tracing::info!(storage_name = %storage_name, "actor cluster ready");
Ok(result) Ok(result)
} }
+202
View File
@@ -0,0 +1,202 @@
//! Raft log snapshot mechanism for log compaction.
//!
//! When the Raft log grows beyond the size threshold, a snapshot is created
//! that captures the current state of all repositories. Old log entries before
//! the snapshot are then discarded.
//!
//! Snapshot format:
//! - `raft-snapshot.dat`: Contains the serialized state at a given log index
//!
//! The snapshot includes:
//! - All repository entries (path, role, last_commit)
//! - The log index at which the snapshot was taken
//! - The term at which the snapshot was taken
use std::collections::HashMap;
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use crate::actor::handler::RepoEntry;
use crate::error::{GitError, GitResult};
/// Snapshot metadata and data.
#[derive(Debug, Clone)]
pub struct RaftSnapshot {
/// The log index at which this snapshot was taken.
pub last_included_index: u64,
/// The term at which this snapshot was taken.
pub last_included_term: u64,
/// All repository entries at the time of the snapshot.
pub repos: HashMap<String, RepoEntry>,
}
impl RaftSnapshot {
/// Create a new snapshot from the current state.
pub fn new(
last_included_index: u64,
last_included_term: u64,
repos: HashMap<String, RepoEntry>,
) -> Self {
Self {
last_included_index,
last_included_term,
repos,
}
}
/// Serialize the snapshot to bytes.
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::new();
// Header
buf.extend(self.last_included_index.to_be_bytes());
buf.extend(self.last_included_term.to_be_bytes());
// Repository count
buf.extend((self.repos.len() as u32).to_be_bytes());
// Each repository entry
for (path, entry) in &self.repos {
encode_string(&mut buf, path);
encode_string(&mut buf, &entry.role);
encode_string(&mut buf, &entry.last_commit);
buf.push(if entry.read_only { 1 } else { 0 });
}
buf
}
/// Deserialize a snapshot from bytes.
pub fn decode(data: &[u8]) -> Option<Self> {
if data.len() < 20 {
return None;
}
let mut offset = 0;
let last_included_index = read_u64(data, &mut offset)?;
let last_included_term = read_u64(data, &mut offset)?;
let repo_count = read_u32(data, &mut offset)? as usize;
let mut repos = HashMap::with_capacity(repo_count);
for _ in 0..repo_count {
let path = read_string(data, &mut offset)?;
let role = read_string(data, &mut offset)?;
let last_commit = read_string(data, &mut offset)?;
let read_only = data.get(offset).copied().unwrap_or(0) == 1;
offset += 1;
repos.insert(path, RepoEntry {
role,
last_commit,
read_only,
});
}
Some(Self {
last_included_index,
last_included_term,
repos,
})
}
}
/// Snapshot storage manager.
pub struct SnapshotStorage {
snapshot_path: PathBuf,
}
impl SnapshotStorage {
pub fn new(data_dir: &Path) -> Self {
Self {
snapshot_path: data_dir.join("raft-snapshot.dat"),
}
}
/// Save a snapshot to disk.
pub fn save(&self, snapshot: &RaftSnapshot) -> GitResult<()> {
let data = snapshot.encode();
let file = std::fs::File::create(&self.snapshot_path).map_err(GitError::Io)?;
let mut writer = BufWriter::new(file);
writer.write_all(&data).map_err(GitError::Io)?;
writer.flush().map_err(GitError::Io)?;
tracing::info!(
index = snapshot.last_included_index,
term = snapshot.last_included_term,
repos = snapshot.repos.len(),
"raft snapshot saved"
);
Ok(())
}
/// Load a snapshot from disk.
pub fn load(&self) -> GitResult<Option<RaftSnapshot>> {
if !self.snapshot_path.exists() {
return Ok(None);
}
let file = std::fs::File::open(&self.snapshot_path).map_err(GitError::Io)?;
let mut reader = BufReader::new(file);
let mut data = Vec::new();
reader.read_to_end(&mut data).map_err(GitError::Io)?;
match RaftSnapshot::decode(&data) {
Some(snapshot) => {
tracing::info!(
index = snapshot.last_included_index,
term = snapshot.last_included_term,
repos = snapshot.repos.len(),
"raft snapshot loaded"
);
Ok(Some(snapshot))
}
None => {
tracing::warn!("failed to decode raft snapshot, ignoring");
Ok(None)
}
}
}
/// Check if a snapshot exists.
pub fn exists(&self) -> bool {
self.snapshot_path.exists()
}
/// Delete the snapshot file.
pub fn delete(&self) -> GitResult<()> {
if self.snapshot_path.exists() {
std::fs::remove_file(&self.snapshot_path).map_err(GitError::Io)?;
}
Ok(())
}
}
// ── Helper functions ─────────────────────────────────────────
fn encode_string(buf: &mut Vec<u8>, s: &str) {
let bytes = s.as_bytes();
buf.extend((bytes.len() as u32).to_be_bytes());
buf.extend(bytes);
}
fn read_u32(data: &[u8], offset: &mut usize) -> Option<u32> {
if *offset + 4 > data.len() {
return None;
}
let val = u32::from_be_bytes(data[*offset..*offset + 4].try_into().ok()?);
*offset += 4;
Some(val)
}
fn read_u64(data: &[u8], offset: &mut usize) -> Option<u64> {
if *offset + 8 > data.len() {
return None;
}
let val = u64::from_be_bytes(data[*offset..*offset + 8].try_into().ok()?);
*offset += 8;
Some(val)
}
fn read_string(data: &[u8], offset: &mut usize) -> Option<String> {
let len = read_u32(data, offset)? as usize;
if *offset + len > data.len() {
return None;
}
let s = String::from_utf8_lossy(&data[*offset..*offset + len]).into_owned();
*offset += len;
Some(s)
}
+50
View File
@@ -355,3 +355,53 @@ fn update_local_ref(repo_path: &Path, ref_name: &str, new_oid: &str) {
Err(e) => tracing::error!(ref_name = %ref_name, error = %e, "update-ref spawn failed"), Err(e) => tracing::error!(ref_name = %ref_name, error = %e, "update-ref spawn failed"),
} }
} }
/// Apply a committed Raft command to the local git repository.
/// This is called on followers when they receive committed entries from the leader.
pub fn apply_raft_command_to_repo(
repo_prefix: &Path,
command: &crate::actor::raft_log::Command,
) {
match command {
crate::actor::raft_log::Command::RefUpdate {
relative_path,
ref_name,
old_oid: _,
new_oid,
} => {
let repo_path = repo_prefix.join(relative_path);
tracing::info!(
relative_path = %relative_path,
ref_name = %ref_name,
new_oid = %new_oid,
"applying RefUpdate from Raft log to local repo"
);
update_local_ref(&repo_path, ref_name, new_oid);
}
crate::actor::raft_log::Command::RegisterRepo {
relative_path,
storage_name: _,
} => {
tracing::info!(
relative_path = %relative_path,
"RegisterRepo from Raft log (no git action needed)"
);
}
crate::actor::raft_log::Command::RemoveRepo { relative_path } => {
tracing::info!(
relative_path = %relative_path,
"RemoveRepo from Raft log (no git action needed)"
);
}
crate::actor::raft_log::Command::SetPrimary {
storage_name,
relative_paths,
} => {
tracing::info!(
storage_name = %storage_name,
paths = relative_paths.len(),
"SetPrimary from Raft log (no git action needed)"
);
}
}
}
+36
View File
@@ -283,8 +283,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/FindCommit"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/FindCommit");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.find_commit", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.find_commit(inner).map_err(into_status)?; let resp = gb.find_commit(inner).map_err(into_status)?;
tracing::info!(%repo, "find_commit done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -296,8 +300,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/ListCommitsByOid"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/ListCommitsByOid");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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_by_oid", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.list_commits_by_oid(inner).map_err(into_status)?; let resp = gb.list_commits_by_oid(inner).map_err(into_status)?;
tracing::info!(%repo, count = resp.commits.len(), "list_commits_by_oid done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -309,8 +317,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CommitIsAncestor"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CommitIsAncestor");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.commit_is_ancestor", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.commit_is_ancestor(inner).map_err(into_status)?; let resp = gb.commit_is_ancestor(inner).map_err(into_status)?;
tracing::info!(%repo, is_ancestor = resp.is_ancestor, "commit_is_ancestor done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -322,8 +334,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CheckObjectsExist"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CheckObjectsExist");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.check_objects_exist", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.check_objects_exist(inner).map_err(into_status)?; let resp = gb.check_objects_exist(inner).map_err(into_status)?;
tracing::info!(%repo, "check_objects_exist done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -335,8 +351,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CommitsByMessage"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CommitsByMessage");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.commits_by_message", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.commits_by_message(inner).map_err(into_status)?; let resp = gb.commits_by_message(inner).map_err(into_status)?;
tracing::info!(%repo, count = resp.commits.len(), "commits_by_message done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -348,8 +368,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/GetCommitStats"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/GetCommitStats");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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_stats", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.get_commit_stats(inner).map_err(into_status)?; let resp = gb.get_commit_stats(inner).map_err(into_status)?;
tracing::info!(%repo, additions = resp.additions, deletions = resp.deletions, changed_files = resp.changed_files, "get_commit_stats done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -361,8 +385,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/LastCommitForPath"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/LastCommitForPath");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.last_commit_for_path", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.last_commit_for_path(inner).map_err(into_status)?; let resp = gb.last_commit_for_path(inner).map_err(into_status)?;
tracing::info!(%repo, "last_commit_for_path done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -374,8 +402,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CountCommits"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CountCommits");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.count_commits", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.count_commits(inner).map_err(into_status)?; let resp = gb.count_commits(inner).map_err(into_status)?;
tracing::info!(%repo, count = resp.count, "count_commits done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -387,8 +419,12 @@ impl commit_service_server::CommitService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CountDivergingCommits"); let m = crate::metrics::RequestMetrics::new("gitks.CommitService/CountDivergingCommits");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.count_diverging_commits", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.count_diverging_commits(inner).map_err(into_status)?; let resp = gb.count_diverging_commits(inner).map_err(into_status)?;
tracing::info!(%repo, left = resp.left_count, right = resp.right_count, "count_diverging_commits done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
+12
View File
@@ -182,8 +182,12 @@ impl diff_service_server::DiffService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.DiffService/RawDiff"); let m = crate::metrics::RequestMetrics::new("gitks.DiffService/RawDiff");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.raw_diff", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let chunks = gb.raw_diff(inner).map_err(into_status)?; let chunks = gb.raw_diff(inner).map_err(into_status)?;
tracing::info!(%repo, "raw_diff streaming started");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(into_stream(chunks))) Ok(tonic::Response::new(into_stream(chunks)))
} }
@@ -195,8 +199,12 @@ impl diff_service_server::DiffService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.DiffService/RawPatch"); let m = crate::metrics::RequestMetrics::new("gitks.DiffService/RawPatch");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.raw_patch", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let chunks = gb.raw_patch(inner).map_err(into_status)?; let chunks = gb.raw_patch(inner).map_err(into_status)?;
tracing::info!(%repo, "raw_patch streaming started");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(into_stream(chunks))) Ok(tonic::Response::new(into_stream(chunks)))
} }
@@ -208,8 +216,12 @@ impl diff_service_server::DiffService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.DiffService/FindChangedPaths"); let m = crate::metrics::RequestMetrics::new("gitks.DiffService/FindChangedPaths");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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.find_changed_paths", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.find_changed_paths(inner).map_err(into_status)?; let resp = gb.find_changed_paths(inner).map_err(into_status)?;
tracing::info!(%repo, paths = resp.paths.len(), "find_changed_paths done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
+30
View File
@@ -12,8 +12,12 @@ impl RefService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.RefService/FindDefaultBranchName"); let m = crate::metrics::RequestMetrics::new("gitks.RefService/FindDefaultBranchName");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; 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!("ref.find_default_branch_name", %repo);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.find_default_branch_name().map_err(super::into_status)?; let resp = gb.find_default_branch_name().map_err(super::into_status)?;
tracing::info!(%repo, "find_default_branch_name done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -25,8 +29,13 @@ impl RefService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.RefService/RefExists"); let m = crate::metrics::RequestMetrics::new("gitks.RefService/RefExists");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
let repo = self.repo_label(inner.repository.as_ref());
let ref_name = inner.ref_name.clone();
let span = tracing::info_span!("ref.ref_exists", %repo, %ref_name);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.ref_exists(inner).map_err(super::into_status)?; let resp = gb.ref_exists(inner).map_err(super::into_status)?;
tracing::info!(%repo, %ref_name, exists = resp.exists, "ref_exists done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -38,8 +47,13 @@ impl RefService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.RefService/UpdateReferences"); let m = crate::metrics::RequestMetrics::new("gitks.RefService/UpdateReferences");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
let repo = self.repo_label(inner.repository.as_ref());
let updates_count = inner.updates.len();
let span = tracing::info_span!("ref.update_references", %repo, %updates_count);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.update_references(inner).map_err(super::into_status)?; let resp = gb.update_references(inner).map_err(super::into_status)?;
tracing::info!(%repo, %updates_count, failed = resp.failed_refs.len(), "update_references done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -51,8 +65,13 @@ impl RefService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.RefService/DeleteRefs"); let m = crate::metrics::RequestMetrics::new("gitks.RefService/DeleteRefs");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
let repo = self.repo_label(inner.repository.as_ref());
let ref_count = inner.ref_names.len();
let span = tracing::info_span!("ref.delete_refs", %repo, %ref_count);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.delete_refs(inner).map_err(super::into_status)?; let resp = gb.delete_refs(inner).map_err(super::into_status)?;
tracing::info!(%repo, %ref_count, failed = resp.failed_refs.len(), "delete_refs done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -64,8 +83,13 @@ impl RefService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.RefService/FindRefsByOID"); let m = crate::metrics::RequestMetrics::new("gitks.RefService/FindRefsByOID");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
let repo = self.repo_label(inner.repository.as_ref());
let oid = inner.oid.clone();
let span = tracing::info_span!("ref.find_refs_by_oid", %repo, %oid);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.find_refs_by_oid(inner).map_err(super::into_status)?; let resp = gb.find_refs_by_oid(inner).map_err(super::into_status)?;
tracing::info!(%repo, %oid, count = resp.refs.len(), "find_refs_by_oid done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -77,8 +101,14 @@ impl RefService for GitksService {
let m = crate::metrics::RequestMetrics::new("gitks.RefService/ListRefs"); let m = crate::metrics::RequestMetrics::new("gitks.RefService/ListRefs");
let inner = request.into_inner(); let inner = request.into_inner();
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
let repo = self.repo_label(inner.repository.as_ref());
let prefixes = inner.prefixes.len();
let pattern = inner.pattern.clone();
let span = tracing::info_span!("ref.list_refs", %repo, %prefixes, %pattern);
let _enter = span.enter();
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.list_all_refs(inner).map_err(super::into_status)?; let resp = gb.list_all_refs(inner).map_err(super::into_status)?;
tracing::info!(%repo, count = resp.refs.len(), "list_refs done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
+13
View File
@@ -12,7 +12,11 @@ impl RemoteService for GitksService {
) -> Result<tonic::Response<FindRemoteRepositoryResponse>, tonic::Status> { ) -> Result<tonic::Response<FindRemoteRepositoryResponse>, tonic::Status> {
let m = crate::metrics::RequestMetrics::new("gitks.RemoteService/FindRemoteRepository"); let m = crate::metrics::RequestMetrics::new("gitks.RemoteService/FindRemoteRepository");
let inner = request.into_inner(); let inner = request.into_inner();
let span = tracing::info_span!("remote.find_remote_repository", remote_url = %inner.remote_url);
let _enter = span.enter();
tracing::info!(remote_url = %inner.remote_url, "find_remote_repository");
let resp = find_remote_repository(inner).map_err(super::into_status)?; let resp = find_remote_repository(inner).map_err(super::into_status)?;
tracing::info!(refs_count = resp.refs.len(), exists = resp.exists, "find_remote_repository done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -23,7 +27,11 @@ impl RemoteService for GitksService {
) -> Result<tonic::Response<FindRemoteRootRefResponse>, tonic::Status> { ) -> Result<tonic::Response<FindRemoteRootRefResponse>, tonic::Status> {
let m = crate::metrics::RequestMetrics::new("gitks.RemoteService/FindRemoteRootRef"); let m = crate::metrics::RequestMetrics::new("gitks.RemoteService/FindRemoteRootRef");
let inner = request.into_inner(); let inner = request.into_inner();
let span = tracing::info_span!("remote.find_remote_root_ref", remote_url = %inner.remote_url);
let _enter = span.enter();
tracing::info!(remote_url = %inner.remote_url, "find_remote_root_ref");
let resp = find_remote_root_ref(inner).map_err(super::into_status)?; let resp = find_remote_root_ref(inner).map_err(super::into_status)?;
tracing::info!(ref_name = %resp.ref_name, target_oid = %resp.target_oid, "find_remote_root_ref done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }
@@ -34,9 +42,14 @@ impl RemoteService for GitksService {
) -> Result<tonic::Response<UpdateRemoteMirrorResponse>, tonic::Status> { ) -> Result<tonic::Response<UpdateRemoteMirrorResponse>, tonic::Status> {
let m = crate::metrics::RequestMetrics::new("gitks.RemoteService/UpdateRemoteMirror"); let m = crate::metrics::RequestMetrics::new("gitks.RemoteService/UpdateRemoteMirror");
let inner = request.into_inner(); let inner = request.into_inner();
let repo = self.repo_label(inner.repository.as_ref());
let span = tracing::info_span!("remote.update_remote_mirror", %repo);
let _enter = span.enter();
tracing::info!(repo = %repo, remote_url = %inner.remote_url, force = inner.force, prune = inner.prune, "update_remote_mirror");
let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?; let _rate = self.acquire_rate_limit(inner.repository.as_ref()).await?;
let gb = self.resolve(inner.repository.as_ref())?; let gb = self.resolve(inner.repository.as_ref())?;
let resp = gb.update_remote_mirror(inner).map_err(super::into_status)?; let resp = gb.update_remote_mirror(inner).map_err(super::into_status)?;
tracing::info!(ok = resp.ok, "update_remote_mirror done");
m.record("ok"); m.record("ok");
Ok(tonic::Response::new(resp)) Ok(tonic::Response::new(resp))
} }