Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
wait for cache start before returning from WaitForCacheSync
There is currently an unintuitive race condition between starting
a cache, waiting for it to sync, and using it the first time.

The crux of the problem is that WaitForCacheSync can return true
BEFORE the Start function has had a chance to set started to true.

This means that Get and List can return ErrCacheNotStarted even
after WaitForCacheSync has returned true.

This commit adds a channel and a wait function that is called from
WaitForCacheSync to ensure that started has been set to true before
returning.
  • Loading branch information
joelanford committed Jan 10, 2020
commit f73674ede179baac2a9aee84a44fa4c58184c300
6 changes: 6 additions & 0 deletions pkg/cache/internal/deleg_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ func (m *InformersMap) WaitForCacheSync(stop <-chan struct{}) bool {
syncedFuncs := append([]cache.InformerSynced(nil), m.structured.HasSyncedFuncs()...)
syncedFuncs = append(syncedFuncs, m.unstructured.HasSyncedFuncs()...)

if !m.structured.waitForStarted(stop) {
return false
}
if !m.unstructured.waitForStarted(stop) {
return false
}
return cache.WaitForCacheSync(stop, syncedFuncs...)
}

Expand Down
15 changes: 15 additions & 0 deletions pkg/cache/internal/informers_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func newSpecificInformersMap(config *rest.Config,
codecs: serializer.NewCodecFactory(scheme),
paramCodec: runtime.NewParameterCodec(scheme),
resync: resync,
startWait: make(chan struct{}),
createListWatcher: createListWatcher,
namespace: namespace,
}
Expand Down Expand Up @@ -104,6 +105,10 @@ type specificInformersMap struct {
// start is true if the informers have been started
started bool

// startWait is a channel that is closed after the
// informer has been started.
startWait chan struct{}

// createClient knows how to create a client and a list object,
// and allows for abstracting over the particulars of structured vs
// unstructured objects.
Expand Down Expand Up @@ -131,10 +136,20 @@ func (ip *specificInformersMap) Start(stop <-chan struct{}) {

// Set started to true so we immediately start any informers added later.
ip.started = true
close(ip.startWait)
}()
<-stop
}

func (ip *specificInformersMap) waitForStarted(stop <-chan struct{}) bool {
select {
case <-ip.startWait:
return true
case <-stop:
return false
}
}

// HasSyncedFuncs returns all the HasSynced functions for the informers in this map.
func (ip *specificInformersMap) HasSyncedFuncs() []cache.InformerSynced {
ip.mu.RLock()
Expand Down