-
Notifications
You must be signed in to change notification settings - Fork 511
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
Make retrying transport and http errors configurable #1122
Make retrying transport and http errors configurable #1122
Conversation
cc132f2
to
822e1a1
Compare
So I don't love how many options this adds, but I get the need for this to be more configurable. For the transport pieces, I think I'd like to revisit this: #740 Ideally, you could supply your own transport via // Wraps with retries and useragent and debug logging
remote.Image(..., remote.WithTransport(remote.Transport{t}))
// No wrapping of t
remote.Image(..., remote.WithTransport(t))
// Configurable backoff stuff.
remote.Image(..., remote.WithTransport(transport.NewRetry(t, ...))) My only issue with that approach is that it's probably a breaking change :/ Another approach might be exposing more on the errors we return from
That would allow you to write a predicate that checked the request method, URL, and status code yourself to determine if you should retry it. |
Note: This is a breaking change Authored-by: Dennis Leon <leonde@vmware.com>
822e1a1
to
4cdd7d1
Compare
Codecov Report
@@ Coverage Diff @@
## main #1122 +/- ##
==========================================
+ Coverage 75.16% 75.39% +0.22%
==========================================
Files 108 108
Lines 7724 7827 +103
==========================================
+ Hits 5806 5901 +95
Misses 1363 1363
- Partials 555 563 +8
Continue to review full report at Codecov.
|
Yeah I think iterating over the API change to make it as minimal as needed makes lots of sense. Both from maintainability and from a usability perspective! With the changes above sounds like a trade-off between a simpler API vs breaking changes to existing APIs. I played around with the idea of using
I did this by creating a new I pushed the changes. I'd love some early feedback to make sure i'm properly understanding your idea to begin with! This PR comes with breaking changes:
Note: Some tests are failing due to these breaking changes. But I wanted to get feedback before proceeding down this path. |
4cdd7d1
to
cfea68c
Compare
I don't think we need
This feels kinda backwards. I think if someone gives us a It would still make sense to me to expose Backoff/Predicate options in I'd expect the default predicate to be somewhat conservative, but having the option would let callers do some pretty flexible things like only retry for certain methods or for certain paths. |
- Useful by consumers providing their own Predicate to determine whether to retry or not Authored-by: Dennis Leon <leonde@vmware.com>
cfea68c
to
bf0d731
Compare
hmmm. I don't see how or put another way... I don't see how a consumer providing their own Retry Transport would ever get an error of type Although I see how exposing the
hmm ok, I mis-understood your code snippet above then
but thinking about it more, it does make sense that it should not wrap, and i think will result in this not being a breaking change. I updated the PR with this.
I think this helps clarify things a lot for me. I was trying to have
I made a first attempt at it. Essentially the |
… http retries Authored-by: Dennis Leon <leonde@vmware.com>
bf0d731
to
a09fa36
Compare
Yeah I didn't love my code snippet, but what you've got here is actually a better solution, I think.
Ah yeah, sorry this wasn't more clear. There were two categories of retry stuff going on, but we kind of lumped them together. Current PR is pretty close to optimal, I think. |
pkg/v1/remote/transport/transport.go
Outdated
|
||
// Transport results in *not* wrapping supplied transport with additional logic such as retries, useragent and debug logging | ||
// Consumers are opt-ing into providing their own transport without any additional wrapping. | ||
type Transport struct { |
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 think all that's left is to make the transport
package actually return these.
I don't love the transport.Transport
name. Maybe transport.Wrapper
or something?
Another issue is that it's kind of a pain to actually construct a transport outside of the remote package (you need to know where layers came from, where you're pushing, etc). We could make that a lot more ergonomic by introducing a new transport implementation that defers those decisions until they're needed, like containerd (#666 (comment)).
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.
Yep! changed it to transport.Wrapper
make the transport package actually return these.
I wasn't 100% sure what you meant here. I took this to mean NewWithContext
should return transport.Wrapper
. I pushed the change.
Another issue is that it's kind of a pain to actually construct a transport outside of the remote package (you need to know where layers came from, where you're pushing, etc). We could make that a lot more ergonomic by introducing a new transport implementation that defers those decisions until they're needed, like containerd (#666 (comment)).
hmm ok, is it possible to split introducing a new deferring transport implementation into a separate PR? Or do you see this as a blocker to this PR?
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 wasn't 100% sure what you meant here. I took this to mean NewWithContext should return transport.Wrapper. I pushed the change.
Yep, that's perfect.
hmm ok, is it possible to split introducing a new deferring transport implementation into a separate PR? Or do you see this as a blocker to this PR?
Not a blocker, just thinking out loud.
pkg/v1/remote/options.go
Outdated
if logs.Enabled(logs.Debug) { | ||
o.transport = transport.NewLogger(o.transport) | ||
} | ||
if _, ok := o.transport.(*transport.Transport); !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.
Add a comment here and in google
describing why we're doing things this way.
Also for the WithTransport
option, we'll want to describe this workaround.
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.
done!
b591fe2
to
8178e55
Compare
8178e55
to
c3c7f34
Compare
- add test to assert behavior around using a transport.Wrapper results in no additional wrapping such as retry is done. refactoring - add comments - rename transport.Transport -> transport.Wrapper - make transport package return transport.Wrapper Authored-by: Dennis Leon <leonde@vmware.com>
c3c7f34
to
13a1b0b
Compare
@jonjohnsonjr Just checking in to see if theres anything else needed to have this merged in? |
Sorry I missed your follow-up commits! One final thing I forgot to mention. If we're dealing with a go-containerregistry/pkg/v1/remote/transport/transport.go Lines 53 to 68 in 8388fde
We can do a little introspection to determine if the the given transport can be reused by looking at go-containerregistry/pkg/v1/remote/transport/bearer.go Lines 99 to 105 in 8388fde
|
788b195
to
4635e06
Compare
Thanks I appreciate the pointers @jonjohnsonjr
I did this by saving state ('pingedRegistries') in
I figured that the scopes should be tied to the registry that the transport pinged against. Hence scopes being a map on the wrapped transport. |
4635e06
to
226b7ec
Compare
Okay so this might make you hate me, but I'm not thrilled with how this turned out after my suggestion (I know... I'm sorry). Do you mind backing this out to the previous revision? We can just submit that because it's useful on its own, then I can revisit the ping optimizations when I have more time to look at it. Again, sorry, I appreciate your patience. |
226b7ec
to
13a1b0b
Compare
@jonjohnsonjr lol no worries. i've reverted that commit fwiw I have enjoyed working on this PR and we get a ton of value from using this library. so more than happy to be patient and get things done right! 😄 |
- Consumers should construct a transport.Wrapper via constructor transport.NewWithContext - options retryBackoff and retryPredicate should only apply to http errors and not lower level transport errors. (Consumers can still provide a transport with the retry behavior they want) Authored-by: Dennis Leon <leonde@vmware.com>
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.
Thanks for sticking with this. This looks good to me -- let's merge and let it bake for a bit while I try to figure out how we can get rid of extra pings :)
Related to #1114
Adds the following 4 options:
Went with a finer level API around configuring these (in my head) 2 groups of retry logic (low-level transport errors and higher level HTTP errors).
I was also aiming to keep the 'default' retry logic values the same (if these options are not provided).