|
1 | 1 | package restserver |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "context" |
4 | 5 | "maps" |
5 | 6 | "net/http" |
6 | 7 | "time" |
@@ -124,7 +125,6 @@ func init() { |
124 | 125 | // Every http response is 200 so we really want cns response code. |
125 | 126 | // Hard tto do with middleware unless we derserialize the responses but making it an explit header works around it. |
126 | 127 | // if that doesn't work we could have a separate countervec just for response codes. |
127 | | - |
128 | 128 | func NewHandlerFuncWithHistogram(handler http.HandlerFunc, histogram *prometheus.HistogramVec) http.HandlerFunc { |
129 | 129 | return func(w http.ResponseWriter, req *http.Request) { |
130 | 130 | start := time.Now() |
@@ -157,14 +157,30 @@ type ipState struct { |
157 | 157 | releasingIPs int64 |
158 | 158 | } |
159 | 159 |
|
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 | +} |
| 164 | + |
| 165 | +// singleton recorder |
| 166 | +var recorder *asyncMetricsRecorder |
| 167 | + |
| 168 | +// run starts the asyncMetricsRecorder and listens for signals to record the metrics. |
| 169 | +func (a asyncMetricsRecorder) run(ctx context.Context) { |
| 170 | + for { |
| 171 | + select { |
| 172 | + case <-ctx.Done(): |
| 173 | + return |
| 174 | + case <-a.sig: |
| 175 | + a.record() |
| 176 | + } |
| 177 | + } |
| 178 | +} |
165 | 179 |
|
| 180 | +// record records the IP Config state metrics to Prometheus. |
| 181 | +func (a asyncMetricsRecorder) record() { |
166 | 182 | var state ipState |
167 | | - for ipConfig := range maps.Values(service.PodIPConfigState) { |
| 183 | + for ipConfig := range maps.Values(a.podIPConfigSrc()) { |
168 | 184 | state.allocatedIPs++ |
169 | 185 | if ipConfig.GetState() == types.Assigned { |
170 | 186 | state.assignedIPs++ |
@@ -195,3 +211,26 @@ func (service *HTTPRestService) publishIPStateMetrics() { |
195 | 211 | pendingProgrammingIPCount.WithLabelValues(labels...).Set(float64(state.programmingIPs)) |
196 | 212 | pendingReleaseIPCount.WithLabelValues(labels...).Set(float64(state.releasingIPs)) |
197 | 213 | } |
| 214 | + |
| 215 | +// publishIPStateMetrics logs and publishes the IP Config state metrics to Prometheus. |
| 216 | +func (service *HTTPRestService) publishIPStateMetrics() { |
| 217 | + if recorder == nil { |
| 218 | + recorder = &asyncMetricsRecorder{ |
| 219 | + podIPConfigSrc: service.PodIPConfigStates, |
| 220 | + sig: make(chan struct{}), |
| 221 | + } |
| 222 | + go recorder.run(context.TODO()) |
| 223 | + } |
| 224 | + select { |
| 225 | + case recorder.sig <- struct{}{}: // signal the recorder to record the metrics |
| 226 | + default: // drop the signal if the recorder already has an event queued |
| 227 | + } |
| 228 | +} |
| 229 | + |
| 230 | +// PodIPConfigStates returns a clone of the IP Config State map. |
| 231 | +func (service *HTTPRestService) PodIPConfigStates() map[string]cns.IPConfigurationStatus { |
| 232 | + // copy state |
| 233 | + service.RLock() |
| 234 | + defer service.RUnlock() |
| 235 | + return maps.Clone(service.PodIPConfigState) |
| 236 | +} |
0 commit comments