Skip to content

Commit c8a11f7

Browse files
authored
test(contrib/drivers/mysql): add concurrent/Hook/Ctx tests (gogf#4708)
## Summary - Add concurrent operation tests - Add Hook mechanism tests (Insert/Update/Delete/Select) - Add Context propagation tests - Add race condition tests **Test coverage added:** ~28 test functions across 5 files Ref gogf#4689 ## Test plan ```bash cd contrib/drivers/mysql go test -v -race -run "TestModel_Concurrent|TestModel_Hook|TestModel_Ctx" ```
1 parent e0c032d commit c8a11f7

5 files changed

Lines changed: 867 additions & 0 deletions
Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2+
//
3+
// This Source Code Form is subject to the terms of the MIT License.
4+
// If a copy of the MIT was not distributed with this file,
5+
// You can obtain one at https://github.com/gogf/gf.
6+
7+
package mysql_test
8+
9+
import (
10+
"fmt"
11+
"sync"
12+
"testing"
13+
14+
"github.com/gogf/gf/v2/database/gdb"
15+
"github.com/gogf/gf/v2/frame/g"
16+
"github.com/gogf/gf/v2/test/gtest"
17+
)
18+
19+
// Test_Concurrent_Insert tests concurrent Insert operations
20+
func Test_Concurrent_Insert(t *testing.T) {
21+
table := createTable()
22+
defer dropTable(table)
23+
24+
gtest.C(t, func(t *gtest.T) {
25+
var wg sync.WaitGroup
26+
concurrency := 10
27+
28+
wg.Add(concurrency)
29+
for i := 0; i < concurrency; i++ {
30+
go func(id int) {
31+
defer wg.Done()
32+
_, err := db.Model(table).Insert(g.Map{
33+
"passport": fmt.Sprintf("user_%d", id),
34+
"password": fmt.Sprintf("pass_%d", id),
35+
"nickname": fmt.Sprintf("name_%d", id),
36+
})
37+
t.AssertNil(err)
38+
}(i + 1)
39+
}
40+
wg.Wait()
41+
42+
// Verify all records inserted
43+
count, err := db.Model(table).Count()
44+
t.AssertNil(err)
45+
t.Assert(count, concurrency)
46+
})
47+
}
48+
49+
// Test_Concurrent_Update tests concurrent Update operations
50+
func Test_Concurrent_Update(t *testing.T) {
51+
table := createInitTable()
52+
defer dropTable(table)
53+
54+
gtest.C(t, func(t *gtest.T) {
55+
var wg sync.WaitGroup
56+
concurrency := 5
57+
58+
wg.Add(concurrency)
59+
for i := 0; i < concurrency; i++ {
60+
go func(id int) {
61+
defer wg.Done()
62+
_, err := db.Model(table).Data(g.Map{
63+
"nickname": fmt.Sprintf("updated_%d", id),
64+
}).Where("id", id+1).Update()
65+
t.AssertNil(err)
66+
}(i)
67+
}
68+
wg.Wait()
69+
70+
// Verify updates
71+
for i := 0; i < concurrency; i++ {
72+
one, err := db.Model(table).Where("id", i+1).One()
73+
t.AssertNil(err)
74+
t.Assert(one["nickname"].String(), fmt.Sprintf("updated_%d", i))
75+
}
76+
})
77+
}
78+
79+
// Test_Concurrent_Delete tests concurrent Delete operations
80+
func Test_Concurrent_Delete(t *testing.T) {
81+
table := createInitTable()
82+
defer dropTable(table)
83+
84+
gtest.C(t, func(t *gtest.T) {
85+
var wg sync.WaitGroup
86+
concurrency := 5
87+
88+
wg.Add(concurrency)
89+
for i := 0; i < concurrency; i++ {
90+
go func(id int) {
91+
defer wg.Done()
92+
_, err := db.Model(table).Where("id", id+1).Delete()
93+
t.AssertNil(err)
94+
}(i)
95+
}
96+
wg.Wait()
97+
98+
// Verify deletions
99+
count, err := db.Model(table).Count()
100+
t.AssertNil(err)
101+
t.Assert(count, TableSize-concurrency)
102+
})
103+
}
104+
105+
// Test_Concurrent_Query tests concurrent Query operations
106+
func Test_Concurrent_Query(t *testing.T) {
107+
table := createInitTable()
108+
defer dropTable(table)
109+
110+
gtest.C(t, func(t *gtest.T) {
111+
var wg sync.WaitGroup
112+
concurrency := 20
113+
114+
wg.Add(concurrency)
115+
for i := 0; i < concurrency; i++ {
116+
go func(id int) {
117+
defer wg.Done()
118+
result, err := db.Model(table).Where("id", (id%TableSize)+1).One()
119+
t.AssertNil(err)
120+
t.AssertNE(result, nil)
121+
}(i)
122+
}
123+
wg.Wait()
124+
})
125+
}
126+
127+
// Test_Concurrent_Transaction tests concurrent transaction operations
128+
func Test_Concurrent_Transaction(t *testing.T) {
129+
table := createTable()
130+
defer dropTable(table)
131+
132+
gtest.C(t, func(t *gtest.T) {
133+
var wg sync.WaitGroup
134+
concurrency := 10
135+
136+
wg.Add(concurrency)
137+
for i := 0; i < concurrency; i++ {
138+
go func(id int) {
139+
defer wg.Done()
140+
err := db.Transaction(ctx, func(ctx g.Ctx, tx gdb.TX) error {
141+
_, err := tx.Model(table).Insert(g.Map{
142+
"passport": fmt.Sprintf("user_%d", id),
143+
"password": fmt.Sprintf("pass_%d", id),
144+
"nickname": fmt.Sprintf("name_%d", id),
145+
})
146+
return err
147+
})
148+
t.AssertNil(err)
149+
}(i + 1)
150+
}
151+
wg.Wait()
152+
153+
// Verify all transactions committed
154+
count, err := db.Model(table).Count()
155+
t.AssertNil(err)
156+
t.Assert(count, concurrency)
157+
})
158+
}
159+
160+
// Test_Concurrent_Mixed_Operations tests mixed concurrent operations
161+
func Test_Concurrent_Mixed_Operations(t *testing.T) {
162+
table := createInitTable()
163+
defer dropTable(table)
164+
165+
gtest.C(t, func(t *gtest.T) {
166+
var wg sync.WaitGroup
167+
operations := 30
168+
169+
wg.Add(operations)
170+
for i := 0; i < operations; i++ {
171+
op := i % 3
172+
switch op {
173+
case 0: // Insert
174+
go func(id int) {
175+
defer wg.Done()
176+
_, _ = db.Model(table).Insert(g.Map{
177+
"passport": fmt.Sprintf("new_user_%d", id),
178+
"password": fmt.Sprintf("new_pass_%d", id),
179+
"nickname": fmt.Sprintf("new_name_%d", id),
180+
})
181+
}(i)
182+
case 1: // Update
183+
go func(id int) {
184+
defer wg.Done()
185+
targetId := (id % TableSize) + 1
186+
_, _ = db.Model(table).Data(g.Map{
187+
"nickname": fmt.Sprintf("concurrent_%d", id),
188+
}).Where("id", targetId).Update()
189+
}(i)
190+
case 2: // Query
191+
go func(id int) {
192+
defer wg.Done()
193+
targetId := (id % TableSize) + 1
194+
_, _ = db.Model(table).Where("id", targetId).One()
195+
}(i)
196+
}
197+
}
198+
wg.Wait()
199+
200+
// Verify database is still consistent
201+
count, err := db.Model(table).Count()
202+
t.AssertNil(err)
203+
t.AssertGT(count, TableSize)
204+
})
205+
}
206+
207+
// Test_Concurrent_Connection_Pool tests connection pool under load
208+
func Test_Concurrent_Connection_Pool(t *testing.T) {
209+
table := createInitTable()
210+
defer dropTable(table)
211+
212+
gtest.C(t, func(t *gtest.T) {
213+
var wg sync.WaitGroup
214+
concurrency := 50
215+
216+
wg.Add(concurrency)
217+
for i := 0; i < concurrency; i++ {
218+
go func(id int) {
219+
defer wg.Done()
220+
// Each goroutine performs multiple operations
221+
for j := 0; j < 5; j++ {
222+
_, err := db.Model(table).Where("id", (id%TableSize)+1).One()
223+
t.AssertNil(err)
224+
}
225+
}(i)
226+
}
227+
wg.Wait()
228+
})
229+
}
230+
231+
// Test_Concurrent_Schema_Switch tests concurrent schema switching
232+
func Test_Concurrent_Schema_Switch(t *testing.T) {
233+
table1 := createTableWithDb(db, "test_schema_1")
234+
table2 := createTableWithDb(db2, "test_schema_2")
235+
defer dropTableWithDb(db, table1)
236+
defer dropTableWithDb(db2, table2)
237+
238+
gtest.C(t, func(t *gtest.T) {
239+
var wg sync.WaitGroup
240+
concurrency := 10
241+
242+
wg.Add(concurrency * 2)
243+
for i := 0; i < concurrency; i++ {
244+
// Insert to schema1
245+
go func(id int) {
246+
defer wg.Done()
247+
_, err := db.Model(table1).Insert(g.Map{
248+
"passport": fmt.Sprintf("user_s1_%d", id),
249+
"password": fmt.Sprintf("pass_%d", id),
250+
"nickname": fmt.Sprintf("name_%d", id),
251+
})
252+
t.AssertNil(err)
253+
}(i)
254+
255+
// Insert to schema2
256+
go func(id int) {
257+
defer wg.Done()
258+
_, err := db2.Model(table2).Insert(g.Map{
259+
"passport": fmt.Sprintf("user_s2_%d", id),
260+
"password": fmt.Sprintf("pass_%d", id),
261+
"nickname": fmt.Sprintf("name_%d", id),
262+
})
263+
t.AssertNil(err)
264+
}(i)
265+
}
266+
wg.Wait()
267+
268+
// Verify both schemas
269+
count1, err := db.Model(table1).Count()
270+
t.AssertNil(err)
271+
t.Assert(count1, concurrency)
272+
273+
count2, err := db2.Model(table2).Count()
274+
t.AssertNil(err)
275+
t.Assert(count2, concurrency)
276+
})
277+
}
278+
279+
// Test_Concurrent_Model_Clone tests concurrent model cloning
280+
func Test_Concurrent_Model_Clone(t *testing.T) {
281+
table := createInitTable()
282+
defer dropTable(table)
283+
284+
gtest.C(t, func(t *gtest.T) {
285+
baseModel := db.Model(table).Where("id>", 0)
286+
var wg sync.WaitGroup
287+
concurrency := 20
288+
289+
wg.Add(concurrency)
290+
for i := 0; i < concurrency; i++ {
291+
go func(id int) {
292+
defer wg.Done()
293+
// Clone model for each goroutine
294+
m := baseModel.Clone()
295+
result, err := m.Where("id<=", TableSize/2).All()
296+
t.AssertNil(err)
297+
t.AssertGT(len(result), 0)
298+
}(i)
299+
}
300+
wg.Wait()
301+
})
302+
}
303+
304+
// Test_Concurrent_Batch_Insert tests concurrent batch insert operations
305+
func Test_Concurrent_Batch_Insert(t *testing.T) {
306+
table := createTable()
307+
defer dropTable(table)
308+
309+
gtest.C(t, func(t *gtest.T) {
310+
var wg sync.WaitGroup
311+
concurrency := 5
312+
batchSize := 10
313+
314+
wg.Add(concurrency)
315+
for i := 0; i < concurrency; i++ {
316+
go func(batchId int) {
317+
defer wg.Done()
318+
batch := make([]g.Map, 0, batchSize)
319+
for j := 0; j < batchSize; j++ {
320+
id := batchId*batchSize + j
321+
batch = append(batch, g.Map{
322+
"passport": fmt.Sprintf("batch_user_%d", id),
323+
"password": fmt.Sprintf("pass_%d", id),
324+
"nickname": fmt.Sprintf("name_%d", id),
325+
})
326+
}
327+
_, err := db.Model(table).Data(batch).Insert()
328+
t.AssertNil(err)
329+
}(i)
330+
}
331+
wg.Wait()
332+
333+
// Verify all batch inserts
334+
count, err := db.Model(table).Count()
335+
t.AssertNil(err)
336+
t.Assert(count, concurrency*batchSize)
337+
})
338+
}

0 commit comments

Comments
 (0)