mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
Merge pull request #75717 from wojtek-t/reduce_critical_sections
Reduce critical sections in cacher::Watch function
This commit is contained in:
commit
df9e66628c
@ -53,6 +53,7 @@ var (
|
|||||||
},
|
},
|
||||||
[]string{"resource"},
|
[]string{"resource"},
|
||||||
)
|
)
|
||||||
|
emptyFunc = func() {}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -339,21 +340,6 @@ func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string,
|
|||||||
|
|
||||||
c.ready.wait()
|
c.ready.wait()
|
||||||
|
|
||||||
// We explicitly use thread unsafe version and do locking ourself to ensure that
|
|
||||||
// no new events will be processed in the meantime. The watchCache will be unlocked
|
|
||||||
// on return from this function.
|
|
||||||
// Note that we cannot do it under Cacher lock, to avoid a deadlock, since the
|
|
||||||
// underlying watchCache is calling processEvent under its lock.
|
|
||||||
c.watchCache.RLock()
|
|
||||||
defer c.watchCache.RUnlock()
|
|
||||||
initEvents, err := c.watchCache.GetAllEventsSinceThreadUnsafe(watchRV)
|
|
||||||
if err != nil {
|
|
||||||
// To match the uncached watch implementation, once we have passed authn/authz/admission,
|
|
||||||
// and successfully parsed a resource version, other errors must fail with a watch event of type ERROR,
|
|
||||||
// rather than a directly returned error.
|
|
||||||
return newErrWatcher(err), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
triggerValue, triggerSupported := "", false
|
triggerValue, triggerSupported := "", false
|
||||||
// TODO: Currently we assume that in a given Cacher object, any <predicate> that is
|
// TODO: Currently we assume that in a given Cacher object, any <predicate> that is
|
||||||
// passed here is aware of exactly the same trigger (at most one).
|
// passed here is aware of exactly the same trigger (at most one).
|
||||||
@ -377,6 +363,25 @@ func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string,
|
|||||||
chanSize = 1000
|
chanSize = 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a watcher here to reduce memory allocations under lock,
|
||||||
|
// given that memory allocation may trigger GC and block the thread.
|
||||||
|
watcher := newCacheWatcher(chanSize, filterWithAttrsFunction(key, pred), emptyFunc, c.versioner)
|
||||||
|
|
||||||
|
// We explicitly use thread unsafe version and do locking ourself to ensure that
|
||||||
|
// no new events will be processed in the meantime. The watchCache will be unlocked
|
||||||
|
// on return from this function.
|
||||||
|
// Note that we cannot do it under Cacher lock, to avoid a deadlock, since the
|
||||||
|
// underlying watchCache is calling processEvent under its lock.
|
||||||
|
c.watchCache.RLock()
|
||||||
|
defer c.watchCache.RUnlock()
|
||||||
|
initEvents, err := c.watchCache.GetAllEventsSinceThreadUnsafe(watchRV)
|
||||||
|
if err != nil {
|
||||||
|
// To match the uncached watch implementation, once we have passed authn/authz/admission,
|
||||||
|
// and successfully parsed a resource version, other errors must fail with a watch event of type ERROR,
|
||||||
|
// rather than a directly returned error.
|
||||||
|
return newErrWatcher(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
// With some events already sent, update resourceVersion so that
|
// With some events already sent, update resourceVersion so that
|
||||||
// events that were buffered and not yet processed won't be delivered
|
// events that were buffered and not yet processed won't be delivered
|
||||||
// to this watcher second time causing going back in time.
|
// to this watcher second time causing going back in time.
|
||||||
@ -384,13 +389,16 @@ func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string,
|
|||||||
watchRV = initEvents[len(initEvents)-1].ResourceVersion
|
watchRV = initEvents[len(initEvents)-1].ResourceVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func() {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
forget := forgetWatcher(c, c.watcherIdx, triggerValue, triggerSupported)
|
// Update watcher.forget function once we can compute it.
|
||||||
watcher := newCacheWatcher(watchRV, chanSize, initEvents, filterWithAttrsFunction(key, pred), forget, c.versioner)
|
watcher.forget = forgetWatcher(c, c.watcherIdx, triggerValue, triggerSupported)
|
||||||
|
|
||||||
c.watchers.addWatcher(watcher, c.watcherIdx, triggerValue, triggerSupported)
|
c.watchers.addWatcher(watcher, c.watcherIdx, triggerValue, triggerSupported)
|
||||||
c.watcherIdx++
|
c.watcherIdx++
|
||||||
|
}()
|
||||||
|
|
||||||
|
go watcher.process(initEvents, watchRV)
|
||||||
return watcher, nil
|
return watcher, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,8 +887,8 @@ type cacheWatcher struct {
|
|||||||
versioner storage.Versioner
|
versioner storage.Versioner
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCacheWatcher(resourceVersion uint64, chanSize int, initEvents []*watchCacheEvent, filter filterWithAttrsFunc, forget func(), versioner storage.Versioner) *cacheWatcher {
|
func newCacheWatcher(chanSize int, filter filterWithAttrsFunc, forget func(), versioner storage.Versioner) *cacheWatcher {
|
||||||
watcher := &cacheWatcher{
|
return &cacheWatcher{
|
||||||
input: make(chan *watchCacheEvent, chanSize),
|
input: make(chan *watchCacheEvent, chanSize),
|
||||||
result: make(chan watch.Event, chanSize),
|
result: make(chan watch.Event, chanSize),
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
@ -889,8 +897,6 @@ func newCacheWatcher(resourceVersion uint64, chanSize int, initEvents []*watchCa
|
|||||||
forget: forget,
|
forget: forget,
|
||||||
versioner: versioner,
|
versioner: versioner,
|
||||||
}
|
}
|
||||||
go watcher.process(initEvents, resourceVersion)
|
|
||||||
return watcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements watch.Interface.
|
// Implements watch.Interface.
|
||||||
|
@ -63,7 +63,8 @@ func TestCacheWatcherCleanupNotBlockedByResult(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// set the size of the buffer of w.result to 0, so that the writes to
|
// set the size of the buffer of w.result to 0, so that the writes to
|
||||||
// w.result is blocked.
|
// w.result is blocked.
|
||||||
w = newCacheWatcher(0, 0, initEvents, filter, forget, testVersioner{})
|
w = newCacheWatcher(0, filter, forget, testVersioner{})
|
||||||
|
go w.process(initEvents, 0)
|
||||||
w.Stop()
|
w.Stop()
|
||||||
if err := wait.PollImmediate(1*time.Second, 5*time.Second, func() (bool, error) {
|
if err := wait.PollImmediate(1*time.Second, 5*time.Second, func() (bool, error) {
|
||||||
lock.RLock()
|
lock.RLock()
|
||||||
@ -181,7 +182,8 @@ TestCase:
|
|||||||
for j := range testCase.events {
|
for j := range testCase.events {
|
||||||
testCase.events[j].ResourceVersion = uint64(j) + 1
|
testCase.events[j].ResourceVersion = uint64(j) + 1
|
||||||
}
|
}
|
||||||
w := newCacheWatcher(0, 0, testCase.events, filter, forget, testVersioner{})
|
w := newCacheWatcher(0, filter, forget, testVersioner{})
|
||||||
|
go w.process(testCase.events, 0)
|
||||||
ch := w.ResultChan()
|
ch := w.ResultChan()
|
||||||
for j, event := range testCase.expected {
|
for j, event := range testCase.expected {
|
||||||
e := <-ch
|
e := <-ch
|
||||||
|
Loading…
Reference in New Issue
Block a user