Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

plumb context.Context everywhere; limit concurrent sub-processes with DEPMAXSUBPROCS #1695

Closed
wants to merge 1 commit into from

Conversation

jmank88
Copy link
Collaborator

@jmank88 jmank88 commented Feb 17, 2018

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 var DEPMAXSUBPROCS is set (positive integer), a semaphore is attached to the context which is picked up and aquired/released from inside cmd.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 to supervisor and using from do() (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 our gps.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 for supervisor. Perhaps SourceManagerConfig could have a context param (optional?), but this is a non-traditional use of Context which doesn't affect our cmd 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

  • Document
  • Changelog
  • Tests

I pre-apologize for sending everyone to rebase hell.


// 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) {
Copy link
Collaborator Author

@jmank88 jmank88 Feb 17, 2018

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.

@jmank88
Copy link
Collaborator Author

jmank88 commented Feb 21, 2018

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 SourceManagerConfig.

@danielbprice
Copy link

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.

@sdboyer sdboyer self-assigned this Jun 5, 2018
@sdboyer
Copy link
Member

sdboyer commented Jun 5, 2018

i'm picking this up, as part of the performance blitz.

@sdboyer sdboyer closed this Jun 5, 2018
@sdboyer sdboyer reopened this Jun 5, 2018
@sdboyer
Copy link
Member

sdboyer commented Jul 4, 2018

Gonna defer this one again for a bit - i think the need is less pressing than some other things. (sorry, ARM folks)

@jmank88
Copy link
Collaborator Author

jmank88 commented Mar 12, 2019

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
}
Copy link
Collaborator

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 ?

Copy link
Collaborator Author

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 {
Copy link
Collaborator

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?

Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

@kevinburke
Copy link
Collaborator

Is it not possible to add this behavior to dep.Ctx, which seems to already exist in a bunch of places?

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.

@jmank88
Copy link
Collaborator Author

jmank88 commented Mar 12, 2019

Is it not possible to add this behavior to dep.Ctx, which seems to already exist in a bunch of places?

dep.Ctx holds high-level user configuration options. It only exists in a few high level functions. I would be in favor of renaming it to something like Config.

@mvdan
Copy link
Member

mvdan commented Sep 4, 2020

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!

@mvdan mvdan closed this Sep 4, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants