@@ -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,19 @@ 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 , ok := value .(* service )
156+ if ! ok {
157+ m .logger .Warn ("failed to convert type with key" + key .(string ) + "." )
158+ return true
166159 }
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- }
160+ if svc .isStale () {
161+ svc .cancelFunc ()
162+ m .logger .Info ("remove stale service " + key .(string ) + "." )
163+ m .services .Delete (key )
164+ }
165+ return true
166+ })
176167}
177168
178169type service struct {
@@ -290,7 +281,7 @@ func (t *topKMetrics) Push(oldMetric, newMetric *MetricData) {
290281 // Check if this oldMetric is the new minimum, find the new minMetric after the updates
291282 if t .minMetric .hashKey == hashValue {
292283 // Find the new minMetrics after update the frequency
293- t .minMetric = t .findMinMetric ()
284+ t .minMetric = t .findMinMetricLocked ()
294285 }
295286 return
296287 }
@@ -300,7 +291,7 @@ func (t *topKMetrics) Push(oldMetric, newMetric *MetricData) {
300291 if newMetric .frequency > t .minMetric .frequency {
301292 delete (t .metricMap , t .minMetric .hashKey )
302293 t .metricMap [hashValue ] = newMetric
303- t .minMetric = t .findMinMetric ()
294+ t .minMetric = t .findMinMetricLocked ()
304295 }
305296 } else {
306297 // Check if this newMetric is the new minimum.
@@ -311,8 +302,17 @@ func (t *topKMetrics) Push(oldMetric, newMetric *MetricData) {
311302 }
312303}
313304
314- // findMinMetric removes and returns the key-value pair with the minimum value.
315- func (t * topKMetrics ) findMinMetric () * MetricData {
305+ func (t * topKMetrics ) Admit (metric * MetricData ) bool {
306+ _ , found := t .metricMap [metric .hashKey ]
307+ if len (t .metricMap ) < t .sizeLimit || found {
308+ return true
309+ }
310+ return false
311+ }
312+
313+ // findMinMetricLocked removes and returns the key-value pair with the minimum value.
314+ // It assumes the caller already holds the read/write lock.
315+ func (t * topKMetrics ) findMinMetricLocked () * MetricData {
316316 // Find the new minimum metric and smallest frequency.
317317 var newMinMetric * MetricData
318318 smallestFrequency := int (^ uint (0 ) >> 1 ) // Initialize with the maximum possible integer value
@@ -326,15 +326,11 @@ func (t *topKMetrics) findMinMetric() *MetricData {
326326 return newMinMetric
327327}
328328
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
329+ func (s * service ) admitMetricDataLocked (metric * MetricData ) bool {
330+ return s .primaryTopK .Admit (metric )
335331}
336332
337- func (s * service ) rollupMetricData (attributes pcommon.Map ) {
333+ func (s * service ) rollupMetricDataLocked (attributes pcommon.Map ) {
338334 for _ , indexAttr := range awsDeclaredMetricAttributes {
339335 if (indexAttr == common .CWMetricAttributeEnvironment ) || (indexAttr == common .CWMetricAttributeLocalService ) || (indexAttr == common .CWMetricAttributeRemoteService ) {
340336 continue
@@ -349,6 +345,44 @@ func (s *service) rollupMetricData(attributes pcommon.Map) {
349345 }
350346}
351347
348+ func (s * service ) rotateVisitRecords () error {
349+ s .rwLock .Lock ()
350+ defer s .rwLock .Unlock ()
351+
352+ cmsDepth := s .primaryCMS .depth
353+ cmsWidth := s .primaryCMS .width
354+ topKLimit := s .primaryTopK .sizeLimit
355+
356+ nextPrimaryCMS := s .secondaryCMS
357+ nextPrimaryTopK := s .secondaryTopK
358+
359+ s .secondaryCMS = NewCountMinSketch (cmsDepth , cmsWidth )
360+ s .secondaryTopK = newTopKMetrics (topKLimit )
361+
362+ if nextPrimaryCMS != nil && nextPrimaryTopK != nil {
363+ s .primaryCMS = nextPrimaryCMS
364+ s .primaryTopK = nextPrimaryTopK
365+ } else {
366+ s .logger .Info (fmt .Sprintf ("[%s] secondary visit records are nil." , s .name ))
367+ }
368+
369+ s .countSnapshot [s .rotations % 3 ] = s .totalCount
370+ s .rotations ++
371+
372+ return nil
373+ }
374+
375+ func (s * service ) isStale () bool {
376+ s .rwLock .RLock ()
377+ defer s .rwLock .RUnlock ()
378+ if s .rotations > 3 {
379+ if s .countSnapshot [0 ] == s .countSnapshot [1 ] && s .countSnapshot [1 ] == s .countSnapshot [2 ] {
380+ return true
381+ }
382+ }
383+ return false
384+ }
385+
352386// 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.
353387func newService (name string , limit int , rotationInterval time.Duration , parentCtx context.Context , logger * zap.Logger ) * service {
354388 depth := defaultCMSDepth
@@ -374,7 +408,7 @@ func newService(name string, limit int, rotationInterval time.Duration, parentCt
374408 select {
375409 case <- rotationTicker .C :
376410 svc .logger .Info (fmt .Sprintf ("[%s] rotating visit records, current rotation %d" , name , svc .rotations ))
377- if err := rotateVisitRecords (svc ); err != nil {
411+ if err := svc . rotateVisitRecords (); err != nil {
378412 svc .logger .Error (fmt .Sprintf ("[%s] failed to rotate visit records." , name ), zap .Error (err ))
379413 }
380414 case <- ctx .Done ():
@@ -389,30 +423,3 @@ func newService(name string, limit int, rotationInterval time.Duration, parentCt
389423 svc .logger .Info (fmt .Sprintf ("[%s] service entry is created.\n " , name ))
390424 return svc
391425}
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