diff options
author | Andrii Zavorotnii <andrii.zavorotnii@gmail.com> | 2020-08-28 08:43:21 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-29 00:43:21 +0900 |
commit | 862b95943f99f3b40e317a79d41c27ac4b742011 (patch) | |
tree | 8ddf5deb99c4466eed725e130060083b29558e55 /sqlite3_go18_test.go | |
parent | Use go-pointer instead of uintptr hacks. (#814) (diff) | |
download | golite-862b95943f99f3b40e317a79d41c27ac4b742011.tar.gz golite-862b95943f99f3b40e317a79d41c27ac4b742011.tar.xz |
Fix "cannot start a transaction within a transaction" issue (#764) (#765)
* Fix "cannot start a transaction within a transaction" issue
[why]
If db.BeginTx(ctx, nil) context is cancelled too fast, "BEGIN" statement can be
completed inside DB, but we still try to cancel it with sqlite3_interrupt.
In such case we get context.Cancelled or context.DeadlineExceeded from exec(),
but operation really completed. Connection returned into pool, and returns "cannot
start a transaction within a transaction" error for next db.BeginTx() call.
[how]
Handle status code returned from cancelled operation.
[testing]
Added unit-test which reproduces issue.
* Reduce TestQueryRowContextCancelParallel concurrency
[why]
Tests times out in travis-ci when run with -race option.
Diffstat (limited to 'sqlite3_go18_test.go')
-rw-r--r-- | sqlite3_go18_test.go | 40 |
1 files changed, 39 insertions, 1 deletions
diff --git a/sqlite3_go18_test.go b/sqlite3_go18_test.go index cfc89b0..5ee3d81 100644 --- a/sqlite3_go18_test.go +++ b/sqlite3_go18_test.go @@ -136,6 +136,44 @@ func TestShortTimeout(t *testing.T) { } } +func TestExecContextCancel(t *testing.T) { + srcTempFilename := TempFilename(t) + defer os.Remove(srcTempFilename) + + db, err := sql.Open("sqlite3", srcTempFilename) + if err != nil { + t.Fatal(err) + } + + defer db.Close() + + ts := time.Now() + initDatabase(t, db, 1000) + spent := time.Since(ts) + if spent < 100*time.Millisecond { + t.Skip("test will be too racy, as ExecContext below will be too fast.") + } + + // expected to be extremely slow query + q := ` +INSERT INTO test_table (key1, key_id, key2, key3, key4, key5, key6, data) +SELECT t1.key1 || t2.key1, t1.key_id || t2.key_id, t1.key2 || t2.key2, t1.key3 || t2.key3, t1.key4 || t2.key4, t1.key5 || t2.key5, t1.key6 || t2.key6, t1.data || t2.data +FROM test_table t1 LEFT OUTER JOIN test_table t2` + // expect query above take ~ same time as setup above + ctx, cancel := context.WithTimeout(context.Background(), spent/2) + defer cancel() + ts = time.Now() + r, err := db.ExecContext(ctx, q) + // racy check + if r != nil { + n, err := r.RowsAffected() + t.Log(n, err, time.Since(ts)) + } + if err != context.DeadlineExceeded { + t.Fatal(err, ctx.Err()) + } +} + func TestQueryRowContextCancel(t *testing.T) { srcTempFilename := TempFilename(t) defer os.Remove(srcTempFilename) @@ -191,7 +229,7 @@ func TestQueryRowContextCancelParallel(t *testing.T) { testCtx, cancel := context.WithCancel(context.Background()) defer cancel() - for i := 0; i < 50; i++ { + for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() |