Skip to content

Commit d00a6af

Browse files
authored
make metrics recording async so that it will not block ip requests
Signed-off-by: Evan Baker <[email protected]>
1 parent e08705a commit d00a6af

File tree

1 file changed

+40
-7
lines changed

1 file changed

+40
-7
lines changed

cns/restserver/metrics.go

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package restserver
33
import (
44
"maps"
55
"net/http"
6+
"sync"
67
"time"
78

89
"github.com/Azure/azure-container-networking/cns"
@@ -124,7 +125,6 @@ func init() {
124125
// Every http response is 200 so we really want cns response code.
125126
// Hard tto do with middleware unless we derserialize the responses but making it an explit header works around it.
126127
// if that doesn't work we could have a separate countervec just for response codes.
127-
128128
func NewHandlerFuncWithHistogram(handler http.HandlerFunc, histogram *prometheus.HistogramVec) http.HandlerFunc {
129129
return func(w http.ResponseWriter, req *http.Request) {
130130
start := time.Now()
@@ -157,14 +157,26 @@ type ipState struct {
157157
releasingIPs int64
158158
}
159159

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()
160+
type asyncMetricsRecorder struct {
161+
podIPConfigSrc func() map[string]cns.IPConfigurationStatus
162+
sig chan struct{}
163+
once sync.Once
164+
}
165+
166+
// singleton recorder
167+
var recorder asyncMetricsRecorder
168+
169+
// run starts the asyncMetricsRecorder and listens for signals to record the metrics.
170+
func (a *asyncMetricsRecorder) run() {
171+
for range a.sig {
172+
a.record()
173+
}
174+
}
165175

176+
// record records the IP Config state metrics to Prometheus.
177+
func (a *asyncMetricsRecorder) record() {
166178
var state ipState
167-
for ipConfig := range maps.Values(service.PodIPConfigState) {
179+
for ipConfig := range maps.Values(a.podIPConfigSrc()) {
168180
state.allocatedIPs++
169181
if ipConfig.GetState() == types.Assigned {
170182
state.assignedIPs++
@@ -195,3 +207,24 @@ func (service *HTTPRestService) publishIPStateMetrics() {
195207
pendingProgrammingIPCount.WithLabelValues(labels...).Set(float64(state.programmingIPs))
196208
pendingReleaseIPCount.WithLabelValues(labels...).Set(float64(state.releasingIPs))
197209
}
210+
211+
// publishIPStateMetrics logs and publishes the IP Config state metrics to Prometheus.
212+
func (service *HTTPRestService) publishIPStateMetrics() {
213+
recorder.once.Do(func() {
214+
recorder.podIPConfigSrc = service.PodIPConfigStates
215+
recorder.sig = make(chan struct{})
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

Comments
 (0)