Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package sql | 5 package sql |
6 | 6 |
7 import ( | 7 import ( |
8 "database/sql/driver" | 8 "database/sql/driver" |
9 "errors" | 9 "errors" |
10 "fmt" | 10 "fmt" |
11 "math/rand" | |
11 "reflect" | 12 "reflect" |
12 "runtime" | 13 "runtime" |
13 "strings" | 14 "strings" |
14 "sync" | 15 "sync" |
15 "testing" | 16 "testing" |
16 "time" | 17 "time" |
17 ) | 18 ) |
18 | 19 |
19 func init() { | 20 func init() { |
20 type dbConn struct { | 21 type dbConn struct { |
21 db *DB | 22 db *DB |
22 c *driverConn | 23 c *driverConn |
23 } | 24 } |
24 freedFrom := make(map[dbConn]string) | 25 freedFrom := make(map[dbConn]string) |
25 putConnHook = func(db *DB, c *driverConn) { | 26 putConnHook = func(db *DB, c *driverConn) { |
26 » » for _, oc := range db.freeConn { | 27 » » if c.listElem != nil { |
27 » » » if oc == c { | 28 » » » // print before panic, as panic may get lost due to conf licting panic |
28 » » » » // print before panic, as panic may get lost due to conflicting panic | 29 » » » // (all goroutines asleep) elsewhere, since we might not unlock |
29 » » » » // (all goroutines asleep) elsewhere, since we m ight not unlock | 30 » » » // the mutex in freeConn here. |
30 » » » » // the mutex in freeConn here. | 31 » » » println("double free of conn. conflicts are:\nA) " + fre edFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) |
31 » » » » println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) | 32 » » » panic("double free of conn.") |
32 » » » » panic("double free of conn.") | |
33 » » » } | |
34 } | 33 } |
35 freedFrom[dbConn{db, c}] = stack() | 34 freedFrom[dbConn{db, c}] = stack() |
36 } | 35 } |
37 } | 36 } |
38 | 37 |
39 const fakeDBName = "foo" | 38 const fakeDBName = "foo" |
40 | 39 |
41 var chrisBirthday = time.Unix(123456789, 0) | 40 var chrisBirthday = time.Unix(123456789, 0) |
42 | 41 |
43 func newTestDB(t testing.TB, name string) *DB { | 42 func newTestDB(t testing.TB, name string) *DB { |
(...skipping 29 matching lines...) Expand all Loading... | |
73 if e := recover(); e != nil { | 72 if e := recover(); e != nil { |
74 fmt.Printf("Panic: %v\n", e) | 73 fmt.Printf("Panic: %v\n", e) |
75 panic(e) | 74 panic(e) |
76 } | 75 } |
77 defer setHookpostCloseConn(nil) | 76 defer setHookpostCloseConn(nil) |
78 setHookpostCloseConn(func(_ *fakeConn, err error) { | 77 setHookpostCloseConn(func(_ *fakeConn, err error) { |
79 if err != nil { | 78 if err != nil { |
80 t.Errorf("Error closing fakeConn: %v", err) | 79 t.Errorf("Error closing fakeConn: %v", err) |
81 } | 80 } |
82 }) | 81 }) |
83 » for i, dc := range db.freeConn { | 82 » for node, i := db.freeConn.Front(), 0; node != nil; node, i = node.Next( ), i+1 { |
83 » » dc := node.Value.(*driverConn) | |
84 if n := len(dc.openStmt); n > 0 { | 84 if n := len(dc.openStmt); n > 0 { |
85 // Just a sanity check. This is legal in | 85 // Just a sanity check. This is legal in |
86 // general, but if we make the tests clean up | 86 // general, but if we make the tests clean up |
87 // their statements first, then we can safely | 87 // their statements first, then we can safely |
88 // verify this is always zero here, and any | 88 // verify this is always zero here, and any |
89 // other value is a leak. | 89 // other value is a leak. |
90 » » » t.Errorf("while closing db, freeConn %d/%d had %d open s tmts; want 0", i, len(db.freeConn), n) | 90 » » » t.Errorf("while closing db, freeConn %d/%d had %d open s tmts; want 0", i, db.freeConn.Len(), n) |
91 } | 91 } |
92 } | 92 } |
93 err := db.Close() | 93 err := db.Close() |
94 if err != nil { | 94 if err != nil { |
95 t.Fatalf("error closing DB: %v", err) | 95 t.Fatalf("error closing DB: %v", err) |
96 } | 96 } |
97 } | 97 } |
98 | 98 |
99 // numPrepares assumes that db has exactly 1 idle conn and returns | 99 // numPrepares assumes that db has exactly 1 idle conn and returns |
100 // its count of calls to Prepare | 100 // its count of calls to Prepare |
101 func numPrepares(t *testing.T, db *DB) int { | 101 func numPrepares(t *testing.T, db *DB) int { |
102 » if n := len(db.freeConn); n != 1 { | 102 » if n := db.freeConn.Len(); n != 1 { |
103 t.Fatalf("free conns = %d; want 1", n) | 103 t.Fatalf("free conns = %d; want 1", n) |
104 } | 104 } |
105 » return db.freeConn[0].ci.(*fakeConn).numPrepare | 105 » return (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn).numPrepa re |
106 } | 106 } |
107 | 107 |
108 func (db *DB) numDeps() int { | 108 func (db *DB) numDeps() int { |
109 db.mu.Lock() | 109 db.mu.Lock() |
110 defer db.mu.Unlock() | 110 defer db.mu.Unlock() |
111 return len(db.dep) | 111 return len(db.dep) |
112 } | 112 } |
113 | 113 |
114 // Dependencies are closed via a goroutine, so this polls waiting for | 114 // Dependencies are closed via a goroutine, so this polls waiting for |
115 // numDeps to fall to want, waiting up to d. | 115 // numDeps to fall to want, waiting up to d. |
116 func (db *DB) numDepsPollUntil(want int, d time.Duration) int { | 116 func (db *DB) numDepsPollUntil(want int, d time.Duration) int { |
117 deadline := time.Now().Add(d) | 117 deadline := time.Now().Add(d) |
118 for { | 118 for { |
119 n := db.numDeps() | 119 n := db.numDeps() |
120 if n <= want || time.Now().After(deadline) { | 120 if n <= want || time.Now().After(deadline) { |
121 return n | 121 return n |
122 } | 122 } |
123 time.Sleep(50 * time.Millisecond) | 123 time.Sleep(50 * time.Millisecond) |
124 } | 124 } |
125 } | 125 } |
126 | 126 |
127 func (db *DB) numFreeConns() int { | 127 func (db *DB) numFreeConns() int { |
128 db.mu.Lock() | 128 db.mu.Lock() |
129 defer db.mu.Unlock() | 129 defer db.mu.Unlock() |
130 » return len(db.freeConn) | 130 » return db.freeConn.Len() |
131 } | 131 } |
132 | 132 |
133 func (db *DB) dumpDeps(t *testing.T) { | 133 func (db *DB) dumpDeps(t *testing.T) { |
134 for fc := range db.dep { | 134 for fc := range db.dep { |
135 db.dumpDep(t, 0, fc, map[finalCloser]bool{}) | 135 db.dumpDep(t, 0, fc, map[finalCloser]bool{}) |
136 } | 136 } |
137 } | 137 } |
138 | 138 |
139 func (db *DB) dumpDep(t *testing.T, depth int, dep finalCloser, seen map[finalCl oser]bool) { | 139 func (db *DB) dumpDep(t *testing.T, depth int, dep finalCloser, seen map[finalCl oser]bool) { |
140 seen[dep] = true | 140 seen[dep] = true |
(...skipping 494 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
635 | 635 |
636 func TestQueryRowClosingStmt(t *testing.T) { | 636 func TestQueryRowClosingStmt(t *testing.T) { |
637 db := newTestDB(t, "people") | 637 db := newTestDB(t, "people") |
638 defer closeDB(t, db) | 638 defer closeDB(t, db) |
639 var name string | 639 var name string |
640 var age int | 640 var age int |
641 err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age, &name) | 641 err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age, &name) |
642 if err != nil { | 642 if err != nil { |
643 t.Fatal(err) | 643 t.Fatal(err) |
644 } | 644 } |
645 » if len(db.freeConn) != 1 { | 645 » if db.freeConn.Len() != 1 { |
646 t.Fatalf("expected 1 free conn") | 646 t.Fatalf("expected 1 free conn") |
647 } | 647 } |
648 » fakeConn := db.freeConn[0].ci.(*fakeConn) | 648 » fakeConn := (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn) |
649 if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != clo sed { | 649 if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != clo sed { |
650 t.Errorf("statement close mismatch: made %d, closed %d", made, c losed) | 650 t.Errorf("statement close mismatch: made %d, closed %d", made, c losed) |
651 } | 651 } |
652 } | 652 } |
653 | 653 |
654 type nullTestRow struct { | 654 type nullTestRow struct { |
655 nullParam interface{} | 655 nullParam interface{} |
656 notNullParam interface{} | 656 notNullParam interface{} |
657 scanNullVal interface{} | 657 scanNullVal interface{} |
658 } | 658 } |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
834 | 834 |
835 func TestMaxIdleConns(t *testing.T) { | 835 func TestMaxIdleConns(t *testing.T) { |
836 db := newTestDB(t, "people") | 836 db := newTestDB(t, "people") |
837 defer closeDB(t, db) | 837 defer closeDB(t, db) |
838 | 838 |
839 tx, err := db.Begin() | 839 tx, err := db.Begin() |
840 if err != nil { | 840 if err != nil { |
841 t.Fatal(err) | 841 t.Fatal(err) |
842 } | 842 } |
843 tx.Commit() | 843 tx.Commit() |
844 » if got := len(db.freeConn); got != 1 { | 844 » if got := db.freeConn.Len(); got != 1 { |
845 t.Errorf("freeConns = %d; want 1", got) | 845 t.Errorf("freeConns = %d; want 1", got) |
846 } | 846 } |
847 | 847 |
848 db.SetMaxIdleConns(0) | 848 db.SetMaxIdleConns(0) |
849 | 849 |
850 » if got := len(db.freeConn); got != 0 { | 850 » if got := db.freeConn.Len(); got != 0 { |
851 t.Errorf("freeConns after set to zero = %d; want 0", got) | 851 t.Errorf("freeConns after set to zero = %d; want 0", got) |
852 } | 852 } |
853 | 853 |
854 tx, err = db.Begin() | 854 tx, err = db.Begin() |
855 if err != nil { | 855 if err != nil { |
856 t.Fatal(err) | 856 t.Fatal(err) |
857 } | 857 } |
858 tx.Commit() | 858 tx.Commit() |
859 » if got := len(db.freeConn); got != 0 { | 859 » if got := db.freeConn.Len(); got != 0 { |
860 t.Errorf("freeConns = %d; want 0", got) | 860 t.Errorf("freeConns = %d; want 0", got) |
861 } | 861 } |
862 } | 862 } |
863 | 863 |
864 func TestMaxOpenConns(t *testing.T) { | |
865 if testing.Short() { | |
866 t.Skip("skipping in short mode") | |
867 } | |
868 defer setHookpostCloseConn(nil) | |
869 setHookpostCloseConn(func(_ *fakeConn, err error) { | |
870 if err != nil { | |
871 t.Errorf("Error closing fakeConn: %v", err) | |
872 } | |
873 }) | |
874 | |
875 db := newTestDB(t, "magicquery") | |
876 defer closeDB(t, db) | |
877 | |
878 driver := db.driver.(*fakeDriver) | |
879 | |
880 // Force the number of open connections to 0 so we can get an accurate | |
881 // count for the test | |
882 db.SetMaxIdleConns(0) | |
883 | |
884 if g, w := db.numFreeConns(), 0; g != w { | |
885 t.Errorf("free conns = %d; want %d", g, w) | |
886 } | |
887 | |
888 if n := db.numDepsPollUntil(0, time.Second); n > 0 { | |
889 t.Errorf("number of dependencies = %d; expected 0", n) | |
890 db.dumpDeps(t) | |
891 } | |
892 | |
893 driver.mu.Lock() | |
894 opens0 := driver.openCount | |
895 closes0 := driver.closeCount | |
896 driver.mu.Unlock() | |
897 | |
898 db.SetMaxIdleConns(10) | |
899 db.SetMaxOpenConns(10) | |
900 | |
901 stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?") | |
902 if err != nil { | |
903 t.Fatal(err) | |
904 } | |
905 | |
906 // Start 50 parallel slow queries. | |
907 const ( | |
908 nquery = 50 | |
909 sleepMillis = 25 | |
910 nbatch = 2 | |
911 ) | |
912 var wg sync.WaitGroup | |
913 for batch := 0; batch < nbatch; batch++ { | |
914 for i := 0; i < nquery; i++ { | |
915 wg.Add(1) | |
916 go func() { | |
917 defer wg.Done() | |
918 var op string | |
919 if err := stmt.QueryRow("sleep", sleepMillis).Sc an(&op); err != nil && err != ErrNoRows { | |
920 t.Error(err) | |
921 } | |
922 }() | |
923 } | |
924 // Sleep for twice the expected length of time for the | |
925 // batch of 50 queries above to finish before starting | |
926 // the next round. | |
927 time.Sleep(2 * sleepMillis * time.Millisecond) | |
928 } | |
929 wg.Wait() | |
930 | |
931 if g, w := db.numFreeConns(), 10; g != w { | |
932 t.Errorf("free conns = %d; want %d", g, w) | |
933 } | |
934 | |
935 if n := db.numDepsPollUntil(20, time.Second); n > 20 { | |
936 t.Errorf("number of dependencies = %d; expected <= 20", n) | |
937 db.dumpDeps(t) | |
938 } | |
939 | |
940 driver.mu.Lock() | |
941 opens := driver.openCount - opens0 | |
942 closes := driver.closeCount - closes0 | |
943 driver.mu.Unlock() | |
944 | |
945 if opens > 10 { | |
946 t.Logf("open calls = %d", opens) | |
947 t.Logf("close calls = %d", closes) | |
948 t.Errorf("db connections opened = %d; want <= 10", opens) | |
949 db.dumpDeps(t) | |
950 } | |
951 | |
952 if err := stmt.Close(); err != nil { | |
953 t.Fatal(err) | |
954 } | |
955 | |
956 if g, w := db.numFreeConns(), 10; g != w { | |
957 t.Errorf("free conns = %d; want %d", g, w) | |
958 } | |
959 | |
960 if n := db.numDepsPollUntil(10, time.Second); n > 10 { | |
961 t.Errorf("number of dependencies = %d; expected <= 10", n) | |
962 db.dumpDeps(t) | |
963 } | |
964 | |
965 db.SetMaxOpenConns(5) | |
966 | |
967 if g, w := db.numFreeConns(), 5; g != w { | |
968 t.Errorf("free conns = %d; want %d", g, w) | |
969 } | |
970 | |
971 if n := db.numDepsPollUntil(5, time.Second); n > 5 { | |
972 t.Errorf("number of dependencies = %d; expected 0", n) | |
973 db.dumpDeps(t) | |
974 } | |
975 | |
976 db.SetMaxOpenConns(0) | |
977 | |
978 if g, w := db.numFreeConns(), 5; g != w { | |
979 t.Errorf("free conns = %d; want %d", g, w) | |
980 } | |
981 | |
982 if n := db.numDepsPollUntil(5, time.Second); n > 5 { | |
983 t.Errorf("number of dependencies = %d; expected 0", n) | |
984 db.dumpDeps(t) | |
985 } | |
986 | |
987 db.SetMaxIdleConns(0) | |
988 | |
989 if g, w := db.numFreeConns(), 0; g != w { | |
990 t.Errorf("free conns = %d; want %d", g, w) | |
991 } | |
992 | |
993 if n := db.numDepsPollUntil(0, time.Second); n > 0 { | |
994 t.Errorf("number of dependencies = %d; expected 0", n) | |
995 db.dumpDeps(t) | |
996 } | |
997 } | |
998 | |
864 // golang.org/issue/5323 | 999 // golang.org/issue/5323 |
865 func TestStmtCloseDeps(t *testing.T) { | 1000 func TestStmtCloseDeps(t *testing.T) { |
866 if testing.Short() { | 1001 if testing.Short() { |
867 t.Skip("skipping in short mode") | 1002 t.Skip("skipping in short mode") |
868 } | 1003 } |
869 defer setHookpostCloseConn(nil) | 1004 defer setHookpostCloseConn(nil) |
870 setHookpostCloseConn(func(_ *fakeConn, err error) { | 1005 setHookpostCloseConn(func(_ *fakeConn, err error) { |
871 if err != nil { | 1006 if err != nil { |
872 t.Errorf("Error closing fakeConn: %v", err) | 1007 t.Errorf("Error closing fakeConn: %v", err) |
873 } | 1008 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
919 } | 1054 } |
920 | 1055 |
921 if n := db.numDepsPollUntil(4, time.Second); n > 4 { | 1056 if n := db.numDepsPollUntil(4, time.Second); n > 4 { |
922 t.Errorf("number of dependencies = %d; expected <= 4", n) | 1057 t.Errorf("number of dependencies = %d; expected <= 4", n) |
923 db.dumpDeps(t) | 1058 db.dumpDeps(t) |
924 } | 1059 } |
925 | 1060 |
926 driver.mu.Lock() | 1061 driver.mu.Lock() |
927 opens := driver.openCount - opens0 | 1062 opens := driver.openCount - opens0 |
928 closes := driver.closeCount - closes0 | 1063 closes := driver.closeCount - closes0 |
1064 openDelta := (driver.openCount - driver.closeCount) - openDelta0 | |
929 driver.mu.Unlock() | 1065 driver.mu.Unlock() |
930 openDelta := (driver.openCount - driver.closeCount) - openDelta0 | |
931 | 1066 |
932 if openDelta > 2 { | 1067 if openDelta > 2 { |
933 t.Logf("open calls = %d", opens) | 1068 t.Logf("open calls = %d", opens) |
934 t.Logf("close calls = %d", closes) | 1069 t.Logf("close calls = %d", closes) |
935 t.Logf("open delta = %d", openDelta) | 1070 t.Logf("open delta = %d", openDelta) |
936 t.Errorf("db connections opened = %d; want <= 2", openDelta) | 1071 t.Errorf("db connections opened = %d; want <= 2", openDelta) |
937 db.dumpDeps(t) | 1072 db.dumpDeps(t) |
938 } | 1073 } |
939 | 1074 |
940 if len(stmt.css) > nquery { | 1075 if len(stmt.css) > nquery { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
978 db.dumpDeps(t) | 1113 db.dumpDeps(t) |
979 t.Errorf("DB = %#v", db) | 1114 t.Errorf("DB = %#v", db) |
980 } | 1115 } |
981 }) | 1116 }) |
982 | 1117 |
983 stmt, err := db.Prepare("SELECT|people|name|") | 1118 stmt, err := db.Prepare("SELECT|people|name|") |
984 if err != nil { | 1119 if err != nil { |
985 t.Fatal(err) | 1120 t.Fatal(err) |
986 } | 1121 } |
987 | 1122 |
988 » if len(db.freeConn) != 1 { | 1123 » if db.freeConn.Len() != 1 { |
989 » » t.Fatalf("expected 1 freeConn; got %d", len(db.freeConn)) | 1124 » » t.Fatalf("expected 1 freeConn; got %d", db.freeConn.Len()) |
990 } | 1125 } |
991 » dc := db.freeConn[0] | 1126 » dc := db.freeConn.Front().Value.(*driverConn) |
992 if dc.closed { | 1127 if dc.closed { |
993 t.Errorf("conn shouldn't be closed") | 1128 t.Errorf("conn shouldn't be closed") |
994 } | 1129 } |
995 | 1130 |
996 if n := len(dc.openStmt); n != 1 { | 1131 if n := len(dc.openStmt); n != 1 { |
997 t.Errorf("driverConn num openStmt = %d; want 1", n) | 1132 t.Errorf("driverConn num openStmt = %d; want 1", n) |
998 } | 1133 } |
999 err = db.Close() | 1134 err = db.Close() |
1000 if err != nil { | 1135 if err != nil { |
1001 t.Errorf("db Close = %v", err) | 1136 t.Errorf("db Close = %v", err) |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1075 db.SetMaxIdleConns(0) | 1210 db.SetMaxIdleConns(0) |
1076 setStrictFakeConnClose(t) | 1211 setStrictFakeConnClose(t) |
1077 defer setStrictFakeConnClose(nil) | 1212 defer setStrictFakeConnClose(nil) |
1078 | 1213 |
1079 _, err := db.Query("SELECT|non_existent|name|") | 1214 _, err := db.Query("SELECT|non_existent|name|") |
1080 if err == nil { | 1215 if err == nil { |
1081 t.Fatal("Quering non-existent table should fail") | 1216 t.Fatal("Quering non-existent table should fail") |
1082 } | 1217 } |
1083 } | 1218 } |
1084 | 1219 |
1220 type concurrentTest interface { | |
1221 init(t testing.TB, db *DB) | |
1222 fini(t testing.TB) | |
bradfitz
2013/08/30 02:47:52
fini isn't a word or common abbreviation Could yo
Tad Glines
2013/08/30 03:37:55
Done
| |
1223 test(t testing.TB) error | |
1224 } | |
1225 | |
1226 type concurrentDBQueryTest struct { | |
1227 db *DB | |
1228 } | |
1229 | |
1230 func (c *concurrentDBQueryTest) init(t testing.TB, db *DB) { | |
1231 c.db = db | |
1232 } | |
1233 | |
1234 func (c *concurrentDBQueryTest) fini(t testing.TB) { | |
1235 c.db = nil | |
1236 } | |
1237 | |
1238 func (c *concurrentDBQueryTest) test(t testing.TB) error { | |
1239 rows, err := c.db.Query("SELECT|people|name|") | |
1240 if err != nil { | |
1241 t.Error(err) | |
1242 return err | |
1243 } | |
1244 var name string | |
1245 for rows.Next() { | |
1246 rows.Scan(&name) | |
1247 } | |
1248 rows.Close() | |
1249 return nil | |
1250 } | |
1251 | |
1252 type concurrentDBExecTest struct { | |
1253 db *DB | |
1254 } | |
1255 | |
1256 func (c *concurrentDBExecTest) init(t testing.TB, db *DB) { | |
1257 c.db = db | |
1258 } | |
1259 | |
1260 func (c *concurrentDBExecTest) fini(t testing.TB) { | |
1261 c.db = nil | |
1262 } | |
1263 | |
1264 func (c *concurrentDBExecTest) test(t testing.TB) error { | |
1265 _, err := c.db.Exec("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=? ", 3, chrisBirthday) | |
1266 if err != nil { | |
1267 t.Error(err) | |
1268 return err | |
1269 } | |
1270 return nil | |
1271 } | |
1272 | |
1273 type concurrentStmtQueryTest struct { | |
1274 db *DB | |
1275 stmt *Stmt | |
1276 } | |
1277 | |
1278 func (c *concurrentStmtQueryTest) init(t testing.TB, db *DB) { | |
1279 c.db = db | |
1280 var err error | |
1281 c.stmt, err = db.Prepare("SELECT|people|name|") | |
1282 if err != nil { | |
1283 t.Fatal(err) | |
1284 } | |
1285 } | |
1286 | |
1287 func (c *concurrentStmtQueryTest) fini(t testing.TB) { | |
1288 if c.stmt != nil { | |
1289 c.stmt.Close() | |
1290 c.stmt = nil | |
1291 } | |
1292 c.db = nil | |
1293 } | |
1294 | |
1295 func (c *concurrentStmtQueryTest) test(t testing.TB) error { | |
1296 rows, err := c.stmt.Query() | |
1297 if err != nil { | |
1298 t.Errorf("error on query: %v", err) | |
1299 return err | |
1300 } | |
1301 | |
1302 var name string | |
1303 for rows.Next() { | |
1304 rows.Scan(&name) | |
1305 } | |
1306 rows.Close() | |
1307 return nil | |
1308 } | |
1309 | |
1310 type concurrentStmtExecTest struct { | |
1311 db *DB | |
1312 stmt *Stmt | |
1313 } | |
1314 | |
1315 func (c *concurrentStmtExecTest) init(t testing.TB, db *DB) { | |
1316 c.db = db | |
1317 var err error | |
1318 c.stmt, err = db.Prepare("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bd ate=?") | |
1319 if err != nil { | |
1320 t.Fatal(err) | |
1321 } | |
1322 } | |
1323 | |
1324 func (c *concurrentStmtExecTest) fini(t testing.TB) { | |
1325 if c.stmt != nil { | |
1326 c.stmt.Close() | |
1327 c.stmt = nil | |
1328 } | |
1329 c.db = nil | |
1330 } | |
1331 | |
1332 func (c *concurrentStmtExecTest) test(t testing.TB) error { | |
1333 _, err := c.stmt.Exec(3, chrisBirthday) | |
1334 if err != nil { | |
1335 t.Errorf("error on exec: %v", err) | |
1336 return err | |
1337 } | |
1338 return nil | |
1339 } | |
1340 | |
1341 type concurrentTxQueryTest struct { | |
1342 db *DB | |
1343 tx *Tx | |
1344 } | |
1345 | |
1346 func (c *concurrentTxQueryTest) init(t testing.TB, db *DB) { | |
1347 c.db = db | |
1348 var err error | |
1349 c.tx, err = c.db.Begin() | |
1350 if err != nil { | |
1351 t.Fatal(err) | |
1352 } | |
1353 } | |
1354 | |
1355 func (c *concurrentTxQueryTest) fini(t testing.TB) { | |
1356 if c.tx != nil { | |
1357 c.tx.Rollback() | |
1358 c.tx = nil | |
1359 } | |
1360 c.db = nil | |
1361 } | |
1362 | |
1363 func (c *concurrentTxQueryTest) test(t testing.TB) error { | |
1364 rows, err := c.db.Query("SELECT|people|name|") | |
1365 if err != nil { | |
1366 t.Error(err) | |
1367 return err | |
1368 } | |
1369 var name string | |
1370 for rows.Next() { | |
1371 rows.Scan(&name) | |
1372 } | |
1373 rows.Close() | |
1374 return nil | |
1375 } | |
1376 | |
1377 type concurrentTxExecTest struct { | |
1378 db *DB | |
1379 tx *Tx | |
1380 } | |
1381 | |
1382 func (c *concurrentTxExecTest) init(t testing.TB, db *DB) { | |
1383 c.db = db | |
1384 var err error | |
1385 c.tx, err = c.db.Begin() | |
1386 if err != nil { | |
1387 t.Fatal(err) | |
1388 } | |
1389 } | |
1390 | |
1391 func (c *concurrentTxExecTest) fini(t testing.TB) { | |
1392 if c.tx != nil { | |
1393 c.tx.Rollback() | |
1394 c.tx = nil | |
1395 } | |
1396 c.db = nil | |
1397 } | |
1398 | |
1399 func (c *concurrentTxExecTest) test(t testing.TB) error { | |
1400 _, err := c.tx.Exec("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=? ", 3, chrisBirthday) | |
1401 if err != nil { | |
1402 t.Error(err) | |
1403 return err | |
1404 } | |
1405 return nil | |
1406 } | |
1407 | |
1408 type concurrentTxStmtQueryTest struct { | |
1409 db *DB | |
1410 tx *Tx | |
1411 stmt *Stmt | |
1412 } | |
1413 | |
1414 func (c *concurrentTxStmtQueryTest) init(t testing.TB, db *DB) { | |
1415 c.db = db | |
1416 var err error | |
1417 c.tx, err = c.db.Begin() | |
1418 if err != nil { | |
1419 t.Fatal(err) | |
1420 } | |
1421 c.stmt, err = c.tx.Prepare("SELECT|people|name|") | |
1422 if err != nil { | |
1423 t.Fatal(err) | |
1424 } | |
1425 } | |
1426 | |
1427 func (c *concurrentTxStmtQueryTest) fini(t testing.TB) { | |
1428 if c.stmt != nil { | |
1429 c.stmt.Close() | |
1430 c.stmt = nil | |
1431 } | |
1432 if c.tx != nil { | |
1433 c.tx.Rollback() | |
1434 c.tx = nil | |
1435 } | |
1436 c.db = nil | |
1437 } | |
1438 | |
1439 func (c *concurrentTxStmtQueryTest) test(t testing.TB) error { | |
1440 rows, err := c.stmt.Query() | |
1441 if err != nil { | |
1442 t.Errorf("error on query: %v", err) | |
1443 return err | |
1444 } | |
1445 | |
1446 var name string | |
1447 for rows.Next() { | |
1448 rows.Scan(&name) | |
1449 } | |
1450 rows.Close() | |
1451 return nil | |
1452 } | |
1453 | |
1454 type concurrentTxStmtExecTest struct { | |
1455 db *DB | |
1456 tx *Tx | |
1457 stmt *Stmt | |
1458 } | |
1459 | |
1460 func (c *concurrentTxStmtExecTest) init(t testing.TB, db *DB) { | |
1461 c.db = db | |
1462 var err error | |
1463 c.tx, err = c.db.Begin() | |
1464 if err != nil { | |
1465 t.Fatal(err) | |
1466 } | |
1467 c.stmt, err = c.tx.Prepare("NOSERT|people|name=Chris,age=?,photo=CPHOTO, bdate=?") | |
1468 if err != nil { | |
1469 t.Fatal(err) | |
1470 } | |
1471 } | |
1472 | |
1473 func (c *concurrentTxStmtExecTest) fini(t testing.TB) { | |
1474 if c.stmt != nil { | |
1475 c.stmt.Close() | |
1476 c.stmt = nil | |
1477 } | |
1478 if c.tx != nil { | |
1479 c.tx.Rollback() | |
1480 c.tx = nil | |
1481 } | |
1482 c.db = nil | |
1483 } | |
1484 | |
1485 func (c *concurrentTxStmtExecTest) test(t testing.TB) error { | |
1486 _, err := c.stmt.Exec(3, chrisBirthday) | |
1487 if err != nil { | |
1488 t.Errorf("error on exec: %v", err) | |
1489 return err | |
1490 } | |
1491 return nil | |
1492 } | |
1493 | |
1494 type concurrentRandomTest struct { | |
1495 tests []concurrentTest | |
1496 } | |
1497 | |
1498 func (c *concurrentRandomTest) init(t testing.TB, db *DB) { | |
1499 c.tests = []concurrentTest{ | |
1500 new(concurrentDBQueryTest), | |
1501 new(concurrentDBExecTest), | |
1502 new(concurrentStmtQueryTest), | |
1503 new(concurrentStmtExecTest), | |
1504 new(concurrentTxQueryTest), | |
1505 new(concurrentTxExecTest), | |
1506 new(concurrentTxStmtQueryTest), | |
1507 new(concurrentTxStmtExecTest), | |
1508 } | |
1509 for _, ct := range c.tests { | |
1510 ct.init(t, db) | |
1511 } | |
1512 } | |
1513 | |
1514 func (c *concurrentRandomTest) fini(t testing.TB) { | |
1515 for _, ct := range c.tests { | |
1516 ct.fini(t) | |
1517 } | |
1518 } | |
1519 | |
1520 func (c *concurrentRandomTest) test(t testing.TB) error { | |
1521 ct := c.tests[rand.Intn(len(c.tests))] | |
1522 return ct.test(t) | |
1523 } | |
1524 | |
1525 func doConcurrentTest(t testing.TB, ct concurrentTest) { | |
1526 maxProcs, numReqs := 1, 500 | |
1527 if testing.Short() { | |
1528 maxProcs, numReqs = 4, 50 | |
1529 } | |
1530 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) | |
1531 | |
1532 db := newTestDB(t, "people") | |
1533 defer closeDB(t, db) | |
1534 | |
1535 ct.init(t, db) | |
1536 defer ct.fini(t) | |
1537 | |
1538 var wg sync.WaitGroup | |
1539 wg.Add(numReqs) | |
1540 | |
1541 reqs := make(chan bool) | |
1542 defer close(reqs) | |
1543 | |
1544 for i := 0; i < maxProcs*2; i++ { | |
1545 go func() { | |
1546 for _ = range reqs { | |
1547 err := ct.test(t) | |
1548 if err != nil { | |
1549 wg.Done() | |
1550 continue | |
1551 } | |
1552 wg.Done() | |
1553 } | |
1554 }() | |
1555 } | |
1556 | |
1557 for i := 0; i < numReqs; i++ { | |
1558 reqs <- true | |
1559 } | |
1560 | |
1561 wg.Wait() | |
1562 } | |
1563 | |
1085 func manyConcurrentQueries(t testing.TB) { | 1564 func manyConcurrentQueries(t testing.TB) { |
1086 maxProcs, numReqs := 16, 500 | 1565 maxProcs, numReqs := 16, 500 |
1087 if testing.Short() { | 1566 if testing.Short() { |
1088 maxProcs, numReqs = 4, 50 | 1567 maxProcs, numReqs = 4, 50 |
1089 } | 1568 } |
1090 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) | 1569 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) |
1091 | 1570 |
1092 db := newTestDB(t, "people") | 1571 db := newTestDB(t, "people") |
1093 defer closeDB(t, db) | 1572 defer closeDB(t, db) |
1094 | 1573 |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1171 drv.mu.Unlock() | 1650 drv.mu.Unlock() |
1172 if opens < 9 { | 1651 if opens < 9 { |
1173 t.Errorf("opens = %d; want >= 9", opens) | 1652 t.Errorf("opens = %d; want >= 9", opens) |
1174 } | 1653 } |
1175 if closes < 9 { | 1654 if closes < 9 { |
1176 t.Errorf("closes = %d; want >= 9", closes) | 1655 t.Errorf("closes = %d; want >= 9", closes) |
1177 } | 1656 } |
1178 } | 1657 } |
1179 | 1658 |
1180 func TestConcurrency(t *testing.T) { | 1659 func TestConcurrency(t *testing.T) { |
1181 » manyConcurrentQueries(t) | 1660 » doConcurrentTest(t, new(concurrentDBQueryTest)) |
1661 » doConcurrentTest(t, new(concurrentDBExecTest)) | |
1662 » doConcurrentTest(t, new(concurrentStmtQueryTest)) | |
1663 » doConcurrentTest(t, new(concurrentStmtExecTest)) | |
1664 » doConcurrentTest(t, new(concurrentTxQueryTest)) | |
1665 » doConcurrentTest(t, new(concurrentTxExecTest)) | |
1666 » doConcurrentTest(t, new(concurrentTxStmtQueryTest)) | |
1667 » doConcurrentTest(t, new(concurrentTxStmtExecTest)) | |
1668 » doConcurrentTest(t, new(concurrentRandomTest)) | |
1182 } | 1669 } |
1183 | 1670 |
1184 func BenchmarkConcurrency(b *testing.B) { | 1671 func BenchmarkConcurrentDBExec(b *testing.B) { |
1185 b.ReportAllocs() | 1672 b.ReportAllocs() |
1673 ct := new(concurrentDBExecTest) | |
1186 for i := 0; i < b.N; i++ { | 1674 for i := 0; i < b.N; i++ { |
1187 » » manyConcurrentQueries(b) | 1675 » » doConcurrentTest(b, ct) |
1188 } | 1676 } |
1189 } | 1677 } |
1678 | |
1679 func BenchmarkConcurrentStmtQuery(b *testing.B) { | |
1680 b.ReportAllocs() | |
1681 ct := new(concurrentStmtQueryTest) | |
1682 for i := 0; i < b.N; i++ { | |
1683 doConcurrentTest(b, ct) | |
1684 } | |
1685 } | |
1686 | |
1687 func BenchmarkConcurrentStmtExec(b *testing.B) { | |
1688 b.ReportAllocs() | |
1689 ct := new(concurrentStmtExecTest) | |
1690 for i := 0; i < b.N; i++ { | |
1691 doConcurrentTest(b, ct) | |
1692 } | |
1693 } | |
1694 | |
1695 func BenchmarkConcurrentTxQuery(b *testing.B) { | |
1696 b.ReportAllocs() | |
1697 ct := new(concurrentTxQueryTest) | |
1698 for i := 0; i < b.N; i++ { | |
1699 doConcurrentTest(b, ct) | |
1700 } | |
1701 } | |
1702 | |
1703 func BenchmarkConcurrentTxExec(b *testing.B) { | |
1704 b.ReportAllocs() | |
1705 ct := new(concurrentTxExecTest) | |
1706 for i := 0; i < b.N; i++ { | |
1707 doConcurrentTest(b, ct) | |
1708 } | |
1709 } | |
1710 | |
1711 func BenchmarkConcurrentTxStmtQuery(b *testing.B) { | |
1712 b.ReportAllocs() | |
1713 ct := new(concurrentTxStmtQueryTest) | |
1714 for i := 0; i < b.N; i++ { | |
1715 doConcurrentTest(b, ct) | |
1716 } | |
1717 } | |
1718 | |
1719 func BenchmarkConcurrentTxStmtExec(b *testing.B) { | |
1720 b.ReportAllocs() | |
1721 ct := new(concurrentTxStmtExecTest) | |
1722 for i := 0; i < b.N; i++ { | |
1723 doConcurrentTest(b, ct) | |
1724 } | |
1725 } | |
1726 | |
1727 func BenchmarkConcurrentRandom(b *testing.B) { | |
1728 b.ReportAllocs() | |
1729 ct := new(concurrentRandomTest) | |
1730 for i := 0; i < b.N; i++ { | |
1731 doConcurrentTest(b, ct) | |
1732 } | |
1733 } | |
OLD | NEW |