222
}
223
224
>
func (r *EndpointRegistryImpl) refreshEndpointsLoop(ctx context.Context, dataReady *dataReady) error {
endpoint_registry.go
225
>
hasLoadedEndpointData := false
226
>
227
>
for ctx.Err() == nil {
228
>
start := time.Now()
229
>
enforceMinWait := true
230
>
if !hasLoadedEndpointData {
231
>
// Loading endpoints for the first time after being (re)enabled, so load with fallback to persistence
232
>
// and unblock any threads waiting on r.dataReady if successful.
233
>
err := backoff.ThrottleRetryContext(ctx, r.loadEndpoints, r.config.refreshRetryPolicy, nil)
234
>
if err == nil {
236
>
enforceMinWait = false
237
>
// Note: do not reload r.dataReady here, use value from argument to ensure that
238
>
// each channel is closed no more than once.
239
>
close(dataReady.ready)
240
>
}
241
>
} else {
242
>
r.dataLock.Lock()
243
>
prevTableVersion := r.tableVersion
244
>
r.dataLock.Unlock()
245
>
246
>
// Endpoints have previously been loaded, so just keep them up to date with long poll requests to
247
>
// matching, without fallback to persistence. Ignoring long poll errors since we will just retry
248
>
// on next loop iteration.
249
>
_ = backoff.ThrottleRetryContext(ctx, r.refreshEndpoints, r.config.refreshRetryPolicy, nil)
250
>
251
>
r.dataLock.Lock()
252
>
enforceMinWait = prevTableVersion == r.tableVersion
253
>
r.dataLock.Unlock()
254
>
}
256
>
257
>
minWaitTime := r.config.refreshMinWait()
258
>
// In general, we want to start a new call immediately on completion of the previous one. But if the remote is
259
>
// broken and returns success immediately, we might end up spinning. So enforce a minimum wait time that
260
>
// increases as long as we keep getting very fast replies. Only enforce the min wait if the remote does not
261
>
// return new data.
262
>
if enforceMinWait && elapsed < minWaitTime {
263
util.InterruptibleSleep(ctx, minWaitTime-elapsed)
264
}
265
}
266
268
}
269
270
// loadEndpoints initializes the in-memory view of endpoints data.
271
// It first tries to load from matching service and falls back to querying persistence directly if matching is unavailable.
272
>
func (r *EndpointRegistryImpl) loadEndpoints(ctx context.Context) error {
endpoint_registry.go
273
>
tableVersion, endpoints, err := r.getAllEndpointsMatchingWithPersistenceFallback(ctx)
274
>
if err != nil {
275
return err
276
}
277
>
endpointsByID := make(map[string]*persistencespb.NexusEndpointEntry, len(endpoints))
endpoint_registry.go
278
>
endpointsByName := make(map[string]*persistencespb.NexusEndpointEntry, len(endpoints))
279
>
for _, endpoint := range endpoints {
281
>
endpointsByName[endpoint.Endpoint.Spec.Name] = endpoint
282
>
}
283
285
>
defer r.dataLock.Unlock()
286
>
287
>
r.tableVersion = tableVersion
288
>
r.endpointsByID = endpointsByID
289
>
r.endpointsByName = endpointsByName
290
>
return nil
291
}
292
293
// refreshEndpoints sends long-poll requests to matching to check for any updates to endpoint data.
294
>
func (r *EndpointRegistryImpl) refreshEndpoints(ctx context.Context) error {
endpoint_registry.go
295
>
r.dataLock.RLock()
296
>
currentTableVersion := r.tableVersion
297
>
r.dataLock.RUnlock()
298
>
299
>
resp, err := r.matchingClient.ListNexusEndpoints(ctx, &matchingservice.ListNexusEndpointsRequest{
300
>
NextPageToken: nil,
301
>
PageSize: int32(r.config.refreshPageSize()),
302
>
LastKnownTableVersion: currentTableVersion,
303
>
Wait: true,
304
>
})
305
>
if err != nil {
306
if ctx.Err() == nil {
307
r.logger.Error("long poll to refresh Nexus endpoints returned error", tag.Error(err))