-
Notifications
You must be signed in to change notification settings - Fork 17
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
Patching for Watcher and http client #5
Conversation
linxGnu
commented
Nov 21, 2018
- Fix go routine spawning by cycling call between scheduleWatch and doWatch
- Prevent spawning too much go routine to notify listener in case listener fail to catch up. This also prevents notifying >2 revisions at the same time (ambiguous order)
- Drain up response (in http client do) when server return malform data and expected response not set
- Refactor code
- Fix go routine spawning by cycling call between scheduleWatch and doWatch - Prevent spawning too much go routine to notify listener in case listener fail to catch up. This also prevents notifying >2 revisions at the same time (ambiguous order) - Drain up response (in http client do) when server return malform data and expected response not set - Refactor code
Codecov Report
@@ Coverage Diff @@
## master #5 +/- ##
==========================================
+ Coverage 33.24% 34.31% +1.07%
==========================================
Files 20 20
Lines 1501 1527 +26
==========================================
+ Hits 499 524 +25
- Misses 901 902 +1
Partials 101 101
Continue to review full report at Codecov.
|
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.
Sorry for taking a long time to review this.
I really appreciate this. :)
@@ -215,29 +215,28 @@ func (c *Client) do(ctx context.Context, req *http.Request, resContent interface | |||
|
|||
res, err := c.client.Do(req) | |||
if err != nil { | |||
select { | |||
case <-ctx.Done(): | |||
return nil, ctx.Err() |
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.
the context's error will be probably more useful?
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.
When context is cancelled or error, res, err
would hold the error. So we don't need ctx.Err()
here
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 a chance that it gets an error response and the user cancels the ctx
before the code reaches here.
Please see https://github.com/google/go-github/blob/master/github/github.go#L484 this.
But it's probably no big deal so we can just return the original err
.
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.
Yes, in case user cancel the ctx
first, then it's his choice to cancel whole process and stop. I think it's no big deal too.
// see also: | ||
// - https://github.com/google/go-github/pull/317 | ||
// - https://forum.golangbridge.org/t/do-i-need-to-read-the-body-before-close-it/5594/4 | ||
io.CopyN(ioutil.Discard, res.Body, 512) |
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.
https://github.com/google/go-github/blob/master/github/github.go#L501
The current version deprecates this feature. Could you recheck it, please?
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 did a test on this when building some production and confirm that it still remains.
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.
Could you also change this as well please? :-)
google/go-github#847
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 mean add more refence link to comment @minwoox
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 I mean that we do not have to drain up 512 bytes since it was introduced in 1.7. So we can revert this to as defer res.Body.Close()
.
Please check out google/go-github#843
and golang/go@18072ad
:-)
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.
@minwoox Yeah, currently we are doing json decoder.
- in case server response with additional malform data, then decoder won't help us.
- the other case is unknown status which we refuse to consume or processing.
Both of case seem not happened in real production being since server always return well format data. However, because we are doing client, we should just ignore what server response and handle all malform things. WDYT @minwoox
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 you on that we have to deal with the case when the response data is malformed.
I have thought about for a while why google/go-github
removes the codes introduced. I believe that
- the client is only sending the request to the Github which emits the controlled responses by them.
- they will get to know easily when the response is modified if they do not have this trick by looking at the new connection count so that they can notice the response is changed and fix it.
- they just want to make it simple by removing the code because there's no problem when the condition meets the above situation.
I was just not sure that we still need this trick when I found that the codes are gone.
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.
Perhaps we do not have useful metrics on Central Dogma, we'd better have this trick.
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.
@huydx, @trustin, @hyangtack You guys have any opinion on this?
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.
OK with keeping this as long as it's well commented.
@@ -45,9 +43,13 @@ type commitWithEntry struct { | |||
|
|||
func (ws *watchService) watchFile(ctx context.Context, projectName, repoName, lastKnownRevision string, | |||
query *Query, timeout time.Duration) <-chan *WatchResult { |
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.
As we discussed before, a user is expecting more than one watch result when he/she gets a channel as the return value. So how about making this method return just *WatchResult
(not the channel) and renaming it to watchFileOneTime
.
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 remove the FileWatcher
and provide watchFile
which returns <-chan *WatchResult
(inside the method it keeps watching)? What do you think?
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.
As we discussed before, a user is expecting more than one watch result when he/she gets a channel as the return value. So how about making this method return just
*WatchResult
(not the channel) and renaming it towatchFileOneTime
.
Yes it's, and I would make it at next PR. This PR is for FileWatcher
only. I think we could keep it and reuse for watchService
:)
@@ -276,56 +316,49 @@ func (ws *watchService) repoWatcher(projectName, repoName, pathPattern string) ( | |||
|
|||
func (w *Watcher) start() { | |||
if atomic.CompareAndSwapInt32(&w.state, initial, started) { | |||
w.scheduleWatch(0) | |||
go w.scheduleWatch() |
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.
Inside of the scheduleWatch()
it spawns another Go routine as well. So how about moving the Go routine in watchRequest()
out to watchFile
and watchRepo
so that this watches with only one Go routine?
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.
Since Watcher
start only called once. So each watcher has only one daemon to do watch file and notify listeners (which are running on others routines) about changes
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.
Yeah but, watchSchedule()
calls doWatch()
and doWatch()
calls doWatchFunc()
.
In doWatchFunc()
, watchFile
is called, and watchRequest()
is called which spawns another Go routine.
Am I missing something? :)
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.
Yeah you are right. But it's blocking until watchRequest()
done, then no more spawn.
In short, there is only one additional routine, this routine do job and watcher daemon wait for it until it done. So it's fine, no routine leak.
It's the problem of old design, so this patch only focus on Watcher. Client and watchService should be next PR. WDYT @minwoox?
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.
Alright~! Let's fix the problem of the old design in next PR. Thank you~!
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.
Thank you very much for hard review ^_^
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 don't know much about Golang. 😄
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.
Mostly nits. Nice work :-)
) | ||
|
||
var ( | ||
ErrLatestNotSet = fmt.Errorf("latest is not set yet") |
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.
(ErrLatestIsNotSet
and ErrWatchedIsClosed
) or (ErrLatestNotSet
and ErrWatcherClosed
) for consistency.
Refactor naming and comment
I have refactor code to address comment from @trustin. PTAL :) |
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.
Thank you!
Thanks a lot. @linxGnu |