[PATCH] Fix equiv. cache invalidation of Node condition.

Equivalence cache for CheckNodeConditionPred becomes invalid when
Node.Spec.Unschedulable changes. This can happen even if
Node.Status.Conditions does not change, so move the logic around.

This logic is covered by integration test
"test/integration/scheduler".TestUnschedulableNodes but equivalence
cache is currently skipped when test pods have no OwnerReference.

Add benchmark for equivalence hashing.

Change equivalence hash function.

This changes the equivalence class hashing function to use as inputs all
the Pod fields which are read by FitPredicates. Before we used a
combination of OwnerReference and PersistentVolumeClaim info, which was
a close approximation. The new method ensures that hashing remains
correct regardless of controller behavior.

The PVCSet field can be removed from equivalencePod because it is
implicitly included in the Volume list.

Tests are now broken.

Move equivalence class hash code.

This moves the equivalence hashing code from
algorithm/predicates/utils.go to core/equivalence_cache.go.

In the process, making the hashing function and hashing function factory
both injectable dependencies is removed.

Fix equivalence cache hash tests.

Co-authored-by: Jonathan Basseri <misterikkit@google.com>
Co-authored-by: Harry Zhang <resouer@gmail.com>
This commit is contained in:
Jonathan Basseri 2018-01-24 17:02:12 -08:00 committed by Harry Zhang
parent c6d0726df8
commit f5ab6d5ad4
8 changed files with 255 additions and 328 deletions

View File

@ -34,7 +34,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/rand:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/rand:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/client-go/listers/core/v1:go_default_library", "//vendor/k8s.io/client-go/listers/core/v1:go_default_library",
"//vendor/k8s.io/client-go/listers/storage/v1:go_default_library", "//vendor/k8s.io/client-go/listers/storage/v1:go_default_library",

View File

