@@ -85,7 +85,12 @@ type Reflector struct {
85
85
// lastSyncResourceVersionMutex guards read/write access to lastSyncResourceVersion
86
86
lastSyncResourceVersionMutex sync.RWMutex
87
87
// WatchListPageSize is the requested chunk size of initial and resync watch lists.
88
- // Defaults to pager.PageSize.
88
+ // If unset, for consistent reads (RV="") or reads that opt-into arbitrarily old data
89
+ // (RV="0") it will default to pager.PageSize, for the rest (RV != "" && RV != "0")
90
+ // it will turn off pagination to allow serving them from watch cache.
91
+ // NOTE: It should be used carefully as paginated lists are always served directly from
92
+ // etcd, which is significantly less efficient and may lead to serious performance and
93
+ // scalability problems.
89
94
WatchListPageSize int64
90
95
}
91
96
@@ -220,6 +225,21 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
220
225
}))
221
226
if r .WatchListPageSize != 0 {
222
227
pager .PageSize = r .WatchListPageSize
228
+ } else {
229
+ // User didn't explicitly request pagination.
230
+ if options .ResourceVersion != "" && options .ResourceVersion != "0" {
231
+ // We also don't turn off pagination for ResourceVersion="0", since watch cache
232
+ // is ignoring Limit in that case anyway, and if watchcache is not enabled we
233
+ // don't introduce regression.
234
+
235
+ // With ResourceVersion != "", we have a possibility to list from watch cache,
236
+ // but we do that (for ResourceVersion != "0") only if Limit is unset.
237
+ // To avoid thundering herd on etcd (e.g. on master upgrades), we explicitly
238
+ // switch off pagination to force listing from watch cache (if enabled).
239
+ // With the existing semantic of RV (result is at least as fresh as provided RV),
240
+ // this is correct and doesn't lead to going back in time.
241
+ pager .PageSize = 0
242
+ }
223
243
}
224
244
225
245
list , err = pager .List (context .Background (), options )
@@ -320,7 +340,9 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
320
340
if err != nil {
321
341
switch {
322
342
case isExpiredError (err ):
323
- r .setIsLastSyncResourceVersionExpired (true )
343
+ // Don't set LastSyncResourceVersionExpired - LIST call with ResourceVersion=RV already
344
+ // has a semantic that it returns data at least as fresh as provided RV.
345
+ // So first try to LIST with setting RV to resource version of last observed object.
324
346
klog .V (4 ).Infof ("%s: watch of %v closed with: %v" , r .name , r .expectedTypeName , err )
325
347
case err == io .EOF :
326
348
// watch closed normally
@@ -344,8 +366,10 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
344
366
if err != errorStopRequested {
345
367
switch {
346
368
case isExpiredError (err ):
347
- r .setIsLastSyncResourceVersionExpired (true )
348
- klog .V (4 ).Infof ("%s: watch of %v ended with: %v" , r .name , r .expectedTypeName , err )
369
+ // Don't set LastSyncResourceVersionExpired - LIST call with ResourceVersion=RV already
370
+ // has a semantic that it returns data at least as fresh as provided RV.
371
+ // So first try to LIST with setting RV to resource version of last observed object.
372
+ klog .V (4 ).Infof ("%s: watch of %v closed with: %v" , r .name , r .expectedTypeName , err )
349
373
default :
350
374
klog .Warningf ("%s: watch of %v ended with: %v" , r .name , r .expectedTypeName , err )
351
375
}
0 commit comments