LEFT | RIGHT |
(no file at all) | |
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" | |
12 "reflect" | 11 "reflect" |
13 "runtime" | 12 "runtime" |
14 "strings" | 13 "strings" |
15 "sync" | 14 "sync" |
16 "testing" | 15 "testing" |
17 "time" | 16 "time" |
18 ) | 17 ) |
19 | 18 |
20 func init() { | 19 func init() { |
21 type dbConn struct { | 20 type dbConn struct { |
22 db *DB | 21 db *DB |
23 c *driverConn | 22 c *driverConn |
24 } | 23 } |
25 freedFrom := make(map[dbConn]string) | 24 freedFrom := make(map[dbConn]string) |
26 putConnHook = func(db *DB, c *driverConn) { | 25 putConnHook = func(db *DB, c *driverConn) { |
27 » » if c.listElem != nil { | 26 » » for _, oc := range db.freeConn { |
28 » » » // print before panic, as panic may get lost due to conf
licting panic | 27 » » » if oc == c { |
29 » » » // (all goroutines asleep) elsewhere, since we might not
unlock | 28 » » » » // print before panic, as panic may get lost due
to conflicting panic |
30 » » » // the mutex in freeConn here. | 29 » » » » // (all goroutines asleep) elsewhere, since we m
ight not unlock |
31 » » » println("double free of conn. conflicts are:\nA) " + fre
edFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) | 30 » » » » // the mutex in freeConn here. |
32 » » » panic("double free of conn.") | 31 » » » » println("double free of conn. conflicts are:\nA)
" + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) |
| 32 » » » » panic("double free of conn.") |
| 33 » » » } |
33 } | 34 } |
34 freedFrom[dbConn{db, c}] = stack() | 35 freedFrom[dbConn{db, c}] = stack() |
35 } | 36 } |
36 } | 37 } |
37 | 38 |
38 const fakeDBName = "foo" | 39 const fakeDBName = "foo" |
39 | 40 |
40 var chrisBirthday = time.Unix(123456789, 0) | 41 var chrisBirthday = time.Unix(123456789, 0) |
41 | 42 |
42 func newTestDB(t testing.TB, name string) *DB { | 43 func newTestDB(t testing.TB, name string) *DB { |
(...skipping 29 matching lines...) Expand all Loading... |
72 if e := recover(); e != nil { | 73 if e := recover(); e != nil { |
73 fmt.Printf("Panic: %v\n", e) | 74 fmt.Printf("Panic: %v\n", e) |
74 panic(e) | 75 panic(e) |
75 } | 76 } |
76 defer setHookpostCloseConn(nil) | 77 defer setHookpostCloseConn(nil) |
77 setHookpostCloseConn(func(_ *fakeConn, err error) { | 78 setHookpostCloseConn(func(_ *fakeConn, err error) { |
78 if err != nil { | 79 if err != nil { |
79 t.Errorf("Error closing fakeConn: %v", err) | 80 t.Errorf("Error closing fakeConn: %v", err) |
80 } | 81 } |
81 }) | 82 }) |
82 » for node, i := db.freeConn.Front(), 0; node != nil; node, i = node.Next(
), i+1 { | 83 » for i, dc := range db.freeConn { |
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, db.freeConn.Len(), n) | 90 » » » t.Errorf("while closing db, freeConn %d/%d had %d open s
tmts; want 0", i, len(db.freeConn), 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 := db.freeConn.Len(); n != 1 { | 102 » if n := len(db.freeConn); 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.Front().Value.(*driverConn)).ci.(*fakeConn).numPrepa
re | 105 » return db.freeConn[0].ci.(*fakeConn).numPrepare |
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 db.freeConn.Len() | 130 » return len(db.freeConn) |
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 db.freeConn.Len() != 1 { | 645 » if len(db.freeConn) != 1 { |
646 t.Fatalf("expected 1 free conn") | 646 t.Fatalf("expected 1 free conn") |
647 } | 647 } |
648 » fakeConn := (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn) | 648 » fakeConn := db.freeConn[0].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 := db.freeConn.Len(); got != 1 { | 844 » if got := len(db.freeConn); 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 := db.freeConn.Len(); got != 0 { | 850 » if got := len(db.freeConn); 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 := db.freeConn.Len(); got != 0 { | 859 » if got := len(db.freeConn); 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) { | 864 // golang.org/issue/5323 |
| 865 func TestStmtCloseDeps(t *testing.T) { |
865 if testing.Short() { | 866 if testing.Short() { |
866 t.Skip("skipping in short mode") | 867 t.Skip("skipping in short mode") |
867 } | 868 } |
868 defer setHookpostCloseConn(nil) | 869 defer setHookpostCloseConn(nil) |
869 setHookpostCloseConn(func(_ *fakeConn, err error) { | 870 setHookpostCloseConn(func(_ *fakeConn, err error) { |
870 if err != nil { | 871 if err != nil { |
871 t.Errorf("Error closing fakeConn: %v", err) | 872 t.Errorf("Error closing fakeConn: %v", err) |
872 } | 873 } |
873 }) | 874 }) |
874 | 875 |
875 db := newTestDB(t, "magicquery") | 876 db := newTestDB(t, "magicquery") |
876 defer closeDB(t, db) | 877 defer closeDB(t, db) |
877 | 878 |
878 driver := db.driver.(*fakeDriver) | 879 driver := db.driver.(*fakeDriver) |
879 | 880 |
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() | 881 driver.mu.Lock() |
894 opens0 := driver.openCount | 882 opens0 := driver.openCount |
895 closes0 := driver.closeCount | 883 closes0 := driver.closeCount |
896 driver.mu.Unlock() | 884 driver.mu.Unlock() |
897 | 885 » openDelta0 := opens0 - closes0 |
898 » db.SetMaxIdleConns(10) | |
899 » db.SetMaxOpenConns(10) | |
900 | 886 |
901 stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?") | 887 stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?") |
902 if err != nil { | 888 if err != nil { |
903 t.Fatal(err) | 889 t.Fatal(err) |
904 } | 890 } |
905 | 891 |
906 // Start 50 parallel slow queries. | 892 // Start 50 parallel slow queries. |
907 const ( | 893 const ( |
908 nquery = 50 | 894 nquery = 50 |
909 sleepMillis = 25 | 895 sleepMillis = 25 |
(...skipping 11 matching lines...) Expand all Loading... |
921 } | 907 } |
922 }() | 908 }() |
923 } | 909 } |
924 // Sleep for twice the expected length of time for the | 910 // Sleep for twice the expected length of time for the |
925 // batch of 50 queries above to finish before starting | 911 // batch of 50 queries above to finish before starting |
926 // the next round. | 912 // the next round. |
927 time.Sleep(2 * sleepMillis * time.Millisecond) | 913 time.Sleep(2 * sleepMillis * time.Millisecond) |
928 } | 914 } |
929 wg.Wait() | 915 wg.Wait() |
930 | 916 |
931 » if g, w := db.numFreeConns(), 10; g != w { | 917 » if g, w := db.numFreeConns(), 2; g != w { |
932 t.Errorf("free conns = %d; want %d", g, w) | 918 t.Errorf("free conns = %d; want %d", g, w) |
933 } | 919 } |
934 | 920 |
935 » if n := db.numDepsPollUntil(20, time.Second); n > 20 { | 921 » if n := db.numDepsPollUntil(4, time.Second); n > 4 { |
936 » » t.Errorf("number of dependencies = %d; expected <= 20", n) | 922 » » t.Errorf("number of dependencies = %d; expected <= 4", n) |
937 db.dumpDeps(t) | 923 db.dumpDeps(t) |
938 } | 924 } |
939 | 925 |
940 driver.mu.Lock() | 926 driver.mu.Lock() |
941 opens := driver.openCount - opens0 | 927 opens := driver.openCount - opens0 |
942 closes := driver.closeCount - closes0 | 928 closes := driver.closeCount - closes0 |
943 driver.mu.Unlock() | 929 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 | |
999 // golang.org/issue/5323 | |
1000 func TestStmtCloseDeps(t *testing.T) { | |
1001 if testing.Short() { | |
1002 t.Skip("skipping in short mode") | |
1003 } | |
1004 defer setHookpostCloseConn(nil) | |
1005 setHookpostCloseConn(func(_ *fakeConn, err error) { | |
1006 if err != nil { | |
1007 t.Errorf("Error closing fakeConn: %v", err) | |
1008 } | |
1009 }) | |
1010 | |
1011 db := newTestDB(t, "magicquery") | |
1012 defer closeDB(t, db) | |
1013 | |
1014 driver := db.driver.(*fakeDriver) | |
1015 | |
1016 driver.mu.Lock() | |
1017 opens0 := driver.openCount | |
1018 closes0 := driver.closeCount | |
1019 driver.mu.Unlock() | |
1020 openDelta0 := opens0 - closes0 | |
1021 | |
1022 stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?") | |
1023 if err != nil { | |
1024 t.Fatal(err) | |
1025 } | |
1026 | |
1027 // Start 50 parallel slow queries. | |
1028 const ( | |
1029 nquery = 50 | |
1030 sleepMillis = 25 | |
1031 nbatch = 2 | |
1032 ) | |
1033 var wg sync.WaitGroup | |
1034 for batch := 0; batch < nbatch; batch++ { | |
1035 for i := 0; i < nquery; i++ { | |
1036 wg.Add(1) | |
1037 go func() { | |
1038 defer wg.Done() | |
1039 var op string | |
1040 if err := stmt.QueryRow("sleep", sleepMillis).Sc
an(&op); err != nil && err != ErrNoRows { | |
1041 t.Error(err) | |
1042 } | |
1043 }() | |
1044 } | |
1045 // Sleep for twice the expected length of time for the | |
1046 // batch of 50 queries above to finish before starting | |
1047 // the next round. | |
1048 time.Sleep(2 * sleepMillis * time.Millisecond) | |
1049 } | |
1050 wg.Wait() | |
1051 | |
1052 if g, w := db.numFreeConns(), 2; g != w { | |
1053 t.Errorf("free conns = %d; want %d", g, w) | |
1054 } | |
1055 | |
1056 if n := db.numDepsPollUntil(4, time.Second); n > 4 { | |
1057 t.Errorf("number of dependencies = %d; expected <= 4", n) | |
1058 db.dumpDeps(t) | |
1059 } | |
1060 | |
1061 driver.mu.Lock() | |
1062 opens := driver.openCount - opens0 | |
1063 closes := driver.closeCount - closes0 | |
1064 openDelta := (driver.openCount - driver.closeCount) - openDelta0 | 930 openDelta := (driver.openCount - driver.closeCount) - openDelta0 |
1065 driver.mu.Unlock() | |
1066 | 931 |
1067 if openDelta > 2 { | 932 if openDelta > 2 { |
1068 t.Logf("open calls = %d", opens) | 933 t.Logf("open calls = %d", opens) |
1069 t.Logf("close calls = %d", closes) | 934 t.Logf("close calls = %d", closes) |
1070 t.Logf("open delta = %d", openDelta) | 935 t.Logf("open delta = %d", openDelta) |
1071 t.Errorf("db connections opened = %d; want <= 2", openDelta) | 936 t.Errorf("db connections opened = %d; want <= 2", openDelta) |
1072 db.dumpDeps(t) | 937 db.dumpDeps(t) |
1073 } | 938 } |
1074 | 939 |
1075 if len(stmt.css) > nquery { | 940 if len(stmt.css) > nquery { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1113 db.dumpDeps(t) | 978 db.dumpDeps(t) |
1114 t.Errorf("DB = %#v", db) | 979 t.Errorf("DB = %#v", db) |
1115 } | 980 } |
1116 }) | 981 }) |
1117 | 982 |
1118 stmt, err := db.Prepare("SELECT|people|name|") | 983 stmt, err := db.Prepare("SELECT|people|name|") |
1119 if err != nil { | 984 if err != nil { |
1120 t.Fatal(err) | 985 t.Fatal(err) |
1121 } | 986 } |
1122 | 987 |
1123 » if db.freeConn.Len() != 1 { | 988 » if len(db.freeConn) != 1 { |
1124 » » t.Fatalf("expected 1 freeConn; got %d", db.freeConn.Len()) | 989 » » t.Fatalf("expected 1 freeConn; got %d", len(db.freeConn)) |
1125 » } | 990 » } |
1126 » dc := db.freeConn.Front().Value.(*driverConn) | 991 » dc := db.freeConn[0] |
1127 if dc.closed { | 992 if dc.closed { |
1128 t.Errorf("conn shouldn't be closed") | 993 t.Errorf("conn shouldn't be closed") |
1129 } | 994 } |
1130 | 995 |
1131 if n := len(dc.openStmt); n != 1 { | 996 if n := len(dc.openStmt); n != 1 { |
1132 t.Errorf("driverConn num openStmt = %d; want 1", n) | 997 t.Errorf("driverConn num openStmt = %d; want 1", n) |
1133 } | 998 } |
1134 err = db.Close() | 999 err = db.Close() |
1135 if err != nil { | 1000 if err != nil { |
1136 t.Errorf("db Close = %v", err) | 1001 t.Errorf("db Close = %v", err) |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1208 defer closeDB(t, db) | 1073 defer closeDB(t, db) |
1209 | 1074 |
1210 db.SetMaxIdleConns(0) | 1075 db.SetMaxIdleConns(0) |
1211 setStrictFakeConnClose(t) | 1076 setStrictFakeConnClose(t) |
1212 defer setStrictFakeConnClose(nil) | 1077 defer setStrictFakeConnClose(nil) |
1213 | 1078 |
1214 _, err := db.Query("SELECT|non_existent|name|") | 1079 _, err := db.Query("SELECT|non_existent|name|") |
1215 if err == nil { | 1080 if err == nil { |
1216 t.Fatal("Quering non-existent table should fail") | 1081 t.Fatal("Quering non-existent table should fail") |
1217 } | 1082 } |
1218 } | |
1219 | |
1220 type concurrentTest interface { | |
1221 init(t testing.TB, db *DB) | |
1222 finish(t testing.TB) | |
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) finish(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) finish(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) finish(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) finish(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) finish(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) finish(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) finish(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) finish(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) finish(t testing.TB) { | |
1515 for _, ct := range c.tests { | |
1516 ct.finish(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.finish(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 } | 1083 } |
1563 | 1084 |
1564 func manyConcurrentQueries(t testing.TB) { | 1085 func manyConcurrentQueries(t testing.TB) { |
1565 maxProcs, numReqs := 16, 500 | 1086 maxProcs, numReqs := 16, 500 |
1566 if testing.Short() { | 1087 if testing.Short() { |
1567 maxProcs, numReqs = 4, 50 | 1088 maxProcs, numReqs = 4, 50 |
1568 } | 1089 } |
1569 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) | 1090 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) |
1570 | 1091 |
1571 db := newTestDB(t, "people") | 1092 db := newTestDB(t, "people") |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1650 drv.mu.Unlock() | 1171 drv.mu.Unlock() |
1651 if opens < 9 { | 1172 if opens < 9 { |
1652 t.Errorf("opens = %d; want >= 9", opens) | 1173 t.Errorf("opens = %d; want >= 9", opens) |
1653 } | 1174 } |
1654 if closes < 9 { | 1175 if closes < 9 { |
1655 t.Errorf("closes = %d; want >= 9", closes) | 1176 t.Errorf("closes = %d; want >= 9", closes) |
1656 } | 1177 } |
1657 } | 1178 } |
1658 | 1179 |
1659 func TestConcurrency(t *testing.T) { | 1180 func TestConcurrency(t *testing.T) { |
1660 » doConcurrentTest(t, new(concurrentDBQueryTest)) | 1181 » manyConcurrentQueries(t) |
1661 » doConcurrentTest(t, new(concurrentDBExecTest)) | 1182 } |
1662 » doConcurrentTest(t, new(concurrentStmtQueryTest)) | 1183 |
1663 » doConcurrentTest(t, new(concurrentStmtExecTest)) | 1184 func BenchmarkConcurrency(b *testing.B) { |
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)) | |
1669 } | |
1670 | |
1671 func BenchmarkConcurrentDBExec(b *testing.B) { | |
1672 b.ReportAllocs() | 1185 b.ReportAllocs() |
1673 ct := new(concurrentDBExecTest) | |
1674 for i := 0; i < b.N; i++ { | 1186 for i := 0; i < b.N; i++ { |
1675 » » doConcurrentTest(b, ct) | 1187 » » manyConcurrentQueries(b) |
1676 » } | 1188 » } |
1677 } | 1189 } |
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 } | |
LEFT | RIGHT |