-
Notifications
You must be signed in to change notification settings - Fork 1k
fix(status): concurrent BasicStatus creation #1135
Conversation
Looks like you sorted out the concurrency. |
Yeah, that sounds reasonable. |
5595c1a
to
60e5c84
Compare
cmd/dep/status.go
Outdated
for err := range errorCh { | ||
ctx.Err.Println(err.Error()) | ||
} | ||
ctx.Err.Println() |
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 didn't really consider if it should still return an error. Could this look like a false positive if a user overlooks the logged errors? Should we return an error, and adjust the caller's error handling to print the buffer followed by a clear error message?
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.
Even that would work. Like conditionally print the out buffer (with incomplete info) depending on the error type. Although, I'm still not sure if that's the best behavior.
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 right, some errors are more severe than others.
What if errored items were still displayed inline, but indicated error somehow? (probably in addition to separate logs, since we don't want to cram messages into the table). Then there would be no mistaking the tail of the output for a false positive, with the errors interleaved.
Or perhaps simplest is best, what if the error output just came last?
@jmank88 So I thought about it and even prototyped the idea. Right now, if How about running
NOTE: the I'll push the commit with these changes. Have a look, or even try it 🙂 |
I really like this in principle. Could we link the final message with the
|
e70cafa
to
bdc7710
Compare
So I added the unknown count too.
And created 2 different error channels for different error types. Error due to failure in listing packages ( Added 3 errors |
cmd/dep/status.go
Outdated
statusError = errFailedUpdate | ||
} | ||
|
||
errorCount = len(errListVerCh) |
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 include len(errListPkgCh)
as well?
Can probably just set it at initilization to len(errListVerCh) + len(errListPkgCh)
, or even just inline that expression down below.
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.
ListPackages errors are less likely to happen(no network). And even if they do, it only affects Children
of a BasicStatus
only for dotOutput. And for such errors, we don't show the result with unknown
s. We just fail.
Error count is only for the unknown status. This should be documented properly, which errors are counted.
cmd/dep/status.go
Outdated
statusError = errMultipleFailures | ||
} else { | ||
statusError = errFailedUpdate | ||
} |
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.
What about this instead:
if statusError == nil {
statusError = errFailedUpdate
} else {
statuError = errMultipleFailures
}
cmd/dep/status.go
Outdated
count int | ||
} | ||
|
||
func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceManager) (bool, bool, errorStatus) { |
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 wonder if the errorStatus
type is worth it. The return lines are sort of complex. Did you consider adding a return value instead? (bool, bool, int, error)
In either case, this function could probably use some documentation regarding what is returned. Actually, a case could be made for naming them. They are all declared in highly visible scope already anyways (bools wouldn't change), plus returns would be simpler.
Maybe (digestMismatch bool, hasMissingPkgs bool, errCnt int, err error)
or 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.
I thought of returning the error count, but then thought it would look weird. Returning error count when there's no error. If you think it's fine, we can return the count 😊
Yes, the return needs documentation. I'll add those variable names for the existing variables, will add error count later if we agree that we need it.
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 thought of returning the error count, but then thought it would look weird. Returning error count when there's no error. If you think it's fine, we can return the count 😊
I don't think there is a problem with this in principle, as long as the documentation makes the purpose clear. The default assumption tends to be that a non-nil err means any other return values are irrelevant, but io.Writer
, for example, always returns the bytes written.
cmd/dep/status.go
Outdated
} | ||
|
||
// While the network churns on ListVersions() requests, statically analyze | ||
// code from the current project. | ||
ptree, err := pkgtree.ListPackages(p.ResolvedAbsRoot, string(p.ImportRoot)) | ||
if err != nil { | ||
return digestMismatch, hasMissingPkgs, errors.Wrapf(err, "analysis of local packages failed") | ||
errStatus.err = errors.Wrapf(err, "analysis of local packages failed") | ||
return digestMismatch, hasMissingPkgs, errStatus |
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.
With the named return vars, you can just do a bare return
when the values are already set.
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, but I think it's better to do explicit returns when the function is big, for clarity.
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.
In that case, can we change most of these to return constant true
or false
for both vars? It looks like only the final hasMissingPkgs
is actually dynamic.
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.
changed and pushed!
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 good, just a few optional nits
cmd/dep/status.go
Outdated
@@ -482,7 +596,7 @@ func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceMana | |||
ctx.Err.Printf("\t%s: %s\n", fail.ex, fail.err.Error()) | |||
} | |||
|
|||
return digestMismatch, hasMissingPkgs, errors.New("address issues with undeducible import paths to get more status information") | |||
return digestMismatch, hasMissingPkgs, 0, errors.New("address issues with undeducible import paths to get more status information") |
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.
This one could be true, false, 0...
cmd/dep/status.go
Outdated
@@ -502,7 +616,7 @@ outer: | |||
} | |||
out.MissingFooter() | |||
|
|||
return digestMismatch, hasMissingPkgs, nil | |||
return digestMismatch, hasMissingPkgs, 0, 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.
This one could be true, hasMissingPkgs...
And then line 566 can go away too.
cmd/dep/status.go
Outdated
digestMismatch, hasMissingPkgs, err := runStatusAll(ctx, out, p, sm) | ||
if err != nil { | ||
return err | ||
digestMismatch, hasMissingPkgs, errCount, errStatus := runStatusAll(ctx, out, p, sm) |
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.
Maybe s/errStatus/err?
cmd/dep/status.go
Outdated
func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceManager) (bool, bool, error) { | ||
var digestMismatch, hasMissingPkgs bool | ||
|
||
func runStatusAll(ctx *dep.Ctx, out outputter, p *dep.Project, sm gps.SourceManager) (digestMismatch bool, hasMissingPkgs bool, errCount int, errStatus 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.
s/errStatus/err here too?
48be3da
to
a69c951
Compare
@jmank88 thanks for spending so much time on this 😊 |
out.BasicFooter() | ||
|
||
return digestMismatch, hasMissingPkgs, nil | ||
return false, false, errCount, 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.
Can this be false, false, 0, nil
?
Everything else looks good!
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.
err... no, I don't think so. That's the only place where we are counting and returning error, for partial results.
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.
Whoops thought I was looking at the final return.
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.
approving, though there are some nits - please do fix those before merging 😄
cmd/dep/status.go
Outdated
@@ -42,6 +43,17 @@ print an extended status output for each dependency of the project. | |||
Status returns exit code zero if all dependencies are in a "good state". | |||
` | |||
|
|||
const ( | |||
shortRev = iota | |||
longRev = iota |
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.
nit: don't need second iota
cmd/dep/status.go
Outdated
@@ -42,6 +43,17 @@ print an extended status output for each dependency of the project. | |||
Status returns exit code zero if all dependencies are in a "good state". | |||
` | |||
|
|||
const ( | |||
shortRev = iota |
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.
prefer to include a type on iota assignments, for clarity/less magic
cmd/dep/status.go
Outdated
case *dotOutput: | ||
ptr, err := sm.ListPackages(proj.Ident(), proj.Version()) | ||
go func(proj gps.LockedProject) { | ||
defer wg.Done() |
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 don't see multiple returns in this func, so no real need for the defer
- just put it at the bottom.
cmd/dep/status.go
Outdated
bs.Children = prm.FlattenFn(paths.IsStandardImportPath) | ||
} | ||
|
||
// Split apart the version from the lock into its constituent parts |
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.
nit: complete sentences (that have periods) are preferred, so add a period - yes, i realize this is my original comment 😄
This change adds concurrent BasicStatus creation, maintaining the order of status output.
Adds options to specify shortRev or longRev to getConsolidatedLatest().
Return explicit values instead of variables in runStatusAll().
a69c951
to
8d45be7
Compare
8d45be7
to
17bb4d6
Compare
This change adds concurrent BasicStatus creation, maintaining the order
of status output.
What does this do / why do we need it?
It reduces the time taken by
status
.Before:
After:
What should your reviewer look out for in this PR?
Correctness of concurrency code.
Do you need help or clarification on anything?
Some help with the detected race condition.Which issue(s) does this PR fix?
Related to #1008 , making status more verbose on failures.