@@ -124,7 +124,6 @@ func init() {
124124// Every http response is 200 so we really want cns response code.
125125// Hard tto do with middleware unless we derserialize the responses but making it an explit header works around it.
126126// if that doesn't work we could have a separate countervec just for response codes.
127-
128127func NewHandlerFuncWithHistogram (handler http.HandlerFunc , histogram * prometheus.HistogramVec ) http.HandlerFunc {
129128 return func (w http.ResponseWriter , req * http.Request ) {
130129 start := time .Now ()
@@ -157,14 +156,25 @@ type ipState struct {
157156 releasingIPs int64
158157}
159158
160- // publishIPStateMetrics logs and publishes the IP Config state metrics to Prometheus.
161- func (service * HTTPRestService ) publishIPStateMetrics () {
162- // copy state
163- service .RLock ()
164- defer service .RUnlock ()
159+ type asyncMetricsRecorder struct {
160+ podIPConfigSrc func () map [string ]cns.IPConfigurationStatus
161+ sig chan struct {}
162+ }
163+
164+ // singleton recorder
165+ var recorder * asyncMetricsRecorder
166+
167+ // run starts the asyncMetricsRecorder and listens for signals to record the metrics.
168+ func (a asyncMetricsRecorder ) run () {
169+ for range a .sig {
170+ a .record ()
171+ }
172+ }
165173
174+ // record records the IP Config state metrics to Prometheus.
175+ func (a asyncMetricsRecorder ) record () {
166176 var state ipState
167- for ipConfig := range maps .Values (service . PodIPConfigState ) {
177+ for ipConfig := range maps .Values (a . podIPConfigSrc () ) {
168178 state .allocatedIPs ++
169179 if ipConfig .GetState () == types .Assigned {
170180 state .assignedIPs ++
@@ -195,3 +205,26 @@ func (service *HTTPRestService) publishIPStateMetrics() {
195205 pendingProgrammingIPCount .WithLabelValues (labels ... ).Set (float64 (state .programmingIPs ))
196206 pendingReleaseIPCount .WithLabelValues (labels ... ).Set (float64 (state .releasingIPs ))
197207}
208+
209+ // publishIPStateMetrics logs and publishes the IP Config state metrics to Prometheus.
210+ func (service * HTTPRestService ) publishIPStateMetrics () {
211+ if recorder == nil {
212+ recorder = & asyncMetricsRecorder {
213+ podIPConfigSrc : service .PodIPConfigStates ,
214+ sig : make (chan struct {}),
215+ }
216+ go recorder .run ()
217+ }
218+ select {
219+ case recorder .sig <- struct {}{}: // signal the recorder to record the metrics
220+ default : // drop the signal if the recorder already has an event queued
221+ }
222+ }
223+
224+ // PodIPConfigStates returns a clone of the IP Config State map.
225+ func (service * HTTPRestService ) PodIPConfigStates () map [string ]cns.IPConfigurationStatus {
226+ // copy state
227+ service .RLock ()
228+ defer service .RUnlock ()
229+ return maps .Clone (service .PodIPConfigState )
230+ }
0 commit comments