refactor(bare): enhance security and performance optimizations

- Remove unnecessary sorting in advertise_refs for deterministic output
- Add path traversal detection and validation in bare_dir construction
- Implement symlink resolution checks to prevent security vulnerabilities
- Refactor cache system with CRC validation and improved metrics
- Integrate repo-specific cache invalidation using indexed keys
- Add comprehensive unit tests for commit operations and diff functionality
- Move configuration constants to centralized config module
- Optimize string operations in disk cache random value generation
- Enhance license detection algorithm with cleaner matching logic
- Streamline argument processing in various git operations
- Update dependencies including crc32fast and flate2 for performance
- Add signal handling capability to tokio runtime configuration
This commit is contained in:
zhenyi
2026-06-12 15:04:12 +08:00
parent e386f44ee2
commit 10a4398e81
41 changed files with 1373 additions and 365 deletions
+71 -15
View File
@@ -65,6 +65,12 @@ struct MetricsInner {
cache_hit_by_namespace: DashMap<String, AtomicU64>,
/// Counter: cache misses by namespace
cache_miss_by_namespace: DashMap<String, AtomicU64>,
/// Histogram: cache value size in bytes
cache_value_size_buckets: DashMap<String, AtomicU64>,
/// Counter: rate-limit rejections by repository
rate_limit_reject_count: DashMap<String, AtomicU64>,
/// Counter: rate-limit acquires by repository
rate_limit_acquire_count: DashMap<String, AtomicU64>,
}
static METRICS: OnceLock<Arc<MetricsInner>> = OnceLock::new();
@@ -99,6 +105,9 @@ fn metrics() -> &'static Arc<MetricsInner> {
cache_eviction_count: DashMap::new(),
cache_hit_by_namespace: DashMap::new(),
cache_miss_by_namespace: DashMap::new(),
cache_value_size_buckets: DashMap::new(),
rate_limit_reject_count: DashMap::new(),
rate_limit_acquire_count: DashMap::new(),
})
})
}
@@ -144,7 +153,6 @@ pub fn record_request(method: &str, status_code: &str, duration: Duration) {
let m = metrics();
let duration_ms = duration.as_millis() as u64;
// Request count
let key = format!("{method}:{status_code}");
m.request_count
.entry(key)
@@ -152,10 +160,8 @@ pub fn record_request(method: &str, status_code: &str, duration: Duration) {
.value()
.fetch_add(1, Ordering::Relaxed);
// Duration histogram
record_duration_bucket(&m.duration_buckets, method, duration_ms);
// Slow request detection
let threshold = m.slow_request_threshold_ms.load(Ordering::Relaxed);
if threshold > 0 && duration_ms >= threshold {
m.slow_request_count
@@ -270,6 +276,46 @@ pub fn record_hook_execution(hook_type: &str, result: &str, duration: Duration)
record_duration_bucket(&m.hook_duration_buckets, hook_type, duration_ms);
}
/// Record cache value size distribution (in bytes).
pub fn record_cache_value_size(namespace: &str, size: usize) {
let m = metrics();
record_size_bucket(&m.cache_value_size_buckets, namespace, size as u64);
}
/// Record a rate-limit rejection event.
pub fn record_rate_limit_reject(repo: &str) {
let m = metrics();
m.rate_limit_reject_count
.entry(repo.to_string())
.or_insert_with(|| AtomicU64::new(0))
.value()
.fetch_add(1, Ordering::Relaxed);
}
/// Record a rate-limit acquire event.
pub fn record_rate_limit_acquire(repo: &str) {
let m = metrics();
m.rate_limit_acquire_count
.entry(repo.to_string())
.or_insert_with(|| AtomicU64::new(0))
.value()
.fetch_add(1, Ordering::Relaxed);
}
/// Record size distribution buckets (log2-based: 1KB, 4KB, 16KB, ..., 1GB).
fn record_size_bucket(map: &DashMap<String, AtomicU64>, label: &str, size: u64) {
let buckets = [1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824];
for &bound in &buckets {
let key = format!("{label}:le_{bound}");
if size <= bound {
map.entry(key)
.or_insert_with(|| AtomicU64::new(0))
.value()
.fetch_add(1, Ordering::Relaxed);
}
}
}
/// Escape a string for use as a Prometheus label value.
/// Replaces `\` → `\\`, `"` → `\"`, `\n` → `\n` per the Prometheus spec.
fn prom_escape(value: &str) -> String {
@@ -349,7 +395,6 @@ pub fn render_metrics() -> String {
out.push_str("# TYPE gitks_repository_count gauge\n");
out.push_str(&format!("gitks_repository_count {repos}\n\n"));
// gRPC requests
render_counter_map(
&mut out,
"gitks_requests_total",
@@ -358,7 +403,6 @@ pub fn render_metrics() -> String {
&["method", "status"],
);
// gRPC duration
render_histogram(
&mut out,
"gitks_request_duration_milliseconds",
@@ -366,7 +410,6 @@ pub fn render_metrics() -> String {
&m.duration_buckets,
);
// Slow requests
render_counter_map(
&mut out,
"gitks_slow_requests_total",
@@ -375,7 +418,6 @@ pub fn render_metrics() -> String {
&["method"],
);
// Cache
let hits = m.cache_hits.load(Ordering::Relaxed);
let misses = m.cache_misses.load(Ordering::Relaxed);
out.push_str("# HELP gitks_cache_hits_total Cache hit count\n");
@@ -385,7 +427,6 @@ pub fn render_metrics() -> String {
out.push_str("# TYPE gitks_cache_misses_total counter\n");
out.push_str(&format!("gitks_cache_misses_total {misses}\n\n"));
// Errors
render_counter_map(
&mut out,
"gitks_errors_total",
@@ -394,7 +435,6 @@ pub fn render_metrics() -> String {
&["kind"],
);
// Git subprocess
render_counter_map(
&mut out,
"gitks_git_cmd_total",
@@ -409,7 +449,6 @@ pub fn render_metrics() -> String {
&m.git_cmd_duration_buckets,
);
// Cache operations
render_counter_map(
&mut out,
"gitks_cache_ops_total",
@@ -424,7 +463,6 @@ pub fn render_metrics() -> String {
&m.cache_op_duration_buckets,
);
// Cache evictions by cause and namespace
render_counter_map(
&mut out,
"gitks_cache_evictions_total",
@@ -433,7 +471,6 @@ pub fn render_metrics() -> String {
&["cause", "namespace"],
);
// Per-namespace cache hits
render_counter_map(
&mut out,
"gitks_cache_hits_by_namespace_total",
@@ -442,7 +479,6 @@ pub fn render_metrics() -> String {
&["namespace"],
);
// Per-namespace cache misses
render_counter_map(
&mut out,
"gitks_cache_misses_by_namespace_total",
@@ -451,7 +487,6 @@ pub fn render_metrics() -> String {
&["namespace"],
);
// Hook execution
render_counter_map(
&mut out,
"gitks_hook_executions_total",
@@ -466,6 +501,28 @@ pub fn render_metrics() -> String {
&m.hook_duration_buckets,
);
render_histogram(
&mut out,
"gitks_cache_value_size_bytes",
"Cache value size distribution in bytes",
&m.cache_value_size_buckets,
);
render_counter_map(
&mut out,
"gitks_rate_limit_rejects_total",
"Rate-limit rejections by repository",
&m.rate_limit_reject_count,
&["repo"],
);
render_counter_map(
&mut out,
"gitks_rate_limit_acquires_total",
"Rate-limit acquires by repository",
&m.rate_limit_acquire_count,
&["repo"],
);
out
}
@@ -688,7 +745,6 @@ impl RequestMetrics {
let duration_ms = duration.as_millis() as u64;
record_request(self.method, status, duration);
// Slow request warning
let threshold = metrics().slow_request_threshold_ms.load(Ordering::Relaxed);
if threshold > 0 && duration_ms >= threshold {
tracing::warn!(