@ -17,12 +17,8 @@ limitations under the License.
package predicates package predicates
import ( import (
"github.com/golang/glog"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/scheduler/algorithm"
schedutil "k8s.io/kubernetes/pkg/scheduler/util" schedutil "k8s.io/kubernetes/pkg/scheduler/util"
) )
@ -70,69 +66,6 @@ func CreateSelectorFromLabels(aL map[string]string) labels.Selector {
return labels.Set(aL).AsSelector() return labels.Set(aL).AsSelector()
} }
// EquivalencePodGenerator is a generator of equivalence class for pod with consideration of PVC info.
type EquivalencePodGenerator struct {
pvcInfo PersistentVolumeClaimInfo
}
// NewEquivalencePodGenerator returns a getEquivalencePod method with consideration of PVC info.
func NewEquivalencePodGenerator(pvcInfo PersistentVolumeClaimInfo) algorithm.GetEquivalencePodFunc {
g := &EquivalencePodGenerator{
pvcInfo: pvcInfo,
}
return g.getEquivalencePod
}
// GetEquivalencePod returns a EquivalencePod which contains a group of pod attributes which can be reused.
func (e *EquivalencePodGenerator) getEquivalencePod(pod *v1.Pod) interface{} {
// For now we only consider pods:
// 1. OwnerReferences is Controller
// 2. with same OwnerReferences
// 3. with same PVC claim
// to be equivalent
for _, ref := range pod.OwnerReferences {
if ref.Controller != nil && *ref.Controller {
pvcSet, err := e.getPVCSet(pod)
if err == nil {
// A pod can only belongs to one controller, so let's return.
return &EquivalencePod{
ControllerRef: ref,
PVCSet: pvcSet,
}
}
// If error encountered, log warning and return nil (i.e. no equivalent pod found)
glog.Warningf("[EquivalencePodGenerator] for pod: %v failed due to: %v", pod.GetName(), err)
return nil
}
}
return nil
}
// getPVCSet returns a set of PVC UIDs of given pod.
func (e *EquivalencePodGenerator) getPVCSet(pod *v1.Pod) (sets.String, error) {
result := sets.NewString()
for _, volume := range pod.Spec.Volumes {
if volume.PersistentVolumeClaim == nil {
continue
}
pvcName := volume.PersistentVolumeClaim.ClaimName
pvc, err := e.pvcInfo.GetPersistentVolumeClaimInfo(pod.GetNamespace(), pvcName)
if err != nil {
return nil, err
}
result.Insert(string(pvc.UID))
}
return result, nil
}
// EquivalencePod is a group of pod attributes which can be reused as equivalence to schedule other pods.
type EquivalencePod struct {
ControllerRef metav1.OwnerReference
PVCSet sets.String
}
// portsConflict check whether existingPorts and wantPorts conflict with each other // portsConflict check whether existingPorts and wantPorts conflict with each other
// return true if we have a conflict // return true if we have a conflict
func portsConflict(existingPorts schedutil.HostPortInfo, wantPorts []*v1.ContainerPort) bool { func portsConflict(existingPorts schedutil.HostPortInfo, wantPorts []*v1.ContainerPort) bool {

View File

@ -78,9 +78,6 @@ type PredicateFailureReason interface {
GetReason() string GetReason() string
} }
// GetEquivalencePodFunc is a function that gets a EquivalencePod from a pod.
type GetEquivalencePodFunc func(pod *v1.Pod) interface{}
// NodeLister interface represents anything that can list nodes for a scheduler. // NodeLister interface represents anything that can list nodes for a scheduler.
type NodeLister interface { type NodeLister interface {
// We explicitly return []*v1.Node, instead of v1.NodeList, to avoid // We explicitly return []*v1.Node, instead of v1.NodeList, to avoid

View File

@ -77,13 +77,6 @@ func init() {
// Fit is determined by node selector query. // Fit is determined by node selector query.
factory.RegisterFitPredicate(predicates.MatchNodeSelectorPred, predicates.PodMatchNodeSelector) factory.RegisterFitPredicate(predicates.MatchNodeSelectorPred, predicates.PodMatchNodeSelector)
// Use equivalence class to speed up heavy predicates phase.
factory.RegisterGetEquivalencePodFunction(
func(args factory.PluginFactoryArgs) algorithm.GetEquivalencePodFunc {
return predicates.NewEquivalencePodGenerator(args.PVCInfo)
},
)
// ServiceSpreadingPriority is a priority config factory that spreads pods by minimizing // ServiceSpreadingPriority is a priority config factory that spreads pods by minimizing
// the number of pods (belonging to the same service) on the same node. // the number of pods (belonging to the same service) on the same node.
// Register the factory so that it's available, but do not include it as part of the default priorities // Register the factory so that it's available, but do not include it as part of the default priorities

View File

@ -37,7 +37,6 @@ const maxCacheEntries = 100
// 2. function to get equivalence pod // 2. function to get equivalence pod
type EquivalenceCache struct { type EquivalenceCache struct {
sync.RWMutex sync.RWMutex
getEquivalencePod algorithm.GetEquivalencePodFunc
algorithmCache map[string]AlgorithmCache algorithmCache map[string]AlgorithmCache
} }
@ -62,10 +61,8 @@ func newAlgorithmCache() AlgorithmCache {
} }
} }
// NewEquivalenceCache creates a EquivalenceCache object. func NewEquivalenceCache() *EquivalenceCache {
func NewEquivalenceCache(getEquivalencePodFunc algorithm.GetEquivalencePodFunc) *EquivalenceCache {
return &EquivalenceCache{ return &EquivalenceCache{
getEquivalencePod: getEquivalencePodFunc,
algorithmCache: make(map[string]AlgorithmCache), algorithmCache: make(map[string]AlgorithmCache),
} }
} }
@ -219,9 +216,11 @@ type equivalenceClassInfo struct {
hash uint64 hash uint64
} }
// getEquivalenceClassInfo returns the equivalence class of given pod. // getEquivalenceClassInfo returns a hash of the given pod.
// The hashing function returns the same value for any two pods that are
// equivalent from the perspective of scheduling.
func (ec *EquivalenceCache) getEquivalenceClassInfo(pod *v1.Pod) *equivalenceClassInfo { func (ec *EquivalenceCache) getEquivalenceClassInfo(pod *v1.Pod) *equivalenceClassInfo {
equivalencePod := ec.getEquivalencePod(pod) equivalencePod := getEquivalenceHash(pod)
if equivalencePod != nil { if equivalencePod != nil {
hash := fnv.New32a() hash := fnv.New32a()
hashutil.DeepHashObject(hash, equivalencePod) hashutil.DeepHashObject(hash, equivalencePod)
@ -231,3 +230,60 @@ func (ec *EquivalenceCache) getEquivalenceClassInfo(pod *v1.Pod) *equivalenceCla
} }
return nil return nil
} }
// equivalencePod is the set of pod attributes which must match for two pods to
// be considered equivalent for scheduling purposes. For correctness, this must
// include any Pod field which is used by a FitPredicate.
//
// NOTE: For equivalence hash to be formally correct, lists and maps in the
// equivalencePod should be normalized. (e.g. by sorting them) However, the
// vast majority of equivalent pod classes are expected to be created from a
// single pod template, so they will all have the same ordering.
type equivalencePod struct {
Namespace *string
Labels map[string]string
Affinity *v1.Affinity
Containers []v1.Container // See note about ordering
InitContainers []v1.Container // See note about ordering
NodeName *string
NodeSelector map[string]string
Tolerations []v1.Toleration
Volumes []v1.Volume // See note about ordering
}
// getEquivalenceHash returns the equivalencePod for a Pod.
func getEquivalenceHash(pod *v1.Pod) *equivalencePod {
ep := &equivalencePod{
Namespace: &pod.Namespace,
Labels: pod.Labels,
Affinity: pod.Spec.Affinity,
Containers: pod.Spec.Containers,
InitContainers: pod.Spec.InitContainers,
NodeName: &pod.Spec.NodeName,
NodeSelector: pod.Spec.NodeSelector,
Tolerations: pod.Spec.Tolerations,
Volumes: pod.Spec.Volumes,
}
// DeepHashObject considers nil and empty slices to be different. Normalize them.
if len(ep.Containers) == 0 {
ep.Containers = nil
}
if len(ep.InitContainers) == 0 {
ep.InitContainers = nil
}
if len(ep.Tolerations) == 0 {
ep.Tolerations = nil
}
if len(ep.Volumes) == 0 {
ep.Volumes = nil
}
// Normalize empty maps also.
if len(ep.Labels) == 0 {
ep.Labels = nil
}
if len(ep.NodeSelector) == 0 {
ep.NodeSelector = nil
}
// TODO(misterikkit): Also normalize nested maps and slices.
return ep
}

