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
proposal: x/sync/errgroup: allow manual management of cancel #54079
Comments
CC @bcmills |
The errgroup context is provided as a convenience. If it's not convenient, isn't it possible to create the context separate from the errgroup? It does't seem like errgroup needs to change. That is, use context.WithCancel and then call that cancel func instead of errgroup.Cancel in the rewrite. |
This proposal has been added to the active column of the proposals project |
That is true even with |
You are right, it is possible indeed.
That's true. I'd like to rephrase what I said in the original post and give another example to better illustrate my point. My original post and the title was a bit misleading as to what I wanted to say, I hope to make it clearer this time. Sorry. While it is true that one could use a different context, I don't think it is a better solution than my proposed one. For example, currently, this is how this could be achieved, as proposed by @rsc, if I understood it correctly: ctx, cancel := context.WithCancel(context.Background())
defer cancel()
errGroup := errgroup.Group{}
tx, _ := db.BeginTx(ctx, &sql.TxOptions{})
errGroup.Go(func() error {
var err error
defer func() {
if err != nil {
cancel()
}
}()
_, err = tx.ExecContext(ctx, "exec one")
return err
})
errGroup.Go(func() error {
var err error
defer func() {
if err != nil {
cancel()
}
}()
_, err = tx.ExecContext(ctx, "exec two")
return err
})
errGroup.Go(func() error {
var err error
defer func() {
if err != nil {
cancel()
}
}()
_, err = tx.ExecContext(ctx, "exec three")
return err
})
if err := errGroup.Wait(); err != nil {
tx.Rollback()
return
}
tx.Commit() The context would only be canceled in case of an error, because the errorgroup is not using it, and the commit would work just fine. But, imo, this looks too repetitive. errGroup, ctx := errgroup.WithContext(context.Background())
defer errGroup.Cancel()
tx, _ := db.BeginTx(ctx, &sql.TxOptions{})
errGroup.Go(func() error {
_, err := tx.ExecContext(ctx, "exec one")
return err
})
errGroup.Go(func() error {
_, err := tx.ExecContext(ctx, "exec two")
return err
})
errGroup.Go(func() error {
_, err := tx.ExecContext(ctx, "exec three")
return err
})
if err := errGroup.WaitNoCancel(); err != nil {
tx.Rollback()
return
}
tx.Commit() This is cleaner and more convinient, imo. The method And it doesn't matter that the method Up to this point, this is all doable with the current implementation of the errgroup package. |
What is the difference between your proposed approach and this one? txCtx, cancel := context.WithCancel(context.Background())
defer cancel()
tx, _ := db.BeginTx(txCtx, &sql.TxOptions{})
g, ctx := errgroup.WithContext(txCtx)
g.Go(func() error {
_, err := tx.ExecContext(ctx, "exec one")
return err
})
g.Go(func() error {
_, err := tx.ExecContext(ctx, "exec two")
return err
})
g.Go(func() error {
_, err := tx.ExecContext(ctx, "exec three")
return err
})
if err := g.Wait(); err != nil {
tx.Rollback()
return
}
tx.Commit() |
I didn't think of that approach. That's a good point. Thanks for the feedback! |
Based on the discussion above, this proposal seems like a likely decline. |
No change in consensus, so declined. |
I ran into an issue while using errgroup and database transactions.
This is similar to what I tried to do:
This code works just fine in case any
Exec
returns an error. But it will never work if allExec
calls succeed, reason beingtx.Commit
will always fail becausectx
is canceled byerrGroup.Wait
.However, if
Wait
did not cancel ctx, this code would work as expected.My proposal is the follow: add two more methods to
errgroup.Group
:Cancel
andWaitNoCancel
(names are just a suggestion and can be changed).This is how they'd look like:
This way, the code I wanted to write would look something like this:
This time,
tx.Commit
would work just fine, because it's ctx would not be canceled byerrGroup.WaitNoCancel
.With the current implementation of
errgroup
, the only way to make this work is to make a Group with no cancel, or to use a different ctx in the transaction. But I don't think this is ideal becauseGroup.Go
does not check if the ctx was canceled before callingf
, thus all queries/execs in the transaction would be executed, even if the Group has already failed.Passing the Group's ctx to the transaction prevents this from happening.
The text was updated successfully, but these errors were encountered: