@@ -50,6 +50,16 @@ use crate::config::CacheType;
5050use crate :: config:: { Config , PreprocessorCacheModeConfig , WriteErrorPolicy } ;
5151use crate :: errors:: * ;
5252
53+ /// Increment an atomic stats counter, handling the Option check.
54+ /// Usage: `inc_stat!(optional_stats, field_name, value)`
55+ macro_rules! inc_stat {
56+ ( $stats: expr, $field: ident, $value: expr) => {
57+ if let Some ( s) = $stats {
58+ s. $field. fetch_add( $value, Ordering :: Relaxed ) ;
59+ }
60+ } ;
61+ }
62+
5363/// Lock-free atomic counters for multi-level cache statistics.
5464/// Stored directly in MultiLevelStorage to avoid mutex contention.
5565struct AtomicLevelStats {
@@ -357,23 +367,6 @@ impl MultiLevelStorage {
357367 MultiLevelStats ( self . atomic_stats . iter ( ) . map ( |s| s. snapshot ( ) ) . collect ( ) )
358368 }
359369
360- /// Record a successful write to a level
361- fn record_write_success ( & self , idx : usize , duration : Duration ) {
362- if let Some ( stats) = self . atomic_stats . get ( idx) {
363- stats. writes . fetch_add ( 1 , Ordering :: Relaxed ) ;
364- stats
365- . write_duration_nanos
366- . fetch_add ( duration. as_nanos ( ) as u64 , Ordering :: Relaxed ) ;
367- }
368- }
369-
370- /// Record a failed write to a level
371- fn record_write_failure ( & self , idx : usize ) {
372- if let Some ( stats) = self . atomic_stats . get ( idx) {
373- stats. write_failures . fetch_add ( 1 , Ordering :: Relaxed ) ;
374- }
375- }
376-
377370 /// Create a multi-level storage from configuration.
378371 ///
379372 /// Returns None if no levels are configured (SCCACHE_MULTILEVEL_CHAIN not set).
@@ -550,18 +543,16 @@ impl MultiLevelStorage {
550543 Ok ( _) => {
551544 let duration = start. elapsed ( ) ;
552545 trace ! ( "Backfilled cache level {} on write in {:?}" , idx, duration) ;
553- if let Some ( stats ) = stats_arc {
554- stats . writes . fetch_add ( 1 , Ordering :: Relaxed ) ;
555- stats
556- . write_duration_nanos
557- . fetch_add ( duration. as_nanos ( ) as u64 , Ordering :: Relaxed ) ;
558- }
546+ inc_stat ! ( stats_arc . as_deref ( ) , writes , 1 ) ;
547+ inc_stat ! (
548+ stats_arc . as_deref ( ) ,
549+ write_duration_nanos,
550+ duration. as_nanos( ) as u64
551+ ) ;
559552 }
560553 Err ( e) => {
561554 debug ! ( "Background write to level {} failed: {}" , idx, e) ;
562- if let Some ( stats) = stats_arc {
563- stats. write_failures . fetch_add ( 1 , Ordering :: Relaxed ) ;
564- }
555+ inc_stat ! ( stats_arc. as_deref( ) , write_failures, 1 ) ;
565556 }
566557 }
567558 } ) ;
@@ -580,17 +571,15 @@ impl Storage for MultiLevelStorage {
580571 debug ! ( "Cache hit at level {} in {:?}" , idx, duration) ;
581572
582573 // Update stats
583- if let Some ( stats ) = self . atomic_stats . get ( idx) {
584- stats . hits . fetch_add ( 1 , Ordering :: Relaxed ) ;
585- stats
586- . hit_duration_nanos
587- . fetch_add ( duration. as_nanos ( ) as u64 , Ordering :: Relaxed ) ;
588- }
574+ inc_stat ! ( self . atomic_stats. get( idx) , hits , 1 ) ;
575+ inc_stat ! (
576+ self . atomic_stats . get ( idx ) ,
577+ hit_duration_nanos,
578+ duration. as_nanos( ) as u64
579+ ) ;
589580 // Mark misses for all levels checked before this hit
590581 for miss_idx in 0 ..idx {
591- if let Some ( stats) = self . atomic_stats . get ( miss_idx) {
592- stats. misses . fetch_add ( 1 , Ordering :: Relaxed ) ;
593- }
582+ inc_stat ! ( self . atomic_stats. get( miss_idx) , misses, 1 ) ;
594583 }
595584
596585 // If hit at level > 0, backfill to faster levels (L0 to L(idx-1))
@@ -602,11 +591,11 @@ impl Storage for MultiLevelStorage {
602591 match level. get_raw ( key) . await {
603592 Ok ( Some ( raw_bytes) ) => {
604593 // Update backfill stats
605- if let Some ( stats ) = self . atomic_stats . get ( hit_level ) {
606- stats
607- . backfills_from
608- . fetch_add ( idx as u64 , Ordering :: Relaxed ) ;
609- }
594+ inc_stat ! (
595+ self . atomic_stats . get ( hit_level ) ,
596+ backfills_from,
597+ idx as u64
598+ ) ;
610599
611600 // Spawn background backfill tasks for each faster level
612601 // Iterate slice directly instead of creating Vec
@@ -629,11 +618,7 @@ impl Storage for MultiLevelStorage {
629618 backfill_idx, hit_level
630619 ) ;
631620 // Update backfill_to stats
632- if let Some ( stats) = stats_arc {
633- stats
634- . backfills_to
635- . fetch_add ( 1 , Ordering :: Relaxed ) ;
636- }
621+ inc_stat ! ( stats_arc. as_deref( ) , backfills_to, 1 ) ;
637622 }
638623 Err ( e) => {
639624 debug ! (
@@ -682,9 +667,7 @@ impl Storage for MultiLevelStorage {
682667
683668 // Mark final miss for all checked levels
684669 for idx in 0 ..self . levels . len ( ) {
685- if let Some ( stats) = self . atomic_stats . get ( idx) {
686- stats. misses . fetch_add ( 1 , Ordering :: Relaxed ) ;
687- }
670+ inc_stat ! ( self . atomic_stats. get( idx) , misses, 1 ) ;
688671 }
689672
690673 Ok ( Cache :: Miss )
@@ -719,10 +702,15 @@ impl Storage for MultiLevelStorage {
719702 Ok ( _) => {
720703 let duration = start. elapsed ( ) ;
721704 trace ! ( "Stored in cache level 0 in {:?}" , duration) ;
722- self . record_write_success ( 0 , duration) ;
705+ inc_stat ! ( self . atomic_stats. first( ) , writes, 1 ) ;
706+ inc_stat ! (
707+ self . atomic_stats. first( ) ,
708+ write_duration_nanos,
709+ duration. as_nanos( ) as u64
710+ ) ;
723711 }
724712 Err ( e) => {
725- self . record_write_failure ( 0 ) ;
713+ inc_stat ! ( self . atomic_stats . first ( ) , write_failures , 1 ) ;
726714 return Err ( e) ;
727715 }
728716 }
@@ -759,20 +747,20 @@ impl Storage for MultiLevelStorage {
759747 if let Err ( e) = result {
760748 // Check if read-only before failing
761749 if !matches ! ( level. check( ) . await , Ok ( CacheMode :: ReadOnly ) ) {
762- if let Some ( stats) = stats_arc {
763- stats. write_failures . fetch_add ( 1 , Ordering :: Relaxed ) ;
764- }
750+ inc_stat ! ( stats_arc. as_deref( ) , write_failures, 1 ) ;
765751 return Err ( anyhow ! (
766752 "Failed to write to cache level {}: {}" ,
767753 idx,
768754 e
769755 ) ) ;
770756 }
771- } else if let Some ( stats) = stats_arc {
772- stats. writes . fetch_add ( 1 , Ordering :: Relaxed ) ;
773- stats
774- . write_duration_nanos
775- . fetch_add ( duration. as_nanos ( ) as u64 , Ordering :: Relaxed ) ;
757+ } else {
758+ inc_stat ! ( stats_arc. as_deref( ) , writes, 1 ) ;
759+ inc_stat ! (
760+ stats_arc. as_deref( ) ,
761+ write_duration_nanos,
762+ duration. as_nanos( ) as u64
763+ ) ;
776764 }
777765 } else {
778766 // L1+ async
@@ -789,16 +777,16 @@ impl Storage for MultiLevelStorage {
789777 if let Err ( e) = result {
790778 // Check if read-only before failing
791779 if !matches ! ( level. check( ) . await , Ok ( CacheMode :: ReadOnly ) ) {
792- if let Some ( stats) = stats_arc {
793- stats. write_failures . fetch_add ( 1 , Ordering :: Relaxed ) ;
794- }
780+ inc_stat ! ( stats_arc. as_deref( ) , write_failures, 1 ) ;
795781 return Err ( anyhow ! ( "Failed to write to cache level {}: {}" , idx, e) ) ;
796782 }
797- } else if let Some ( stats) = stats_arc {
798- stats. writes . fetch_add ( 1 , Ordering :: Relaxed ) ;
799- stats
800- . write_duration_nanos
801- . fetch_add ( duration. as_nanos ( ) as u64 , Ordering :: Relaxed ) ;
783+ } else {
784+ inc_stat ! ( stats_arc. as_deref( ) , writes, 1 ) ;
785+ inc_stat ! (
786+ stats_arc. as_deref( ) ,
787+ write_duration_nanos,
788+ duration. as_nanos( ) as u64
789+ ) ;
802790 }
803791 }
804792
0 commit comments