diff options
author | mattn <mattn.jp@gmail.com> | 2019-11-19 01:19:53 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-19 01:19:53 +0900 |
commit | 590d44c02bca83987d23f6eab75e6d0ddf95f644 (patch) | |
tree | 2d1979323f14971f927380c6a687a523aa033486 /sqlite3_go18_test.go | |
parent | Merge pull request #760 from mattn/sqlite-amalgamation-3300100 (diff) | |
parent | Fix context cancellation racy handling (diff) | |
download | golite-590d44c02bca83987d23f6eab75e6d0ddf95f644.tar.gz golite-590d44c02bca83987d23f6eab75e6d0ddf95f644.tar.xz |
Merge pull request #744 from azavorotnii/ctx_cancel
Fix context cancellation racy handling
Diffstat (limited to 'sqlite3_go18_test.go')
-rw-r--r-- | sqlite3_go18_test.go | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/sqlite3_go18_test.go b/sqlite3_go18_test.go index f295bb6..cfc89b0 100644 --- a/sqlite3_go18_test.go +++ b/sqlite3_go18_test.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "math/rand" "os" + "sync" "testing" "time" ) @@ -135,6 +136,93 @@ func TestShortTimeout(t *testing.T) { } } +func TestQueryRowContextCancel(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() + initDatabase(t, db, 100) + + const query = `SELECT key_id FROM test_table ORDER BY key2 ASC` + var keyID string + unexpectedErrors := make(map[string]int) + for i := 0; i < 10000; i++ { + ctx, cancel := context.WithCancel(context.Background()) + row := db.QueryRowContext(ctx, query) + + cancel() + // it is fine to get "nil" as context cancellation can be handled with delay + if err := row.Scan(&keyID); err != nil && err != context.Canceled { + if err.Error() == "sql: Rows are closed" { + // see https://github.com/golang/go/issues/24431 + // fixed in 1.11.1 to properly return context error + continue + } + unexpectedErrors[err.Error()]++ + } + } + for errText, count := range unexpectedErrors { + t.Error(errText, count) + } +} + +func TestQueryRowContextCancelParallel(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) + + const query = `SELECT key_id FROM test_table ORDER BY key2 ASC` + wg := sync.WaitGroup{} + defer wg.Wait() + + testCtx, cancel := context.WithCancel(context.Background()) + defer cancel() + + for i := 0; i < 50; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + var keyID string + for { + select { + case <-testCtx.Done(): + return + default: + } + ctx, cancel := context.WithCancel(context.Background()) + row := db.QueryRowContext(ctx, query) + + cancel() + _ = row.Scan(&keyID) // see TestQueryRowContextCancel + } + }() + } + + var keyID string + for i := 0; i < 10000; i++ { + // note that testCtx is not cancelled during query execution + row := db.QueryRowContext(testCtx, query) + + if err := row.Scan(&keyID); err != nil { + t.Fatal(i, err) + } + } +} + func TestExecCancel(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { |