-
Notifications
You must be signed in to change notification settings - Fork 1k
plumb context.Context everywhere; limit concurrent sub-processes with DEPMAXSUBPROCS #1695
Conversation
|
||
// CtxWithCmdLimit returns a copy ctx with a semaphore value limiting the number | ||
// of concurrent `cmd`s. Returns an error if n is not positive. | ||
func CtxWithCmdLimit(ctx context.Context, n int) (context.Context, 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.
This is available for anyone using package gps
, dep
, or cmd/dep
as a library to modify their Context
.
We should probably think about whether we want this limit to apply to each source manager, or each function or method invocation. Right now the caller decides, by sharing contexts as much or as little as they'd like. If we only want to limit per source manager, then this PR can become much simpler and less consequential, with fewer exported API changes, and no external context (would still be simplest to use context internally for now, but could change) - just a new optional field on |
I'm still hoping to see this change hit master. It has worked well for me in my testing. Simultaneously not having this has been causing my coworkers pain. |
i'm picking this up, as part of the performance blitz. |
Gonna defer this one again for a bit - i think the need is less pressing than some other things. (sorry, ARM folks) |
Rebased |
@@ -24,7 +25,7 @@ func (a Analyzer) HasDepMetadata(path string) bool { | |||
|
|||
// DeriveManifestAndLock reads and returns the manifest at path/ManifestName or nil if one is not found. | |||
// The Lock is always nil for now. | |||
func (a Analyzer) DeriveManifestAndLock(path string, n gps.ProjectRoot) (gps.Manifest, gps.Lock, error) { | |||
func (a Analyzer) DeriveManifestAndLock(ctx context.Context, path string, n gps.ProjectRoot) (gps.Manifest, gps.Lock, error) { | |||
if !a.HasDepMetadata(path) { | |||
return nil, nil, 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 we do f.SetDeadline(ctx.Deadline()) 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.
Yes? But I haven't addressed timeouts or cancellation in this PR, so maybe in a follow up covering those.
@@ -58,18 +59,18 @@ func (cmd *checkCommand) Register(fs *flag.FlagSet) { | |||
fs.BoolVar(&cmd.quiet, "q", false, "Suppress non-error output") | |||
} | |||
|
|||
func (cmd *checkCommand) Run(ctx *dep.Ctx, args []string) error { | |||
logger := ctx.Out | |||
func (cmd *checkCommand) Run(ctx context.Context, depCtx *dep.Ctx, args []string) 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.
What's the difference between ctx
and depCtx
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.
or I guess, they have different API's, but why is the depCtx
insufficient here? it doesn't seem like we are using ctx
at all.
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 checkCommand
does not use the context.Context
, but it must implement the command
interface.
Is it not possible to add this behavior to One way to test would be to have a function hook before whatever logic is gating new subprocess creation. By default the hook is nil. In the test, edit the function hook to be non-nil and record whatever information you need. |
|
Dep was officially deprecated earlier this year, and the proposal to archive this repository was accepted. As such, I'm closing outstanding issues before archiving the repository. For any further comments, please use the proposal thread on the Go issue tracker. Thanks! |
What does this do / why do we need it?
This PR (1) plumbs
context.Context
everywhere as a prerequisite for (2) limiting concurrent sub-processes. When the env varDEPMAXSUBPROCS
is set (positive integer), a semaphore is attached to the context which is picked up and aquired/released from insidecmd.CombinedOutput
.I went back and forth a bit about whether this is an appropriate use of
context.Context
, but we wanted to plumb it around more anyways (#423), so it won't be wasted even if we go another direction. Other considered solutions for passing the semaphore around: global variable (yuck); attaching tosupervisor
and using fromdo()
(too high level) or passing it along to each vcs struct (complicated/messy).This limits all of our get/fetch/update vcs calls, but not direct use of
masterminds/vcs.Repo
methods, since they don't go through ourgps.commandContext()
. I don't see a clean way to limit those without going too high level and also limiting method calls which e.g. only look up a struct field.I got nearly all of the
context.TODO()
s, except for:svnRepo.CommitInfo()
: Overrides a method who's signature we don't control.gps.NewSourceManager
: Creates a context forsupervisor
. PerhapsSourceManagerConfig
could have a context param (optional?), but this is a non-traditional use ofContext
which doesn't affect ourcmd
semaphore anyhow.Do you need help or clarification on anything?
Any bright ideas about how to test this? Measuring the timing of sets of sleep commands under different limits seems sloppy.
Is
DEPMAXSUBPROCS
an appropriate name?Where should I document this?
Which issue(s) does this PR fix?
#423, #1689
TODO
I pre-apologize for sending everyone to rebase hell.