@@ -21,6 +21,7 @@ import (
21
21
"reflect"
22
22
"strconv"
23
23
"sync"
24
+ "time"
24
25
25
26
"k8s.io/klog"
26
27
@@ -92,16 +93,20 @@ type EndpointChangeTracker struct {
92
93
// isIPv6Mode indicates if change tracker is under IPv6/IPv4 mode. Nil means not applicable.
93
94
isIPv6Mode * bool
94
95
recorder record.EventRecorder
96
+ // Map from the Endpoints namespaced-name to the times of the triggers that caused the endpoints
97
+ // object to change. Used to calculate the network-programming-latency.
98
+ lastChangeTriggerTimes map [types.NamespacedName ][]time.Time
95
99
}
96
100
97
101
// NewEndpointChangeTracker initializes an EndpointsChangeMap
98
102
func NewEndpointChangeTracker (hostname string , makeEndpointInfo makeEndpointFunc , isIPv6Mode * bool , recorder record.EventRecorder ) * EndpointChangeTracker {
99
103
return & EndpointChangeTracker {
100
- hostname : hostname ,
101
- items : make (map [types.NamespacedName ]* endpointsChange ),
102
- makeEndpointInfo : makeEndpointInfo ,
103
- isIPv6Mode : isIPv6Mode ,
104
- recorder : recorder ,
104
+ hostname : hostname ,
105
+ items : make (map [types.NamespacedName ]* endpointsChange ),
106
+ makeEndpointInfo : makeEndpointInfo ,
107
+ isIPv6Mode : isIPv6Mode ,
108
+ recorder : recorder ,
109
+ lastChangeTriggerTimes : make (map [types.NamespacedName ][]time.Time ),
105
110
}
106
111
}
107
112
@@ -133,14 +138,38 @@ func (ect *EndpointChangeTracker) Update(previous, current *v1.Endpoints) bool {
133
138
change .previous = ect .endpointsToEndpointsMap (previous )
134
139
ect .items [namespacedName ] = change
135
140
}
141
+ if t := getLastChangeTriggerTime (endpoints ); ! t .IsZero () {
142
+ ect .lastChangeTriggerTimes [namespacedName ] =
143
+ append (ect .lastChangeTriggerTimes [namespacedName ], t )
144
+ }
136
145
change .current = ect .endpointsToEndpointsMap (current )
137
146
// if change.previous equal to change.current, it means no change
138
147
if reflect .DeepEqual (change .previous , change .current ) {
139
148
delete (ect .items , namespacedName )
149
+ // Reset the lastChangeTriggerTimes for the Endpoints object. Given that the network programming
150
+ // SLI is defined as the duration between a time of an event and a time when the network was
151
+ // programmed to incorporate that event, if there are events that happened between two
152
+ // consecutive syncs and that canceled each other out, e.g. pod A added -> pod A deleted,
153
+ // there will be no network programming for them and thus no network programming latency metric
154
+ // should be exported.
155
+ delete (ect .lastChangeTriggerTimes , namespacedName )
140
156
}
141
157
return len (ect .items ) > 0
142
158
}
143
159
160
+ // getLastChangeTriggerTime returns the time.Time value of the EndpointsLastChangeTriggerTime
161
+ // annotation stored in the given endpoints object or the "zero" time if the annotation wasn't set
162
+ // or was set incorrectly.
163
+ func getLastChangeTriggerTime (endpoints * v1.Endpoints ) time.Time {
164
+ val , err := time .Parse (time .RFC3339Nano , endpoints .Annotations [v1 .EndpointsLastChangeTriggerTime ])
165
+ if err != nil {
166
+ klog .Warningf ("Error while parsing EndpointsLastChangeTriggerTimeAnnotation: '%s'. Error is %v" ,
167
+ endpoints .Annotations [v1 .EndpointsLastChangeTriggerTime ], err )
168
+ // In case of error val = time.Zero, which is ignored in the upstream code.
169
+ }
170
+ return val
171
+ }
172
+
144
173
// endpointsChange contains all changes to endpoints that happened since proxy rules were synced. For a single object,
145
174
// changes are accumulated, i.e. previous is state from before applying the changes,
146
175
// current is state after applying the changes.
@@ -157,14 +186,19 @@ type UpdateEndpointMapResult struct {
157
186
StaleEndpoints []ServiceEndpoint
158
187
// StaleServiceNames identifies if a service is stale.
159
188
StaleServiceNames []ServicePortName
189
+ // List of the trigger times for all endpoints objects that changed. It's used to export the
190
+ // network programming latency.
191
+ LastChangeTriggerTimes []time.Time
160
192
}
161
193
162
194
// UpdateEndpointsMap updates endpointsMap base on the given changes.
163
195
func UpdateEndpointsMap (endpointsMap EndpointsMap , changes * EndpointChangeTracker ) (result UpdateEndpointMapResult ) {
164
196
result .StaleEndpoints = make ([]ServiceEndpoint , 0 )
165
197
result .StaleServiceNames = make ([]ServicePortName , 0 )
198
+ result .LastChangeTriggerTimes = make ([]time.Time , 0 )
166
199
167
- endpointsMap .apply (changes , & result .StaleEndpoints , & result .StaleServiceNames )
200
+ endpointsMap .apply (
201
+ changes , & result .StaleEndpoints , & result .StaleServiceNames , & result .LastChangeTriggerTimes )
168
202
169
203
// TODO: If this will appear to be computationally expensive, consider
170
204
// computing this incrementally similarly to endpointsMap.
@@ -241,7 +275,10 @@ func (ect *EndpointChangeTracker) endpointsToEndpointsMap(endpoints *v1.Endpoint
241
275
// apply the changes to EndpointsMap and updates stale endpoints and service-endpoints pair. The `staleEndpoints` argument
242
276
// is passed in to store the stale udp endpoints and `staleServiceNames` argument is passed in to store the stale udp service.
243
277
// The changes map is cleared after applying them.
244
- func (endpointsMap EndpointsMap ) apply (changes * EndpointChangeTracker , staleEndpoints * []ServiceEndpoint , staleServiceNames * []ServicePortName ) {
278
+ // In addition it returns (via argument) and resets the lastChangeTriggerTimes for all endpoints
279
+ // that were changed and will result in syncing the proxy rules.
280
+ func (endpointsMap EndpointsMap ) apply (changes * EndpointChangeTracker , staleEndpoints * []ServiceEndpoint ,
281
+ staleServiceNames * []ServicePortName , lastChangeTriggerTimes * []time.Time ) {
245
282
if changes == nil {
246
283
return
247
284
}
@@ -253,6 +290,10 @@ func (endpointsMap EndpointsMap) apply(changes *EndpointChangeTracker, staleEndp
253
290
detectStaleConnections (change .previous , change .current , staleEndpoints , staleServiceNames )
254
291
}
255
292
changes .items = make (map [types.NamespacedName ]* endpointsChange )
293
+ for _ , lastChangeTriggerTime := range changes .lastChangeTriggerTimes {
294
+ * lastChangeTriggerTimes = append (* lastChangeTriggerTimes , lastChangeTriggerTime ... )
295
+ }
296
+ changes .lastChangeTriggerTimes = make (map [types.NamespacedName ][]time.Time )
256
297
}
257
298
258
299
// Merge ensures that the current EndpointsMap contains all <service, endpoints> pairs from the EndpointsMap passed in.
0 commit comments