Chromium Code Reviews| Index: impl/memory/context.go |
| diff --git a/impl/memory/context.go b/impl/memory/context.go |
| index bb784ec540c1603c2194a1f5da2f36e42106285a..ea761bd538b0c8b70e80bbd0ed121ad5a43fc1df 100644 |
| --- a/impl/memory/context.go |
| +++ b/impl/memory/context.go |
| @@ -135,28 +135,52 @@ var memContextKey memContextKeyType |
| // test-access API for TaskQueue better (instead of trying to reconstitute the |
| // state of the task queue from a bunch of datastore accesses). |
| func (d *dsImpl) RunInTransaction(f func(context.Context) error, o *ds.TransactionOptions) error { |
| - curMC := cur(d.c) |
| + errNope := errors.New("nope") |
| - txnMC := curMC.mkTxn(o) |
| + // Keep in separate function for defers. |
| + loopBody := func(applyForReal bool) error { |
| + curMC := cur(d.c) |
| + |
| + txnMC := curMC.mkTxn(o) |
| + |
| + defer func() { |
| + txnMC.Lock() |
| + defer txnMC.Unlock() |
| + |
| + txnMC.endTxn() |
| + }() |
| + |
| + if err := f(context.WithValue(d.c, memContextKey, txnMC)); err != nil { |
| + return err |
| + } |
| - defer func() { |
| txnMC.Lock() |
| defer txnMC.Unlock() |
| - txnMC.endTxn() |
| - }() |
| + if !applyForReal { |
| + // Be defensive and expect that f may return ErrConcurrentTransaction. So |
| + // use some other error to signal that we skipped commit. |
| + return errNope |
| + } |
| - if err := f(context.WithValue(d.c, memContextKey, txnMC)); err != nil { |
| - return err |
| + if curMC.canApplyTxn(txnMC) { |
| + curMC.applyTxn(d.c, txnMC) |
| + } else { |
| + return ds.ErrConcurrentTransaction |
| + } |
| + return nil |
| } |
| - txnMC.Lock() |
| - defer txnMC.Unlock() |
| - |
| - if curMC.canApplyTxn(txnMC) { |
| - curMC.applyTxn(d.c, txnMC) |
| - } else { |
| - return ds.ErrConcurrentTransaction |
| + // From GAE docs for TransactionOptions: "If omitted, it defaults to 3." |
| + maxAttempts := 3 |
| + if o != nil && o.Attempts != 0 { |
| + maxAttempts = o.Attempts |
| + } |
| + for attempt := 0; attempt < maxAttempts; attempt++ { |
| + err := loopBody(attempt >= d.txnFakeRetry) |
| + if err != errNope { |
| + return err |
| + } |
| } |
| - return nil |
| + return ds.ErrConcurrentTransaction |
|
iannucci
2015/09/11 22:31:11
Can we return a different error when the retries d
Vadim Sh.
2015/09/14 01:07:20
Real GAE returns ErrConcurrentTransaction: https:/
|
| } |