aboutsummaryrefslogtreecommitdiff
path: root/sqlite3_go113_test.go
diff options
context:
space:
mode:
authorAndrii Zavorotnii <andrii.zavorotnii@gmail.com>2020-08-28 08:43:21 -0700
committerGitHub <noreply@github.com>2020-08-29 00:43:21 +0900
commit862b95943f99f3b40e317a79d41c27ac4b742011 (patch)
tree8ddf5deb99c4466eed725e130060083b29558e55 /sqlite3_go113_test.go
parentUse go-pointer instead of uintptr hacks. (#814) (diff)
downloadgolite-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_go113_test.go')
-rw-r--r--sqlite3_go113_test.go74
1 files changed, 74 insertions, 0 deletions
diff --git a/sqlite3_go113_test.go b/sqlite3_go113_test.go
new file mode 100644
index 0000000..74036f8
--- /dev/null
+++ b/sqlite3_go113_test.go
@@ -0,0 +1,74 @@
+// Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+// +build go1.13,cgo
+
+package sqlite3
+
+import (
+ "context"
+ "database/sql"
+ "database/sql/driver"
+ "os"
+ "testing"
+)
+
+func TestBeginTxCancel(t *testing.T) {
+ srcTempFilename := TempFilename(t)
+ defer os.Remove(srcTempFilename)
+
+ db, err := sql.Open("sqlite3", srcTempFilename)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ db.SetMaxOpenConns(10)
+ db.SetMaxIdleConns(5)
+
+ defer db.Close()
+ initDatabase(t, db, 100)
+
+ // create several go-routines to expose racy issue
+ for i := 0; i < 1000; i++ {
+ func() {
+ ctx, cancel := context.WithCancel(context.Background())
+ conn, err := db.Conn(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ if err := conn.Close(); err != nil {
+ t.Error(err)
+ }
+ }()
+
+ err = conn.Raw(func(driverConn interface{}) error {
+ d, ok := driverConn.(driver.ConnBeginTx)
+ if !ok {
+ t.Fatal("unexpected: wrong type")
+ }
+
+ go cancel() // make it cancel concurrently with exec("BEGIN");
+ tx, err := d.BeginTx(ctx, driver.TxOptions{})
+ switch err {
+ case nil:
+ switch err := tx.Rollback(); err {
+ case nil, sql.ErrTxDone:
+ default:
+ return err
+ }
+ case context.Canceled:
+ default:
+ // must not fail with "cannot start a transaction within a transaction"
+ return err
+ }
+ return nil
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ }()
+ }
+}