mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
Optimize internal data structure of EvenPodsSpread
- Rename 'topologyPairsPodSpreadMap' to 'podSpreadCache' - New struct `criticalPaths criticalPaths` - Add unified method `*criticalPaths.update()` for: - regular update - addPod in preemption case - remotePod in preemption case
This commit is contained in:
parent
7f1a3965fd
commit
8f559ea53b
@ -39,7 +39,7 @@ import (
|
|||||||
type PredicateMetadata interface {
|
type PredicateMetadata interface {
|
||||||
ShallowCopy() PredicateMetadata
|
ShallowCopy() PredicateMetadata
|
||||||
AddPod(addedPod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) error
|
AddPod(addedPod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) error
|
||||||
RemovePod(deletedPod *v1.Pod) error
|
RemovePod(deletedPod *v1.Pod, node *v1.Node) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// PredicateMetadataProducer is a function that computes predicate metadata for a given pod.
|
// PredicateMetadataProducer is a function that computes predicate metadata for a given pod.
|
||||||
@ -67,17 +67,67 @@ type topologyPairsMaps struct {
|
|||||||
podToTopologyPairs map[string]topologyPairSet
|
podToTopologyPairs map[string]topologyPairSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// topologyPairsPodSpreadMap combines topologyKeyToMinPodsMap and topologyPairsMaps
|
type criticalPath struct {
|
||||||
|
// topologyValue denotes the topology value mapping to topology key.
|
||||||
|
topologyValue string
|
||||||
|
// matchNum denotes the number of matching pods.
|
||||||
|
matchNum int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAVEAT: the reason that `[2]criticalPath` can work is based on the implementation of current
|
||||||
|
// preemption algorithm, in particular the following 2 facts:
|
||||||
|
// Fact 1: we only preempt pods on the same node, instead of pods on multiple nodes.
|
||||||
|
// Fact 2: each node is evaluated on a separate copy of the metadata during its preemption cycle.
|
||||||
|
// If we plan to turn to a more complex algorithm like "arbitrary pods on multiple nodes", this
|
||||||
|
// structure needs to be revisited.
|
||||||
|
type criticalPaths [2]criticalPath
|
||||||
|
|
||||||
|
func newCriticalPaths() *criticalPaths {
|
||||||
|
return &criticalPaths{{matchNum: math.MaxInt32}, {matchNum: math.MaxInt32}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (paths *criticalPaths) update(tpVal string, num int32) {
|
||||||
|
// first verify if `tpVal` exists or not
|
||||||
|
i := -1
|
||||||
|
if tpVal == paths[0].topologyValue {
|
||||||
|
i = 0
|
||||||
|
} else if tpVal == paths[1].topologyValue {
|
||||||
|
i = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= 0 {
|
||||||
|
// `tpVal` exists
|
||||||
|
paths[i].matchNum = num
|
||||||
|
if paths[0].matchNum > paths[1].matchNum {
|
||||||
|
// swap paths[0] and paths[1]
|
||||||
|
paths[0], paths[1] = paths[1], paths[0]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// `tpVal` doesn't exist
|
||||||
|
if num < paths[0].matchNum {
|
||||||
|
// update paths[1] with paths[0]
|
||||||
|
paths[1] = paths[0]
|
||||||
|
// update paths[0]
|
||||||
|
paths[0].topologyValue, paths[0].matchNum = tpVal, num
|
||||||
|
} else if num < paths[1].matchNum {
|
||||||
|
// update paths[1]
|
||||||
|
paths[1].topologyValue, paths[1].matchNum = tpVal, num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// podSpreadCache combines tpKeyToCriticalPaths and tpPairToMatchNum
|
||||||
// to represent:
|
// to represent:
|
||||||
// (1) minimum number of pods matched on the spread constraints.
|
// (1) critical paths where the least pods are matched on each spread constraint.
|
||||||
// (2) how existing pods match incoming pod on its spread constraints.
|
// (2) number of pods matched on each spread constraint.
|
||||||
type topologyPairsPodSpreadMap struct {
|
type podSpreadCache struct {
|
||||||
// This map is keyed with a topology key, and valued with minimum number
|
// We record 2 critical paths instead of all critical paths here.
|
||||||
// of pods matched on that topology domain.
|
// criticalPaths[0].matchNum always holds the minimum matching number.
|
||||||
// TODO(Huang-Wei): refactor to {tpKey->tpValSet(or tpValSlice)}
|
// criticalPaths[1].matchNum is always greater or equal to criticalPaths[0].matchNum, but
|
||||||
topologyKeyToMinPodsMap map[string]int32
|
// it's not guaranteed to be the 2nd minimum match number.
|
||||||
// TODO(Huang-Wei): refactor to {tpPair->count, podName->tpPairSet(optional)}
|
tpKeyToCriticalPaths map[string]*criticalPaths
|
||||||
*topologyPairsMaps
|
// tpPairToMatchNum is keyed with topologyPair, and valued with the number of matching pods.
|
||||||
|
tpPairToMatchNum map[topologyPair]int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: When new fields are added/removed or logic is changed, please make sure that
|
// NOTE: When new fields are added/removed or logic is changed, please make sure that
|
||||||
@ -105,9 +155,9 @@ type predicateMetadata struct {
|
|||||||
// which should be accounted only by the extenders. This set is synthesized
|
// which should be accounted only by the extenders. This set is synthesized
|
||||||
// from scheduler extender configuration and does not change per pod.
|
// from scheduler extender configuration and does not change per pod.
|
||||||
ignoredExtendedResources sets.String
|
ignoredExtendedResources sets.String
|
||||||
// Similar to the map for pod (anti-)affinity, but imposes additional min matches info
|
// podSpreadCache holds info of the minimum match number on each topology spread constraint,
|
||||||
// to describe minimum match number on each topology spread constraint.
|
// and the match number of all valid topology pairs.
|
||||||
topologyPairsPodSpreadMap *topologyPairsPodSpreadMap
|
podSpreadCache *podSpreadCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that predicateMetadata implements algorithm.PredicateMetadata.
|
// Ensure that predicateMetadata implements algorithm.PredicateMetadata.
|
||||||
@ -154,9 +204,9 @@ func (pfactory *PredicateMetadataFactory) GetMetadata(pod *v1.Pod, nodeNameToInf
|
|||||||
if pod == nil {
|
if pod == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// existingPodSpreadConstraintsMap represents how existing pods match "pod"
|
// existingPodSpreadCache represents how existing pods match "pod"
|
||||||
// on its spread constraints
|
// on its spread constraints
|
||||||
existingPodSpreadConstraintsMap, err := getTPMapMatchingSpreadConstraints(pod, nodeNameToInfoMap)
|
existingPodSpreadCache, err := getExistingPodSpreadCache(pod, nodeNameToInfoMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Error calculating spreadConstraintsMap: %v", err)
|
klog.Errorf("Error calculating spreadConstraintsMap: %v", err)
|
||||||
return nil
|
return nil
|
||||||
@ -182,7 +232,7 @@ func (pfactory *PredicateMetadataFactory) GetMetadata(pod *v1.Pod, nodeNameToInf
|
|||||||
topologyPairsPotentialAffinityPods: incomingPodAffinityMap,
|
topologyPairsPotentialAffinityPods: incomingPodAffinityMap,
|
||||||
topologyPairsPotentialAntiAffinityPods: incomingPodAntiAffinityMap,
|
topologyPairsPotentialAntiAffinityPods: incomingPodAntiAffinityMap,
|
||||||
topologyPairsAntiAffinityPodsMap: existingPodAntiAffinityMap,
|
topologyPairsAntiAffinityPodsMap: existingPodAntiAffinityMap,
|
||||||
topologyPairsPodSpreadMap: existingPodSpreadConstraintsMap,
|
podSpreadCache: existingPodSpreadCache,
|
||||||
}
|
}
|
||||||
for predicateName, precomputeFunc := range predicateMetadataProducers {
|
for predicateName, precomputeFunc := range predicateMetadataProducers {
|
||||||
klog.V(10).Infof("Precompute: %v", predicateName)
|
klog.V(10).Infof("Precompute: %v", predicateName)
|
||||||
@ -191,7 +241,7 @@ func (pfactory *PredicateMetadataFactory) GetMetadata(pod *v1.Pod, nodeNameToInf
|
|||||||
return predicateMetadata
|
return predicateMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTPMapMatchingSpreadConstraints(pod *v1.Pod, nodeInfoMap map[string]*schedulernodeinfo.NodeInfo) (*topologyPairsPodSpreadMap, error) {
|
func getExistingPodSpreadCache(pod *v1.Pod, nodeInfoMap map[string]*schedulernodeinfo.NodeInfo) (*podSpreadCache, error) {
|
||||||
// We have feature gating in APIServer to strip the spec
|
// We have feature gating in APIServer to strip the spec
|
||||||
// so don't need to re-check feature gate, just check length of constraints.
|
// so don't need to re-check feature gate, just check length of constraints.
|
||||||
constraints := getHardTopologySpreadConstraints(pod)
|
constraints := getHardTopologySpreadConstraints(pod)
|
||||||
@ -207,14 +257,15 @@ func getTPMapMatchingSpreadConstraints(pod *v1.Pod, nodeInfoMap map[string]*sche
|
|||||||
errCh := schedutil.NewErrorChannel()
|
errCh := schedutil.NewErrorChannel()
|
||||||
var lock sync.Mutex
|
var lock sync.Mutex
|
||||||
|
|
||||||
topologyPairsPodSpreadMap := &topologyPairsPodSpreadMap{
|
// TODO(Huang-Wei): It might be possible to use "make(map[topologyPair]*int32)".
|
||||||
// topologyKeyToMinPodsMap will be initialized with proper size later.
|
// In that case, need to consider how to init each tpPairToCount[pair] in an atomic fashion.
|
||||||
topologyPairsMaps: newTopologyPairsMaps(),
|
m := podSpreadCache{
|
||||||
|
tpKeyToCriticalPaths: make(map[string]*criticalPaths, len(constraints)),
|
||||||
|
tpPairToMatchNum: make(map[topologyPair]int32),
|
||||||
}
|
}
|
||||||
|
addTopologyPairMatchNum := func(pair topologyPair, num int32) {
|
||||||
appendTopologyPairsMaps := func(toAppend *topologyPairsMaps) {
|
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
topologyPairsPodSpreadMap.appendMaps(toAppend)
|
m.tpPairToMatchNum[pair] += num
|
||||||
lock.Unlock()
|
lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,9 +288,8 @@ func getTPMapMatchingSpreadConstraints(pod *v1.Pod, nodeInfoMap map[string]*sche
|
|||||||
if !NodeLabelsMatchSpreadConstraints(node.Labels, constraints) {
|
if !NodeLabelsMatchSpreadConstraints(node.Labels, constraints) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nodeTopologyMaps := newTopologyPairsMaps()
|
|
||||||
for _, constraint := range constraints {
|
for _, constraint := range constraints {
|
||||||
pairAdded := false
|
matchTotal := int32(0)
|
||||||
// nodeInfo.Pods() can be empty; or all pods don't fit
|
// nodeInfo.Pods() can be empty; or all pods don't fit
|
||||||
for _, existingPod := range nodeInfo.Pods() {
|
for _, existingPod := range nodeInfo.Pods() {
|
||||||
if existingPod.Namespace != pod.Namespace {
|
if existingPod.Namespace != pod.Namespace {
|
||||||
@ -251,26 +301,12 @@ func getTPMapMatchingSpreadConstraints(pod *v1.Pod, nodeInfoMap map[string]*sche
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
// constraint.TopologyKey is already guaranteed to be present
|
matchTotal++
|
||||||
pair := topologyPair{key: constraint.TopologyKey, value: node.Labels[constraint.TopologyKey]}
|
|
||||||
nodeTopologyMaps.addTopologyPair(pair, existingPod)
|
|
||||||
pairAdded = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If needed, append topology pair without entry of pods.
|
pair := topologyPair{key: constraint.TopologyKey, value: node.Labels[constraint.TopologyKey]}
|
||||||
// For example, on node-x, there is no pod matching spread constraints,
|
addTopologyPairMatchNum(pair, matchTotal)
|
||||||
// but node-x should be also considered as a match (with match number 0)
|
|
||||||
// i.e. <node: node-x>: {}
|
|
||||||
if !pairAdded {
|
|
||||||
pair := topologyPair{
|
|
||||||
key: constraint.TopologyKey,
|
|
||||||
value: node.Labels[constraint.TopologyKey],
|
|
||||||
}
|
|
||||||
nodeTopologyMaps.addTopologyPairWithoutPods(pair)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
appendTopologyPairsMaps(nodeTopologyMaps)
|
|
||||||
}
|
}
|
||||||
workqueue.ParallelizeUntil(ctx, 16, len(allNodeNames), processNode)
|
workqueue.ParallelizeUntil(ctx, 16, len(allNodeNames), processNode)
|
||||||
|
|
||||||
@ -279,18 +315,15 @@ func getTPMapMatchingSpreadConstraints(pod *v1.Pod, nodeInfoMap map[string]*sche
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculate min match for each topology pair
|
// calculate min match for each topology pair
|
||||||
topologyPairsPodSpreadMap.topologyKeyToMinPodsMap = make(map[string]int32, len(constraints))
|
for i := 0; i < len(constraints); i++ {
|
||||||
for _, constraint := range constraints {
|
key := constraints[i].TopologyKey
|
||||||
topologyPairsPodSpreadMap.topologyKeyToMinPodsMap[constraint.TopologyKey] = math.MaxInt32
|
m.tpKeyToCriticalPaths[key] = newCriticalPaths()
|
||||||
}
|
}
|
||||||
for pair, podSet := range topologyPairsPodSpreadMap.topologyPairToPods {
|
for pair, num := range m.tpPairToMatchNum {
|
||||||
// TODO(Huang-Wei): short circuit unvisited portions of <topologyKey: any value>
|
m.tpKeyToCriticalPaths[pair.key].update(pair.value, num)
|
||||||
// if we already see 0 as min match of that topologyKey.
|
|
||||||
if l := int32(len(podSet)); l < topologyPairsPodSpreadMap.topologyKeyToMinPodsMap[pair.key] {
|
|
||||||
topologyPairsPodSpreadMap.topologyKeyToMinPodsMap[pair.key] = l
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return topologyPairsPodSpreadMap, nil
|
|
||||||
|
return &m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHardTopologySpreadConstraints(pod *v1.Pod) (constraints []v1.TopologySpreadConstraint) {
|
func getHardTopologySpreadConstraints(pod *v1.Pod) (constraints []v1.TopologySpreadConstraint) {
|
||||||
@ -337,7 +370,9 @@ func newTopologyPairsMaps() *topologyPairsMaps {
|
|||||||
|
|
||||||
func (m *topologyPairsMaps) addTopologyPair(pair topologyPair, pod *v1.Pod) {
|
func (m *topologyPairsMaps) addTopologyPair(pair topologyPair, pod *v1.Pod) {
|
||||||
podFullName := schedutil.GetPodFullName(pod)
|
podFullName := schedutil.GetPodFullName(pod)
|
||||||
m.addTopologyPairWithoutPods(pair)
|
if m.topologyPairToPods[pair] == nil {
|
||||||
|
m.topologyPairToPods[pair] = make(map[*v1.Pod]struct{})
|
||||||
|
}
|
||||||
m.topologyPairToPods[pair][pod] = struct{}{}
|
m.topologyPairToPods[pair][pod] = struct{}{}
|
||||||
if m.podToTopologyPairs[podFullName] == nil {
|
if m.podToTopologyPairs[podFullName] == nil {
|
||||||
m.podToTopologyPairs[podFullName] = make(map[topologyPair]struct{})
|
m.podToTopologyPairs[podFullName] = make(map[topologyPair]struct{})
|
||||||
@ -345,13 +380,6 @@ func (m *topologyPairsMaps) addTopologyPair(pair topologyPair, pod *v1.Pod) {
|
|||||||
m.podToTopologyPairs[podFullName][pair] = struct{}{}
|
m.podToTopologyPairs[podFullName][pair] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add a topology pair holder if needed
|
|
||||||
func (m *topologyPairsMaps) addTopologyPairWithoutPods(pair topologyPair) {
|
|
||||||
if m.topologyPairToPods[pair] == nil {
|
|
||||||
m.topologyPairToPods[pair] = make(map[*v1.Pod]struct{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *topologyPairsMaps) removePod(deletedPod *v1.Pod) {
|
func (m *topologyPairsMaps) removePod(deletedPod *v1.Pod) {
|
||||||
deletedPodFullName := schedutil.GetPodFullName(deletedPod)
|
deletedPodFullName := schedutil.GetPodFullName(deletedPod)
|
||||||
for pair := range m.podToTopologyPairs[deletedPodFullName] {
|
for pair := range m.podToTopologyPairs[deletedPodFullName] {
|
||||||
@ -368,12 +396,8 @@ func (m *topologyPairsMaps) appendMaps(toAppend *topologyPairsMaps) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for pair := range toAppend.topologyPairToPods {
|
for pair := range toAppend.topologyPairToPods {
|
||||||
if podSet := toAppend.topologyPairToPods[pair]; len(podSet) == 0 {
|
for pod := range toAppend.topologyPairToPods[pair] {
|
||||||
m.addTopologyPairWithoutPods(pair)
|
m.addTopologyPair(pair, pod)
|
||||||
} else {
|
|
||||||
for pod := range podSet {
|
|
||||||
m.addTopologyPair(pair, pod)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,8 +408,16 @@ func (m *topologyPairsMaps) clone() *topologyPairsMaps {
|
|||||||
return copy
|
return copy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *topologyPairsPodSpreadMap) addPod(addedPod, preemptorPod *v1.Pod, node *v1.Node) error {
|
func (c *podSpreadCache) addPod(addedPod, preemptorPod *v1.Pod, node *v1.Node) error {
|
||||||
if addedPod.Namespace != preemptorPod.Namespace {
|
return c.updatePod(addedPod, preemptorPod, node, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *podSpreadCache) removePod(deletedPod, preemptorPod *v1.Pod, node *v1.Node) {
|
||||||
|
c.updatePod(deletedPod, preemptorPod, node, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *podSpreadCache) updatePod(updatedPod, preemptorPod *v1.Pod, node *v1.Node, delta int32) error {
|
||||||
|
if updatedPod.Namespace != preemptorPod.Namespace || node == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
constraints := getHardTopologySpreadConstraints(preemptorPod)
|
constraints := getHardTopologySpreadConstraints(preemptorPod)
|
||||||
@ -393,98 +425,45 @@ func (m *topologyPairsPodSpreadMap) addPod(addedPod, preemptorPod *v1.Pod, node
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// records which topology key(s) needs to be updated
|
podLabelSet := labels.Set(updatedPod.Labels)
|
||||||
minMatchNeedingUpdate := make(map[string]struct{})
|
|
||||||
podLabelSet := labels.Set(addedPod.Labels)
|
|
||||||
for _, constraint := range constraints {
|
for _, constraint := range constraints {
|
||||||
if match, err := PodMatchesSpreadConstraint(podLabelSet, constraint); err != nil {
|
if match, err := PodMatchesSpreadConstraint(podLabelSet, constraint); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !match {
|
} else if !match {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pair := topologyPair{
|
|
||||||
key: constraint.TopologyKey,
|
|
||||||
value: node.Labels[constraint.TopologyKey],
|
|
||||||
}
|
|
||||||
// it means current node is one of the critical paths of topologyKeyToMinPodsMap[TopologyKey]
|
|
||||||
if int32(len(m.topologyPairToPods[pair])) == m.topologyKeyToMinPodsMap[pair.key] {
|
|
||||||
minMatchNeedingUpdate[pair.key] = struct{}{}
|
|
||||||
}
|
|
||||||
m.addTopologyPair(pair, addedPod)
|
|
||||||
}
|
|
||||||
// no need to addTopologyPairWithoutPods b/c if a pair without pods must be present,
|
|
||||||
// it should have already been created earlier in removePod() phase
|
|
||||||
|
|
||||||
// In most cases, min match map doesn't need to be updated.
|
k, v := constraint.TopologyKey, node.Labels[constraint.TopologyKey]
|
||||||
// But it's required to be updated when current node is the ONLY critical path which impacts
|
pair := topologyPair{key: k, value: v}
|
||||||
// the min match. With that said, in this case min match needs to be updated to min match + 1
|
c.tpPairToMatchNum[pair] = c.tpPairToMatchNum[pair] + delta
|
||||||
if len(minMatchNeedingUpdate) != 0 {
|
|
||||||
// TODO(Huang-Wei): performance can be optimized.
|
c.tpKeyToCriticalPaths[k].update(v, c.tpPairToMatchNum[pair])
|
||||||
// A possible solution is to record number of critical paths which co-impact the min match.
|
|
||||||
tempMinMatchMap := make(map[string]int32, len(minMatchNeedingUpdate))
|
|
||||||
for key := range minMatchNeedingUpdate {
|
|
||||||
tempMinMatchMap[key] = math.MaxInt32
|
|
||||||
}
|
|
||||||
for pair, podSet := range m.topologyPairToPods {
|
|
||||||
if _, ok := minMatchNeedingUpdate[pair.key]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if l := int32(len(podSet)); l < tempMinMatchMap[pair.key] {
|
|
||||||
tempMinMatchMap[pair.key] = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for key, tempMin := range tempMinMatchMap {
|
|
||||||
if tempMin == m.topologyKeyToMinPodsMap[key]+1 {
|
|
||||||
m.topologyKeyToMinPodsMap[key] = tempMin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *topologyPairsPodSpreadMap) removePod(deletedPod *v1.Pod) {
|
func (c *podSpreadCache) clone() *podSpreadCache {
|
||||||
if m == nil || deletedPod == nil {
|
// c could be nil when EvenPodsSpread feature is disabled
|
||||||
return
|
if c == nil {
|
||||||
}
|
|
||||||
|
|
||||||
deletedPodFullName := schedutil.GetPodFullName(deletedPod)
|
|
||||||
pairSet, ok := m.podToTopologyPairs[deletedPodFullName]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
topologyPairToPods := m.topologyPairToPods
|
|
||||||
for pair := range pairSet {
|
|
||||||
delete(topologyPairToPods[pair], deletedPod)
|
|
||||||
// if topologyPairToPods[pair] is empty after deletion
|
|
||||||
// don't clean it up as that topology counts as a match now
|
|
||||||
|
|
||||||
// removal of the deletedPod would probably genereate a smaller matching number
|
|
||||||
// so re-calculate minMatch to a smaller value if possible
|
|
||||||
if l := int32(len(topologyPairToPods[pair])); l < m.topologyKeyToMinPodsMap[pair.key] {
|
|
||||||
m.topologyKeyToMinPodsMap[pair.key] = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete(m.podToTopologyPairs, deletedPodFullName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *topologyPairsPodSpreadMap) clone() *topologyPairsPodSpreadMap {
|
|
||||||
// m could be nil when EvenPodsSpread feature is disabled
|
|
||||||
if m == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
copy := &topologyPairsPodSpreadMap{
|
copy := podSpreadCache{
|
||||||
topologyKeyToMinPodsMap: make(map[string]int32),
|
tpKeyToCriticalPaths: make(map[string]*criticalPaths),
|
||||||
topologyPairsMaps: m.topologyPairsMaps.clone(),
|
tpPairToMatchNum: make(map[topologyPair]int32),
|
||||||
}
|
}
|
||||||
for key, minMatched := range m.topologyKeyToMinPodsMap {
|
for tpKey, paths := range c.tpKeyToCriticalPaths {
|
||||||
copy.topologyKeyToMinPodsMap[key] = minMatched
|
copy.tpKeyToCriticalPaths[tpKey] = &criticalPaths{paths[0], paths[1]}
|
||||||
}
|
}
|
||||||
return copy
|
for tpPair, matchNum := range c.tpPairToMatchNum {
|
||||||
|
copyPair := topologyPair{key: tpPair.key, value: tpPair.value}
|
||||||
|
copy.tpPairToMatchNum[copyPair] = matchNum
|
||||||
|
}
|
||||||
|
return ©
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemovePod changes predicateMetadata assuming that the given `deletedPod` is
|
// RemovePod changes predicateMetadata assuming that the given `deletedPod` is
|
||||||
// deleted from the system.
|
// deleted from the system.
|
||||||
func (meta *predicateMetadata) RemovePod(deletedPod *v1.Pod) error {
|
func (meta *predicateMetadata) RemovePod(deletedPod *v1.Pod, node *v1.Node) error {
|
||||||
deletedPodFullName := schedutil.GetPodFullName(deletedPod)
|
deletedPodFullName := schedutil.GetPodFullName(deletedPod)
|
||||||
if deletedPodFullName == schedutil.GetPodFullName(meta.pod) {
|
if deletedPodFullName == schedutil.GetPodFullName(meta.pod) {
|
||||||
return fmt.Errorf("deletedPod and meta.pod must not be the same")
|
return fmt.Errorf("deletedPod and meta.pod must not be the same")
|
||||||
@ -494,7 +473,7 @@ func (meta *predicateMetadata) RemovePod(deletedPod *v1.Pod) error {
|
|||||||
meta.topologyPairsPotentialAffinityPods.removePod(deletedPod)
|
meta.topologyPairsPotentialAffinityPods.removePod(deletedPod)
|
||||||
meta.topologyPairsPotentialAntiAffinityPods.removePod(deletedPod)
|
meta.topologyPairsPotentialAntiAffinityPods.removePod(deletedPod)
|
||||||
// Delete pod from the pod spread topology maps.
|
// Delete pod from the pod spread topology maps.
|
||||||
meta.topologyPairsPodSpreadMap.removePod(deletedPod)
|
meta.podSpreadCache.removePod(deletedPod, meta.pod, node)
|
||||||
// All pods in the serviceAffinityMatchingPodList are in the same namespace.
|
// All pods in the serviceAffinityMatchingPodList are in the same namespace.
|
||||||
// So, if the namespace of the first one is not the same as the namespace of the
|
// So, if the namespace of the first one is not the same as the namespace of the
|
||||||
// deletedPod, we don't need to check the list, as deletedPod isn't in the list.
|
// deletedPod, we don't need to check the list, as deletedPod isn't in the list.
|
||||||
@ -556,9 +535,9 @@ func (meta *predicateMetadata) AddPod(addedPod *v1.Pod, nodeInfo *schedulernodei
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update meta.topologyPairsPodSpreadMap if meta.pod has hard spread constraints
|
// Update meta.podSpreadCache if meta.pod has hard spread constraints
|
||||||
// and addedPod matches that
|
// and addedPod matches that
|
||||||
if err := meta.topologyPairsPodSpreadMap.addPod(addedPod, meta.pod, nodeInfo.Node()); err != nil {
|
if err := meta.podSpreadCache.addPod(addedPod, meta.pod, nodeInfo.Node()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,7 +567,7 @@ func (meta *predicateMetadata) ShallowCopy() PredicateMetadata {
|
|||||||
newPredMeta.topologyPairsPotentialAffinityPods = meta.topologyPairsPotentialAffinityPods.clone()
|
newPredMeta.topologyPairsPotentialAffinityPods = meta.topologyPairsPotentialAffinityPods.clone()
|
||||||
newPredMeta.topologyPairsPotentialAntiAffinityPods = meta.topologyPairsPotentialAntiAffinityPods.clone()
|
newPredMeta.topologyPairsPotentialAntiAffinityPods = meta.topologyPairsPotentialAntiAffinityPods.clone()
|
||||||
newPredMeta.topologyPairsAntiAffinityPodsMap = meta.topologyPairsAntiAffinityPodsMap.clone()
|
newPredMeta.topologyPairsAntiAffinityPodsMap = meta.topologyPairsAntiAffinityPodsMap.clone()
|
||||||
newPredMeta.topologyPairsPodSpreadMap = meta.topologyPairsPodSpreadMap.clone()
|
newPredMeta.podSpreadCache = meta.podSpreadCache.clone()
|
||||||
newPredMeta.serviceAffinityMatchingPodServices = append([]*v1.Service(nil),
|
newPredMeta.serviceAffinityMatchingPodServices = append([]*v1.Service(nil),
|
||||||
meta.serviceAffinityMatchingPodServices...)
|
meta.serviceAffinityMatchingPodServices...)
|
||||||
newPredMeta.serviceAffinityMatchingPodList = append([]*v1.Pod(nil),
|
newPredMeta.serviceAffinityMatchingPodList = append([]*v1.Pod(nil),
|
||||||
|
@ -385,7 +385,7 @@ func TestPredicateMetadata_AddRemovePod(t *testing.T) {
|
|||||||
// Remove the added pod and from existingPodsMeta1 an make sure it is equal
|
// Remove the added pod and from existingPodsMeta1 an make sure it is equal
|
||||||
// to meta generated for existing pods.
|
// to meta generated for existing pods.
|
||||||
existingPodsMeta2, _ := getMeta(st.FakePodLister(test.existingPods))
|
existingPodsMeta2, _ := getMeta(st.FakePodLister(test.existingPods))
|
||||||
if err := existingPodsMeta1.RemovePod(test.addedPod); err != nil {
|
if err := existingPodsMeta1.RemovePod(test.addedPod, nil); err != nil {
|
||||||
t.Errorf("error removing pod from meta: %v", err)
|
t.Errorf("error removing pod from meta: %v", err)
|
||||||
}
|
}
|
||||||
if err := predicateMetadataEquivalent(existingPodsMeta1, existingPodsMeta2); err != nil {
|
if err := predicateMetadataEquivalent(existingPodsMeta1, existingPodsMeta2); err != nil {
|
||||||
@ -512,37 +512,13 @@ func TestPredicateMetadata_ShallowCopy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
topologyPairsPodSpreadMap: &topologyPairsPodSpreadMap{
|
podSpreadCache: &podSpreadCache{
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"name": 1},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
"name": {{"nodeA", 1}, {"nodeC", 2}},
|
||||||
topologyPairToPods: map[topologyPair]podSet{
|
},
|
||||||
{key: "name", value: "nodeA"}: {
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
|
{key: "name", value: "nodeA"}: 1,
|
||||||
Spec: v1.PodSpec{NodeName: "nodeA"},
|
{key: "name", value: "nodeC"}: 2,
|
||||||
}: struct{}{},
|
|
||||||
},
|
|
||||||
{key: "name", value: "nodeC"}: {
|
|
||||||
&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p2"},
|
|
||||||
Spec: v1.PodSpec{
|
|
||||||
NodeName: "nodeC",
|
|
||||||
},
|
|
||||||
}: struct{}{},
|
|
||||||
&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p6", Labels: selector1},
|
|
||||||
Spec: v1.PodSpec{NodeName: "nodeC"},
|
|
||||||
}: struct{}{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p1_": {
|
|
||||||
topologyPair{key: "name", value: "nodeA"}: struct{}{},
|
|
||||||
},
|
|
||||||
"p2_": {
|
|
||||||
topologyPair{key: "name", value: "nodeC"}: struct{}{},
|
|
||||||
},
|
|
||||||
"p6_": {
|
|
||||||
topologyPair{key: "name", value: "nodeC"}: struct{}{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serviceAffinityInUse: true,
|
serviceAffinityInUse: true,
|
||||||
@ -916,15 +892,12 @@ func TestPodMatchesSpreadConstraint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
||||||
// we need to inject the exact pod pointers to want.topologyPairsMaps.topologyPairToPods
|
|
||||||
// otherwise, *pod (as key of a map) will always fail in reflect.DeepEqual()
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
pod *v1.Pod
|
pod *v1.Pod
|
||||||
nodes []*v1.Node
|
nodes []*v1.Node
|
||||||
existingPods []*v1.Pod
|
existingPods []*v1.Pod
|
||||||
injectPodPointers map[topologyPair][]int
|
want *podSpreadCache
|
||||||
want *topologyPairsPodSpreadMap
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "clean cluster with one spreadConstraint",
|
name: "clean cluster with one spreadConstraint",
|
||||||
@ -937,16 +910,13 @@ func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||||
st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(),
|
st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
// denotes no existing pod is matched on this zone pair, but still needed to be
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
// calculated if incoming pod matches its own spread constraints
|
"zone": {{"zone1", 0}, {"zone2", 0}},
|
||||||
{key: "zone", value: "zone1"}: {},
|
},
|
||||||
{key: "zone", value: "zone2"}: {},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
},
|
{key: "zone", value: "zone1"}: 0,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone2"}: 0,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 0},
|
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
|
||||||
podToTopologyPairs: make(map[string]topologyPairSet),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -968,22 +938,44 @@ func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
|||||||
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(),
|
||||||
st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
// denotes existingPods[0,1,2]
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone1"}: {0, 1, 2},
|
"zone": {{"zone2", 2}, {"zone1", 3}},
|
||||||
// denotes existingPods[3,4]
|
},
|
||||||
{key: "zone", value: "zone2"}: {3, 4},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
|
{key: "zone", value: "zone1"}: 3,
|
||||||
|
{key: "zone", value: "zone2"}: 2,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
want: &topologyPairsPodSpreadMap{
|
},
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 2},
|
{
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
name: "normal case with one spreadConstraint, on a 3-zone cluster",
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
pod: st.MakePod().Name("p").Label("foo", "").SpreadConstraint(
|
||||||
"p-a1_": newPairSet("zone", "zone1"),
|
1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(),
|
||||||
"p-a2_": newPairSet("zone", "zone1"),
|
).Obj(),
|
||||||
"p-b1_": newPairSet("zone", "zone1"),
|
nodes: []*v1.Node{
|
||||||
"p-y1_": newPairSet("zone", "zone2"),
|
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||||
"p-y2_": newPairSet("zone", "zone2"),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
},
|
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||||
|
st.MakeNode().Name("node-y").Label("zone", "zone2").Label("node", "node-y").Obj(),
|
||||||
|
st.MakeNode().Name("node-o").Label("zone", "zone3").Label("node", "node-o").Obj(),
|
||||||
|
st.MakeNode().Name("node-p").Label("zone", "zone3").Label("node", "node-p").Obj(),
|
||||||
|
},
|
||||||
|
existingPods: []*v1.Pod{
|
||||||
|
st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||||
|
st.MakePod().Name("p-a2").Node("node-a").Label("foo", "").Obj(),
|
||||||
|
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Obj(),
|
||||||
|
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(),
|
||||||
|
st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(),
|
||||||
|
},
|
||||||
|
want: &podSpreadCache{
|
||||||
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
|
"zone": {{"zone3", 0}, {"zone2", 2}},
|
||||||
|
},
|
||||||
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
|
{key: "zone", value: "zone1"}: 3,
|
||||||
|
{key: "zone", value: "zone2"}: 2,
|
||||||
|
{key: "zone", value: "zone3"}: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1005,18 +997,13 @@ func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
|||||||
st.MakePod().Name("p-y1").Namespace("ns2").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y1").Namespace("ns2").Node("node-y").Label("foo", "").Obj(),
|
||||||
st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y2").Node("node-y").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {0, 2},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {4},
|
"zone": {{"zone2", 1}, {"zone1", 2}},
|
||||||
},
|
},
|
||||||
want: &topologyPairsPodSpreadMap{
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 1},
|
{key: "zone", value: "zone1"}: 2,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "zone", value: "zone2"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-a1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-y2_": newPairSet("zone", "zone2"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1041,26 +1028,18 @@ func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
|||||||
st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(),
|
||||||
st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {0, 1, 2},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {3, 4, 5, 6},
|
"zone": {{"zone1", 3}, {"zone2", 4}},
|
||||||
{key: "node", value: "node-a"}: {0, 1},
|
"node": {{"node-x", 0}, {"node-b", 1}},
|
||||||
{key: "node", value: "node-b"}: {2},
|
},
|
||||||
{key: "node", value: "node-x"}: {},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
{key: "node", value: "node-y"}: {3, 4, 5, 6},
|
{key: "zone", value: "zone1"}: 3,
|
||||||
},
|
{key: "zone", value: "zone2"}: 4,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "node", value: "node-a"}: 2,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 3, "node": 0},
|
{key: "node", value: "node-b"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-x"}: 0,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-y"}: 4,
|
||||||
"p-a1_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-a2_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1", "node", "node-b"),
|
|
||||||
"p-y1_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y2_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y3_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y4_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1086,25 +1065,17 @@ func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
|||||||
st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(),
|
||||||
st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {0, 1, 2},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {3, 4, 5, 6},
|
"zone": {{"zone1", 3}, {"zone2", 4}},
|
||||||
{key: "node", value: "node-a"}: {0, 1},
|
"node": {{"node-b", 1}, {"node-a", 2}},
|
||||||
{key: "node", value: "node-b"}: {2},
|
},
|
||||||
{key: "node", value: "node-y"}: {3, 4, 5, 6},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
},
|
{key: "zone", value: "zone1"}: 3,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone2"}: 4,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 3, "node": 1},
|
{key: "node", value: "node-a"}: 2,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-b"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-y"}: 4,
|
||||||
"p-a1_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-a2_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1", "node", "node-b"),
|
|
||||||
"p-y1_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y2_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y3_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y4_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1121,20 +1092,19 @@ func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
|||||||
},
|
},
|
||||||
existingPods: []*v1.Pod{
|
existingPods: []*v1.Pod{
|
||||||
st.MakePod().Name("p-a").Node("node-a").Label("foo", "").Obj(),
|
st.MakePod().Name("p-a").Node("node-a").Label("foo", "").Obj(),
|
||||||
|
st.MakePod().Name("p-b").Node("node-b").Label("bar", "").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {0},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {},
|
"zone": {{"zone2", 0}, {"zone1", 1}},
|
||||||
{key: "node", value: "node-a"}: {},
|
"node": {{"node-a", 0}, {"node-y", 0}},
|
||||||
{key: "node", value: "node-b"}: {},
|
},
|
||||||
{key: "node", value: "node-y"}: {},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
},
|
{key: "zone", value: "zone1"}: 1,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone2"}: 0,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 0, "node": 0},
|
{key: "node", value: "node-a"}: 0,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-b"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-y"}: 0,
|
||||||
"p-a_": newPairSet("zone", "zone1"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1158,25 +1128,17 @@ func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
|||||||
st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(),
|
||||||
st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Label("bar", "").Obj(),
|
st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Label("bar", "").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {0, 1, 2},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {3, 4, 5, 6},
|
"zone": {{"zone1", 3}, {"zone2", 4}},
|
||||||
{key: "node", value: "node-a"}: {1},
|
"node": {{"node-b", 0}, {"node-a", 1}},
|
||||||
{key: "node", value: "node-b"}: {},
|
},
|
||||||
{key: "node", value: "node-y"}: {4, 6},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
},
|
{key: "zone", value: "zone1"}: 3,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone2"}: 4,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 3, "node": 0},
|
{key: "node", value: "node-a"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-b"}: 0,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-y"}: 2,
|
||||||
"p-a1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-a2_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-y1_": newPairSet("zone", "zone2"),
|
|
||||||
"p-y2_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y3_": newPairSet("zone", "zone2"),
|
|
||||||
"p-y4_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1202,61 +1164,46 @@ func TestGetTPMapMatchingSpreadConstraints(t *testing.T) {
|
|||||||
st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y3").Node("node-y").Label("foo", "").Obj(),
|
||||||
st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y4").Node("node-y").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {0, 1, 2},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {3, 4, 5, 6},
|
"zone": {{"zone1", 3}, {"zone2", 4}},
|
||||||
{key: "node", value: "node-a"}: {0, 1},
|
"node": {{"node-b", 1}, {"node-a", 2}},
|
||||||
{key: "node", value: "node-b"}: {2},
|
},
|
||||||
{key: "node", value: "node-y"}: {3, 4, 5, 6},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
},
|
{key: "zone", value: "zone1"}: 3,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone2"}: 4,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 3, "node": 1},
|
{key: "node", value: "node-a"}: 2,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-b"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-y"}: 4,
|
||||||
"p-a1_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-a2_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1", "node", "node-b"),
|
|
||||||
"p-y1_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y2_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y3_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
"p-y4_": newPairSet("zone", "zone2", "node", "node-y"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tt.want.topologyPairToPods = make(map[topologyPair]podSet)
|
|
||||||
for pair, indexes := range tt.injectPodPointers {
|
|
||||||
pSet := make(podSet)
|
|
||||||
for _, i := range indexes {
|
|
||||||
pSet[tt.existingPods[i]] = struct{}{}
|
|
||||||
}
|
|
||||||
tt.want.topologyPairToPods[pair] = pSet
|
|
||||||
}
|
|
||||||
nodeInfoMap := schedulernodeinfo.CreateNodeNameToInfoMap(tt.existingPods, tt.nodes)
|
nodeInfoMap := schedulernodeinfo.CreateNodeNameToInfoMap(tt.existingPods, tt.nodes)
|
||||||
if got, _ := getTPMapMatchingSpreadConstraints(tt.pod, nodeInfoMap); !reflect.DeepEqual(got, tt.want) {
|
got, _ := getExistingPodSpreadCache(tt.pod, nodeInfoMap)
|
||||||
t.Errorf("getTPMapMatchingSpreadConstraints() = %v, want %v", got, tt.want)
|
got.sortCriticalPaths()
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("getExistingPodSpreadCache() = %v, want %v", *got, *tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPodSpreadMap_addPod(t *testing.T) {
|
func TestPodSpreadCache_addPod(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
preemptorPod *v1.Pod
|
preemptor *v1.Pod
|
||||||
addedPod *v1.Pod
|
addedPod *v1.Pod
|
||||||
existingPods []*v1.Pod
|
existingPods []*v1.Pod
|
||||||
nodeIdx int // denotes which node 'addedPod' belongs to
|
nodeIdx int // denotes which node 'addedPod' belongs to
|
||||||
nodes []*v1.Node
|
nodes []*v1.Node
|
||||||
injectPodPointers map[topologyPair][]int // non-negative index refers to existingPods[i], negative index refers to addedPod
|
want *podSpreadCache
|
||||||
want *topologyPairsPodSpreadMap
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "node a and b both impact current min match",
|
name: "node a and b both impact current min match",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||||
@ -1266,24 +1213,19 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "node", value: "node-a"}: {-1},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "node", value: "node-b"}: {},
|
"node": {{"node-b", 0}, {"node-a", 1}},
|
||||||
},
|
},
|
||||||
want: &topologyPairsPodSpreadMap{
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
// min match map shouldn't be changed b/c node-b is still on the critical path
|
{key: "node", value: "node-a"}: 1,
|
||||||
// determining min match
|
{key: "node", value: "node-b"}: 0,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"node": 0},
|
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-a1_": newPairSet("node", "node-a"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "only node a impacts current min match",
|
name: "only node a impacts current min match",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||||
@ -1295,24 +1237,19 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "node", value: "node-a"}: {-1},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "node", value: "node-b"}: {0},
|
"node": {{"node-a", 1}, {"node-b", 1}},
|
||||||
},
|
},
|
||||||
want: &topologyPairsPodSpreadMap{
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
// min match should be changed from 0 to 1
|
{key: "node", value: "node-a"}: 1,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"node": 1},
|
{key: "node", value: "node-b"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-a1_": newPairSet("node", "node-a"),
|
|
||||||
"p-b1_": newPairSet("node", "node-b"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "add a pod with mis-matched namespace doesn't change topologyKeyToMinPodsMap",
|
name: "add a pod with mis-matched namespace doesn't change topologyKeyToMinPodsMap",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
addedPod: st.MakePod().Name("p-a1").Namespace("ns1").Node("node-a").Label("foo", "").Obj(),
|
addedPod: st.MakePod().Name("p-a1").Namespace("ns1").Node("node-a").Label("foo", "").Obj(),
|
||||||
@ -1324,24 +1261,19 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "node", value: "node-a"}: {},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "node", value: "node-b"}: {0},
|
"node": {{"node-a", 0}, {"node-b", 1}},
|
||||||
},
|
},
|
||||||
want: &topologyPairsPodSpreadMap{
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
// min match remains the same
|
{key: "node", value: "node-a"}: 0,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"node": 0},
|
{key: "node", value: "node-b"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
// "p-a1_": newPairSet("node", "node-a") shouldn't exist
|
|
||||||
"p-b1_": newPairSet("node", "node-b"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "add pod on non-critical node won't trigger re-calculation",
|
name: "add pod on non-critical node won't trigger re-calculation",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
addedPod: st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(),
|
addedPod: st.MakePod().Name("p-b2").Node("node-b").Label("foo", "").Obj(),
|
||||||
@ -1353,23 +1285,19 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "node", value: "node-a"}: {},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "node", value: "node-b"}: {-1, 0},
|
"node": {{"node-a", 0}, {"node-b", 2}},
|
||||||
},
|
},
|
||||||
want: &topologyPairsPodSpreadMap{
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"node": 0},
|
{key: "node", value: "node-a"}: 0,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-b"}: 2,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-b1_": newPairSet("node", "node-b"),
|
|
||||||
"p-b2_": newPairSet("node", "node-b"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "node a and x both impact topologyKeyToMinPodsMap on zone and node",
|
name: "node a and x both impact topologyKeyToMinPodsMap on zone and node",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
@ -1380,24 +1308,22 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {-1},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {},
|
"zone": {{"zone2", 0}, {"zone1", 1}},
|
||||||
{key: "node", value: "node-a"}: {-1},
|
"node": {{"node-x", 0}, {"node-a", 1}},
|
||||||
{key: "node", value: "node-x"}: {},
|
},
|
||||||
},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone1"}: 1,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 0, "node": 0},
|
{key: "zone", value: "zone2"}: 0,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-a"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-x"}: 0,
|
||||||
"p-a1_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "only node a impacts topologyKeyToMinPodsMap on zone and node",
|
name: "only node a impacts topologyKeyToMinPodsMap on zone and node",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
@ -1410,25 +1336,22 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
st.MakeNode().Name("node-a").Label("zone", "zone1").Label("node", "node-a").Obj(),
|
||||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {-1},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {0},
|
"zone": {{"zone1", 1}, {"zone2", 1}},
|
||||||
{key: "node", value: "node-a"}: {-1},
|
"node": {{"node-a", 1}, {"node-x", 1}},
|
||||||
{key: "node", value: "node-x"}: {0},
|
},
|
||||||
},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone1"}: 1,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 1, "node": 1},
|
{key: "zone", value: "zone2"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-a"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-x"}: 1,
|
||||||
"p-a1_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-x1_": newPairSet("zone", "zone2", "node", "node-x"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "node a impacts topologyKeyToMinPodsMap on node, node x impacts topologyKeyToMinPodsMap on zone",
|
name: "node a impacts topologyKeyToMinPodsMap on node, node x impacts topologyKeyToMinPodsMap on zone",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").
|
||||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
@ -1444,35 +1367,30 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {-1, 0, 1},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {2},
|
"zone": {{"zone2", 1}, {"zone1", 3}},
|
||||||
{key: "node", value: "node-a"}: {-1},
|
"node": {{"node-a", 1}, {"node-x", 1}},
|
||||||
{key: "node", value: "node-b"}: {0, 1},
|
},
|
||||||
{key: "node", value: "node-x"}: {2},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
},
|
{key: "zone", value: "zone1"}: 3,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone2"}: 1,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 1, "node": 1},
|
{key: "node", value: "node-a"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-b"}: 2,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-x"}: 1,
|
||||||
"p-a1_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1", "node", "node-b"),
|
|
||||||
"p-b2_": newPairSet("zone", "zone1", "node", "node-b"),
|
|
||||||
"p-x1_": newPairSet("zone", "zone2", "node", "node-x"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "constraints hold different labelSelectors, node a impacts topologyKeyToMinPodsMap on node",
|
name: "constraints hold different labelSelectors, node a impacts topologyKeyToMinPodsMap on zone",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("bar").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("bar").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Obj(),
|
||||||
existingPods: []*v1.Pod{
|
existingPods: []*v1.Pod{
|
||||||
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Obj(),
|
st.MakePod().Name("p-b1").Node("node-b").Label("foo", "").Label("bar", "").Obj(),
|
||||||
st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(),
|
st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Label("bar", "").Obj(),
|
||||||
st.MakePod().Name("p-x2").Node("node-x").Label("bar", "").Obj(),
|
st.MakePod().Name("p-x2").Node("node-x").Label("bar", "").Obj(),
|
||||||
},
|
},
|
||||||
nodeIdx: 0,
|
nodeIdx: 0,
|
||||||
@ -1481,35 +1399,30 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {-1, 0},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {1},
|
"zone": {{"zone2", 1}, {"zone1", 2}},
|
||||||
{key: "node", value: "node-a"}: {},
|
"node": {{"node-a", 0}, {"node-b", 1}},
|
||||||
{key: "node", value: "node-b"}: {0},
|
},
|
||||||
{key: "node", value: "node-x"}: {2},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
},
|
{key: "zone", value: "zone1"}: 2,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone2"}: 1,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 1, "node": 0},
|
{key: "node", value: "node-a"}: 0,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-b"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-x"}: 2,
|
||||||
"p-a1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1", "node", "node-b"),
|
|
||||||
"p-x1_": newPairSet("zone", "zone2"),
|
|
||||||
"p-x2_": newPairSet("node", "node-x"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "constraints hold different labelSelectors, node a impacts topologyKeyToMinPodsMap on both zone and node",
|
name: "constraints hold different labelSelectors, node a impacts topologyKeyToMinPodsMap on both zone and node",
|
||||||
preemptorPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
preemptor: st.MakePod().Name("p").Label("foo", "").Label("bar", "").
|
||||||
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj()).
|
||||||
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("bar").Obj()).
|
SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("bar").Obj()).
|
||||||
Obj(),
|
Obj(),
|
||||||
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Label("bar", "").Obj(),
|
addedPod: st.MakePod().Name("p-a1").Node("node-a").Label("foo", "").Label("bar", "").Obj(),
|
||||||
existingPods: []*v1.Pod{
|
existingPods: []*v1.Pod{
|
||||||
st.MakePod().Name("p-b1").Node("node-b").Label("bar", "").Obj(),
|
st.MakePod().Name("p-b1").Node("node-b").Label("bar", "").Obj(),
|
||||||
st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(),
|
st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Label("bar", "").Obj(),
|
||||||
st.MakePod().Name("p-x2").Node("node-x").Label("bar", "").Obj(),
|
st.MakePod().Name("p-x2").Node("node-x").Label("bar", "").Obj(),
|
||||||
},
|
},
|
||||||
nodeIdx: 0,
|
nodeIdx: 0,
|
||||||
@ -1518,62 +1431,45 @@ func TestPodSpreadMap_addPod(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||||
},
|
},
|
||||||
injectPodPointers: map[topologyPair][]int{
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone1"}: {-1},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "zone", value: "zone2"}: {1},
|
"zone": {{"zone1", 1}, {"zone2", 1}},
|
||||||
{key: "node", value: "node-a"}: {-1},
|
"node": {{"node-a", 1}, {"node-b", 1}},
|
||||||
{key: "node", value: "node-b"}: {0},
|
},
|
||||||
{key: "node", value: "node-x"}: {2},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
},
|
{key: "zone", value: "zone1"}: 1,
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone2"}: 1,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 1, "node": 1},
|
{key: "node", value: "node-a"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-b"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
{key: "node", value: "node-x"}: 2,
|
||||||
"p-a1_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-b1_": newPairSet("node", "node-b"),
|
|
||||||
"p-x1_": newPairSet("zone", "zone2"),
|
|
||||||
"p-x2_": newPairSet("node", "node-x"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tt.want.topologyPairToPods = make(map[topologyPair]podSet)
|
|
||||||
for pair, indexes := range tt.injectPodPointers {
|
|
||||||
pSet := make(podSet)
|
|
||||||
for _, i := range indexes {
|
|
||||||
if i >= 0 {
|
|
||||||
pSet[tt.existingPods[i]] = struct{}{}
|
|
||||||
} else {
|
|
||||||
pSet[tt.addedPod] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tt.want.topologyPairToPods[pair] = pSet
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeInfoMap := schedulernodeinfo.CreateNodeNameToInfoMap(tt.existingPods, tt.nodes)
|
nodeInfoMap := schedulernodeinfo.CreateNodeNameToInfoMap(tt.existingPods, tt.nodes)
|
||||||
podSpreadMap, _ := getTPMapMatchingSpreadConstraints(tt.preemptorPod, nodeInfoMap)
|
podSpreadCache, _ := getExistingPodSpreadCache(tt.preemptor, nodeInfoMap)
|
||||||
|
|
||||||
podSpreadMap.addPod(tt.addedPod, tt.preemptorPod, tt.nodes[tt.nodeIdx])
|
podSpreadCache.addPod(tt.addedPod, tt.preemptor, tt.nodes[tt.nodeIdx])
|
||||||
if !reflect.DeepEqual(podSpreadMap, tt.want) {
|
podSpreadCache.sortCriticalPaths()
|
||||||
t.Errorf("podSpreadMap#addPod() = %v, want %v", podSpreadMap, tt.want)
|
if !reflect.DeepEqual(podSpreadCache, tt.want) {
|
||||||
|
t.Errorf("podSpreadCache#addPod() = %v, want %v", podSpreadCache, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPodSpreadMap_removePod(t *testing.T) {
|
func TestPodSpreadCache_removePod(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
preemptor *v1.Pod // preemptor pod
|
preemptor *v1.Pod // preemptor pod
|
||||||
nodes []*v1.Node
|
nodes []*v1.Node
|
||||||
existingPods []*v1.Pod
|
existingPods []*v1.Pod
|
||||||
deletedPodIdx int // need to reuse *Pod of existingPods[i]
|
deletedPodIdx int // need to reuse *Pod of existingPods[i]
|
||||||
deletedPod *v1.Pod // if deletedPodIdx is invalid, this field is bypassed
|
deletedPod *v1.Pod // this field is used only when deletedPodIdx is -1
|
||||||
injectPodPointers map[topologyPair][]int
|
nodeIdx int // denotes which node "deletedPod" belongs to
|
||||||
want *topologyPairsPodSpreadMap
|
want *podSpreadCache
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// A high priority pod may not be scheduled due to node taints or resource shortage.
|
// A high priority pod may not be scheduled due to node taints or resource shortage.
|
||||||
@ -1593,18 +1489,14 @@ func TestPodSpreadMap_removePod(t *testing.T) {
|
|||||||
st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(),
|
st.MakePod().Name("p-x1").Node("node-x").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
deletedPodIdx: 0, // remove pod "p-a1"
|
deletedPodIdx: 0, // remove pod "p-a1"
|
||||||
injectPodPointers: map[topologyPair][]int{
|
nodeIdx: 0, // node-a
|
||||||
{key: "zone", value: "zone1"}: {1},
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone2"}: {2},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
},
|
"zone": {{"zone1", 1}, {"zone2", 1}},
|
||||||
want: &topologyPairsPodSpreadMap{
|
},
|
||||||
// topologyKeyToMinPodsMap actually doesn't change
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 1},
|
{key: "zone", value: "zone1"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "zone", value: "zone2"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-b1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-x1_": newPairSet("zone", "zone2"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1626,20 +1518,14 @@ func TestPodSpreadMap_removePod(t *testing.T) {
|
|||||||
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
deletedPodIdx: 0, // remove pod "p-a1"
|
deletedPodIdx: 0, // remove pod "p-a1"
|
||||||
injectPodPointers: map[topologyPair][]int{
|
nodeIdx: 0, // node-a
|
||||||
{key: "zone", value: "zone1"}: {1},
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone2"}: {2, 3},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
},
|
"zone": {{"zone1", 1}, {"zone2", 2}},
|
||||||
want: &topologyPairsPodSpreadMap{
|
},
|
||||||
// topologyKeyToMinPodsMap is expected to be re-calculated from {"zone": 2}
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
// to {"zone": 1}
|
{key: "zone", value: "zone1"}: 1,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 1},
|
{key: "zone", value: "zone2"}: 2,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-b1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-x1_": newPairSet("zone", "zone2"),
|
|
||||||
"p-y1_": newPairSet("zone", "zone2"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1662,20 +1548,14 @@ func TestPodSpreadMap_removePod(t *testing.T) {
|
|||||||
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(),
|
st.MakePod().Name("p-y1").Node("node-y").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
deletedPodIdx: 0, // remove pod "p-a0"
|
deletedPodIdx: 0, // remove pod "p-a0"
|
||||||
injectPodPointers: map[topologyPair][]int{
|
nodeIdx: 0, // node-a
|
||||||
{key: "zone", value: "zone1"}: {1, 2},
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone2"}: {3, 4},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
},
|
"zone": {{"zone1", 2}, {"zone2", 2}},
|
||||||
want: &topologyPairsPodSpreadMap{
|
},
|
||||||
// topologyKeyToMinPodsMap is unchanged
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 2},
|
{key: "zone", value: "zone1"}: 2,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "zone", value: "zone2"}: 2,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-a1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-x1_": newPairSet("zone", "zone2"),
|
|
||||||
"p-y1_": newPairSet("zone", "zone2"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1698,20 +1578,14 @@ func TestPodSpreadMap_removePod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
deletedPodIdx: -1,
|
deletedPodIdx: -1,
|
||||||
deletedPod: st.MakePod().Name("p-a0").Node("node-a").Label("bar", "").Obj(),
|
deletedPod: st.MakePod().Name("p-a0").Node("node-a").Label("bar", "").Obj(),
|
||||||
injectPodPointers: map[topologyPair][]int{
|
nodeIdx: 0, // node-a
|
||||||
{key: "zone", value: "zone1"}: {0, 1},
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone2"}: {2, 3},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
},
|
"zone": {{"zone1", 2}, {"zone2", 2}},
|
||||||
want: &topologyPairsPodSpreadMap{
|
},
|
||||||
// topologyKeyToMinPodsMap is unchanged
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 2},
|
{key: "zone", value: "zone1"}: 2,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "zone", value: "zone2"}: 2,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-a1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1"),
|
|
||||||
"p-x1_": newPairSet("zone", "zone2"),
|
|
||||||
"p-y1_": newPairSet("zone", "zone2"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1734,41 +1608,26 @@ func TestPodSpreadMap_removePod(t *testing.T) {
|
|||||||
st.MakePod().Name("p-x2").Node("node-x").Label("foo", "").Obj(),
|
st.MakePod().Name("p-x2").Node("node-x").Label("foo", "").Obj(),
|
||||||
},
|
},
|
||||||
deletedPodIdx: 3, // remove pod "p-x1"
|
deletedPodIdx: 3, // remove pod "p-x1"
|
||||||
injectPodPointers: map[topologyPair][]int{
|
nodeIdx: 2, // node-x
|
||||||
{key: "zone", value: "zone1"}: {0, 1, 2},
|
want: &podSpreadCache{
|
||||||
{key: "zone", value: "zone2"}: {4},
|
tpKeyToCriticalPaths: map[string]*criticalPaths{
|
||||||
{key: "node", value: "node-a"}: {0, 1},
|
"zone": {{"zone2", 1}, {"zone1", 3}},
|
||||||
{key: "node", value: "node-b"}: {2},
|
"node": {{"node-b", 1}, {"node-x", 1}},
|
||||||
{key: "node", value: "node-x"}: {4},
|
},
|
||||||
},
|
tpPairToMatchNum: map[topologyPair]int32{
|
||||||
want: &topologyPairsPodSpreadMap{
|
{key: "zone", value: "zone1"}: 3,
|
||||||
// topologyKeyToMinPodsMap is expected to be re-calculated from {"zone": 2, "node": 1}
|
{key: "zone", value: "zone2"}: 1,
|
||||||
// to {"zone": 1, "node": 1}
|
{key: "node", value: "node-a"}: 2,
|
||||||
topologyKeyToMinPodsMap: map[string]int32{"zone": 1, "node": 1},
|
{key: "node", value: "node-b"}: 1,
|
||||||
topologyPairsMaps: &topologyPairsMaps{
|
{key: "node", value: "node-x"}: 1,
|
||||||
podToTopologyPairs: map[string]topologyPairSet{
|
|
||||||
"p-a1_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-a2_": newPairSet("zone", "zone1", "node", "node-a"),
|
|
||||||
"p-b1_": newPairSet("zone", "zone1", "node", "node-b"),
|
|
||||||
"p-x2_": newPairSet("zone", "zone2", "node", "node-x"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tt.want.topologyPairToPods = make(map[topologyPair]podSet)
|
|
||||||
for pair, indexes := range tt.injectPodPointers {
|
|
||||||
pSet := make(podSet)
|
|
||||||
for _, i := range indexes {
|
|
||||||
pSet[tt.existingPods[i]] = struct{}{}
|
|
||||||
}
|
|
||||||
tt.want.topologyPairToPods[pair] = pSet
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeInfoMap := schedulernodeinfo.CreateNodeNameToInfoMap(tt.existingPods, tt.nodes)
|
nodeInfoMap := schedulernodeinfo.CreateNodeNameToInfoMap(tt.existingPods, tt.nodes)
|
||||||
podSpreadMap, _ := getTPMapMatchingSpreadConstraints(tt.preemptor, nodeInfoMap)
|
podSpreadCache, _ := getExistingPodSpreadCache(tt.preemptor, nodeInfoMap)
|
||||||
|
|
||||||
var deletedPod *v1.Pod
|
var deletedPod *v1.Pod
|
||||||
if tt.deletedPodIdx < len(tt.existingPods) && tt.deletedPodIdx >= 0 {
|
if tt.deletedPodIdx < len(tt.existingPods) && tt.deletedPodIdx >= 0 {
|
||||||
@ -1776,9 +1635,10 @@ func TestPodSpreadMap_removePod(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
deletedPod = tt.deletedPod
|
deletedPod = tt.deletedPod
|
||||||
}
|
}
|
||||||
podSpreadMap.removePod(deletedPod)
|
podSpreadCache.removePod(deletedPod, tt.preemptor, tt.nodes[tt.nodeIdx])
|
||||||
if !reflect.DeepEqual(podSpreadMap, tt.want) {
|
podSpreadCache.sortCriticalPaths()
|
||||||
t.Errorf("podSpreadMap#removePod() = %v, want %v", podSpreadMap, tt.want)
|
if !reflect.DeepEqual(podSpreadCache, tt.want) {
|
||||||
|
t.Errorf("podSpreadCache#removePod() = %v, want %v", podSpreadCache, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1827,7 +1687,7 @@ func BenchmarkTestGetTPMapMatchingSpreadConstraints(b *testing.B) {
|
|||||||
nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(existingPods, allNodes)
|
nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(existingPods, allNodes)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
getTPMapMatchingSpreadConstraints(tt.pod, nodeNameToInfo)
|
getExistingPodSpreadCache(tt.pod, nodeNameToInfo)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1846,3 +1706,14 @@ func newPairSet(kv ...string) topologyPairSet {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sortCriticalPaths is only served for testing purpose.
|
||||||
|
func (c *podSpreadCache) sortCriticalPaths() {
|
||||||
|
for _, paths := range c.tpKeyToCriticalPaths {
|
||||||
|
// If two paths both hold minimum matching number, and topologyValue is unordered.
|
||||||
|
if paths[0].matchNum == paths[1].matchNum && paths[0].topologyValue > paths[1].topologyValue {
|
||||||
|
// Swap topologyValue to make them sorted alphabetically.
|
||||||
|
paths[0].topologyValue, paths[1].topologyValue = paths[1].topologyValue, paths[0].topologyValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1796,15 +1796,15 @@ func EvenPodsSpreadPredicate(pod *v1.Pod, meta PredicateMetadata, nodeInfo *sche
|
|||||||
return true, nil, nil
|
return true, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var topologyPairsPodSpreadMap *topologyPairsPodSpreadMap
|
var podSpreadCache *podSpreadCache
|
||||||
if predicateMeta, ok := meta.(*predicateMetadata); ok {
|
if predicateMeta, ok := meta.(*predicateMetadata); ok {
|
||||||
topologyPairsPodSpreadMap = predicateMeta.topologyPairsPodSpreadMap
|
podSpreadCache = predicateMeta.podSpreadCache
|
||||||
} else { // We don't have precomputed metadata. We have to follow a slow path to check spread constraints.
|
} else { // We don't have precomputed metadata. We have to follow a slow path to check spread constraints.
|
||||||
// TODO(Huang-Wei): get it implemented
|
// TODO(autoscaler): get it implemented
|
||||||
return false, nil, errors.New("metadata not pre-computed for EvenPodsSpreadPredicate")
|
return false, nil, errors.New("metadata not pre-computed for EvenPodsSpreadPredicate")
|
||||||
}
|
}
|
||||||
|
|
||||||
if topologyPairsPodSpreadMap == nil || len(topologyPairsPodSpreadMap.topologyKeyToMinPodsMap) == 0 {
|
if podSpreadCache == nil || len(podSpreadCache.tpPairToMatchNum) == 0 {
|
||||||
return true, nil, nil
|
return true, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1821,25 +1821,24 @@ func EvenPodsSpreadPredicate(pod *v1.Pod, meta PredicateMetadata, nodeInfo *sche
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
selfMatchNum := 0
|
selfMatchNum := int32(0)
|
||||||
if selfMatch {
|
if selfMatch {
|
||||||
selfMatchNum = 1
|
selfMatchNum = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pair := topologyPair{key: tpKey, value: tpVal}
|
pair := topologyPair{key: tpKey, value: tpVal}
|
||||||
minMatchNum, ok := topologyPairsPodSpreadMap.topologyKeyToMinPodsMap[tpKey]
|
paths, ok := podSpreadCache.tpKeyToCriticalPaths[tpKey]
|
||||||
if !ok {
|
if !ok {
|
||||||
// error which should not happen
|
// error which should not happen
|
||||||
klog.Errorf("internal error: get minMatchNum from key %q of %#v", tpKey, topologyPairsPodSpreadMap.topologyKeyToMinPodsMap)
|
klog.Errorf("internal error: get paths from key %q of %#v", tpKey, podSpreadCache.tpKeyToCriticalPaths)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// judging criteria:
|
// judging criteria:
|
||||||
// 'existing matching num' + 'if self-match (1 or 0)' - 'global min matching num' <= 'maxSkew'
|
// 'existing matching num' + 'if self-match (1 or 0)' - 'global min matching num' <= 'maxSkew'
|
||||||
matchNum := len(topologyPairsPodSpreadMap.topologyPairToPods[pair])
|
minMatchNum := paths[0].matchNum
|
||||||
|
matchNum := podSpreadCache.tpPairToMatchNum[pair]
|
||||||
// cast to int to avoid potential overflow.
|
skew := matchNum + selfMatchNum - minMatchNum
|
||||||
skew := matchNum + selfMatchNum - int(minMatchNum)
|
if skew > constraint.MaxSkew {
|
||||||
if skew > int(constraint.MaxSkew) {
|
|
||||||
klog.V(5).Infof("node '%s' failed spreadConstraint[%s]: matchNum(%d) + selfMatchNum(%d) - minMatchNum(%d) > maxSkew(%d)", node.Name, tpKey, matchNum, selfMatchNum, minMatchNum, constraint.MaxSkew)
|
klog.V(5).Infof("node '%s' failed spreadConstraint[%s]: matchNum(%d) + selfMatchNum(%d) - minMatchNum(%d) > maxSkew(%d)", node.Name, tpKey, matchNum, selfMatchNum, minMatchNum, constraint.MaxSkew)
|
||||||
return false, []PredicateFailureReason{ErrTopologySpreadConstraintsNotMatch}, nil
|
return false, []PredicateFailureReason{ErrTopologySpreadConstraintsNotMatch}, nil
|
||||||
}
|
}
|
||||||
|
@ -38,15 +38,15 @@ type topologyPair struct {
|
|||||||
|
|
||||||
type topologySpreadConstraintsMap struct {
|
type topologySpreadConstraintsMap struct {
|
||||||
// nodeNameToPodCounts is keyed with node name, and valued with the number of matching pods.
|
// nodeNameToPodCounts is keyed with node name, and valued with the number of matching pods.
|
||||||
nodeNameToPodCounts map[string]int64
|
nodeNameToPodCounts map[string]int32
|
||||||
// topologyPairToPodCounts is keyed with topologyPair, and valued with the number of matching pods.
|
// topologyPairToPodCounts is keyed with topologyPair, and valued with the number of matching pods.
|
||||||
topologyPairToPodCounts map[topologyPair]*int64
|
topologyPairToPodCounts map[topologyPair]*int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTopologySpreadConstraintsMap() *topologySpreadConstraintsMap {
|
func newTopologySpreadConstraintsMap() *topologySpreadConstraintsMap {
|
||||||
return &topologySpreadConstraintsMap{
|
return &topologySpreadConstraintsMap{
|
||||||
nodeNameToPodCounts: make(map[string]int64),
|
nodeNameToPodCounts: make(map[string]int32),
|
||||||
topologyPairToPodCounts: make(map[topologyPair]*int64),
|
topologyPairToPodCounts: make(map[topologyPair]*int32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ func newTopologySpreadConstraintsMap() *topologySpreadConstraintsMap {
|
|||||||
// This function iterates <nodes> to filter out the nodes which don't have required topologyKey(s),
|
// This function iterates <nodes> to filter out the nodes which don't have required topologyKey(s),
|
||||||
// and initialize two maps:
|
// and initialize two maps:
|
||||||
// 1) t.topologyPairToPodCounts: keyed with both eligible topology pair and node names.
|
// 1) t.topologyPairToPodCounts: keyed with both eligible topology pair and node names.
|
||||||
// 2) t.nodeNameToPodCounts: keyed with node name, and valued with a *int64 pointer for eligible node only.
|
// 2) t.nodeNameToPodCounts: keyed with node name, and valued with a *int32 pointer for eligible node only.
|
||||||
func (t *topologySpreadConstraintsMap) initialize(pod *v1.Pod, nodes []*v1.Node) {
|
func (t *topologySpreadConstraintsMap) initialize(pod *v1.Pod, nodes []*v1.Node) {
|
||||||
constraints := getSoftTopologySpreadConstraints(pod)
|
constraints := getSoftTopologySpreadConstraints(pod)
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
@ -64,7 +64,7 @@ func (t *topologySpreadConstraintsMap) initialize(pod *v1.Pod, nodes []*v1.Node)
|
|||||||
for _, constraint := range constraints {
|
for _, constraint := range constraints {
|
||||||
pair := topologyPair{key: constraint.TopologyKey, value: node.Labels[constraint.TopologyKey]}
|
pair := topologyPair{key: constraint.TopologyKey, value: node.Labels[constraint.TopologyKey]}
|
||||||
if t.topologyPairToPodCounts[pair] == nil {
|
if t.topologyPairToPodCounts[pair] == nil {
|
||||||
t.topologyPairToPodCounts[pair] = new(int64)
|
t.topologyPairToPodCounts[pair] = new(int32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.nodeNameToPodCounts[node.Name] = 0
|
t.nodeNameToPodCounts[node.Name] = 0
|
||||||
@ -122,7 +122,7 @@ func CalculateEvenPodsSpreadPriority(pod *v1.Pod, nodeNameToInfo map[string]*sch
|
|||||||
}
|
}
|
||||||
|
|
||||||
// <matchSum> indicates how many pods (on current node) match the <constraint>.
|
// <matchSum> indicates how many pods (on current node) match the <constraint>.
|
||||||
matchSum := int64(0)
|
matchSum := int32(0)
|
||||||
for _, existingPod := range nodeInfo.Pods() {
|
for _, existingPod := range nodeInfo.Pods() {
|
||||||
match, err := predicates.PodMatchesSpreadConstraint(existingPod.Labels, constraint)
|
match, err := predicates.PodMatchesSpreadConstraint(existingPod.Labels, constraint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -133,7 +133,7 @@ func CalculateEvenPodsSpreadPriority(pod *v1.Pod, nodeNameToInfo map[string]*sch
|
|||||||
matchSum++
|
matchSum++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
atomic.AddInt64(t.topologyPairToPodCounts[pair], matchSum)
|
atomic.AddInt32(t.topologyPairToPodCounts[pair], matchSum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
workqueue.ParallelizeUntil(ctx, 16, len(allNodeNames), processAllNode)
|
workqueue.ParallelizeUntil(ctx, 16, len(allNodeNames), processAllNode)
|
||||||
@ -141,9 +141,9 @@ func CalculateEvenPodsSpreadPriority(pod *v1.Pod, nodeNameToInfo map[string]*sch
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var minCount int64 = math.MaxInt64
|
var minCount int32 = math.MaxInt32
|
||||||
// <total> sums up the number of matching pods on each qualified topology pair
|
// <total> sums up the number of matching pods on each qualified topology pair
|
||||||
var total int64
|
var total int32
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
if _, ok := t.nodeNameToPodCounts[node.Name]; !ok {
|
if _, ok := t.nodeNameToPodCounts[node.Name]; !ok {
|
||||||
continue
|
continue
|
||||||
|
@ -31,8 +31,8 @@ func Test_topologySpreadConstraintsMap_initialize(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
pod *v1.Pod
|
pod *v1.Pod
|
||||||
nodes []*v1.Node
|
nodes []*v1.Node
|
||||||
wantNodeNameMap map[string]int64
|
wantNodeNameMap map[string]int32
|
||||||
wantTopologyPairMap map[topologyPair]*int64
|
wantTopologyPairMap map[topologyPair]*int32
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "normal case",
|
name: "normal case",
|
||||||
@ -45,17 +45,17 @@ func Test_topologySpreadConstraintsMap_initialize(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
st.MakeNode().Name("node-x").Label("zone", "zone2").Label("node", "node-x").Obj(),
|
||||||
},
|
},
|
||||||
wantNodeNameMap: map[string]int64{
|
wantNodeNameMap: map[string]int32{
|
||||||
"node-a": 0,
|
"node-a": 0,
|
||||||
"node-b": 0,
|
"node-b": 0,
|
||||||
"node-x": 0,
|
"node-x": 0,
|
||||||
},
|
},
|
||||||
wantTopologyPairMap: map[topologyPair]*int64{
|
wantTopologyPairMap: map[topologyPair]*int32{
|
||||||
{key: "zone", value: "zone1"}: new(int64),
|
{key: "zone", value: "zone1"}: new(int32),
|
||||||
{key: "zone", value: "zone2"}: new(int64),
|
{key: "zone", value: "zone2"}: new(int32),
|
||||||
{key: "node", value: "node-a"}: new(int64),
|
{key: "node", value: "node-a"}: new(int32),
|
||||||
{key: "node", value: "node-b"}: new(int64),
|
{key: "node", value: "node-b"}: new(int32),
|
||||||
{key: "node", value: "node-x"}: new(int64),
|
{key: "node", value: "node-x"}: new(int32),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -69,14 +69,14 @@ func Test_topologySpreadConstraintsMap_initialize(t *testing.T) {
|
|||||||
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
st.MakeNode().Name("node-b").Label("zone", "zone1").Label("node", "node-b").Obj(),
|
||||||
st.MakeNode().Name("node-x").Label("node", "node-x").Obj(),
|
st.MakeNode().Name("node-x").Label("node", "node-x").Obj(),
|
||||||
},
|
},
|
||||||
wantNodeNameMap: map[string]int64{
|
wantNodeNameMap: map[string]int32{
|
||||||
"node-a": 0,
|
"node-a": 0,
|
||||||
"node-b": 0,
|
"node-b": 0,
|
||||||
},
|
},
|
||||||
wantTopologyPairMap: map[topologyPair]*int64{
|
wantTopologyPairMap: map[topologyPair]*int32{
|
||||||
{key: "zone", value: "zone1"}: new(int64),
|
{key: "zone", value: "zone1"}: new(int32),
|
||||||
{key: "node", value: "node-a"}: new(int64),
|
{key: "node", value: "node-a"}: new(int32),
|
||||||
{key: "node", value: "node-b"}: new(int64),
|
{key: "node", value: "node-b"}: new(int32),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1107,7 +1107,7 @@ func selectVictimsOnNode(
|
|||||||
removePod := func(rp *v1.Pod) {
|
removePod := func(rp *v1.Pod) {
|
||||||
nodeInfoCopy.RemovePod(rp)
|
nodeInfoCopy.RemovePod(rp)
|
||||||
if meta != nil {
|
if meta != nil {
|
||||||
meta.RemovePod(rp)
|
meta.RemovePod(rp, nodeInfoCopy.Node())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addPod := func(ap *v1.Pod) {
|
addPod := func(ap *v1.Pod) {
|
||||||
|
Loading…
Reference in New Issue
Block a user