Skip to content
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

Enhancement: allow parallel stop, pause, unpause #24761

Merged
merged 2 commits into from
Sep 13, 2016

Conversation

WeiZhang555
Copy link
Contributor

@WeiZhang555 WeiZhang555 commented Jul 18, 2016

Fixes #24724

Stop multiple containers in parallel to speed up stop process, allow
maximum 50 parallel stops.

Signed-off-by: Zhang Wei zhangwei555@huawei.com

@WeiZhang555
Copy link
Contributor Author

defer waitAll.Done()
defer atomic.AddInt32(&count, -1)
if err := dockerCli.Client().ContainerStop(ctx, toStop, &timeout); err != nil {
errs = append(errs, err.Error())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a mutex for errs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I almost forget my PR. You're right, thanks @AkihiroSuda 😄

@WeiZhang555
Copy link
Contributor Author

Append a commit from @abhinavdahiya, thanks again for his help! 😄


var maxParallel int32 = 1000
//A buffered channel can be used like a semaphore, for instance to limit throughput. https://golang.org/doc/effective_go.html#channels
var sem = make(chan int, maxParallel)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: chan struct{}

@WeiZhang555
Copy link
Contributor Author

@tonistiigi I've made some updates since your last comments, what do you think of this?

@WeiZhang555
Copy link
Contributor Author

@tonistiigi Updated as per your suggestion, PTAL. Thanks 😄

@WeiZhang555
Copy link
Contributor Author

What's current status? ping @tonistiigi @vdemeester @cpuguy83

@tonistiigi
Copy link
Member

Design SGTM @vdemeester WDYT?

@vdemeester
Copy link
Member

Design LGTM too 👼

waitAll.Done()
}()

var maxParallel int32 = 50
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: const, also move to the beginning of the function

@WeiZhang555
Copy link
Contributor Author

@tonistiigi Updated, PTAL.

@vdemeester
Copy link
Member

LGTM 🐸

@WeiZhang555
Copy link
Contributor Author

ping @tonistiigi

@vdemeester
Copy link
Member

/cc @dnephin

output[container] <- err
} else {
output[container] <- nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason to check err != nil here. You're also doing the same action in both branches, just send directly to the channel:

err := ...
output[container] <- err

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct 👍

@dnephin
Copy link
Member

dnephin commented Sep 9, 2016

I like the idea, I left some comments on the code.

I also wonder if it would make sense to make this more generic. I suspect we could re-use this for other operations as well (pause/unpause comes to mind). We did something similar in Compose.

Maybe something like:

const maxParallel int32 = 50

type operation func(ctx context.Context, string id)

func ParallelOperation(ctx context.Context, args []string, op operation) ([]string, []error) {
    var waitAll sync.WaitGroup
    var errs []error
    var result []string
    output := map[string]chan error{}
    var sem = make(chan struct{}, maxParallel)

    waitAll.Add(len(args))
    for _, arg := range args {
        sem <- struct{}{} // Wait for active queue sem to drain.
        go func(arg string) {
            defer waitAll.Done()
            output[arg] <- op(ctx, arg)
            <- sem
        }(arg)
    waitAll.Wait()
    for _, arg := range args {
        err = <- output[arg]
        if err != nil {
            errs = append(errs, err)
        } else {
            result = append(result, arg)
        }
    }
    return result, errs
}

(the timeout can be passed as a closure)

@AkihiroSuda
Copy link
Member

nit: +1(non-binding) for @dnephin 's suggestion, and it'd be better if maxParallel is dynamically determined using the value of runtime.NumCPU() 😄

@WeiZhang555
Copy link
Contributor Author

I'm also +1 on @dnephin 's non-binding suggestion 😄 . I had the plan of add parallel operations on other command, but thought it would be better that we finish this first as an example implementation so that we can do the last quickly.

I will make some modifications first as per @dnephin 's suggestions.

@WeiZhang555 WeiZhang555 force-pushed the parallel-stop branch 2 times, most recently from 89b3505 to 32f1b7c Compare September 12, 2016 09:23
@WeiZhang555
Copy link
Contributor Author

WeiZhang555 commented Sep 12, 2016

Hi all, I make a generic function as @dnephin suggested. Also add parallel operation support for pause/unpause as example although pause/unpause is usually fast and don't profit much from it.

I will add modify more time-consuming command such as start/kill/... maybe in different PR 😄

PTAL @ALL

@@ -34,8 +34,11 @@ func runPause(dockerCli *command.DockerCli, opts *pauseOptions) error {
ctx := context.Background()

var errs []string
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, id string) error {
return dockerCli.Client().ContainerPause(ctx, id)
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: I think you can just pass dockerCli.Client().ContainerPause directly as the last argument, since it already has the right signature

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right! 👍

@dnephin
Copy link
Member

dnephin commented Sep 12, 2016

LGTM

@tonistiigi tonistiigi added the status/failing-ci Indicates that the PR in its current state fails the test suite label Sep 12, 2016
Stop multiple containers in parallel to speed up stop process, allow
maximum 50 parallel stops.

Signed-off-by: Abhinav Dahiya <abhinavdtu2012@gmail.com>
Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
Support parallel pause/unpause

Signed-off-by: Zhang Wei <zhangwei555@huawei.com>
@WeiZhang555
Copy link
Contributor Author

Parallel start bring a failed test case, remove that first. Let's focus on parallel stop/pause/unpause first.

@thaJeztah thaJeztah removed the status/failing-ci Indicates that the PR in its current state fails the test suite label Sep 13, 2016
@vdemeester
Copy link
Member

LGTM 🐸

@thaJeztah thaJeztah changed the title Enhancement: allow parallel stop Enhancement: allow parallel stop, pause, unpause Sep 13, 2016
@thaJeztah
Copy link
Member

@dnephin latest version LGTY?

@dnephin
Copy link
Member

dnephin commented Sep 13, 2016

LGTM

@dnephin dnephin merged commit c2decbe into moby:master Sep 13, 2016
@thaJeztah
Copy link
Member

Thanks @WeiZhang555!

@WeiZhang555 WeiZhang555 deleted the parallel-stop branch September 14, 2016 02:11
dnephin added a commit to dnephin/docker that referenced this pull request Apr 17, 2017
Enhancement: allow parallel stop, pause, unpause
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants