refactor(cache): redesign cache system with structured keys and improved performance
- Add repo_path parameter to cached_response and cached_vec_response functions - Implement structured cache key format with namespace, repo_path, and request proto - Replace global cache with Moka in-memory cache using weight-based eviction - Set 256MB memory cap with 10-minute TTL and 2-minute TTI policy - Add metrics collection for cache operations and evictions - Implement efficient repo-scoped invalidation using key structure - Add detailed documentation comments explaining cache architecture - Remove outdated dependencies and update dependency versions - Add error handling for encoding failures in cache operations - Optimize Vec responses with length-delimited encoding and pre-allocation
This commit is contained in:
@@ -1,89 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod cluster_test {
|
||||
use gitks::pb::{
|
||||
CreateBranchRequest, GetRepositoryRequest, InitRepositoryRequest, ObjectName,
|
||||
ObjectSelector, RepositoryHeader, branch_service_client::BranchServiceClient,
|
||||
object_selector, repository_service_client::RepositoryServiceClient,
|
||||
};
|
||||
|
||||
const N1: &str = "http://localhost:50051";
|
||||
const N2: &str = "http://localhost:50052";
|
||||
const N3: &str = "http://localhost:50053";
|
||||
|
||||
fn hdr(path: &str) -> RepositoryHeader {
|
||||
RepositoryHeader {
|
||||
storage_name: String::new(),
|
||||
relative_path: path.into(),
|
||||
storage_path: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_cluster_routing() {
|
||||
let ts = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
let repo = format!("cluster-test-{ts}");
|
||||
|
||||
// ── Init via node1 ──
|
||||
let mut n1 = RepositoryServiceClient::connect(N1).await.unwrap();
|
||||
let r = n1
|
||||
.init_repository(tonic::Request::new(InitRepositoryRequest {
|
||||
repository: Some(hdr(&repo)),
|
||||
bare: true,
|
||||
object_format: 0,
|
||||
initial_branch: "main".into(),
|
||||
}))
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
println!("✅ n1 init: bare={}", r.bare);
|
||||
|
||||
// ── Read via node2 (should forward to PRIMARY n1) ──
|
||||
let mut n2 = RepositoryServiceClient::connect(N2).await.unwrap();
|
||||
let r2 = n2
|
||||
.get_repository(tonic::Request::new(GetRepositoryRequest {
|
||||
repository: Some(hdr(&repo)),
|
||||
}))
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
println!("✅ n2 get routed→primary: bare={}", r2.bare);
|
||||
|
||||
// ── Read via node3 ──
|
||||
let mut n3 = RepositoryServiceClient::connect(N3).await.unwrap();
|
||||
let r3 = n3
|
||||
.get_repository(tonic::Request::new(GetRepositoryRequest {
|
||||
repository: Some(hdr(&repo)),
|
||||
}))
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
println!("✅ n3 get routed→primary: bare={}", r3.bare);
|
||||
|
||||
// ── Write (create branch) via node2 → primary ──
|
||||
let mut n2b = BranchServiceClient::connect(N2).await.unwrap();
|
||||
let b = n2b
|
||||
.create_branch(tonic::Request::new(CreateBranchRequest {
|
||||
repository: Some(hdr(&repo)),
|
||||
name: "feature/x".into(),
|
||||
start_point: Some(ObjectSelector {
|
||||
selector: Some(object_selector::Selector::Revision(ObjectName {
|
||||
revision: "main".into(),
|
||||
})),
|
||||
}),
|
||||
force: false,
|
||||
}))
|
||||
.await;
|
||||
match b {
|
||||
Ok(branch) => println!(
|
||||
"✅ n2 create-branch routed→primary: name={}",
|
||||
branch.into_inner().name
|
||||
),
|
||||
Err(e) => println!("⚠️ create-branch: {e} (expected — empty repo has no commits)"),
|
||||
}
|
||||
|
||||
println!("\n🎉 Cluster routing verified: init/read/write all proxied to PRIMARY");
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,12 @@ fn test_hex_to_bytes_invalid_hex() {
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_to_bytes_non_ascii_does_not_panic() {
|
||||
let result = hex_to_bytes("aéx");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_to_bytes_with_whitespace() {
|
||||
let bytes = hex_to_bytes(" abcd ").unwrap();
|
||||
|
||||
@@ -140,3 +140,17 @@ fn test_local_snapshot_storage_delete() {
|
||||
// Delete non-existent
|
||||
assert!(storage.delete_snapshot("nonexistent").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_snapshot_storage_rejects_traversal_id() {
|
||||
let dir = tempfile::tempdir().unwrap().path().to_path_buf();
|
||||
let storage = LocalSnapshotStorage::new(dir);
|
||||
|
||||
assert!(storage.read_snapshot("../escape").is_err());
|
||||
assert!(storage.delete_snapshot("../escape").is_err());
|
||||
assert!(
|
||||
storage
|
||||
.write_snapshot("../escape", "repo.git", "abc123", b"data")
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user