@@ -585,9 +585,10 @@ func (t *Thing[T]) CacheStats(ctx context.Context) CacheStats {
585585
586586// localCache implements CacheClient using in-memory sync.Map.
587587type localCache struct {
588- store sync.Map // map[string]string
589- locks sync.Map // map[string]struct{}
590- counters sync.Map // map[string]int
588+ store sync.Map // map[string]string
589+ locks sync.Map // map[string]struct{}
590+ counters sync.Map // map[string]int, now each counter is a separate key
591+ countersMu sync.Mutex // protects counters increment
591592}
592593
593594// DefaultLocalCache is the default in-memory cache client for Thing ORM.
@@ -632,19 +633,18 @@ func (m *localCache) Exists(key string) bool {
632633}
633634
634635func (m * localCache ) GetModel (ctx context.Context , key string , dest interface {}) error {
635- counters := m .getCounters ()
636- counters ["GetModel" ]++
636+ m .incrCounter ("GetModel" )
637637
638638 raw , ok := m .store .Load (key )
639639 if ! ok {
640- counters [ "GetModelMiss" ] ++
640+ m . incrCounter ( "GetModelMiss" )
641641 return common .ErrNotFound
642642 }
643643
644644 // Assuming raw is []byte from Gob encoding
645645 b , ok := raw .([]byte )
646646 if ! ok {
647- counters [ "GetModelError" ] ++
647+ m . incrCounter ( "GetModelError" )
648648 log .Printf ("ERROR: localCache.GetModel: value for key '%s' is not []byte, but %T" , key , raw )
649649 return fmt .Errorf ("localCache: cached value for key '%s' is not []byte (%T)" , key , raw )
650650 }
@@ -672,7 +672,7 @@ func (m *localCache) GetModel(ctx context.Context, key string, dest interface{})
672672 return fmt .Errorf ("localCache: Gob Unmarshal error for key '%s' (map decode error: %v, direct decode error: %w)" , key , err , directDecodeErr )
673673 }
674674 // Direct decode successful
675- counters [ "GetModelHit" ] ++
675+ m . incrCounter ( "GetModelHit" )
676676 return nil
677677 }
678678
@@ -720,13 +720,12 @@ func (m *localCache) GetModel(ctx context.Context, key string, dest interface{})
720720 return fmt .Errorf ("localCache.GetModel: inconsistent state for key '%s', decoded to map but dest is not struct" , key )
721721 }
722722
723- counters [ "GetModelHit" ] ++
723+ m . incrCounter ( "GetModelHit" )
724724 return nil
725725}
726726
727727func (m * localCache ) SetModel (ctx context.Context , key string , model interface {}, fieldsToCache []string , expiration time.Duration ) error {
728- counters := m .getCounters ()
729- counters ["SetModel" ]++
728+ m .incrCounter ("SetModel" )
730729
731730 val := reflect .ValueOf (model )
732731 if val .Kind () == reflect .Ptr {
@@ -777,18 +776,17 @@ func (m *localCache) DeleteModel(ctx context.Context, key string) error {
777776}
778777
779778func (m * localCache ) GetQueryIDs (ctx context.Context , queryKey string ) ([]int64 , error ) {
780- counters := m .getCounters ()
781- counters ["GetQueryIDs" ]++
779+ m .incrCounter ("GetQueryIDs" )
782780
783781 raw , ok := m .store .Load (queryKey )
784782 if ! ok {
785- counters [ "GetQueryIDsMiss" ] ++
783+ m . incrCounter ( "GetQueryIDsMiss" )
786784 return nil , common .ErrNotFound
787785 }
788786
789787 b , ok := raw .([]byte )
790788 if ! ok {
791- counters [ "GetQueryIDsError" ] ++
789+ m . incrCounter ( "GetQueryIDsError" )
792790 return nil , fmt .Errorf ("localCache: cached query IDs for key '%s' is not []byte (%T)" , queryKey , raw )
793791 }
794792
@@ -798,13 +796,12 @@ func (m *localCache) GetQueryIDs(ctx context.Context, queryKey string) ([]int64,
798796 if err := decoder .Decode (& ids ); err != nil {
799797 return nil , fmt .Errorf ("localCache: Gob Unmarshal error for query key '%s': %w" , queryKey , err )
800798 }
801- counters [ "GetQueryIDsHit" ] ++
799+ m . incrCounter ( "GetQueryIDsHit" )
802800 return ids , nil
803801}
804802
805803func (m * localCache ) SetQueryIDs (ctx context.Context , queryKey string , ids []int64 , expiration time.Duration ) error {
806- counters := m .getCounters ()
807- counters ["SetQueryIDs" ]++
804+ m .incrCounter ("SetQueryIDs" )
808805
809806 if ids == nil {
810807 ids = []int64 {}
@@ -839,28 +836,21 @@ func (m *localCache) ReleaseLock(ctx context.Context, lockKey string) error {
839836}
840837
841838func (m * localCache ) incrCounter (name string ) {
839+ m .countersMu .Lock ()
840+ defer m .countersMu .Unlock ()
842841 val , _ := m .counters .LoadOrStore (name , 0 )
843842 m .counters .Store (name , val .(int )+ 1 )
844843}
845844
846845func (m * localCache ) GetCacheStats (ctx context.Context ) CacheStats {
847846 clonedCounters := make (map [string ]int )
848- counters := m .getCounters ()
849- for k , v := range counters {
850- clonedCounters [k ] = v
851- }
852- return CacheStats {Counters : clonedCounters }
853- }
854-
855- // Helper to get or initialize counters map for localCache
856- func (m * localCache ) getCounters () map [string ]int {
857- if v , ok := m .counters .Load ("actual_counters" ); ok {
858- if casted , castOk := v .(map [string ]int ); castOk {
859- return casted
847+ m .counters .Range (func (key , value any ) bool {
848+ k , ok1 := key .(string )
849+ v , ok2 := value .(int )
850+ if ok1 && ok2 {
851+ clonedCounters [k ] = v
860852 }
861- }
862- // Initialize if not found or wrong type
863- newCounters := make (map [string ]int )
864- m .counters .Store ("actual_counters" , newCounters )
865- return newCounters
853+ return true
854+ })
855+ return CacheStats {Counters : clonedCounters }
866856}
0 commit comments