-
Notifications
You must be signed in to change notification settings - Fork 53
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
Provide a new means to update a Eureka application record periodically #37
Conversation
Also, while I did include a set of example calls to these new methods, I did not include tests. I admit that I am unsure how best to test these methods, because I can't rely on having a particular Eureka server available. I considered writing a fake And now that I think about it more, if these new methods were free functions instead of being methods on type ApplicationSupplier interface {
getApp(string) (*Application, error)
} The struct Let me know what you think. |
@seh Thank you so much for your contribution! We'll look over it and try to get it merged as soon as possible. Just wanted to make sure you knew we're listening. :-) |
} | ||
go func() { | ||
for u := range updates { | ||
if u.Err != nil { |
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.
Should this be either u.Err == nil
or u.App != nil
(or both)?
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's an inconsistency with the documentation for Latest
at line 183, which says that if the most recent update attempt fails, we do want to assign nil
to u.App
here—and later return nil
from Latest
. Here, though, since we ignore failed updates—where an error arose—we won't wind up assigning nil
to s.app
unless GetApp
can return a nil Application
pointer with no corresponding error
. My reading of GetApp
says that it won't return a nil Application
pointer when the corresponding error
is nil, meaning that we'd only ever assign a non-nil Application
pointer to s.app
here.
Given that, I can see two choices:
- Remove this guard condition here, and always take the latest
Application
pointer, even if it's nil, making it consistent with the documentation forLatest
. - Guard on
u.App
not being nil here, as you suggest, and amend the documentation forLatest
to indicate that onceLatest
returns non-nil, it will never return nil again. Unfortunately, it can still return nil if no update attempts have succeeded yet.
I can see callers wanting a kind of self-preservation here, where if we had a good Application
in hand at time T1, and later at time T2 we fail to update it, and at T3 we ask for the latest Application
, we might like to have the "last known good" one still available to us. We could make this an option on NewAppSource
—a boolean preserveLastGood
parameter.
I'd appreciate your feedback.
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.
Between your two choices, I think I'd favor the first one. If the most recent update attempt wasn't successful it doesn't seem like we should try to hide that from the caller.
As far as the self-preservation idea, I can definitely see it being useful but I'm a bit torn. It seems like something that might be use-case specific and should be implemented on the caller-side.
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 agree with @jcox92; while I definitely see the value of a self-preservation option, as a library, I think it's better left up to the implementor to decide for him- or herself that it's desired functionality. I vote option 1.
Thanks for the great analysis. 👍
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 changed NewAppSource
to always take the latest Application
update, whether it succeeded or failed to provide a non-nil pointer.
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.
And yet reading the code today, I see that that intended change to NewAppSource
never made it in.
I'm adjusting it now to align properly with the VIP address monitoring that I'm adding for #53.
I think that's a good way to make these more testable. Although instead of making them free functions, you could make them methods on the |
|
||
// Stop turns off an AppSource, so that it will no longer attempt to update its | ||
// latest application. | ||
// |
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.
Blank line unnecessary
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've seen that in the standard library, the style is to separate documentation paragraphs with blank lines. Are you suggesting that the sentence that follows the blank line should be part of the preceding paragraph?
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.
Ah okay. I didn't realize you intended to make it a new paragraph. 👍
982277d
to
1d60481
Compare
Well, I noticed when trying to introduce this interface is that I am now less certain of the utility of introducing the proposed // ApplicationSupplier is the interface that wraps the GetApp method, allowing
// retrieval of registered Eureka applications.
//
// GetApp returns the Eureka application with the given name. If no such
// application exists, it returns AppNotFoundError.
type ApplicationSupplier interface {
GetApp(name string) (*Application, error)
} Instead, the best I can see doing is hoisting type ApplicationUpdater interface {
ScheduleAppUpdates(name string, prime bool, done <-chan struct{}) <-chan AppUpdate {
} Then: func NewAppSource(u ApplicationUpdater, name string, prime bool) *AppSource {
// ... That makes |
I'd appreciate any further critique you can offer here. |
Sorry for letting this sit so long @seh. I missed your most recent commit. Other than tests, I think you've addressed all of my concerns. It'd be nice to be able to write tests, but I think mocking out the I'm good with getting this merged in. @tysonstewart or @ryansb any additional thoughts? |
// and continues until the supplied done channel is either closed or has a value | ||
// available. Once done sending updates to the returned channel, it closes it. | ||
// | ||
// If prime is true, it sends at least one application update outcome to the |
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.
How would you feel about calling the prime
parameter await
instead?
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.
That's a fine suggestion. I'll make the change shortly.
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 made the change to the NewAppSource
method, forgetting to change this one too. I'll do that now.
Looks good to me, just the minor nit on the |
1d60481
to
2bd71ba
Compare
Define the ScheduleAppUpdates and NewAppSource methods on EurekaConnection. The former supplies a channel of periodic updates to an application, while the latter holds the latest successfully acquired update of an application and makes it available for reading safely from any goroutine.
2bd71ba
to
c9259b5
Compare
Define the
ScheduleAppUpdates
andNewAppSource
methods onEurekaConnection
. The former supplies a channel of periodic updates to an application, while the latter holds the latest successfully acquired update of an application and makes it available for reading safely from any goroutine.This addresses #36, but for now I left
EurekaConnection
'sUpdateApp
method in place, for fear of breaking existing callers. I considered documenting it as "deprecated", but without a commitment to remove it by a given date it felt disingenuous to wag a finger like that.