Unverified Commit 28d86ac4 authored by k8s-ci-robot's avatar k8s-ci-robot Committed by GitHub

Merge pull request #67308 from cofyc/fix67260

Use monotonically increasing generation to prevent equivalence cache race
parents 9159c9ce b3f1e120
...@@ -50,7 +50,6 @@ go_test( ...@@ -50,7 +50,6 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
......
...@@ -555,13 +555,6 @@ func (cache *schedulerCache) ListPDBs(selector labels.Selector) ([]*policy.PodDi ...@@ -555,13 +555,6 @@ func (cache *schedulerCache) ListPDBs(selector labels.Selector) ([]*policy.PodDi
return pdbs, nil return pdbs, nil
} }
func (cache *schedulerCache) IsUpToDate(n *NodeInfo) bool {
cache.mu.RLock()
defer cache.mu.RUnlock()
node, ok := cache.nodes[n.Node().Name]
return ok && n.generation == node.generation
}
func (cache *schedulerCache) run() { func (cache *schedulerCache) run() {
go wait.Until(cache.cleanupExpiredAssumedPods, cache.period, cache.stop) go wait.Until(cache.cleanupExpiredAssumedPods, cache.period, cache.stop)
} }
......
...@@ -30,7 +30,6 @@ import ( ...@@ -30,7 +30,6 @@ import (
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
priorityutil "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util" priorityutil "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util"
...@@ -1336,26 +1335,3 @@ func TestPDBOperations(t *testing.T) { ...@@ -1336,26 +1335,3 @@ func TestPDBOperations(t *testing.T) {
} }
} }
} }
func TestIsUpToDate(t *testing.T) {
cache := New(time.Duration(0), wait.NeverStop)
if err := cache.AddNode(&v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "n1"}}); err != nil {
t.Errorf("Could not add node: %v", err)
}
s := cache.Snapshot()
node := s.Nodes["n1"]
if !cache.IsUpToDate(node) {
t.Errorf("Node incorrectly marked as stale")
}
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p1", UID: "p1"}, Spec: v1.PodSpec{NodeName: "n1"}}
if err := cache.AddPod(pod); err != nil {
t.Errorf("Could not add pod: %v", err)
}
if cache.IsUpToDate(node) {
t.Errorf("Node incorrectly marked as up to date")
}
badNode := &NodeInfo{node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "n2"}}}
if cache.IsUpToDate(badNode) {
t.Errorf("Nonexistant node incorrectly marked as up to date")
}
}
...@@ -123,9 +123,6 @@ type Cache interface { ...@@ -123,9 +123,6 @@ type Cache interface {
// Snapshot takes a snapshot on current cache // Snapshot takes a snapshot on current cache
Snapshot() *Snapshot Snapshot() *Snapshot
// IsUpToDate returns true if the given NodeInfo matches the current data in the cache.
IsUpToDate(n *NodeInfo) bool
// NodeTree returns a node tree structure // NodeTree returns a node tree structure
NodeTree() *NodeTree NodeTree() *NodeTree
} }
......
...@@ -27,7 +27,6 @@ go_test( ...@@ -27,7 +27,6 @@ go_test(
"//pkg/scheduler/algorithm:go_default_library", "//pkg/scheduler/algorithm:go_default_library",
"//pkg/scheduler/algorithm/predicates:go_default_library", "//pkg/scheduler/algorithm/predicates:go_default_library",
"//pkg/scheduler/cache:go_default_library", "//pkg/scheduler/cache:go_default_library",
"//pkg/scheduler/testing:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
......
...@@ -111,6 +111,25 @@ type genericScheduler struct { ...@@ -111,6 +111,25 @@ type genericScheduler struct {
percentageOfNodesToScore int32 percentageOfNodesToScore int32
} }
// snapshot snapshots equivalane cache and node infos for all fit and priority
// functions.
func (g *genericScheduler) snapshot() error {
// IMPORTANT NOTE: We must snapshot equivalence cache before snapshotting
// scheduler cache, otherwise stale data may be written into equivalence
// cache, e.g.
// 1. snapshot cache
// 2. event arrives, updating cache and invalidating predicates or whole node cache
// 3. snapshot ecache
// 4. evaludate predicates
// 5. stale result will be written to ecache
if g.equivalenceCache != nil {
g.equivalenceCache.Snapshot()
}
// Used for all fit and priority funcs.
return g.cache.UpdateNodeNameToInfoMap(g.cachedNodeInfoMap)
}
// Schedule tries to schedule the given pod to one of the nodes in the node list. // Schedule tries to schedule the given pod to one of the nodes in the node list.
// If it succeeds, it will return the name of the node. // If it succeeds, it will return the name of the node.
// If it fails, it will return a FitError error with reasons. // If it fails, it will return a FitError error with reasons.
...@@ -130,8 +149,7 @@ func (g *genericScheduler) Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister ...@@ -130,8 +149,7 @@ func (g *genericScheduler) Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister
return "", ErrNoNodesAvailable return "", ErrNoNodesAvailable
} }
// Used for all fit and priority funcs. err = g.snapshot()
err = g.cache.UpdateNodeNameToInfoMap(g.cachedNodeInfoMap)
if err != nil { if err != nil {
return "", err return "", err
} }
...@@ -227,7 +245,7 @@ func (g *genericScheduler) Preempt(pod *v1.Pod, nodeLister algorithm.NodeLister, ...@@ -227,7 +245,7 @@ func (g *genericScheduler) Preempt(pod *v1.Pod, nodeLister algorithm.NodeLister,
if !ok || fitError == nil { if !ok || fitError == nil {
return nil, nil, nil, nil return nil, nil, nil, nil
} }
err := g.cache.UpdateNodeNameToInfoMap(g.cachedNodeInfoMap) err := g.snapshot()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
...@@ -396,14 +414,13 @@ func (g *genericScheduler) findNodesThatFit(pod *v1.Pod, nodes []*v1.Node) ([]*v ...@@ -396,14 +414,13 @@ func (g *genericScheduler) findNodesThatFit(pod *v1.Pod, nodes []*v1.Node) ([]*v
var nodeCache *equivalence.NodeCache var nodeCache *equivalence.NodeCache
nodeName := g.cache.NodeTree().Next() nodeName := g.cache.NodeTree().Next()
if g.equivalenceCache != nil { if g.equivalenceCache != nil {
nodeCache, _ = g.equivalenceCache.GetNodeCache(nodeName) nodeCache = g.equivalenceCache.LoadNodeCache(nodeName)
} }
fits, failedPredicates, err := podFitsOnNode( fits, failedPredicates, err := podFitsOnNode(
pod, pod,
meta, meta,
g.cachedNodeInfoMap[nodeName], g.cachedNodeInfoMap[nodeName],
g.predicates, g.predicates,
g.cache,
nodeCache, nodeCache,
g.schedulingQueue, g.schedulingQueue,
g.alwaysCheckAllPredicates, g.alwaysCheckAllPredicates,
...@@ -516,7 +533,6 @@ func podFitsOnNode( ...@@ -516,7 +533,6 @@ func podFitsOnNode(
meta algorithm.PredicateMetadata, meta algorithm.PredicateMetadata,
info *schedulercache.NodeInfo, info *schedulercache.NodeInfo,
predicateFuncs map[string]algorithm.FitPredicate, predicateFuncs map[string]algorithm.FitPredicate,
cache schedulercache.Cache,
nodeCache *equivalence.NodeCache, nodeCache *equivalence.NodeCache,
queue SchedulingQueue, queue SchedulingQueue,
alwaysCheckAllPredicates bool, alwaysCheckAllPredicates bool,
...@@ -558,7 +574,7 @@ func podFitsOnNode( ...@@ -558,7 +574,7 @@ func podFitsOnNode(
// TODO(bsalamat): consider using eCache and adding proper eCache invalidations // TODO(bsalamat): consider using eCache and adding proper eCache invalidations
// when pods are nominated or their nominations change. // when pods are nominated or their nominations change.
eCacheAvailable = equivClass != nil && nodeCache != nil && !podsAdded eCacheAvailable = equivClass != nil && nodeCache != nil && !podsAdded
for _, predicateKey := range predicates.Ordering() { for predicateID, predicateKey := range predicates.Ordering() {
var ( var (
fit bool fit bool
reasons []algorithm.PredicateFailureReason reasons []algorithm.PredicateFailureReason
...@@ -567,7 +583,7 @@ func podFitsOnNode( ...@@ -567,7 +583,7 @@ func podFitsOnNode(
//TODO (yastij) : compute average predicate restrictiveness to export it as Prometheus metric //TODO (yastij) : compute average predicate restrictiveness to export it as Prometheus metric
if predicate, exist := predicateFuncs[predicateKey]; exist { if predicate, exist := predicateFuncs[predicateKey]; exist {
if eCacheAvailable { if eCacheAvailable {
fit, reasons, err = nodeCache.RunPredicate(predicate, predicateKey, pod, metaToUse, nodeInfoToUse, equivClass, cache) fit, reasons, err = nodeCache.RunPredicate(predicate, predicateKey, predicateID, pod, metaToUse, nodeInfoToUse, equivClass)
} else { } else {
fit, reasons, err = predicate(pod, metaToUse, nodeInfoToUse) fit, reasons, err = predicate(pod, metaToUse, nodeInfoToUse)
} }
...@@ -991,7 +1007,7 @@ func selectVictimsOnNode( ...@@ -991,7 +1007,7 @@ func selectVictimsOnNode(
// that we should check is if the "pod" is failing to schedule due to pod affinity // that we should check is if the "pod" is failing to schedule due to pod affinity
// failure. // failure.
// TODO(bsalamat): Consider checking affinity to lower priority pods if feasible with reasonable performance. // TODO(bsalamat): Consider checking affinity to lower priority pods if feasible with reasonable performance.
if fits, _, err := podFitsOnNode(pod, meta, nodeInfoCopy, fitPredicates, nil, nil, queue, false, nil); !fits { if fits, _, err := podFitsOnNode(pod, meta, nodeInfoCopy, fitPredicates, nil, queue, false, nil); !fits {
if err != nil { if err != nil {
glog.Warningf("Encountered error while selecting victims on node %v: %v", nodeInfo.Node().Name, err) glog.Warningf("Encountered error while selecting victims on node %v: %v", nodeInfo.Node().Name, err)
} }
...@@ -1005,7 +1021,7 @@ func selectVictimsOnNode( ...@@ -1005,7 +1021,7 @@ func selectVictimsOnNode(
violatingVictims, nonViolatingVictims := filterPodsWithPDBViolation(potentialVictims.Items, pdbs) violatingVictims, nonViolatingVictims := filterPodsWithPDBViolation(potentialVictims.Items, pdbs)
reprievePod := func(p *v1.Pod) bool { reprievePod := func(p *v1.Pod) bool {
addPod(p) addPod(p)
fits, _, _ := podFitsOnNode(pod, meta, nodeInfoCopy, fitPredicates, nil, nil, queue, false, nil) fits, _, _ := podFitsOnNode(pod, meta, nodeInfoCopy, fitPredicates, nil, queue, false, nil)
if !fits { if !fits {
removePod(p) removePod(p)
victims = append(victims, p) victims = append(victims, p)
......
...@@ -1474,7 +1474,10 @@ func TestCacheInvalidationRace(t *testing.T) { ...@@ -1474,7 +1474,10 @@ func TestCacheInvalidationRace(t *testing.T) {
cacheInvalidated: make(chan struct{}), cacheInvalidated: make(chan struct{}),
} }
eCache := equivalence.NewCache() ps := map[string]algorithm.FitPredicate{"testPredicate": testPredicate}
algorithmpredicates.SetPredicatesOrdering([]string{"testPredicate"})
eCache := equivalence.NewCache(algorithmpredicates.Ordering())
eCache.GetNodeCache(testNode.Name)
// Ensure that equivalence cache invalidation happens after the scheduling cycle starts, but before // Ensure that equivalence cache invalidation happens after the scheduling cycle starts, but before
// the equivalence cache would be updated. // the equivalence cache would be updated.
go func() { go func() {
...@@ -1490,12 +1493,91 @@ func TestCacheInvalidationRace(t *testing.T) { ...@@ -1490,12 +1493,91 @@ func TestCacheInvalidationRace(t *testing.T) {
}() }()
// Set up the scheduler. // Set up the scheduler.
prioritizers := []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}
pvcLister := schedulertesting.FakePersistentVolumeClaimLister([]*v1.PersistentVolumeClaim{})
scheduler := NewGenericScheduler(
mockCache,
eCache,
NewSchedulingQueue(),
ps,
algorithm.EmptyPredicateMetadataProducer,
prioritizers,
algorithm.EmptyPriorityMetadataProducer,
nil, nil, pvcLister, true, false,
schedulerapi.DefaultPercentageOfNodesToScore)
// First scheduling attempt should fail.
nodeLister := schedulertesting.FakeNodeLister(makeNodeList([]string{"machine1"}))
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "test-pod"}}
machine, err := scheduler.Schedule(pod, nodeLister)
if machine != "" || err == nil {
t.Error("First scheduling attempt did not fail")
}
// Second scheduling attempt should succeed because cache was invalidated.
_, err = scheduler.Schedule(pod, nodeLister)
if err != nil {
t.Errorf("Second scheduling attempt failed: %v", err)
}
if callCount != 2 {
t.Errorf("Predicate should have been called twice. Was called %d times.", callCount)
}
}
// TestCacheInvalidationRace2 tests that cache invalidation is correctly handled
// when an invalidation event happens while a predicate is running.
func TestCacheInvalidationRace2(t *testing.T) {
// Create a predicate that returns false the first time and true on subsequent calls.
var (
podWillFit = false
callCount int
cycleStart = make(chan struct{})
cacheInvalidated = make(chan struct{})
once sync.Once
)
testPredicate := func(pod *v1.Pod,
meta algorithm.PredicateMetadata,
nodeInfo *schedulercache.NodeInfo) (bool, []algorithm.PredicateFailureReason, error) {
callCount++
once.Do(func() {
cycleStart <- struct{}{}
<-cacheInvalidated
})
if !podWillFit {
podWillFit = true
return false, []algorithm.PredicateFailureReason{algorithmpredicates.ErrFakePredicate}, nil
}
return true, nil, nil
}
// Set up the mock cache.
cache := schedulercache.New(time.Duration(0), wait.NeverStop)
testNode := &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1"}}
cache.AddNode(testNode)
ps := map[string]algorithm.FitPredicate{"testPredicate": testPredicate} ps := map[string]algorithm.FitPredicate{"testPredicate": testPredicate}
algorithmpredicates.SetPredicatesOrdering([]string{"testPredicate"}) algorithmpredicates.SetPredicatesOrdering([]string{"testPredicate"})
eCache := equivalence.NewCache(algorithmpredicates.Ordering())
eCache.GetNodeCache(testNode.Name)
// Ensure that equivalence cache invalidation happens after the scheduling cycle starts, but before
// the equivalence cache would be updated.
go func() {
<-cycleStart
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "new-pod", UID: "new-pod"},
Spec: v1.PodSpec{NodeName: "machine1"}}
if err := cache.AddPod(pod); err != nil {
t.Errorf("Could not add pod to cache: %v", err)
}
eCache.InvalidateAllPredicatesOnNode("machine1")
cacheInvalidated <- struct{}{}
}()
// Set up the scheduler.
prioritizers := []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}} prioritizers := []algorithm.PriorityConfig{{Map: EqualPriorityMap, Weight: 1}}
pvcLister := schedulertesting.FakePersistentVolumeClaimLister([]*v1.PersistentVolumeClaim{}) pvcLister := schedulertesting.FakePersistentVolumeClaimLister([]*v1.PersistentVolumeClaim{})
scheduler := NewGenericScheduler( scheduler := NewGenericScheduler(
mockCache, cache,
eCache, eCache,
NewSchedulingQueue(), NewSchedulingQueue(),
ps, ps,
......
...@@ -733,9 +733,11 @@ func (c *configFactory) updatePodInCache(oldObj, newObj interface{}) { ...@@ -733,9 +733,11 @@ func (c *configFactory) updatePodInCache(oldObj, newObj interface{}) {
return return
} }
// NOTE: Because the scheduler uses snapshots of schedulerCache and the live // NOTE: Updates must be written to scheduler cache before invalidating
// version of equivalencePodCache, updates must be written to schedulerCache // equivalence cache, because we could snapshot equivalence cache after the
// before invalidating equivalencePodCache. // invalidation and then snapshot the cache itself. If the cache is
// snapshotted before updates are written, we would update equivalence
// cache with stale information which is based on snapshot of old cache.
if err := c.schedulerCache.UpdatePod(oldPod, newPod); err != nil { if err := c.schedulerCache.UpdatePod(oldPod, newPod); err != nil {
glog.Errorf("scheduler cache UpdatePod failed: %v", err) glog.Errorf("scheduler cache UpdatePod failed: %v", err)
} }
...@@ -822,9 +824,11 @@ func (c *configFactory) deletePodFromCache(obj interface{}) { ...@@ -822,9 +824,11 @@ func (c *configFactory) deletePodFromCache(obj interface{}) {
glog.Errorf("cannot convert to *v1.Pod: %v", t) glog.Errorf("cannot convert to *v1.Pod: %v", t)
return return
} }
// NOTE: Because the scheduler uses snapshots of schedulerCache and the live // NOTE: Updates must be written to scheduler cache before invalidating
// version of equivalencePodCache, updates must be written to schedulerCache // equivalence cache, because we could snapshot equivalence cache after the
// before invalidating equivalencePodCache. // invalidation and then snapshot the cache itself. If the cache is
// snapshotted before updates are written, we would update equivalence
// cache with stale information which is based on snapshot of old cache.
if err := c.schedulerCache.RemovePod(pod); err != nil { if err := c.schedulerCache.RemovePod(pod); err != nil {
glog.Errorf("scheduler cache RemovePod failed: %v", err) glog.Errorf("scheduler cache RemovePod failed: %v", err)
} }
...@@ -861,15 +865,17 @@ func (c *configFactory) addNodeToCache(obj interface{}) { ...@@ -861,15 +865,17 @@ func (c *configFactory) addNodeToCache(obj interface{}) {
return return
} }
if err := c.schedulerCache.AddNode(node); err != nil { // NOTE: Because the scheduler uses equivalence cache for nodes, we need
glog.Errorf("scheduler cache AddNode failed: %v", err) // to create it before adding node into scheduler cache.
}
if c.enableEquivalenceClassCache { if c.enableEquivalenceClassCache {
// GetNodeCache() will lazily create NodeCache for given node if it does not exist. // GetNodeCache() will lazily create NodeCache for given node if it does not exist.
c.equivalencePodCache.GetNodeCache(node.GetName()) c.equivalencePodCache.GetNodeCache(node.GetName())
} }
if err := c.schedulerCache.AddNode(node); err != nil {
glog.Errorf("scheduler cache AddNode failed: %v", err)
}
c.podQueue.MoveAllToActiveQueue() c.podQueue.MoveAllToActiveQueue()
// NOTE: add a new node does not affect existing predicates in equivalence cache // NOTE: add a new node does not affect existing predicates in equivalence cache
} }
...@@ -886,9 +892,11 @@ func (c *configFactory) updateNodeInCache(oldObj, newObj interface{}) { ...@@ -886,9 +892,11 @@ func (c *configFactory) updateNodeInCache(oldObj, newObj interface{}) {
return return
} }
// NOTE: Because the scheduler uses snapshots of schedulerCache and the live // NOTE: Updates must be written to scheduler cache before invalidating
// version of equivalencePodCache, updates must be written to schedulerCache // equivalence cache, because we could snapshot equivalence cache after the
// before invalidating equivalencePodCache. // invalidation and then snapshot the cache itself. If the cache is
// snapshotted before updates are written, we would update equivalence
// cache with stale information which is based on snapshot of old cache.
if err := c.schedulerCache.UpdateNode(oldNode, newNode); err != nil { if err := c.schedulerCache.UpdateNode(oldNode, newNode); err != nil {
glog.Errorf("scheduler cache UpdateNode failed: %v", err) glog.Errorf("scheduler cache UpdateNode failed: %v", err)
} }
...@@ -982,9 +990,11 @@ func (c *configFactory) deleteNodeFromCache(obj interface{}) { ...@@ -982,9 +990,11 @@ func (c *configFactory) deleteNodeFromCache(obj interface{}) {
glog.Errorf("cannot convert to *v1.Node: %v", t) glog.Errorf("cannot convert to *v1.Node: %v", t)
return return
} }
// NOTE: Because the scheduler uses snapshots of schedulerCache and the live // NOTE: Updates must be written to scheduler cache before invalidating
// version of equivalencePodCache, updates must be written to schedulerCache // equivalence cache, because we could snapshot equivalence cache after the
// before invalidating equivalencePodCache. // invalidation and then snapshot the cache itself. If the cache is
// snapshotted before updates are written, we would update equivalence
// cache with stale information which is based on snapshot of old cache.
if err := c.schedulerCache.RemoveNode(node); err != nil { if err := c.schedulerCache.RemoveNode(node); err != nil {
glog.Errorf("scheduler cache RemoveNode failed: %v", err) glog.Errorf("scheduler cache RemoveNode failed: %v", err)
} }
...@@ -1178,7 +1188,7 @@ func (c *configFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String, ...@@ -1178,7 +1188,7 @@ func (c *configFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String,
// Init equivalence class cache // Init equivalence class cache
if c.enableEquivalenceClassCache { if c.enableEquivalenceClassCache {
c.equivalencePodCache = equivalence.NewCache() c.equivalencePodCache = equivalence.NewCache(predicates.Ordering())
glog.Info("Created equivalence class cache") glog.Info("Created equivalence class cache")
} }
...@@ -1414,9 +1424,11 @@ func (c *configFactory) MakeDefaultErrorFunc(backoff *util.PodBackoff, podQueue ...@@ -1414,9 +1424,11 @@ func (c *configFactory) MakeDefaultErrorFunc(backoff *util.PodBackoff, podQueue
_, err := c.client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{}) _, err := c.client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
if err != nil && errors.IsNotFound(err) { if err != nil && errors.IsNotFound(err) {
node := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeName}} node := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeName}}
// NOTE: Because the scheduler uses snapshots of schedulerCache and the live // NOTE: Updates must be written to scheduler cache before invalidating
// version of equivalencePodCache, updates must be written to schedulerCache // equivalence cache, because we could snapshot equivalence cache after the
// before invalidating equivalencePodCache. // invalidation and then snapshot the cache itself. If the cache is
// snapshotted before updates are written, we would update equivalence
// cache with stale information which is based on snapshot of old cache.
c.schedulerCache.RemoveNode(&node) c.schedulerCache.RemoveNode(&node)
// invalidate cached predicate for the node // invalidate cached predicate for the node
if c.enableEquivalenceClassCache { if c.enableEquivalenceClassCache {
......
...@@ -329,9 +329,11 @@ func (sched *Scheduler) assume(assumed *v1.Pod, host string) error { ...@@ -329,9 +329,11 @@ func (sched *Scheduler) assume(assumed *v1.Pod, host string) error {
// If the binding fails, scheduler will release resources allocated to assumed pod // If the binding fails, scheduler will release resources allocated to assumed pod
// immediately. // immediately.
assumed.Spec.NodeName = host assumed.Spec.NodeName = host
// NOTE: Because the scheduler uses snapshots of SchedulerCache and the live // NOTE: Updates must be written to scheduler cache before invalidating
// version of Ecache, updates must be written to SchedulerCache before // equivalence cache, because we could snapshot equivalence cache after the
// invalidating Ecache. // invalidation and then snapshot the cache itself. If the cache is
// snapshotted before updates are written, we would update equivalence
// cache with stale information which is based on snapshot of old cache.
if err := sched.config.SchedulerCache.AssumePod(assumed); err != nil { if err := sched.config.SchedulerCache.AssumePod(assumed); err != nil {
glog.Errorf("scheduler cache AssumePod failed: %v", err) glog.Errorf("scheduler cache AssumePod failed: %v", err)
......
...@@ -106,8 +106,5 @@ func (f *FakeCache) Snapshot() *schedulercache.Snapshot { ...@@ -106,8 +106,5 @@ func (f *FakeCache) Snapshot() *schedulercache.Snapshot {
return &schedulercache.Snapshot{} return &schedulercache.Snapshot{}
} }
// IsUpToDate is a fake method for testing
func (f *FakeCache) IsUpToDate(*schedulercache.NodeInfo) bool { return true }
// NodeTree is a fake method for testing. // NodeTree is a fake method for testing.
func (f *FakeCache) NodeTree() *schedulercache.NodeTree { return nil } func (f *FakeCache) NodeTree() *schedulercache.NodeTree { return nil }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment