k8s.io/client-go@v0.31.1/tools/cache/delta_fifo.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cache 18 19 import ( 20 "errors" 21 "fmt" 22 "sync" 23 "time" 24 25 "k8s.io/apimachinery/pkg/util/sets" 26 27 "k8s.io/klog/v2" 28 utiltrace "k8s.io/utils/trace" 29 ) 30 31 // DeltaFIFOOptions is the configuration parameters for DeltaFIFO. All are 32 // optional. 33 type DeltaFIFOOptions struct { 34 35 // KeyFunction is used to figure out what key an object should have. (It's 36 // exposed in the returned DeltaFIFO's KeyOf() method, with additional 37 // handling around deleted objects and queue state). 38 // Optional, the default is MetaNamespaceKeyFunc. 39 KeyFunction KeyFunc 40 41 // KnownObjects is expected to return a list of keys that the consumer of 42 // this queue "knows about". It is used to decide which items are missing 43 // when Replace() is called; 'Deleted' deltas are produced for the missing items. 44 // KnownObjects may be nil if you can tolerate missing deletions on Replace(). 45 KnownObjects KeyListerGetter 46 47 // EmitDeltaTypeReplaced indicates that the queue consumer 48 // understands the Replaced DeltaType. Before the `Replaced` event type was 49 // added, calls to Replace() were handled the same as Sync(). For 50 // backwards-compatibility purposes, this is false by default. 51 // When true, `Replaced` events will be sent for items passed to a Replace() call. 52 // When false, `Sync` events will be sent instead. 53 EmitDeltaTypeReplaced bool 54 55 // If set, will be called for objects before enqueueing them. Please 56 // see the comment on TransformFunc for details. 57 Transformer TransformFunc 58 } 59 60 // DeltaFIFO is like FIFO, but differs in two ways. One is that the 61 // accumulator associated with a given object's key is not that object 62 // but rather a Deltas, which is a slice of Delta values for that 63 // object. Applying an object to a Deltas means to append a Delta 64 // except when the potentially appended Delta is a Deleted and the 65 // Deltas already ends with a Deleted. In that case the Deltas does 66 // not grow, although the terminal Deleted will be replaced by the new 67 // Deleted if the older Deleted's object is a 68 // DeletedFinalStateUnknown. 69 // 70 // The other difference is that DeltaFIFO has two additional ways that 71 // an object can be applied to an accumulator: Replaced and Sync. 72 // If EmitDeltaTypeReplaced is not set to true, Sync will be used in 73 // replace events for backwards compatibility. Sync is used for periodic 74 // resync events. 75 // 76 // DeltaFIFO is a producer-consumer queue, where a Reflector is 77 // intended to be the producer, and the consumer is whatever calls 78 // the Pop() method. 79 // 80 // DeltaFIFO solves this use case: 81 // - You want to process every object change (delta) at most once. 82 // - When you process an object, you want to see everything 83 // that's happened to it since you last processed it. 84 // - You want to process the deletion of some of the objects. 85 // - You might want to periodically reprocess objects. 86 // 87 // DeltaFIFO's Pop(), Get(), and GetByKey() methods return 88 // interface{} to satisfy the Store/Queue interfaces, but they 89 // will always return an object of type Deltas. List() returns 90 // the newest object from each accumulator in the FIFO. 91 // 92 // A DeltaFIFO's knownObjects KeyListerGetter provides the abilities 93 // to list Store keys and to get objects by Store key. The objects in 94 // question are called "known objects" and this set of objects 95 // modifies the behavior of the Delete, Replace, and Resync methods 96 // (each in a different way). 97 // 98 // A note on threading: If you call Pop() in parallel from multiple 99 // threads, you could end up with multiple threads processing slightly 100 // different versions of the same object. 101 type DeltaFIFO struct { 102 // lock/cond protects access to 'items' and 'queue'. 103 lock sync.RWMutex 104 cond sync.Cond 105 106 // `items` maps a key to a Deltas. 107 // Each such Deltas has at least one Delta. 108 items map[string]Deltas 109 110 // `queue` maintains FIFO order of keys for consumption in Pop(). 111 // There are no duplicates in `queue`. 112 // A key is in `queue` if and only if it is in `items`. 113 queue []string 114 115 // populated is true if the first batch of items inserted by Replace() has been populated 116 // or Delete/Add/Update/AddIfNotPresent was called first. 117 populated bool 118 // initialPopulationCount is the number of items inserted by the first call of Replace() 119 initialPopulationCount int 120 121 // keyFunc is used to make the key used for queued item 122 // insertion and retrieval, and should be deterministic. 123 keyFunc KeyFunc 124 125 // knownObjects list keys that are "known" --- affecting Delete(), 126 // Replace(), and Resync() 127 knownObjects KeyListerGetter 128 129 // Used to indicate a queue is closed so a control loop can exit when a queue is empty. 130 // Currently, not used to gate any of CRUD operations. 131 closed bool 132 133 // emitDeltaTypeReplaced is whether to emit the Replaced or Sync 134 // DeltaType when Replace() is called (to preserve backwards compat). 135 emitDeltaTypeReplaced bool 136 137 // Called with every object if non-nil. 138 transformer TransformFunc 139 } 140 141 // TransformFunc allows for transforming an object before it will be processed. 142 // 143 // The most common usage pattern is to clean-up some parts of the object to 144 // reduce component memory usage if a given component doesn't care about them. 145 // 146 // New in v1.27: TransformFunc sees the object before any other actor, and it 147 // is now safe to mutate the object in place instead of making a copy. 148 // 149 // It's recommended for the TransformFunc to be idempotent. 150 // It MUST be idempotent if objects already present in the cache are passed to 151 // the Replace() to avoid re-mutating them. Default informers do not pass 152 // existing objects to Replace though. 153 // 154 // Note that TransformFunc is called while inserting objects into the 155 // notification queue and is therefore extremely performance sensitive; please 156 // do not do anything that will take a long time. 157 type TransformFunc func(interface{}) (interface{}, error) 158 159 // DeltaType is the type of a change (addition, deletion, etc) 160 type DeltaType string 161 162 // Change type definition 163 const ( 164 Added DeltaType = "Added" 165 Updated DeltaType = "Updated" 166 Deleted DeltaType = "Deleted" 167 // Replaced is emitted when we encountered watch errors and had to do a 168 // relist. We don't know if the replaced object has changed. 169 // 170 // NOTE: Previous versions of DeltaFIFO would use Sync for Replace events 171 // as well. Hence, Replaced is only emitted when the option 172 // EmitDeltaTypeReplaced is true. 173 Replaced DeltaType = "Replaced" 174 // Sync is for synthetic events during a periodic resync. 175 Sync DeltaType = "Sync" 176 ) 177 178 // Delta is a member of Deltas (a list of Delta objects) which 179 // in its turn is the type stored by a DeltaFIFO. It tells you what 180 // change happened, and the object's state after* that change. 181 // 182 // [*] Unless the change is a deletion, and then you'll get the final 183 // state of the object before it was deleted. 184 type Delta struct { 185 Type DeltaType 186 Object interface{} 187 } 188 189 // Deltas is a list of one or more 'Delta's to an individual object. 190 // The oldest delta is at index 0, the newest delta is the last one. 191 type Deltas []Delta 192 193 // NewDeltaFIFO returns a Queue which can be used to process changes to items. 194 // 195 // keyFunc is used to figure out what key an object should have. (It is 196 // exposed in the returned DeltaFIFO's KeyOf() method, with additional handling 197 // around deleted objects and queue state). 198 // 199 // 'knownObjects' may be supplied to modify the behavior of Delete, 200 // Replace, and Resync. It may be nil if you do not need those 201 // modifications. 202 // 203 // TODO: consider merging keyLister with this object, tracking a list of 204 // "known" keys when Pop() is called. Have to think about how that 205 // affects error retrying. 206 // 207 // NOTE: It is possible to misuse this and cause a race when using an 208 // external known object source. 209 // Whether there is a potential race depends on how the consumer 210 // modifies knownObjects. In Pop(), process function is called under 211 // lock, so it is safe to update data structures in it that need to be 212 // in sync with the queue (e.g. knownObjects). 213 // 214 // Example: 215 // In case of sharedIndexInformer being a consumer 216 // (https://github.com/kubernetes/kubernetes/blob/0cdd940f/staging/src/k8s.io/client-go/tools/cache/shared_informer.go#L192), 217 // there is no race as knownObjects (s.indexer) is modified safely 218 // under DeltaFIFO's lock. The only exceptions are GetStore() and 219 // GetIndexer() methods, which expose ways to modify the underlying 220 // storage. Currently these two methods are used for creating Lister 221 // and internal tests. 222 // 223 // Also see the comment on DeltaFIFO. 224 // 225 // Warning: This constructs a DeltaFIFO that does not differentiate between 226 // events caused by a call to Replace (e.g., from a relist, which may 227 // contain object updates), and synthetic events caused by a periodic resync 228 // (which just emit the existing object). See https://issue.k8s.io/86015 for details. 229 // 230 // Use `NewDeltaFIFOWithOptions(DeltaFIFOOptions{..., EmitDeltaTypeReplaced: true})` 231 // instead to receive a `Replaced` event depending on the type. 232 // 233 // Deprecated: Equivalent to NewDeltaFIFOWithOptions(DeltaFIFOOptions{KeyFunction: keyFunc, KnownObjects: knownObjects}) 234 func NewDeltaFIFO(keyFunc KeyFunc, knownObjects KeyListerGetter) *DeltaFIFO { 235 return NewDeltaFIFOWithOptions(DeltaFIFOOptions{ 236 KeyFunction: keyFunc, 237 KnownObjects: knownObjects, 238 }) 239 } 240 241 // NewDeltaFIFOWithOptions returns a Queue which can be used to process changes to 242 // items. See also the comment on DeltaFIFO. 243 func NewDeltaFIFOWithOptions(opts DeltaFIFOOptions) *DeltaFIFO { 244 if opts.KeyFunction == nil { 245 opts.KeyFunction = MetaNamespaceKeyFunc 246 } 247 248 f := &DeltaFIFO{ 249 items: map[string]Deltas{}, 250 queue: []string{}, 251 keyFunc: opts.KeyFunction, 252 knownObjects: opts.KnownObjects, 253 254 emitDeltaTypeReplaced: opts.EmitDeltaTypeReplaced, 255 transformer: opts.Transformer, 256 } 257 f.cond.L = &f.lock 258 return f 259 } 260 261 var ( 262 _ = Queue(&DeltaFIFO{}) // DeltaFIFO is a Queue 263 ) 264 265 var ( 266 // ErrZeroLengthDeltasObject is returned in a KeyError if a Deltas 267 // object with zero length is encountered (should be impossible, 268 // but included for completeness). 269 ErrZeroLengthDeltasObject = errors.New("0 length Deltas object; can't get key") 270 ) 271 272 // Close the queue. 273 func (f *DeltaFIFO) Close() { 274 f.lock.Lock() 275 defer f.lock.Unlock() 276 f.closed = true 277 f.cond.Broadcast() 278 } 279 280 // KeyOf exposes f's keyFunc, but also detects the key of a Deltas object or 281 // DeletedFinalStateUnknown objects. 282 func (f *DeltaFIFO) KeyOf(obj interface{}) (string, error) { 283 if d, ok := obj.(Deltas); ok { 284 if len(d) == 0 { 285 return "", KeyError{obj, ErrZeroLengthDeltasObject} 286 } 287 obj = d.Newest().Object 288 } 289 if d, ok := obj.(DeletedFinalStateUnknown); ok { 290 return d.Key, nil 291 } 292 return f.keyFunc(obj) 293 } 294 295 // HasSynced returns true if an Add/Update/Delete/AddIfNotPresent are called first, 296 // or the first batch of items inserted by Replace() has been popped. 297 func (f *DeltaFIFO) HasSynced() bool { 298 f.lock.Lock() 299 defer f.lock.Unlock() 300 return f.hasSynced_locked() 301 } 302 303 func (f *DeltaFIFO) hasSynced_locked() bool { 304 return f.populated && f.initialPopulationCount == 0 305 } 306 307 // Add inserts an item, and puts it in the queue. The item is only enqueued 308 // if it doesn't already exist in the set. 309 func (f *DeltaFIFO) Add(obj interface{}) error { 310 f.lock.Lock() 311 defer f.lock.Unlock() 312 f.populated = true 313 return f.queueActionLocked(Added, obj) 314 } 315 316 // Update is just like Add, but makes an Updated Delta. 317 func (f *DeltaFIFO) Update(obj interface{}) error { 318 f.lock.Lock() 319 defer f.lock.Unlock() 320 f.populated = true 321 return f.queueActionLocked(Updated, obj) 322 } 323 324 // Delete is just like Add, but makes a Deleted Delta. If the given 325 // object does not already exist, it will be ignored. (It may have 326 // already been deleted by a Replace (re-list), for example.) In this 327 // method `f.knownObjects`, if not nil, provides (via GetByKey) 328 // _additional_ objects that are considered to already exist. 329 func (f *DeltaFIFO) Delete(obj interface{}) error { 330 id, err := f.KeyOf(obj) 331 if err != nil { 332 return KeyError{obj, err} 333 } 334 f.lock.Lock() 335 defer f.lock.Unlock() 336 f.populated = true 337 if f.knownObjects == nil { 338 if _, exists := f.items[id]; !exists { 339 // Presumably, this was deleted when a relist happened. 340 // Don't provide a second report of the same deletion. 341 return nil 342 } 343 } else { 344 // We only want to skip the "deletion" action if the object doesn't 345 // exist in knownObjects and it doesn't have corresponding item in items. 346 // Note that even if there is a "deletion" action in items, we can ignore it, 347 // because it will be deduped automatically in "queueActionLocked" 348 _, exists, err := f.knownObjects.GetByKey(id) 349 _, itemsExist := f.items[id] 350 if err == nil && !exists && !itemsExist { 351 // Presumably, this was deleted when a relist happened. 352 // Don't provide a second report of the same deletion. 353 return nil 354 } 355 } 356 357 // exist in items and/or KnownObjects 358 return f.queueActionLocked(Deleted, obj) 359 } 360 361 // AddIfNotPresent inserts an item, and puts it in the queue. If the item is already 362 // present in the set, it is neither enqueued nor added to the set. 363 // 364 // This is useful in a single producer/consumer scenario so that the consumer can 365 // safely retry items without contending with the producer and potentially enqueueing 366 // stale items. 367 // 368 // Important: obj must be a Deltas (the output of the Pop() function). Yes, this is 369 // different from the Add/Update/Delete functions. 370 func (f *DeltaFIFO) AddIfNotPresent(obj interface{}) error { 371 deltas, ok := obj.(Deltas) 372 if !ok { 373 return fmt.Errorf("object must be of type deltas, but got: %#v", obj) 374 } 375 id, err := f.KeyOf(deltas) 376 if err != nil { 377 return KeyError{obj, err} 378 } 379 f.lock.Lock() 380 defer f.lock.Unlock() 381 f.addIfNotPresent(id, deltas) 382 return nil 383 } 384 385 // addIfNotPresent inserts deltas under id if it does not exist, and assumes the caller 386 // already holds the fifo lock. 387 func (f *DeltaFIFO) addIfNotPresent(id string, deltas Deltas) { 388 f.populated = true 389 if _, exists := f.items[id]; exists { 390 return 391 } 392 393 f.queue = append(f.queue, id) 394 f.items[id] = deltas 395 f.cond.Broadcast() 396 } 397 398 // re-listing and watching can deliver the same update multiple times in any 399 // order. This will combine the most recent two deltas if they are the same. 400 func dedupDeltas(deltas Deltas) Deltas { 401 n := len(deltas) 402 if n < 2 { 403 return deltas 404 } 405 a := &deltas[n-1] 406 b := &deltas[n-2] 407 if out := isDup(a, b); out != nil { 408 deltas[n-2] = *out 409 return deltas[:n-1] 410 } 411 return deltas 412 } 413 414 // If a & b represent the same event, returns the delta that ought to be kept. 415 // Otherwise, returns nil. 416 // TODO: is there anything other than deletions that need deduping? 417 func isDup(a, b *Delta) *Delta { 418 if out := isDeletionDup(a, b); out != nil { 419 return out 420 } 421 // TODO: Detect other duplicate situations? Are there any? 422 return nil 423 } 424 425 // keep the one with the most information if both are deletions. 426 func isDeletionDup(a, b *Delta) *Delta { 427 if b.Type != Deleted || a.Type != Deleted { 428 return nil 429 } 430 // Do more sophisticated checks, or is this sufficient? 431 if _, ok := b.Object.(DeletedFinalStateUnknown); ok { 432 return a 433 } 434 return b 435 } 436 437 // queueActionLocked appends to the delta list for the object. 438 // Caller must lock first. 439 func (f *DeltaFIFO) queueActionLocked(actionType DeltaType, obj interface{}) error { 440 return f.queueActionInternalLocked(actionType, actionType, obj) 441 } 442 443 // queueActionInternalLocked appends to the delta list for the object. 444 // The actionType is emitted and must honor emitDeltaTypeReplaced. 445 // The internalActionType is only used within this function and must 446 // ignore emitDeltaTypeReplaced. 447 // Caller must lock first. 448 func (f *DeltaFIFO) queueActionInternalLocked(actionType, internalActionType DeltaType, obj interface{}) error { 449 id, err := f.KeyOf(obj) 450 if err != nil { 451 return KeyError{obj, err} 452 } 453 454 // Every object comes through this code path once, so this is a good 455 // place to call the transform func. 456 // 457 // If obj is a DeletedFinalStateUnknown tombstone or the action is a Sync, 458 // then the object have already gone through the transformer. 459 // 460 // If the objects already present in the cache are passed to Replace(), 461 // the transformer must be idempotent to avoid re-mutating them, 462 // or coordinate with all readers from the cache to avoid data races. 463 // Default informers do not pass existing objects to Replace. 464 if f.transformer != nil { 465 _, isTombstone := obj.(DeletedFinalStateUnknown) 466 if !isTombstone && internalActionType != Sync { 467 var err error 468 obj, err = f.transformer(obj) 469 if err != nil { 470 return err 471 } 472 } 473 } 474 475 oldDeltas := f.items[id] 476 newDeltas := append(oldDeltas, Delta{actionType, obj}) 477 newDeltas = dedupDeltas(newDeltas) 478 479 if len(newDeltas) > 0 { 480 if _, exists := f.items[id]; !exists { 481 f.queue = append(f.queue, id) 482 } 483 f.items[id] = newDeltas 484 f.cond.Broadcast() 485 } else { 486 // This never happens, because dedupDeltas never returns an empty list 487 // when given a non-empty list (as it is here). 488 // If somehow it happens anyway, deal with it but complain. 489 if oldDeltas == nil { 490 klog.Errorf("Impossible dedupDeltas for id=%q: oldDeltas=%#+v, obj=%#+v; ignoring", id, oldDeltas, obj) 491 return nil 492 } 493 klog.Errorf("Impossible dedupDeltas for id=%q: oldDeltas=%#+v, obj=%#+v; breaking invariant by storing empty Deltas", id, oldDeltas, obj) 494 f.items[id] = newDeltas 495 return fmt.Errorf("Impossible dedupDeltas for id=%q: oldDeltas=%#+v, obj=%#+v; broke DeltaFIFO invariant by storing empty Deltas", id, oldDeltas, obj) 496 } 497 return nil 498 } 499 500 // List returns a list of all the items; it returns the object 501 // from the most recent Delta. 502 // You should treat the items returned inside the deltas as immutable. 503 func (f *DeltaFIFO) List() []interface{} { 504 f.lock.RLock() 505 defer f.lock.RUnlock() 506 return f.listLocked() 507 } 508 509 func (f *DeltaFIFO) listLocked() []interface{} { 510 list := make([]interface{}, 0, len(f.items)) 511 for _, item := range f.items { 512 list = append(list, item.Newest().Object) 513 } 514 return list 515 } 516 517 // ListKeys returns a list of all the keys of the objects currently 518 // in the FIFO. 519 func (f *DeltaFIFO) ListKeys() []string { 520 f.lock.RLock() 521 defer f.lock.RUnlock() 522 list := make([]string, 0, len(f.queue)) 523 for _, key := range f.queue { 524 list = append(list, key) 525 } 526 return list 527 } 528 529 // Get returns the complete list of deltas for the requested item, 530 // or sets exists=false. 531 // You should treat the items returned inside the deltas as immutable. 532 func (f *DeltaFIFO) Get(obj interface{}) (item interface{}, exists bool, err error) { 533 key, err := f.KeyOf(obj) 534 if err != nil { 535 return nil, false, KeyError{obj, err} 536 } 537 return f.GetByKey(key) 538 } 539 540 // GetByKey returns the complete list of deltas for the requested item, 541 // setting exists=false if that list is empty. 542 // You should treat the items returned inside the deltas as immutable. 543 func (f *DeltaFIFO) GetByKey(key string) (item interface{}, exists bool, err error) { 544 f.lock.RLock() 545 defer f.lock.RUnlock() 546 d, exists := f.items[key] 547 if exists { 548 // Copy item's slice so operations on this slice 549 // won't interfere with the object we return. 550 d = copyDeltas(d) 551 } 552 return d, exists, nil 553 } 554 555 // IsClosed checks if the queue is closed 556 func (f *DeltaFIFO) IsClosed() bool { 557 f.lock.Lock() 558 defer f.lock.Unlock() 559 return f.closed 560 } 561 562 // Pop blocks until the queue has some items, and then returns one. If 563 // multiple items are ready, they are returned in the order in which they were 564 // added/updated. The item is removed from the queue (and the store) before it 565 // is returned, so if you don't successfully process it, you need to add it back 566 // with AddIfNotPresent(). 567 // process function is called under lock, so it is safe to update data structures 568 // in it that need to be in sync with the queue (e.g. knownKeys). The PopProcessFunc 569 // may return an instance of ErrRequeue with a nested error to indicate the current 570 // item should be requeued (equivalent to calling AddIfNotPresent under the lock). 571 // process should avoid expensive I/O operation so that other queue operations, i.e. 572 // Add() and Get(), won't be blocked for too long. 573 // 574 // Pop returns a 'Deltas', which has a complete list of all the things 575 // that happened to the object (deltas) while it was sitting in the queue. 576 func (f *DeltaFIFO) Pop(process PopProcessFunc) (interface{}, error) { 577 f.lock.Lock() 578 defer f.lock.Unlock() 579 for { 580 for len(f.queue) == 0 { 581 // When the queue is empty, invocation of Pop() is blocked until new item is enqueued. 582 // When Close() is called, the f.closed is set and the condition is broadcasted. 583 // Which causes this loop to continue and return from the Pop(). 584 if f.closed { 585 return nil, ErrFIFOClosed 586 } 587 588 f.cond.Wait() 589 } 590 isInInitialList := !f.hasSynced_locked() 591 id := f.queue[0] 592 f.queue = f.queue[1:] 593 depth := len(f.queue) 594 if f.initialPopulationCount > 0 { 595 f.initialPopulationCount-- 596 } 597 item, ok := f.items[id] 598 if !ok { 599 // This should never happen 600 klog.Errorf("Inconceivable! %q was in f.queue but not f.items; ignoring.", id) 601 continue 602 } 603 delete(f.items, id) 604 // Only log traces if the queue depth is greater than 10 and it takes more than 605 // 100 milliseconds to process one item from the queue. 606 // Queue depth never goes high because processing an item is locking the queue, 607 // and new items can't be added until processing finish. 608 // https://github.com/kubernetes/kubernetes/issues/103789 609 if depth > 10 { 610 trace := utiltrace.New("DeltaFIFO Pop Process", 611 utiltrace.Field{Key: "ID", Value: id}, 612 utiltrace.Field{Key: "Depth", Value: depth}, 613 utiltrace.Field{Key: "Reason", Value: "slow event handlers blocking the queue"}) 614 defer trace.LogIfLong(100 * time.Millisecond) 615 } 616 err := process(item, isInInitialList) 617 if e, ok := err.(ErrRequeue); ok { 618 f.addIfNotPresent(id, item) 619 err = e.Err 620 } 621 // Don't need to copyDeltas here, because we're transferring 622 // ownership to the caller. 623 return item, err 624 } 625 } 626 627 // Replace atomically does two things: (1) it adds the given objects 628 // using the Sync or Replace DeltaType and then (2) it does some deletions. 629 // In particular: for every pre-existing key K that is not the key of 630 // an object in `list` there is the effect of 631 // `Delete(DeletedFinalStateUnknown{K, O})` where O is the latest known 632 // object of K. The pre-existing keys are those in the union set of the keys in 633 // `f.items` and `f.knownObjects` (if not nil). The last known object for key K is 634 // the one present in the last delta in `f.items`. If there is no delta for K 635 // in `f.items`, it is the object in `f.knownObjects` 636 func (f *DeltaFIFO) Replace(list []interface{}, _ string) error { 637 f.lock.Lock() 638 defer f.lock.Unlock() 639 keys := make(sets.String, len(list)) 640 641 // keep backwards compat for old clients 642 action := Sync 643 if f.emitDeltaTypeReplaced { 644 action = Replaced 645 } 646 647 // Add Sync/Replaced action for each new item. 648 for _, item := range list { 649 key, err := f.KeyOf(item) 650 if err != nil { 651 return KeyError{item, err} 652 } 653 keys.Insert(key) 654 if err := f.queueActionInternalLocked(action, Replaced, item); err != nil { 655 return fmt.Errorf("couldn't enqueue object: %v", err) 656 } 657 } 658 659 // Do deletion detection against objects in the queue 660 queuedDeletions := 0 661 for k, oldItem := range f.items { 662 if keys.Has(k) { 663 continue 664 } 665 // Delete pre-existing items not in the new list. 666 // This could happen if watch deletion event was missed while 667 // disconnected from apiserver. 668 var deletedObj interface{} 669 if n := oldItem.Newest(); n != nil { 670 deletedObj = n.Object 671 672 // if the previous object is a DeletedFinalStateUnknown, we have to extract the actual Object 673 if d, ok := deletedObj.(DeletedFinalStateUnknown); ok { 674 deletedObj = d.Obj 675 } 676 } 677 queuedDeletions++ 678 if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { 679 return err 680 } 681 } 682 683 if f.knownObjects != nil { 684 // Detect deletions for objects not present in the queue, but present in KnownObjects 685 knownKeys := f.knownObjects.ListKeys() 686 for _, k := range knownKeys { 687 if keys.Has(k) { 688 continue 689 } 690 if len(f.items[k]) > 0 { 691 continue 692 } 693 694 deletedObj, exists, err := f.knownObjects.GetByKey(k) 695 if err != nil { 696 deletedObj = nil 697 klog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k) 698 } else if !exists { 699 deletedObj = nil 700 klog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k) 701 } 702 queuedDeletions++ 703 if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil { 704 return err 705 } 706 } 707 } 708 709 if !f.populated { 710 f.populated = true 711 f.initialPopulationCount = keys.Len() + queuedDeletions 712 } 713 714 return nil 715 } 716 717 // Resync adds, with a Sync type of Delta, every object listed by 718 // `f.knownObjects` whose key is not already queued for processing. 719 // If `f.knownObjects` is `nil` then Resync does nothing. 720 func (f *DeltaFIFO) Resync() error { 721 f.lock.Lock() 722 defer f.lock.Unlock() 723 724 if f.knownObjects == nil { 725 return nil 726 } 727 728 keys := f.knownObjects.ListKeys() 729 for _, k := range keys { 730 if err := f.syncKeyLocked(k); err != nil { 731 return err 732 } 733 } 734 return nil 735 } 736 737 func (f *DeltaFIFO) syncKeyLocked(key string) error { 738 obj, exists, err := f.knownObjects.GetByKey(key) 739 if err != nil { 740 klog.Errorf("Unexpected error %v during lookup of key %v, unable to queue object for sync", err, key) 741 return nil 742 } else if !exists { 743 klog.Infof("Key %v does not exist in known objects store, unable to queue object for sync", key) 744 return nil 745 } 746 747 // If we are doing Resync() and there is already an event queued for that object, 748 // we ignore the Resync for it. This is to avoid the race, in which the resync 749 // comes with the previous value of object (since queueing an event for the object 750 // doesn't trigger changing the underlying store <knownObjects>. 751 id, err := f.KeyOf(obj) 752 if err != nil { 753 return KeyError{obj, err} 754 } 755 if len(f.items[id]) > 0 { 756 return nil 757 } 758 759 if err := f.queueActionLocked(Sync, obj); err != nil { 760 return fmt.Errorf("couldn't queue object: %v", err) 761 } 762 return nil 763 } 764 765 // A KeyListerGetter is anything that knows how to list its keys and look up by key. 766 type KeyListerGetter interface { 767 KeyLister 768 KeyGetter 769 } 770 771 // A KeyLister is anything that knows how to list its keys. 772 type KeyLister interface { 773 ListKeys() []string 774 } 775 776 // A KeyGetter is anything that knows how to get the value stored under a given key. 777 type KeyGetter interface { 778 // GetByKey returns the value associated with the key, or sets exists=false. 779 GetByKey(key string) (value interface{}, exists bool, err error) 780 } 781 782 // Oldest is a convenience function that returns the oldest delta, or 783 // nil if there are no deltas. 784 func (d Deltas) Oldest() *Delta { 785 if len(d) > 0 { 786 return &d[0] 787 } 788 return nil 789 } 790 791 // Newest is a convenience function that returns the newest delta, or 792 // nil if there are no deltas. 793 func (d Deltas) Newest() *Delta { 794 if n := len(d); n > 0 { 795 return &d[n-1] 796 } 797 return nil 798 } 799 800 // copyDeltas returns a shallow copy of d; that is, it copies the slice but not 801 // the objects in the slice. This allows Get/List to return an object that we 802 // know won't be clobbered by a subsequent modifications. 803 func copyDeltas(d Deltas) Deltas { 804 d2 := make(Deltas, len(d)) 805 copy(d2, d) 806 return d2 807 } 808 809 // DeletedFinalStateUnknown is placed into a DeltaFIFO in the case where an object 810 // was deleted but the watch deletion event was missed while disconnected from 811 // apiserver. In this case we don't know the final "resting" state of the object, so 812 // there's a chance the included `Obj` is stale. 813 type DeletedFinalStateUnknown struct { 814 Key string 815 Obj interface{} 816 }