Skip to content

Commit

Permalink
Add a design document to filter cache by field
Browse files Browse the repository at this point in the history
The controller-runtime is caching all the resource instance even if not
all of them end up being reconciled, this design document describe a
proposal to fix part of that problem.

Signed-off-by: Quique Llorente <ellorent@redhat.com>
  • Loading branch information
qinqon committed Mar 9, 2021
1 parent a4e89ae commit 69c3cbf
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 0 deletions.
89 changes: 89 additions & 0 deletions designs/filter-cache-by-field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
Filter cache ListWatch by field
===================
## Motivation

The controller-runtime subscribe to changes using a cache mechanism that get
updated using kubernetes informers, those informers implement a `ListWatch`
that specify the namespace and type specified at controller builder time.

So the only possible filtering of that cache is namespace and resource type,
this means that if some user is interested only on Reconcile some resource with
specific fields it will still cache all the instances on that type increasing
the memory and CPU load. This also get worse in case controller-runtime is
deployed as a daemonset instead of a deployment since all the nodes with
daemonset's pods will expect to receive notification of all the instances and
that can end with scalation problems depending on the type or resource it's
watching.

The alternative that can be done with the current controller-runtme is
to implement a custom cache that do the filtering since cache is pluggable
into the controller-runtime [1]

This proposal is related to the following issue [2]

## Proposal

Incrase `cache.Options` as follow:

```golang
type Options struct {
Scheme *runtime.Scheme
Mapper meta.RESTMapper
Resync *time.Duration
Namespace string
FieldSelectorByResource map[schema.GroupResource]string
}
```

Incrase `manager.Options` as follow:

```golang
type Options struct {
CacheFieldSelectorByResource map[schema.GroupResource]string
```
Pass the new manager options field field cache options and this
is passed to informer's ListWatch and add the filtering option:
```golang

# At pkg/cluster/cluster.go

cache, err := options.NewCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod, Namespace: options.Namespace, FieldSelectorByResource: options.CacheFieldSelectorByResource})


# At pkg/cache/internal/informers_map.go

func (ip *specificInformersMap) findFieldSelectorByGVR(gvr schema.GroupVersionResource) string {
gr := schema.GroupResource{Group: gvr.Group, Resource: gvr.Resource}
return ip.fieldSelectorByResource[gr]
}

ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
opts.FieldSelector = ip.findFieldSelectorByGVR(mapping.Resource)
...
```
Here is a PR with the implementatin at the `pkg/cache` part [3]
## Example
Users will change the Manager options, similar to namespace to specify what
Fields they want to limit the cache to by resource.
```golang
ctrlOptions := ctrl.Options{
...
CacheFieldSelectorByResource: map[schema.GroupResource]string{
{Group: "", Resource: "nodes"}: "metadata.name=node01",
{Group: "", Resource: "nodenetworkstate"}: "metadata.name=node01",
},
}
...
}
```
[1] https://github.com/nmstate/kubernetes-nmstate/pull/687
[2] https://github.com/kubernetes-sigs/controller-runtime/issues/244
[3] https://github.com/kubernetes-sigs/controller-runtime/pull/1404
6 changes: 6 additions & 0 deletions pkg/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ type Options struct {
// supported, k8s docs related to field selector [1].
// [1] https://kubernetes.io/docs/concepts/overview/working-with-objects/field-selectors/
FieldSelectorByResource map[schema.GroupResource]string

// LabelSelectorByResource restricts the cache's ListWatch to the desired
// labels per resource, the map's string values follow the label selector
// k8s format, for example "environment=production,tier=frontend" a [1].
// [1] https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
LabelSelectorByResource map[schema.GroupResource]string
}

var defaultResyncTime = 10 * time.Hour
Expand Down

0 comments on commit 69c3cbf

Please sign in to comment.