View File

@ -21,12 +21,132 @@ import (
"testing" "testing"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/scheduler/algorithm" "k8s.io/kubernetes/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates" "k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
) )
// makeBasicPod returns a Pod object with many of the fields populated.
func makeBasicPod(name string) *v1.Pod {
isController := true
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "test-ns",
Labels: map[string]string{"app": "web", "env": "prod"},
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "ReplicationController",
Name: "rc",
UID: "123",
Controller: &isController,
},
},
},
Spec: v1.PodSpec{
Affinity: &v1.Affinity{
NodeAffinity: &v1.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
NodeSelectorTerms: []v1.NodeSelectorTerm{
{
MatchExpressions: []v1.NodeSelectorRequirement{
{
Key: "failure-domain.beta.kubernetes.io/zone",
Operator: "Exists",
},
},
},
},
},
},
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "db"}},
TopologyKey: "kubernetes.io/hostname",
},
},
},
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "web"}},
TopologyKey: "kubernetes.io/hostname",
},
},
},
},
InitContainers: []v1.Container{
{
Name: "init-pause",
Image: "gcr.io/google_containers/pause",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("1"),
"mem": resource.MustParse("100Mi"),
},
},
},
},
Containers: []v1.Container{
{
Name: "pause",
Image: "gcr.io/google_containers/pause",
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("1"),
"mem": resource.MustParse("100Mi"),
},
},
VolumeMounts: []v1.VolumeMount{
{
Name: "nfs",
MountPath: "/srv/data",
},
},
},
},
NodeSelector: map[string]string{"node-type": "awesome"},
Tolerations: []v1.Toleration{
{
Effect: "NoSchedule",
Key: "experimental",
Operator: "Exists",
},
},
Volumes: []v1.Volume{
{
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "someEBSVol1",
},
},
},
{
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "someEBSVol2",
},
},
},
{
Name: "nfs",
VolumeSource: v1.VolumeSource{
NFS: &v1.NFSVolumeSource{
Server: "nfs.corp.example.com",
},
},
},
},
},
}
}
type predicateItemType struct { type predicateItemType struct {
fit bool fit bool
reasons []algorithm.PredicateFailureReason reasons []algorithm.PredicateFailureReason
@ -70,9 +190,7 @@ func TestUpdateCachedPredicateItem(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
// this case does not need to calculate equivalence hash, just pass an empty function ecache := NewEquivalenceCache()
fakeGetEquivalencePodFunc := func(pod *v1.Pod) interface{} { return nil }
ecache := NewEquivalenceCache(fakeGetEquivalencePodFunc)
if test.expectPredicateMap { if test.expectPredicateMap {
ecache.algorithmCache[test.nodeName] = newAlgorithmCache() ecache.algorithmCache[test.nodeName] = newAlgorithmCache()
predicateItem := HostPredicate{ predicateItem := HostPredicate{
@ -191,9 +309,7 @@ func TestPredicateWithECache(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
// this case does not need to calculate equivalence hash, just pass an empty function ecache := NewEquivalenceCache()
fakeGetEquivalencePodFunc := func(pod *v1.Pod) interface{} { return nil }
ecache := NewEquivalenceCache(fakeGetEquivalencePodFunc)
// set cached item to equivalence cache // set cached item to equivalence cache
ecache.UpdateCachedPredicateItem( ecache.UpdateCachedPredicateItem(
test.podName, test.podName,
@ -240,205 +356,46 @@ func TestPredicateWithECache(t *testing.T) {
} }
} }
func TestGetHashEquivalencePod(t *testing.T) { func TestGetEquivalenceHash(t *testing.T) {
testNamespace := "test" ecache := NewEquivalenceCache()
pvcInfo := predicates.FakePersistentVolumeClaimInfo{ pod1 := makeBasicPod("pod1")
{ pod2 := makeBasicPod("pod2")
ObjectMeta: metav1.ObjectMeta{UID: "someEBSVol1", Name: "someEBSVol1", Namespace: testNamespace},
Spec: v1.PersistentVolumeClaimSpec{VolumeName: "someEBSVol1"},
},
{
ObjectMeta: metav1.ObjectMeta{UID: "someEBSVol2", Name: "someEBSVol2", Namespace: testNamespace},
Spec: v1.PersistentVolumeClaimSpec{VolumeName: "someNonEBSVol"},
},
{
ObjectMeta: metav1.ObjectMeta{UID: "someEBSVol3-0", Name: "someEBSVol3-0", Namespace: testNamespace},
Spec: v1.PersistentVolumeClaimSpec{VolumeName: "pvcWithDeletedPV"},
},
{
ObjectMeta: metav1.ObjectMeta{UID: "someEBSVol3-1", Name: "someEBSVol3-1", Namespace: testNamespace},
Spec: v1.PersistentVolumeClaimSpec{VolumeName: "anotherPVCWithDeletedPV"},
},
}
// use default equivalence class generator pod3 := makeBasicPod("pod3")
ecache := NewEquivalenceCache(predicates.NewEquivalencePodGenerator(pvcInfo)) pod3.Spec.Volumes = []v1.Volume{
isController := true
pod1 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
Namespace: testNamespace,
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "ReplicationController",
Name: "rc",
UID: "123",
Controller: &isController,
},
},
},
Spec: v1.PodSpec{
Volumes: []v1.Volume{
{ {
VolumeSource: v1.VolumeSource{ VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "someEBSVol1", ClaimName: "someEBSVol111",
},
},
},
{
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "someEBSVol2",
},
},
}, },
}, },
}, },
} }
pod2 := &v1.Pod{ pod4 := makeBasicPod("pod4")
ObjectMeta: metav1.ObjectMeta{ pod4.Spec.Volumes = []v1.Volume{
Name: "pod2",
Namespace: testNamespace,
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "ReplicationController",
Name: "rc",
UID: "123",
Controller: &isController,
},
},
},
Spec: v1.PodSpec{
Volumes: []v1.Volume{
{ {
VolumeSource: v1.VolumeSource{ VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "someEBSVol2", ClaimName: "someEBSVol222",
},
},
},
{
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "someEBSVol1",
},
},
}, },
}, },
}, },
} }
pod3 := &v1.Pod{ pod5 := makeBasicPod("pod5")
ObjectMeta: metav1.ObjectMeta{ pod5.Spec.Volumes = []v1.Volume{}
Name: "pod3",
Namespace: testNamespace,
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "ReplicationController",
Name: "rc",
UID: "567",
Controller: &isController,
},
},
},
Spec: v1.PodSpec{
Volumes: []v1.Volume{
{
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "someEBSVol3-1",
},
},
},
},
},
}
pod4 := &v1.Pod{ pod6 := makeBasicPod("pod6")
ObjectMeta: metav1.ObjectMeta{ pod6.Spec.Volumes = nil
Name: "pod4",
Namespace: testNamespace,
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "ReplicationController",
Name: "rc",
UID: "567",
Controller: &isController,
},
},
},
Spec: v1.PodSpec{
Volumes: []v1.Volume{
{
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "someEBSVol3-0",
},
},
},
},
},
}
pod5 := &v1.Pod{ pod7 := makeBasicPod("pod7")
ObjectMeta: metav1.ObjectMeta{ pod7.Spec.NodeSelector = nil
Name: "pod5",
Namespace: testNamespace,
},
}
pod6 := &v1.Pod{ pod8 := makeBasicPod("pod8")
ObjectMeta: metav1.ObjectMeta{ pod8.Spec.NodeSelector = make(map[string]string)
Name: "pod6",
Namespace: testNamespace,
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "ReplicationController",
Name: "rc",
UID: "567",
Controller: &isController,
},
},
},
Spec: v1.PodSpec{
Volumes: []v1.Volume{
{
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "no-exists-pvc",
},
},
},
},
},
}
pod7 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod7",
Namespace: testNamespace,
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "ReplicationController",
Name: "rc",
UID: "567",
Controller: &isController,
},
},
},
}
type podInfo struct { type podInfo struct {
pod *v1.Pod pod *v1.Pod
@ -446,39 +403,41 @@ func TestGetHashEquivalencePod(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string
podInfoList []podInfo podInfoList []podInfo
isEquivalent bool isEquivalent bool
}{ }{
// pods with same controllerRef and same pvc claim
{ {
name: "pods with everything the same except name",
podInfoList: []podInfo{ podInfoList: []podInfo{
{pod: pod1, hashIsValid: true}, {pod: pod1, hashIsValid: true},
{pod: pod2, hashIsValid: true}, {pod: pod2, hashIsValid: true},
}, },
isEquivalent: true, isEquivalent: true,
}, },
// pods with same controllerRef but different pvc claim
{ {
name: "pods that only differ in their PVC volume sources",
podInfoList: []podInfo{ podInfoList: []podInfo{
{pod: pod3, hashIsValid: true}, {pod: pod3, hashIsValid: true},
{pod: pod4, hashIsValid: true}, {pod: pod4, hashIsValid: true},
}, },
isEquivalent: false, isEquivalent: false,
}, },
// pod without controllerRef
{ {
name: "pods that have no volumes, but one uses nil and one uses an empty slice",
podInfoList: []podInfo{ podInfoList: []podInfo{
{pod: pod5, hashIsValid: false}, {pod: pod5, hashIsValid: true},
{pod: pod6, hashIsValid: true},
}, },
isEquivalent: false, isEquivalent: true,
}, },
// pods with same controllerRef but one has non-exists pvc claim
{ {
name: "pods that have no NodeSelector, but one uses nil and one uses an empty map",
podInfoList: []podInfo{ podInfoList: []podInfo{
{pod: pod6, hashIsValid: false},
{pod: pod7, hashIsValid: true}, {pod: pod7, hashIsValid: true},
{pod: pod8, hashIsValid: true},
}, },
isEquivalent: false, isEquivalent: true,
}, },
} }
@ -488,6 +447,7 @@ func TestGetHashEquivalencePod(t *testing.T) {
) )
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
for i, podInfo := range test.podInfoList { for i, podInfo := range test.podInfoList {
testPod := podInfo.pod testPod := podInfo.pod
eclassInfo := ecache.getEquivalenceClassInfo(testPod) eclassInfo := ecache.getEquivalenceClassInfo(testPod)
@ -510,6 +470,7 @@ func TestGetHashEquivalencePod(t *testing.T) {
} }
} }
} }
})
} }
} }
@ -554,9 +515,7 @@ func TestInvalidateCachedPredicateItemOfAllNodes(t *testing.T) {
}, },
}, },
} }
// this case does not need to calculate equivalence hash, just pass an empty function ecache := NewEquivalenceCache()
fakeGetEquivalencePodFunc := func(pod *v1.Pod) interface{} { return nil }
ecache := NewEquivalenceCache(fakeGetEquivalencePodFunc)
for _, test := range tests { for _, test := range tests {
// set cached item to equivalence cache // set cached item to equivalence cache
@ -623,9 +582,7 @@ func TestInvalidateAllCachedPredicateItemOfNode(t *testing.T) {
}, },
}, },
} }
// this case does not need to calculate equivalence hash, just pass an empty function ecache := NewEquivalenceCache()
fakeGetEquivalencePodFunc := func(pod *v1.Pod) interface{} { return nil }
ecache := NewEquivalenceCache(fakeGetEquivalencePodFunc)
for _, test := range tests { for _, test := range tests {
// set cached item to equivalence cache // set cached item to equivalence cache
@ -649,3 +606,10 @@ func TestInvalidateAllCachedPredicateItemOfNode(t *testing.T) {
} }
} }
} }
func BenchmarkEquivalenceHash(b *testing.B) {
pod := makeBasicPod("test")
for i := 0; i < b.N; i++ {
getEquivalenceHash(pod)
}
}

