@@ -22,9 +22,11 @@ import (
22
22
"fmt"
23
23
"reflect"
24
24
"sort"
25
+ "sync"
25
26
26
27
v1 "k8s.io/api/core/v1"
27
28
discovery "k8s.io/api/discovery/v1alpha1"
29
+ "k8s.io/apimachinery/pkg/labels"
28
30
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
29
31
"k8s.io/apimachinery/pkg/util/sets"
30
32
v1listers "k8s.io/client-go/listers/core/v1"
@@ -34,6 +36,76 @@ import (
34
36
"k8s.io/kubernetes/pkg/util/hash"
35
37
)
36
38
39
+ // ServiceSelectorCache is a cache of service selectors to avoid high CPU consumption caused by frequent calls to AsSelectorPreValidated (see #73527)
40
+ type ServiceSelectorCache struct {
41
+ lock sync.RWMutex
42
+ cache map [string ]labels.Selector
43
+ }
44
+
45
+ // NewServiceSelectorCache init ServiceSelectorCache for both endpoint controller and endpointSlice controller.
46
+ func NewServiceSelectorCache () * ServiceSelectorCache {
47
+ return & ServiceSelectorCache {
48
+ cache : map [string ]labels.Selector {},
49
+ }
50
+ }
51
+
52
+ // Get return selector and existence in ServiceSelectorCache by key.
53
+ func (sc * ServiceSelectorCache ) Get (key string ) (labels.Selector , bool ) {
54
+ sc .lock .RLock ()
55
+ selector , ok := sc .cache [key ]
56
+ // fine-grained lock improves GetPodServiceMemberships performance(16.5%) than defer measured by BenchmarkGetPodServiceMemberships
57
+ sc .lock .RUnlock ()
58
+ return selector , ok
59
+ }
60
+
61
+ // Update can update or add a selector in ServiceSelectorCache while service's selector changed.
62
+ func (sc * ServiceSelectorCache ) Update (key string , rawSelector map [string ]string ) labels.Selector {
63
+ sc .lock .Lock ()
64
+ defer sc .lock .Unlock ()
65
+ selector := labels .Set (rawSelector ).AsSelectorPreValidated ()
66
+ sc .cache [key ] = selector
67
+ return selector
68
+ }
69
+
70
+ // Delete can delete selector which exist in ServiceSelectorCache.
71
+ func (sc * ServiceSelectorCache ) Delete (key string ) {
72
+ sc .lock .Lock ()
73
+ defer sc .lock .Unlock ()
74
+ delete (sc .cache , key )
75
+ }
76
+
77
+ // GetPodServiceMemberships returns a set of Service keys for Services that have
78
+ // a selector matching the given pod.
79
+ func (sc * ServiceSelectorCache ) GetPodServiceMemberships (serviceLister v1listers.ServiceLister , pod * v1.Pod ) (sets.String , error ) {
80
+ set := sets.String {}
81
+ services , err := serviceLister .Services (pod .Namespace ).List (labels .Everything ())
82
+ if err != nil {
83
+ return set , err
84
+ }
85
+
86
+ var selector labels.Selector
87
+ for _ , service := range services {
88
+ if service .Spec .Selector == nil {
89
+ // if the service has a nil selector this means selectors match nothing, not everything.
90
+ continue
91
+ }
92
+ key , err := controller .KeyFunc (service )
93
+ if err != nil {
94
+ return nil , err
95
+ }
96
+ if v , ok := sc .Get (key ); ok {
97
+ selector = v
98
+ } else {
99
+ selector = sc .Update (key , service .Spec .Selector )
100
+ }
101
+
102
+ if selector .Matches (labels .Set (pod .Labels )) {
103
+ set .Insert (key )
104
+ }
105
+ }
106
+ return set , nil
107
+ }
108
+
37
109
// EndpointsMatch is a type of function that returns true if pod endpoints match.
38
110
type EndpointsMatch func (* v1.Pod , * v1.Pod ) bool
39
111
@@ -100,29 +172,9 @@ func PodChanged(oldPod, newPod *v1.Pod, endpointChanged EndpointsMatch) (bool, b
100
172
return endpointChanged (newPod , oldPod ), labelsChanged
101
173
}
102
174
103
- // GetPodServiceMemberships returns a set of Service keys for Services that have
104
- // a selector matching the given pod.
105
- func GetPodServiceMemberships (serviceLister v1listers.ServiceLister , pod * v1.Pod ) (sets.String , error ) {
106
- set := sets.String {}
107
- services , err := serviceLister .GetPodServices (pod )
108
- if err != nil {
109
- // don't log this error because this function makes pointless
110
- // errors when no services match
111
- return set , nil
112
- }
113
- for i := range services {
114
- key , err := controller .KeyFunc (services [i ])
115
- if err != nil {
116
- return nil , err
117
- }
118
- set .Insert (key )
119
- }
120
- return set , nil
121
- }
122
-
123
175
// GetServicesToUpdateOnPodChange returns a set of Service keys for Services
124
176
// that have potentially been affected by a change to this pod.
125
- func GetServicesToUpdateOnPodChange (serviceLister v1listers.ServiceLister , old , cur interface {}, endpointChanged EndpointsMatch ) sets.String {
177
+ func GetServicesToUpdateOnPodChange (serviceLister v1listers.ServiceLister , selectorCache * ServiceSelectorCache , old , cur interface {}, endpointChanged EndpointsMatch ) sets.String {
126
178
newPod := cur .(* v1.Pod )
127
179
oldPod := old .(* v1.Pod )
128
180
if newPod .ResourceVersion == oldPod .ResourceVersion {
@@ -138,14 +190,14 @@ func GetServicesToUpdateOnPodChange(serviceLister v1listers.ServiceLister, old,
138
190
return sets.String {}
139
191
}
140
192
141
- services , err := GetPodServiceMemberships (serviceLister , newPod )
193
+ services , err := selectorCache . GetPodServiceMemberships (serviceLister , newPod )
142
194
if err != nil {
143
195
utilruntime .HandleError (fmt .Errorf ("Unable to get pod %s/%s's service memberships: %v" , newPod .Namespace , newPod .Name , err ))
144
196
return sets.String {}
145
197
}
146
198
147
199
if labelsChanged {
148
- oldServices , err := GetPodServiceMemberships (serviceLister , oldPod )
200
+ oldServices , err := selectorCache . GetPodServiceMemberships (serviceLister , oldPod )
149
201
if err != nil {
150
202
utilruntime .HandleError (fmt .Errorf ("Unable to get pod %s/%s's service memberships: %v" , newPod .Namespace , newPod .Name , err ))
151
203
}
0 commit comments