Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

controller/engine: use local cache for client read requests #554

Merged
merged 3 commits into from
Oct 13, 2023

Conversation

sttts
Copy link
Contributor

@sttts sttts commented Sep 20, 2023

Description of your changes

Two changes:

  1. The controller engine starts a new cache to run controllers dynamically against changing GVRs/GVKs. The cache holds informers and the informers back clients. In today's design, every new cache will lead to new informers even though the parent manager might already have an informer. This PR changes that by sharing the parent cache for all GVRs/GVKs, and only route the dynamic GVR to the new one.

    This is an optimization in general, but it is especially interesting and necessary in feat[compositions]: realtime compositor – part 2: changes to MRs crossplane#4637 to share MR informers.

    This PRs introduces a cache wrapper that can route by GVK. A similar thing exists in controller-runtime, but it is not exposed.

  2. Indexes can only added to an informer before it is started. To allow custom indexes, Start is split into creation and the actual start of the controller (and its backing cache).

I have:

  • Read and followed Crossplane's contribution process.
  • Run make reviewable test to ensure this PR is ready for review.

How has this code been tested

Through crossplane/crossplane#4637 and through unit tests.

@sttts sttts requested review from a team as code owners September 20, 2023 13:58
@sttts sttts requested review from bobh66 and ytsarev September 20, 2023 13:58
@negz
Copy link
Member

negz commented Sep 28, 2023

In today's design, every new manager will lead to new informers even though the parent manager might already have an informer.

I haven't had a chance to look closely at this yet, but I wanted to note that there was a reason for this. It's been a while, but I'm pretty sure it had something to do with not being able to stop an individual informer cleanly, at least at the time this code was added.

@sttts sttts force-pushed the sttts-engine-cached-client branch 5 times, most recently from 341baf8 to 9746110 Compare October 5, 2023 21:14
@sttts
Copy link
Contributor Author

sttts commented Oct 6, 2023

but I'm pretty sure it had something to do with not being able to stop an individual informer cleanly

I get that. It's still the case in controller-runtime (not so in client-go). My point in this PR is that there is no need to start a new cache for everything else than the GVK you want to dynamically watch. I.e. this PR gives you composition for caches.

Copy link
Member

@negz negz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, just a few minor things to clarify before I approve.

pkg/controller/cache.go Outdated Show resolved Hide resolved
// whose lifecycle is coupled to the controller. The controller is created with
// the supplied options, and configured with the supplied watches. It is not
// started yet.
func (e *Engine) Create(name string, o controller.Options, w ...Watch) (NamedController, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to return an interface here? Could we export nameController and return that type?

It's fine if it helps in some practical way, but I prefer to avoid it if it doesn't. Especially in libraries I find it frustrating as a user when I want to click through in my IDE or Godoc to explore the behaviour of the returned type, only to find it's returning an interface. I then need to go dig into the implementation to find out what type it's actually returning and how it works. (I experience this frequently when using controller-runtime, which returns a lot of interfaces that are satisfied by a single, private type.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess struct is fine. Let me try.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted back to interface. The reason is that a struct cannot be mocked.

pkg/controller/engine.go Outdated Show resolved Hide resolved
pkg/controller/cache.go Show resolved Hide resolved
sttts added 2 commits October 13, 2023 11:48
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
@sttts sttts force-pushed the sttts-engine-cached-client branch from 55772bd to d77466e Compare October 13, 2023 09:48
@sttts
Copy link
Contributor Author

sttts commented Oct 13, 2023

@negz addressed all comments.

Signed-off-by: Dr. Stefan Schimanski <stefan.schimanski@upbound.io>
Copy link
Member

@negz negz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I like the routed cache pattern.

@negz negz merged commit 80a3e75 into crossplane:master Oct 13, 2023
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants