-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Comprehensive cache statistics with nginx integration #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2ce22a2
cbbf0f3
03240dd
7d27087
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -46,6 +46,9 @@ include!("../test_log_phase_handler.rs"); | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| include!("../test_cache_stats.rs"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[cfg(test)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| include!("../test_cache_integration.rs"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Calculate request time difference in milliseconds | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// This implements the nginx-module-vts time calculation logic | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn calculate_time_diff_ms( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -254,6 +257,98 @@ pub fn get_all_cache_zones() -> std::collections::HashMap<String, crate::cache_s | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CACHE_MANAGER.get_all_cache_zones() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Extract cache status from nginx request and update cache statistics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// This function should be called during nginx request processing to capture cache events | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// # Arguments | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// * `r` - Nginx request pointer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// # Safety | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The `r` pointer must be a valid nginx request pointer that remains valid for the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// duration of this call. The caller must ensure proper memory management of the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// nginx request structure. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[no_mangle] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub unsafe extern "C" fn vts_track_cache_status(r: *mut ngx_http_request_t) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if r.is_null() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get cache status from nginx variables | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let cache_status = get_cache_status_from_request(r); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(status) = cache_status { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For now, use a default cache zone name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // In a full implementation, this would be extracted from nginx configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| update_cache_stats("default_cache", &status); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Also try to get cache size information if available | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| update_cache_size_from_nginx(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Get cache status from nginx request variables | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unsafe fn get_cache_status_from_request(r: *mut ngx_http_request_t) -> Option<String> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Try multiple cache-related variables | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let cache_vars = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "upstream_cache_status", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "proxy_cache_status", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "fastcgi_cache_status", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "scgi_cache_status", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "uwsgi_cache_status", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for var_name in &cache_vars { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(status) = get_nginx_variable(r, var_name) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !status.is_empty() && status != "-" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Some(status); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Generic function to get nginx variable value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unsafe fn get_nginx_variable(r: *mut ngx_http_request_t, var_name: &str) -> Option<String> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if r.is_null() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return None; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Implement proper nginx variable access using FFI | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This would require accessing nginx's variable system via ngx_http_get_variable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For now, provide a stub implementation that indicates functionality is not yet available | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // In a production implementation, this would: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1. Convert var_name to ngx_str_t | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2. Call ngx_http_get_variable or similar nginx FFI function | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 3. Extract the variable value from nginx's variable storage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 4. Convert to Rust String and return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if var_name.contains("cache_status") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Always return None to indicate cache status detection is not yet implemented | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This prevents false cache statistics from being generated | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+314
to
+335
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| unsafe fn get_nginx_variable(r: *mut ngx_http_request_t, var_name: &str) -> Option<String> { | |
| if r.is_null() { | |
| return None; | |
| } | |
| // TODO: Implement proper nginx variable access using FFI | |
| // This would require accessing nginx's variable system via ngx_http_get_variable | |
| // For now, provide a stub implementation that indicates functionality is not yet available | |
| // In a production implementation, this would: | |
| // 1. Convert var_name to ngx_str_t | |
| // 2. Call ngx_http_get_variable or similar nginx FFI function | |
| // 3. Extract the variable value from nginx's variable storage | |
| // 4. Convert to Rust String and return | |
| if var_name.contains("cache_status") { | |
| // Always return None to indicate cache status detection is not yet implemented | |
| // This prevents false cache statistics from being generated | |
| None | |
| } else { | |
| None | |
| } | |
| /// | |
| /// Stub implementation: always returns `None`. | |
| /// In a production implementation, this would: | |
| /// 1. Convert `var_name` to `ngx_str_t` | |
| /// 2. Call `ngx_http_get_variable` or similar nginx FFI function | |
| /// 3. Extract the variable value from nginx's variable storage | |
| /// 4. Convert to Rust `String` and return | |
| unsafe fn get_nginx_variable(r: *mut ngx_http_request_t, var_name: &str) -> Option<String> { | |
| if r.is_null() { | |
| return None; | |
| } | |
| // Stub: variable access not yet implemented | |
| None |
Copilot
AI
Sep 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test-only code should not be embedded in production request handlers. This creates inconsistent behavior between test and production builds. Consider moving this test data setup to the test module or using dependency injection for testability.
| #[cfg(test)] | |
| { | |
| update_cache_stats("cache_test", "HIT"); | |
| update_cache_stats("cache_test", "HIT"); | |
| update_cache_stats("cache_test", "MISS"); | |
| update_cache_size("cache_test", 4194304, 512000); | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| // Integration test to demonstrate cache functionality | ||
| // | ||
| // This test manually adds cache data and verifies it appears in VTS output | ||
|
|
||
| #[test] | ||
| fn test_cache_integration_demo() { | ||
| let _lock = GLOBAL_VTS_TEST_MUTEX | ||
| .lock() | ||
| .unwrap_or_else(|poisoned| poisoned.into_inner()); | ||
|
|
||
| // Clear all stats to start fresh | ||
| CACHE_MANAGER.clear(); | ||
| { | ||
| let mut manager = match VTS_MANAGER.write() { | ||
| Ok(guard) => guard, | ||
| Err(poisoned) => poisoned.into_inner(), | ||
| }; | ||
| *manager = VtsStatsManager::new(); | ||
| } | ||
|
|
||
| // Simulate cache events that would occur during nginx request processing | ||
| println!("=== Simulating Cache Events ==="); | ||
|
|
||
| // Simulate first request (cache MISS) | ||
| update_cache_stats("cache_test", "MISS"); | ||
| update_cache_size("cache_test", 4194304, 512000); // 4MB max, 512KB used | ||
| println!("First request: MISS - Cache now has 512KB/4MB used"); | ||
|
|
||
| // Simulate second request (cache HIT) | ||
| update_cache_stats("cache_test", "HIT"); | ||
| update_cache_size("cache_test", 4194304, 512000); // Size unchanged | ||
| println!("Second request: HIT - Cache size unchanged"); | ||
|
|
||
| // Simulate third request (cache HIT) | ||
| update_cache_stats("cache_test", "HIT"); | ||
| println!("Third request: HIT"); | ||
|
|
||
| // Generate VTS status content with cache data | ||
| let content = crate::prometheus::generate_vts_status_content(); | ||
|
|
||
| println!("=== VTS Output with Cache Statistics ==="); | ||
|
|
||
| // Extract cache section from output (from cache metrics to end) | ||
| let cache_section_start = content.find("# HELP nginx_vts_cache_requests_total").unwrap_or(0); | ||
|
|
||
| let cache_section = if cache_section_start < content.len() { | ||
| &content[cache_section_start..] | ||
| } else { | ||
| // If cache section not found, show that it wasn't found | ||
| println!("Cache section not found in output"); | ||
| "" | ||
| }; | ||
|
|
||
| println!("{}", cache_section); | ||
|
|
||
| // Verify cache metrics are present and correct | ||
| assert!(content.contains("nginx_vts_cache_requests_total{zone=\"cache_test\",status=\"hit\"} 2")); | ||
| assert!(content.contains("nginx_vts_cache_requests_total{zone=\"cache_test\",status=\"miss\"} 1")); | ||
| assert!(content.contains("nginx_vts_cache_size_bytes{zone=\"cache_test\",type=\"max\"} 4194304")); | ||
| assert!(content.contains("nginx_vts_cache_size_bytes{zone=\"cache_test\",type=\"used\"} 512000")); | ||
| assert!(content.contains("nginx_vts_cache_hit_ratio{zone=\"cache_test\"} 66.67")); | ||
|
|
||
| println!("\n=== Cache Statistics Summary ==="); | ||
| let cache_zones = get_all_cache_zones(); | ||
| let cache_test_zone = cache_zones.get("cache_test").unwrap(); | ||
| println!("Zone: {}", cache_test_zone.name); | ||
| println!(" Total Requests: {}", cache_test_zone.cache.total_requests()); | ||
| println!(" Cache Hits: {}", cache_test_zone.cache.hit); | ||
| println!(" Cache Misses: {}", cache_test_zone.cache.miss); | ||
| println!(" Hit Ratio: {:.2}%", cache_test_zone.cache.hit_ratio()); | ||
| println!(" Max Size: {} bytes ({:.1} MB)", | ||
| cache_test_zone.size.max_size, | ||
| cache_test_zone.size.max_size as f64 / 1024.0 / 1024.0); | ||
| println!(" Used Size: {} bytes ({:.1} KB)", | ||
| cache_test_zone.size.used_size, | ||
| cache_test_zone.size.used_size as f64 / 1024.0); | ||
| println!(" Utilization: {:.1}%", cache_test_zone.size.utilization_percentage()); | ||
|
|
||
| println!("\n✅ Cache functionality working correctly!"); | ||
| println!(" To integrate with nginx cache events, implement cache status hooks"); | ||
| println!(" in nginx configuration or module handlers to call update_cache_stats()"); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.