View File

@ -134,6 +134,8 @@ type configFactory struct {
alwaysCheckAllPredicates bool alwaysCheckAllPredicates bool
} }
var _ scheduler.Configurator = &configFactory{}
// NewConfigFactory initializes the default implementation of a Configurator To encourage eventual privatization of the struct type, we only // NewConfigFactory initializes the default implementation of a Configurator To encourage eventual privatization of the struct type, we only
// return the interface. // return the interface.
func NewConfigFactory( func NewConfigFactory(
@ -1046,14 +1048,8 @@ func (c *configFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String,
} }
// Init equivalence class cache // Init equivalence class cache
if c.enableEquivalenceClassCache && getEquivalencePodFuncFactory != nil { if c.enableEquivalenceClassCache {
pluginArgs, err := c.getPluginArgs() c.equivalencePodCache = core.NewEquivalenceCache()
if err != nil {
return nil, err
}
c.equivalencePodCache = core.NewEquivalenceCache(
getEquivalencePodFuncFactory(*pluginArgs),
)
glog.Info("Created equivalence class cache") glog.Info("Created equivalence class cache")
} }

View File

@ -75,9 +75,6 @@ type PriorityConfigFactory struct {
Weight int Weight int
} }
// EquivalencePodFuncFactory produces a function to get equivalence class for given pod.
type EquivalencePodFuncFactory func(PluginFactoryArgs) algorithm.GetEquivalencePodFunc
var ( var (
schedulerFactoryMutex sync.Mutex schedulerFactoryMutex sync.Mutex
@ -90,9 +87,6 @@ var (
// Registered metadata producers // Registered metadata producers
priorityMetadataProducer PriorityMetadataProducerFactory priorityMetadataProducer PriorityMetadataProducerFactory
predicateMetadataProducer PredicateMetadataProducerFactory predicateMetadataProducer PredicateMetadataProducerFactory
// get equivalence pod function
getEquivalencePodFuncFactory EquivalencePodFuncFactory
) )
const ( const (
@ -346,11 +340,6 @@ func RegisterCustomPriorityFunction(policy schedulerapi.PriorityPolicy) string {
return RegisterPriorityConfigFactory(policy.Name, *pcf) return RegisterPriorityConfigFactory(policy.Name, *pcf)
} }
// RegisterGetEquivalencePodFunction registers equivalenceFuncFactory to produce equivalence class for given pod.
func RegisterGetEquivalencePodFunction(equivalenceFuncFactory EquivalencePodFuncFactory) {
getEquivalencePodFuncFactory = equivalenceFuncFactory
}
// IsPriorityFunctionRegistered is useful for testing providers. // IsPriorityFunctionRegistered is useful for testing providers.
func IsPriorityFunctionRegistered(name string) bool { func IsPriorityFunctionRegistered(name string) bool {
schedulerFactoryMutex.Lock() schedulerFactoryMutex.Lock()