Merge pull request #36889 from wojtek-t/reuse_fields_and_labels

Automatic merge from submit-queue

Reuse fields and labels

This should significantly reduce memory allocations in apiserver in large cluster.
Explanation:
- every kubelet is refreshing watch every 5-10 minutes (this generally is not causing relist - it just renews watch)
- that means, in 5000-node cluster, we are issuing ~10 watches per second
- since we don't have "watch heartbets", the watch is issued from previously received resourceVersion
- to make some assumption, let's assume pods are evenly spread across pods, and writes for them are evenly spread - that means, that a given kubelet is interested in 1 per 5000 pod changes
- with that assumption, each watch, has to process 2500 (on average) previous watch events
- for each of such even, we are currently computing fields.

This PR is fixing this problem.
This commit is contained in:
Kubernetes Submit Queue
2016-12-02 21:49:43 -08:00
committed by GitHub
78 changed files with 558 additions and 362 deletions

View File

@@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix,
configmap.Strategy,
newListFunc,
configmap.GetAttrs,
storage.NoTriggerPublisher)
store := &registry.Store{

View File

@@ -88,18 +88,20 @@ func ConfigMapToSelectableFields(cfg *api.ConfigMap) fields.Set {
return generic.ObjectMetaFieldsSet(&cfg.ObjectMeta, true)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
cfg, ok := obj.(*api.ConfigMap)
if !ok {
return nil, nil, fmt.Errorf("given object is not a ConfigMap")
}
return labels.Set(cfg.ObjectMeta.Labels), ConfigMapToSelectableFields(cfg), nil
}
// MatchConfigMap returns a generic matcher for a given label and field selector.
func MatchConfigMap(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
cfg, ok := obj.(*api.ConfigMap)
if !ok {
return nil, nil, fmt.Errorf("given object is not a ConfigMap")
}
return labels.Set(cfg.ObjectMeta.Labels), ConfigMapToSelectableFields(cfg), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -69,6 +69,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix,
controller.Strategy,
newListFunc,
controller.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -118,20 +118,23 @@ func ControllerToSelectableFields(controller *api.ReplicationController) fields.
return generic.MergeFieldsSets(objectMetaFieldsSet, controllerSpecificFieldsSet)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
rc, ok := obj.(*api.ReplicationController)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a replication controller.")
}
return labels.Set(rc.ObjectMeta.Labels), ControllerToSelectableFields(rc), nil
}
// MatchController is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific
// labels/fields.
func MatchController(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
rc, ok := obj.(*api.ReplicationController)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a replication controller.")
}
return labels.Set(rc.ObjectMeta.Labels), ControllerToSelectableFields(rc), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix,
endpoint.Strategy,
newListFunc,
endpoint.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -79,27 +79,21 @@ func (endpointsStrategy) AllowUnconditionalUpdate() bool {
return true
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
endpoints, ok := obj.(*api.Endpoints)
if !ok {
return nil, nil, fmt.Errorf("invalid object type %#v", obj)
}
return endpoints.Labels, EndpointsToSelectableFields(endpoints), nil
}
// MatchEndpoints returns a generic matcher for a given label and field selector.
func MatchEndpoints(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate {
return pkgstorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
endpoints, ok := obj.(*api.Endpoints)
if !ok {
return nil, nil, fmt.Errorf("invalid object type %#v", obj)
}
// Compute fields only if field selectors is non-empty
// (otherwise those won't be used).
// Those are generally also not needed if label selector does
// not match labels, but additional computation of it is expensive.
var endpointsFields fields.Set
if !field.Empty() {
endpointsFields = EndpointsToSelectableFields(endpoints)
}
return endpoints.Labels, endpointsFields, nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -70,17 +70,20 @@ func (eventStrategy) AllowUnconditionalUpdate() bool {
return true
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
event, ok := obj.(*api.Event)
if !ok {
return nil, nil, fmt.Errorf("not an event")
}
return labels.Set(event.Labels), EventToSelectableFields(event), nil
}
func MatchEvent(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
event, ok := obj.(*api.Event)
if !ok {
return nil, nil, fmt.Errorf("not an event")
}
return labels.Set(event.Labels), EventToSelectableFields(event), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix,
limitrange.Strategy,
newListFunc,
limitrange.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -85,16 +85,19 @@ func (limitrangeStrategy) Export(api.Context, runtime.Object, bool) error {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
lr, ok := obj.(*api.LimitRange)
if !ok {
return nil, nil, fmt.Errorf("given object is not a limit range.")
}
return labels.Set(lr.ObjectMeta.Labels), LimitRangeToSelectableFields(lr), nil
}
func MatchLimitRange(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
lr, ok := obj.(*api.LimitRange)
if !ok {
return nil, nil, fmt.Errorf("given object is not a limit range.")
}
return labels.Set(lr.ObjectMeta.Labels), LimitRangeToSelectableFields(lr), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -60,6 +60,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST, *FinalizeREST) {
prefix,
namespace.Strategy,
newListFunc,
namespace.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -135,18 +135,21 @@ func (namespaceFinalizeStrategy) PrepareForUpdate(ctx api.Context, obj, old runt
newNamespace.Status = oldNamespace.Status
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
namespaceObj, ok := obj.(*api.Namespace)
if !ok {
return nil, nil, fmt.Errorf("not a namespace")
}
return labels.Set(namespaceObj.Labels), NamespaceToSelectableFields(namespaceObj), nil
}
// MatchNamespace returns a generic matcher for a given label and field selector.
func MatchNamespace(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
namespaceObj, ok := obj.(*api.Namespace)
if !ok {
return nil, nil, fmt.Errorf("not a namespace")
}
return labels.Set(namespaceObj.Labels), NamespaceToSelectableFields(namespaceObj), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -79,6 +79,7 @@ func NewStorage(opts generic.RESTOptions, kubeletClientConfig client.KubeletClie
prefix,
node.Strategy,
newListFunc,
node.GetAttrs,
node.NodeNameTriggerFunc)
store := &registry.Store{

View File

@@ -144,27 +144,21 @@ func NodeToSelectableFields(node *api.Node) fields.Set {
return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet)
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
nodeObj, ok := obj.(*api.Node)
if !ok {
return nil, nil, fmt.Errorf("not a node")
}
return labels.Set(nodeObj.ObjectMeta.Labels), NodeToSelectableFields(nodeObj), nil
}
// MatchNode returns a generic matcher for a given label and field selector.
func MatchNode(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate {
return pkgstorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
nodeObj, ok := obj.(*api.Node)
if !ok {
return nil, nil, fmt.Errorf("not a node")
}
// Compute fields only if field selectors is non-empty
// (otherwise those won't be used).
// Those are generally also not needed if label selector does
// not match labels, but additional computation of it is expensive.
var nodeFields fields.Set
if !field.Empty() {
nodeFields = NodeToSelectableFields(nodeObj)
}
return labels.Set(nodeObj.ObjectMeta.Labels), nodeFields, nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
IndexFields: []string{"metadata.name"},
}
}

View File

@@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix,
persistentvolume.Strategy,
newListFunc,
persistentvolume.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -95,18 +95,21 @@ func (persistentvolumeStatusStrategy) ValidateUpdate(ctx api.Context, obj, old r
return validation.ValidatePersistentVolumeStatusUpdate(obj.(*api.PersistentVolume), old.(*api.PersistentVolume))
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
persistentvolumeObj, ok := obj.(*api.PersistentVolume)
if !ok {
return nil, nil, fmt.Errorf("not a persistentvolume")
}
return labels.Set(persistentvolumeObj.Labels), PersistentVolumeToSelectableFields(persistentvolumeObj), nil
}
// MatchPersistentVolume returns a generic matcher for a given label and field selector.
func MatchPersistentVolumes(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
persistentvolumeObj, ok := obj.(*api.PersistentVolume)
if !ok {
return nil, nil, fmt.Errorf("not a persistentvolume")
}
return labels.Set(persistentvolumeObj.Labels), PersistentVolumeToSelectableFields(persistentvolumeObj), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix,
persistentvolumeclaim.Strategy,
newListFunc,
persistentvolumeclaim.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -95,18 +95,21 @@ func (persistentvolumeclaimStatusStrategy) ValidateUpdate(ctx api.Context, obj,
return validation.ValidatePersistentVolumeClaimStatusUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim))
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
persistentvolumeclaimObj, ok := obj.(*api.PersistentVolumeClaim)
if !ok {
return nil, nil, fmt.Errorf("not a persistentvolumeclaim")
}
return labels.Set(persistentvolumeclaimObj.Labels), PersistentVolumeClaimToSelectableFields(persistentvolumeclaimObj), nil
}
// MatchPersistentVolumeClaim returns a generic matcher for a given label and field selector.
func MatchPersistentVolumeClaim(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
persistentvolumeclaimObj, ok := obj.(*api.PersistentVolumeClaim)
if !ok {
return nil, nil, fmt.Errorf("not a persistentvolumeclaim")
}
return labels.Set(persistentvolumeclaimObj.Labels), PersistentVolumeClaimToSelectableFields(persistentvolumeclaimObj), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -69,6 +69,7 @@ func NewStorage(opts generic.RESTOptions, k client.ConnectionInfoGetter, proxyTr
prefix,
pod.Strategy,
newListFunc,
pod.GetAttrs,
pod.NodeNameTriggerFunc,
)

View File

@@ -155,27 +155,21 @@ func (podStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object
return validation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod))
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
pod, ok := obj.(*api.Pod)
if !ok {
return nil, nil, fmt.Errorf("not a pod")
}
return labels.Set(pod.ObjectMeta.Labels), PodToSelectableFields(pod), nil
}
// MatchPod returns a generic matcher for a given label and field selector.
func MatchPod(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
pod, ok := obj.(*api.Pod)
if !ok {
return nil, nil, fmt.Errorf("not a pod")
}
// Compute fields only if field selectors is non-empty
// (otherwise those won't be used).
// Those are generally also not needed if label selector does
// not match labels, but additional computation of it is expensive.
var podFields fields.Set
if !field.Empty() {
podFields = PodToSelectableFields(pod)
}
return labels.Set(pod.ObjectMeta.Labels), podFields, nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
IndexFields: []string{"spec.nodeName"},
}
}

View File

@@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix,
podtemplate.Strategy,
newListFunc,
podtemplate.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -86,16 +86,19 @@ func PodTemplateToSelectableFields(podTemplate *api.PodTemplate) fields.Set {
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
pt, ok := obj.(*api.PodTemplate)
if !ok {
return nil, nil, fmt.Errorf("given object is not a pod template.")
}
return labels.Set(pt.ObjectMeta.Labels), PodTemplateToSelectableFields(pt), nil
}
func MatchPodTemplate(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
pt, ok := obj.(*api.PodTemplate)
if !ok {
return nil, nil, fmt.Errorf("given object is not a pod template.")
}
return labels.Set(pt.ObjectMeta.Labels), PodTemplateToSelectableFields(pt), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix,
resourcequota.Strategy,
newListFunc,
resourcequota.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -98,18 +98,21 @@ func (resourcequotaStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runt
return validation.ValidateResourceQuotaStatusUpdate(obj.(*api.ResourceQuota), old.(*api.ResourceQuota))
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
resourcequotaObj, ok := obj.(*api.ResourceQuota)
if !ok {
return nil, nil, fmt.Errorf("not a resourcequota")
}
return labels.Set(resourcequotaObj.Labels), ResourceQuotaToSelectableFields(resourcequotaObj), nil
}
// MatchResourceQuota returns a generic matcher for a given label and field selector.
func MatchResourceQuota(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
resourcequotaObj, ok := obj.(*api.ResourceQuota)
if !ok {
return nil, nil, fmt.Errorf("not a resourcequota")
}
return labels.Set(resourcequotaObj.Labels), ResourceQuotaToSelectableFields(resourcequotaObj), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix,
secret.Strategy,
newListFunc,
secret.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -94,18 +94,21 @@ func (s strategy) Export(ctx api.Context, obj runtime.Object, exact bool) error
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
secret, ok := obj.(*api.Secret)
if !ok {
return nil, nil, fmt.Errorf("not a secret")
}
return labels.Set(secret.Labels), SelectableFields(secret), nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
secret, ok := obj.(*api.Secret)
if !ok {
return nil, nil, fmt.Errorf("not a secret")
}
return labels.Set(secret.Labels), SelectableFields(secret), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix,
service.Strategy,
newListFunc,
service.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -101,17 +101,20 @@ func (svcStrategy) Export(ctx api.Context, obj runtime.Object, exact bool) error
return nil
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
service, ok := obj.(*api.Service)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a service")
}
return labels.Set(service.ObjectMeta.Labels), ServiceToSelectableFields(service), nil
}
func MatchServices(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
service, ok := obj.(*api.Service)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a service")
}
return labels.Set(service.ObjectMeta.Labels), ServiceToSelectableFields(service), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}

View File

@@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix,
serviceaccount.Strategy,
newListFunc,
serviceaccount.GetAttrs,
storage.NoTriggerPublisher,
)

View File

@@ -77,18 +77,21 @@ func (strategy) AllowUnconditionalUpdate() bool {
return true
}
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
sa, ok := obj.(*api.ServiceAccount)
if !ok {
return nil, nil, fmt.Errorf("not a serviceaccount")
}
return labels.Set(sa.Labels), SelectableFields(sa), nil
}
// Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
sa, ok := obj.(*api.ServiceAccount)
if !ok {
return nil, nil, fmt.Errorf("not a serviceaccount")
}
return labels.Set(sa.Labels), SelectableFields(sa), nil
},
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}