@@ -51,8 +51,7 @@ type MetricsLimiter struct {
5151
5252 logger * zap.Logger
5353 ctx context.Context
54- mapLock sync.RWMutex
55- services map [string ]* service
54+ services sync.Map
5655}
5756
5857func NewMetricsLimiter (config * config.LimiterConfig , logger * zap.Logger ) Limiter {
@@ -70,7 +69,7 @@ func NewMetricsLimiter(config *config.LimiterConfig, logger *zap.Logger) Limiter
7069
7170 logger : logger ,
7271 ctx : ctx ,
73- services : map [ string ] * service {},
72+ services : sync. Map {},
7473 }
7574
7675 go func () {
@@ -97,18 +96,16 @@ func (m *MetricsLimiter) Admit(metricName string, attributes, resourceAttributes
9796 }
9897 admitted := true
9998
100- m .mapLock .RLock ()
101- svc := m .services [serviceName ]
102- m .mapLock .RUnlock ()
103- if svc == nil {
104- m .mapLock .Lock ()
105- svc = m .services [serviceName ]
106- if svc == nil {
107- svc = newService (serviceName , m .DropThreshold , m .RotationInterval , m .ctx , m .logger )
108- m .services [serviceName ] = svc
99+ val , loaded := m .services .Load (serviceName )
100+ if ! loaded {
101+ valToStore := newService (serviceName , m .DropThreshold , m .RotationInterval , m .ctx , m .logger )
102+ val , loaded = m .services .LoadOrStore (serviceName , valToStore )
103+ if loaded {
104+ valToStore .cancelFunc ()
105+ m .logger .Info (fmt .Sprintf ("[%s] cancel newly created service entry as an existing one is found" , serviceName ))
109106 }
110- m .mapLock .Unlock ()
111107 }
108+ svc := val .(* service )
112109
113110 metricData := newMetricData (serviceName , metricName , labels )
114111
@@ -118,8 +115,10 @@ func (m *MetricsLimiter) Admit(metricName string, attributes, resourceAttributes
118115 return true , nil
119116 }
120117
121- if ! svc .admitMetricData (metricData ) {
122- svc .rollupMetricData (attributes )
118+ svc .rwLock .Lock ()
119+ defer svc .rwLock .Unlock ()
120+ if ! svc .admitMetricDataLocked (metricData ) {
121+ svc .rollupMetricDataLocked (attributes )
123122
124123 svc .totalRollup ++
125124 admitted = false
@@ -130,10 +129,6 @@ func (m *MetricsLimiter) Admit(metricName string, attributes, resourceAttributes
130129 }
131130
132131 svc .totalMetricSent ++
133-
134- svc .rwLock .RLock ()
135- defer svc .rwLock .RUnlock ()
136-
137132 svc .totalCount ++
138133 svc .InsertMetricDataToPrimary (metricData )
139134 svc .InsertMetricDataToSecondary (metricData )
@@ -156,23 +151,15 @@ func (m *MetricsLimiter) filterAWSDeclaredAttributes(attributes, resourceAttribu
156151}
157152
158153func (m * MetricsLimiter ) removeStaleServices () {
159- var svcToRemove []string
160- for name , svc := range m .services {
161- if svc .rotations > 3 {
162- if svc .countSnapshot [0 ] == svc .countSnapshot [1 ] && svc .countSnapshot [1 ] == svc .countSnapshot [2 ] {
163- svc .cancelFunc ()
164- svcToRemove = append (svcToRemove , name )
165- }
154+ m .services .Range (func (key , value any ) bool {
155+ svc := value .(* service )
156+ if svc .isStale () {
157+ svc .cancelFunc ()
158+ m .logger .Info ("remove stale service " + key .(string ) + "." )
159+ m .services .Delete (key )
166160 }
167- }
168-
169- m .mapLock .Lock ()
170- defer m .mapLock .Unlock ()
171-
172- for _ , name := range svcToRemove {
173- m .logger .Info ("remove stale service " + name + "." )
174- delete (m .services , name )
175- }
161+ return true
162+ })
176163}
177164
178165type service struct {
@@ -290,7 +277,7 @@ func (t *topKMetrics) Push(oldMetric, newMetric *MetricData) {
290277 // Check if this oldMetric is the new minimum, find the new minMetric after the updates
291278 if t .minMetric .hashKey == hashValue {
292279 // Find the new minMetrics after update the frequency
293- t .minMetric = t .findMinMetric ()
280+ t .minMetric = t .findMinMetricLocked ()
294281 }
295282 return
296283 }
@@ -300,7 +287,7 @@ func (t *topKMetrics) Push(oldMetric, newMetric *MetricData) {
300287 if newMetric .frequency > t .minMetric .frequency {
301288 delete (t .metricMap , t .minMetric .hashKey )
302289 t .metricMap [hashValue ] = newMetric
303- t .minMetric = t .findMinMetric ()
290+ t .minMetric = t .findMinMetricLocked ()
304291 }
305292 } else {
306293 // Check if this newMetric is the new minimum.
@@ -311,8 +298,17 @@ func (t *topKMetrics) Push(oldMetric, newMetric *MetricData) {
311298 }
312299}
313300
314- // findMinMetric removes and returns the key-value pair with the minimum value.
315- func (t * topKMetrics ) findMinMetric () * MetricData {
301+ func (t * topKMetrics ) Admit (metric * MetricData ) bool {
302+ _ , found := t .metricMap [metric .hashKey ]
303+ if len (t .metricMap ) < t .sizeLimit || found {
304+ return true
305+ }
306+ return false
307+ }
308+
309+ // findMinMetricLocked removes and returns the key-value pair with the minimum value.
310+ // It assumes the caller already holds the read/write lock.
311+ func (t * topKMetrics ) findMinMetricLocked () * MetricData {
316312 // Find the new minimum metric and smallest frequency.
317313 var newMinMetric * MetricData
318314 smallestFrequency := int (^ uint (0 ) >> 1 ) // Initialize with the maximum possible integer value
@@ -326,15 +322,11 @@ func (t *topKMetrics) findMinMetric() *MetricData {
326322 return newMinMetric
327323}
328324
329- func (s * service ) admitMetricData (metric * MetricData ) bool {
330- _ , found := s .primaryTopK .metricMap [metric .hashKey ]
331- if len (s .primaryTopK .metricMap ) < s .primaryTopK .sizeLimit || found {
332- return true
333- }
334- return false
325+ func (s * service ) admitMetricDataLocked (metric * MetricData ) bool {
326+ return s .primaryTopK .Admit (metric )
335327}
336328
337- func (s * service ) rollupMetricData (attributes pcommon.Map ) {
329+ func (s * service ) rollupMetricDataLocked (attributes pcommon.Map ) {
338330 for _ , indexAttr := range awsDeclaredMetricAttributes {
339331 if (indexAttr == common .CWMetricAttributeEnvironment ) || (indexAttr == common .CWMetricAttributeLocalService ) || (indexAttr == common .CWMetricAttributeRemoteService ) {
340332 continue
@@ -349,6 +341,44 @@ func (s *service) rollupMetricData(attributes pcommon.Map) {
349341 }
350342}
351343
344+ func (s * service ) rotateVisitRecords () error {
345+ s .rwLock .Lock ()
346+ defer s .rwLock .Unlock ()
347+
348+ cmsDepth := s .primaryCMS .depth
349+ cmsWidth := s .primaryCMS .width
350+ topKLimit := s .primaryTopK .sizeLimit
351+
352+ nextPrimaryCMS := s .secondaryCMS
353+ nextPrimaryTopK := s .secondaryTopK
354+
355+ s .secondaryCMS = NewCountMinSketch (cmsDepth , cmsWidth )
356+ s .secondaryTopK = newTopKMetrics (topKLimit )
357+
358+ if nextPrimaryCMS != nil && nextPrimaryTopK != nil {
359+ s .primaryCMS = nextPrimaryCMS
360+ s .primaryTopK = nextPrimaryTopK
361+ } else {
362+ s .logger .Info (fmt .Sprintf ("[%s] secondary visit records are nil." , s .name ))
363+ }
364+
365+ s .countSnapshot [s .rotations % 3 ] = s .totalCount
366+ s .rotations ++
367+
368+ return nil
369+ }
370+
371+ func (s * service ) isStale () bool {
372+ s .rwLock .RLock ()
373+ defer s .rwLock .RUnlock ()
374+ if s .rotations > 3 {
375+ if s .countSnapshot [0 ] == s .countSnapshot [1 ] && s .countSnapshot [1 ] == s .countSnapshot [2 ] {
376+ return true
377+ }
378+ }
379+ return false
380+ }
381+
352382// As a starting point, you can use rules of thumb, such as setting the depth to be around 4-6 times the logarithm of the expected number of distinct items and the width based on your memory constraints. However, these are rough guidelines, and the optimal size will depend on your unique application and requirements.
353383func newService (name string , limit int , rotationInterval time.Duration , parentCtx context.Context , logger * zap.Logger ) * service {
354384 depth := defaultCMSDepth
@@ -374,7 +404,7 @@ func newService(name string, limit int, rotationInterval time.Duration, parentCt
374404 select {
375405 case <- rotationTicker .C :
376406 svc .logger .Info (fmt .Sprintf ("[%s] rotating visit records, current rotation %d" , name , svc .rotations ))
377- if err := rotateVisitRecords (svc ); err != nil {
407+ if err := svc . rotateVisitRecords (); err != nil {
378408 svc .logger .Error (fmt .Sprintf ("[%s] failed to rotate visit records." , name ), zap .Error (err ))
379409 }
380410 case <- ctx .Done ():
@@ -389,30 +419,3 @@ func newService(name string, limit int, rotationInterval time.Duration, parentCt
389419 svc .logger .Info (fmt .Sprintf ("[%s] service entry is created.\n " , name ))
390420 return svc
391421}
392-
393- func rotateVisitRecords (svc * service ) error {
394- svc .rwLock .Lock ()
395- defer svc .rwLock .Unlock ()
396-
397- cmsDepth := svc .primaryCMS .depth
398- cmsWidth := svc .primaryCMS .width
399- topKLimit := svc .primaryTopK .sizeLimit
400-
401- nextPrimaryCMS := svc .secondaryCMS
402- nextPrimaryTopK := svc .secondaryTopK
403-
404- svc .secondaryCMS = NewCountMinSketch (cmsDepth , cmsWidth )
405- svc .secondaryTopK = newTopKMetrics (topKLimit )
406-
407- if nextPrimaryCMS != nil && nextPrimaryTopK != nil {
408- svc .primaryCMS = nextPrimaryCMS
409- svc .primaryTopK = nextPrimaryTopK
410- } else {
411- svc .logger .Info (fmt .Sprintf ("[%s] secondary visit records are nil." , svc .name ))
412- }
413-
414- svc .countSnapshot [svc .rotations % 3 ] = svc .totalCount
415- svc .rotations ++
416-
417- return nil
418- }
0 commit comments