-
Notifications
You must be signed in to change notification settings - Fork 1k
vendor directory writes: add counts to verbose logging, limits writers, abort on error #1043
Conversation
@@ -148,6 +148,10 @@ func (i ProjectIdentifier) errString() string { | |||
return fmt.Sprintf("%s (from %s)", i.ProjectRoot, i.Source) | |||
} | |||
|
|||
func (i ProjectIdentifier) String() string { | |||
return i.errString() |
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.
Any objections to this errString
format becoming the canonical exported String method format? fmt.Sprintf("%s (from %s)", i.ProjectRoot, i.Source)
errString()
has 36 usages, all of which are passed to formatting functions with %s
, so I'd like to do a follow up PR to just absorb that method into this one and let them all implicitly call 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.
mmm yes, i like this. the individual properties are accessible already - callers can easily construct their own output if they so choose. 👍
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.
(separate PR, though)
One thing I regret not doing in retrospective, especially after going through #903, is that we really want to stop writing vendor as soon as an error occurs (since continuing is useless). This impacts logging somehow, but we can merge these changes and apply that after. |
internal/gps/result.go
Outdated
|
||
var err error | ||
defer func() { | ||
if r := recover(); r != 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.
Do we get panic
s 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.
I haven't seen one, but I wanted to be careful not to return a false positive if one occurs.
I went ahead and added this because I was playing around with it and, although it's more complex, it actually ended up cleaner (e.g. no more |
internal/gps/result.go
Outdated
} | ||
close(writeCh) | ||
// Launch writers. | ||
writers := runtime.GOMAXPROCS(-1) |
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 an optimal number is some factor of GOMAXPROCS
, or ultimately something configurable.
Related discussion on how this might be configurable: #1028 (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.
i'm not sure that GOMAXPROCS is the right thing, here - the goroutines themselves aren't actually doing work, but end up mostly in a sleep mode waiting for their spawned subprocesses. limiting it to GOMAXPROCS seems likely to be lower than we want.
i think i'd rather just pick an arbitrary number to begin with - let's say 16 - and see how we do.
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.
looks mostly good
internal/gps/result.go
Outdated
} | ||
close(writeCh) | ||
// Launch writers. | ||
writers := runtime.GOMAXPROCS(-1) |
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'm not sure that GOMAXPROCS is the right thing, here - the goroutines themselves aren't actually doing work, but end up mostly in a sleep mode waiting for their spawned subprocesses. limiting it to GOMAXPROCS seems likely to be lower than we want.
i think i'd rather just pick an arbitrary number to begin with - let's say 16 - and see how we do.
msg := "Wrote" | ||
if resp.err != nil { | ||
if len(errs) == 0 { | ||
close(cancel) |
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.
need to know to break out of the loop explicitly here. i believe there's a possibility of a double-close on the cancel channel:
- X and Y are all sent into the
writeCh
, and are all picked up by their own goroutine - X finishes first and fails, indicates as much to the
respCh
; it terminates, decrementing the wg - main goroutine receives the fail and closes
cancel
, logs result, returns to loop and waits for new value - meanwhile, Y finishes and also fails, sends to
respCh
, and terminates, closingrespCh
from the waiter goroutine - however, main goroutine is still waiting on the respCh and is already working before it receives second fail, panics on double-close
note that because the range
loop will not terminate on a buffered channel until the chan is both closed AND empty, i believe this is guaranteed to happen if two workers simultaneously encounter an 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.
The close(cancel)
is protected by len(errs) == 0
so that the channel is only closed on the first error. We could break and abort on the first error (and only report that single error), but since we want to gracefully block/wait/shutdown anyways, it seemed cleaner to inspect all responses and collect all errors (e.g. might be relevant to investigating the original error; we don't want to block/wait in silence then not log anything; etc...).
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.
gah, you're right, i just breezed right by that check. ok 👍
What does this do / why do we need it?
This change adds counts to some of the verbose logging lists:
dry-run
vendor directories (Would have written..
)Wrote...
/Failed to write...
, previously loggedWriting...
prior to work)Failed to write dep tree...
)2 is concurrent and I/O limited, so this should help communicate progress to the user. 1 and 3 log immediately, but I still think the count/total is valuable for reference, grouping, and just scrolling through a really long list.
Example:
Edit: Second commit refactors the concurrency for limiting writers and aborting on error.
What should your reviewer look out for in this PR?
Which issue(s) does this PR fix?
Follow up from #1037