-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Add shared cache for resolvers #8825
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
base: main
Are you sure you want to change the base?
Conversation
|
The following is the coverage report on the affected files.
|
1c42710
to
c606874
Compare
The following is the coverage report on the affected files.
|
c606874
to
a512086
Compare
The following is the coverage report on the affected files.
|
a512086
to
0d8d277
Compare
The following is the coverage report on the affected files.
|
The following is the coverage report on the affected files.
|
The following is the coverage report on the affected files.
|
76731e4
to
d54830f
Compare
The following is the coverage report on the affected files.
|
/kind feature |
The following is the coverage report on the affected files.
|
9a9a2e7
to
7df3f08
Compare
7df3f08
to
f2d4035
Compare
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
The following is the coverage report on the affected files.
|
f2d4035
to
07a5312
Compare
The following is the coverage report on the affected files.
|
07a5312
to
7054be7
Compare
The following is the coverage report on the affected files.
|
7054be7
to
aa82830
Compare
The following is the coverage report on the affected files.
|
/assign |
aa82830
to
6aca950
Compare
The following is the coverage report on the affected files.
|
/test all |
@brianwcook: No presubmit jobs available for tektoncd/pipeline@main In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
This commit adds caching for bundle and git resolvers to reduce chances of being rate limited by registries and git forges. - Add cache interface and in-memory implementation - Add cache configuration options for bundle and git resolvers - Add documentation for cache configuration Signed-off-by: Brian Cook <bcook@redhat.com>
6aca950
to
0d1fc0e
Compare
The following is the coverage report on the affected files.
|
i see in the failed test:
|
/retest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @brianwcook ! Thank you for this great contribution 😸 👍
The PR status is open and I started reviewing but it looks like its still work in progress if I am not mistaken. I thought to publish the comments I have so far anyway, I hope it's ok and hope they are helpful.
I think this PR would benefit from a few e2e tests.
Another consideration is to maybe extend caching to the cluster resolver. I had a conversation with a user on Slack last week and he might be running into rate limiting imposed by k8 when fetching many (100+) tasks. Here is the issue #8889, but it has not all the details. The thread in slack has more details.
} | ||
} | ||
|
||
// Resolve uses the given params to resolve the requested file or resource. | ||
func (r *Resolver) Resolve(ctx context.Context, req *v1beta1.ResolutionRequestSpec) (resolutionframework.ResolvedResource, error) { | ||
if len(req.Params) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use the guard pattern here. It makes the code better readable:
if len(req.Params) > 0 { | |
if len(req.Params) == 0 { | |
// Remove this error once resolution of url has been implemented. | |
return nil, errors.New("the Resolve method has not been implemented.") | |
} | |
if bundle.IsDisabled(ctx) { | |
return nil, errors.New(bundle.DisabledError) | |
} | |
// ... all the other code |
cache.GetGlobalCache().InitializeLogger(ctx) | ||
|
||
// Generate cache key | ||
cacheKey, err := cache.GenerateCacheKey(LabelValueBundleResolverType, req.Params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generating cacheKey
and getting a value from cache could be one Get
operation taking LabelValueBundleResolverType, req.Params
as arguments. It would simplify the code here a bit.
|
||
if useCache { | ||
// Initialize cache logger | ||
cache.GetGlobalCache().InitializeLogger(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add cache as a property to the Resolver struct and initialize the cache logger in the main right before passing it to framework.NewController(ctx, &bundle.Resolver{cache: cache.GetGlobalCache()})
from cmd/resolvers/main.go.
// Determine if caching should be used based on cache mode | ||
useCache := ShouldUseCache(opts) | ||
|
||
if useCache { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ShouldUseCache(opts)
can be used here and in the other if statement, the name is descriptive and there would be one local variable less.
|
||
// Cache the result if caching is enabled | ||
if useCache { | ||
cacheKey, _ := cache.GenerateCacheKey(LabelValueBundleResolverType, req.Params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar here. Both operations could be just the Add
operation handling cacheKey
generation internally.
} | ||
} | ||
|
||
// Resolve uses the given params to resolve the requested file or resource. | ||
func (r *Resolver) Resolve(ctx context.Context, req *v1beta1.ResolutionRequestSpec) (resolutionframework.ResolvedResource, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a bit of new logic now added to the Resolve
method so it makes sense to write a few unit tests for it. Easier said then done when I take a look at TestResolve
in pkg/remoteresolution/resolver/bundle/resolver_test.go
.
But I think with a small refactoring here you could easy write unit tests just for this method without the need for a complex test setup.
Put bundle.ResolveRequest
as a function property on Resolver
and set it in main.go
then you can easy fake it in your unit tests.
return IsCommitHash(gitRevision) | ||
} | ||
} | ||
|
||
// Resolve performs the work of fetching a file from git given a map of | ||
// parameters. | ||
func (r *Resolver) Resolve(ctx context.Context, req *v1beta1.ResolutionRequestSpec) (resolutionframework.ResolvedResource, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most comments from the bundle resolver apply here also:
- using guard pattern
- setting cache as a property on the
Resolver
struct (and rename the currentcache
tosecretCache
) - inline
useCache
variable - simplify cache usage by keeping
cacheKey
generation insideGet
andAdd
- use function properties for testing
- adding unit tests to it
} | ||
|
||
// Check cache first | ||
if cached, ok := cache.GetGlobalCache().Get(cacheKey); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use generics here but if its only resolutionframework.ResolvedResource
which is returned the cache could also be specific and return only this resource.
if params[git.UrlParam] != "" { | ||
return g.ResolveGitClone(ctx) | ||
resource, err = g.ResolveGitClone(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you put ResolveGitClone
and ResolveAPIGit
as function properties on this struct instead on GitResolver
down below you could overwrite them here for testing and testing of this method becomes easier.
|
||
// Check cache first | ||
if cached, ok := cache.GetGlobalCache().Get(cacheKey); ok { | ||
if resource, ok := cached.(resolutionframework.ResolvedResource); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just food for thought: does it makes sense to find a way to enrich ResolvedResource
somehow with an annotation that it was cached? This would make unit/e2e tests easier. Wdys?
Changes
Resolvers are awesome but can be noisy. In high traffic environments we have seen rate limiting both from registries when using the bundle resolver and from git forges when using the git resolver. This PR adds caching to the resolvers (except the cluster resolver). It is only enabled by default when 'safe' - meaning that the object was fetched by some hash / digest. Otherwise, it can be turned on with a resolver parameter.
Note
This PR is a work in progress posted for initial thoughts and feedback. It works for some cases already (git resolver) - others, maybe not.
Submitter Checklist
As the author of this PR, please check off the items in this checklist:
/kind <type>
. Valid types are bug, cleanup, design, documentation, feature, flake, misc, question, tepRelease Notes