Skip to content

Commit 1e7c953

Browse files
committed
Merge branch 'main' into snapshot-conn-explicit-locks
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
2 parents 4dcc3ff + ae8d61e commit 1e7c953

File tree

121 files changed

+6309
-2216
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+6309
-2216
lines changed

go/mysql/collations/integration/main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func mysqlconn(t *testing.T) *mysql.Conn {
4747
if err != nil {
4848
t.Fatal(err)
4949
}
50-
if !strings.HasPrefix(conn.ServerVersion, "8.0.") {
50+
if !strings.HasPrefix(conn.ServerVersion, "8.") {
5151
conn.Close()
5252
t.Skipf("collation integration tests are only supported in MySQL 8.0+")
5353
}

go/mysql/sqlerror/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ const (
250250
ERJSONValueTooBig = ErrorCode(3150)
251251
ERJSONDocumentTooDeep = ErrorCode(3157)
252252

253+
ERLockNowait = ErrorCode(3572)
253254
ERRegexpStringNotTerminated = ErrorCode(3684)
254255
ERRegexpBufferOverflow = ErrorCode(3684)
255256
ERRegexpIllegalArgument = ErrorCode(3685)

go/test/endtoend/utils/utils.go

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -256,51 +256,46 @@ func WaitForAuthoritative(t *testing.T, ks, tbl string, readVSchema func() (*int
256256

257257
// WaitForKsError waits for the ks error field to be populated and returns it.
258258
func WaitForKsError(t *testing.T, vtgateProcess cluster.VtgateProcess, ks string) string {
259+
var errString string
260+
WaitForVschemaCondition(t, vtgateProcess, ks, func(t *testing.T, keyspace map[string]interface{}) bool {
261+
ksErr, fieldPresent := keyspace["error"]
262+
if !fieldPresent {
263+
return false
264+
}
265+
var ok bool
266+
errString, ok = ksErr.(string)
267+
return ok
268+
})
269+
return errString
270+
}
271+
272+
// WaitForVschemaCondition waits for the condition to be true
273+
func WaitForVschemaCondition(t *testing.T, vtgateProcess cluster.VtgateProcess, ks string, conditionMet func(t *testing.T, keyspace map[string]interface{}) bool) {
259274
timeout := time.After(60 * time.Second)
260275
for {
261276
select {
262277
case <-timeout:
263-
t.Fatalf("schema tracking did not find error in '%s'", ks)
264-
return ""
278+
t.Fatalf("schema tracking did not met the condition within the time for keyspace: %s", ks)
265279
default:
266280
res, err := vtgateProcess.ReadVSchema()
267281
require.NoError(t, err, res)
268282
kss := convertToMap(*res)["keyspaces"]
269283
ksMap := convertToMap(convertToMap(kss)[ks])
270-
ksErr, fieldPresent := ksMap["error"]
271-
if !fieldPresent {
272-
time.Sleep(100 * time.Millisecond)
273-
continue
284+
if conditionMet(t, ksMap) {
285+
return
274286
}
275-
errString, isErr := ksErr.(string)
276-
if !isErr {
277-
time.Sleep(100 * time.Millisecond)
278-
continue
279-
}
280-
return errString
287+
time.Sleep(100 * time.Millisecond)
281288
}
282289
}
283290
}
284291

285292
// WaitForTableDeletions waits for a table to be deleted
286-
func WaitForTableDeletions(ctx context.Context, t *testing.T, vtgateProcess cluster.VtgateProcess, ks, tbl string) error {
287-
for {
288-
select {
289-
case <-ctx.Done():
290-
return fmt.Errorf("schema tracking still found the table '%s'", tbl)
291-
default:
292-
res, err := vtgateProcess.ReadVSchema()
293-
require.NoError(t, err, res)
294-
keyspacesMap := convertToMap(*res)["keyspaces"]
295-
ksMap := convertToMap(keyspacesMap)[ks]
296-
tablesMap := convertToMap(ksMap)["tables"]
297-
_, isPresent := convertToMap(tablesMap)[tbl]
298-
if !isPresent {
299-
return nil
300-
}
301-
time.Sleep(100 * time.Millisecond)
302-
}
303-
}
293+
func WaitForTableDeletions(ctx context.Context, t *testing.T, vtgateProcess cluster.VtgateProcess, ks, tbl string) {
294+
WaitForVschemaCondition(t, vtgateProcess, ks, func(t *testing.T, keyspace map[string]interface{}) bool {
295+
tablesMap := keyspace["tables"]
296+
_, isPresent := convertToMap(tablesMap)[tbl]
297+
return !isPresent
298+
})
304299
}
305300

306301
// WaitForColumn waits for a table's column to be present

go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go

Lines changed: 86 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"vitess.io/vitess/go/sqltypes"
3232
"vitess.io/vitess/go/test/endtoend/utils"
3333
"vitess.io/vitess/go/vt/log"
34+
"vitess.io/vitess/go/vt/sqlparser"
3435
)
3536

3637
type QueryFormat string
@@ -52,6 +53,7 @@ type fuzzer struct {
5253
updateShare int
5354
concurrency int
5455
queryFormat QueryFormat
56+
fkState *bool
5557

5658
// shouldStop is an internal state variable, that tells the fuzzer
5759
// whether it should stop or not.
@@ -71,7 +73,7 @@ type debugInfo struct {
7173
}
7274

7375
// newFuzzer creates a new fuzzer struct.
74-
func newFuzzer(concurrency int, maxValForId int, maxValForCol int, insertShare int, deleteShare int, updateShare int, queryFormat QueryFormat) *fuzzer {
76+
func newFuzzer(concurrency int, maxValForId int, maxValForCol int, insertShare int, deleteShare int, updateShare int, queryFormat QueryFormat, fkState *bool) *fuzzer {
7577
fz := &fuzzer{
7678
concurrency: concurrency,
7779
maxValForId: maxValForId,
@@ -80,6 +82,7 @@ func newFuzzer(concurrency int, maxValForId int, maxValForCol int, insertShare i
8082
deleteShare: deleteShare,
8183
updateShare: updateShare,
8284
queryFormat: queryFormat,
85+
fkState: fkState,
8386
wg: sync.WaitGroup{},
8487
}
8588
// Initially the fuzzer thread is stopped.
@@ -123,25 +126,26 @@ func (fz *fuzzer) generateQuery() []string {
123126
}
124127

125128
func getInsertType() string {
126-
return "insert"
129+
return []string{"insert", "replace"}[rand.Intn(2)]
127130
}
128131

129132
// generateInsertDMLQuery generates an INSERT query from the parameters for the fuzzer.
130133
func (fz *fuzzer) generateInsertDMLQuery(insertType string) string {
131134
tableId := rand.Intn(len(fkTables))
132135
idValue := 1 + rand.Intn(fz.maxValForId)
133136
tableName := fkTables[tableId]
137+
setVarFkChecksVal := fz.getSetVarFkChecksVal()
134138
if tableName == "fk_t20" {
135139
colValue := rand.Intn(1 + fz.maxValForCol)
136140
col2Value := rand.Intn(1 + fz.maxValForCol)
137-
return fmt.Sprintf("%s into %v (id, col, col2) values (%v, %v, %v)", insertType, tableName, idValue, convertIntValueToString(colValue), convertIntValueToString(col2Value))
141+
return fmt.Sprintf("%s %vinto %v (id, col, col2) values (%v, %v, %v)", insertType, setVarFkChecksVal, tableName, idValue, convertIntValueToString(colValue), convertIntValueToString(col2Value))
138142
} else if isMultiColFkTable(tableName) {
139143
colaValue := rand.Intn(1 + fz.maxValForCol)
140144
colbValue := rand.Intn(1 + fz.maxValForCol)
141-
return fmt.Sprintf("%s into %v (id, cola, colb) values (%v, %v, %v)", insertType, tableName, idValue, convertIntValueToString(colaValue), convertIntValueToString(colbValue))
145+
return fmt.Sprintf("%s %vinto %v (id, cola, colb) values (%v, %v, %v)", insertType, setVarFkChecksVal, tableName, idValue, convertIntValueToString(colaValue), convertIntValueToString(colbValue))
142146
} else {
143147
colValue := rand.Intn(1 + fz.maxValForCol)
144-
return fmt.Sprintf("%s into %v (id, col) values (%v, %v)", insertType, tableName, idValue, convertIntValueToString(colValue))
148+
return fmt.Sprintf("%s %vinto %v (id, col) values (%v, %v)", insertType, setVarFkChecksVal, tableName, idValue, convertIntValueToString(colValue))
145149
}
146150
}
147151

@@ -150,10 +154,11 @@ func (fz *fuzzer) generateUpdateDMLQuery() string {
150154
tableId := rand.Intn(len(fkTables))
151155
idValue := 1 + rand.Intn(fz.maxValForId)
152156
tableName := fkTables[tableId]
157+
setVarFkChecksVal := fz.getSetVarFkChecksVal()
153158
if tableName == "fk_t20" {
154159
colValue := convertIntValueToString(rand.Intn(1 + fz.maxValForCol))
155160
col2Value := convertIntValueToString(rand.Intn(1 + fz.maxValForCol))
156-
return fmt.Sprintf("update %v set col = %v, col2 = %v where id = %v", tableName, colValue, col2Value, idValue)
161+
return fmt.Sprintf("update %v%v set col = %v, col2 = %v where id = %v", setVarFkChecksVal, tableName, colValue, col2Value, idValue)
157162
} else if isMultiColFkTable(tableName) {
158163
if rand.Intn(2) == 0 {
159164
colaValue := convertIntValueToString(rand.Intn(1 + fz.maxValForCol))
@@ -162,23 +167,24 @@ func (fz *fuzzer) generateUpdateDMLQuery() string {
162167
colaValue = fz.generateExpression(rand.Intn(4)+1, "cola", "colb", "id")
163168
colbValue = fz.generateExpression(rand.Intn(4)+1, "cola", "colb", "id")
164169
}
165-
return fmt.Sprintf("update %v set cola = %v, colb = %v where id = %v", tableName, colaValue, colbValue, idValue)
170+
return fmt.Sprintf("update %v%v set cola = %v, colb = %v where id = %v", setVarFkChecksVal, tableName, colaValue, colbValue, idValue)
166171
} else {
167172
colValue := fz.generateExpression(rand.Intn(4)+1, "cola", "colb", "id")
168173
colToUpdate := []string{"cola", "colb"}[rand.Intn(2)]
169174
return fmt.Sprintf("update %v set %v = %v where id = %v", tableName, colToUpdate, colValue, idValue)
170175
}
171176
} else {
172177
colValue := fz.generateExpression(rand.Intn(4)+1, "col", "id")
173-
return fmt.Sprintf("update %v set col = %v where id = %v", tableName, colValue, idValue)
178+
return fmt.Sprintf("update %v%v set col = %v where id = %v", setVarFkChecksVal, tableName, colValue, idValue)
174179
}
175180
}
176181

177182
// generateDeleteDMLQuery generates a DELETE query from the parameters for the fuzzer.
178183
func (fz *fuzzer) generateDeleteDMLQuery() string {
179184
tableId := rand.Intn(len(fkTables))
180185
idValue := 1 + rand.Intn(fz.maxValForId)
181-
query := fmt.Sprintf("delete from %v where id = %v", fkTables[tableId], idValue)
186+
setVarFkChecksVal := fz.getSetVarFkChecksVal()
187+
query := fmt.Sprintf("delete %vfrom %v where id = %v", setVarFkChecksVal, fkTables[tableId], idValue)
182188
return query
183189
}
184190

@@ -204,6 +210,9 @@ func (fz *fuzzer) runFuzzerThread(t *testing.T, sharded bool, fuzzerThreadId int
204210
// Create a MySQL Compare that connects to both Vitess and MySQL and runs the queries against both.
205211
mcmp, err := utils.NewMySQLCompare(t, vtParams, mysqlParams)
206212
require.NoError(t, err)
213+
if fz.fkState != nil {
214+
mcmp.Exec(fmt.Sprintf("SET FOREIGN_KEY_CHECKS=%v", sqlparser.FkChecksStateString(fz.fkState)))
215+
}
207216
var vitessDb, mysqlDb *sql.DB
208217
if fz.queryFormat == PreparedStatementPacket {
209218
// Open another connection to Vitess using the go-sql-driver so that we can send prepared statements as COM_STMT_PREPARE packets.
@@ -464,6 +473,21 @@ func (fz *fuzzer) generateParameterizedDeleteQuery() (query string, params []any
464473
return fmt.Sprintf("delete from %v where id = ?", fkTables[tableId]), []any{idValue}
465474
}
466475

476+
// getSetVarFkChecksVal generates an optimizer hint to randomly set the foreign key checks to on or off or leave them unaltered.
477+
func (fz *fuzzer) getSetVarFkChecksVal() string {
478+
if fz.concurrency != 1 {
479+
return ""
480+
}
481+
val := rand.Intn(3)
482+
if val == 0 {
483+
return ""
484+
}
485+
if val == 1 {
486+
return "/*+ SET_VAR(foreign_key_checks=On) */ "
487+
}
488+
return "/*+ SET_VAR(foreign_key_checks=Off) */ "
489+
}
490+
467491
// TestFkFuzzTest is a fuzzer test that works by querying the database concurrently.
468492
// We have a pre-written set of query templates that we will use, but the data in the queries will
469493
// be randomly generated. The intent is that we hammer the database as a real-world application would
@@ -615,57 +639,65 @@ func TestFkFuzzTest(t *testing.T) {
615639
updateShare: 50,
616640
}}
617641

618-
for _, tt := range testcases {
619-
for _, testSharded := range []bool{false, true} {
620-
for _, queryFormat := range []QueryFormat{OlapSQLQueries, SQLQueries, PreparedStatmentQueries, PreparedStatementPacket} {
621-
t.Run(getTestName(tt.name, testSharded)+fmt.Sprintf(" QueryFormat - %v", queryFormat), func(t *testing.T) {
622-
mcmp, closer := start(t)
623-
defer closer()
624-
// Set the correct keyspace to use from VtGates.
625-
if testSharded {
626-
t.Skip("Skip test since we don't have sharded foreign key support yet")
627-
_ = utils.Exec(t, mcmp.VtConn, "use `ks`")
628-
} else {
629-
_ = utils.Exec(t, mcmp.VtConn, "use `uks`")
642+
valTrue := true
643+
valFalse := false
644+
for _, fkState := range []*bool{nil, &valTrue, &valFalse} {
645+
for _, tt := range testcases {
646+
for _, testSharded := range []bool{false, true} {
647+
for _, queryFormat := range []QueryFormat{OlapSQLQueries, SQLQueries, PreparedStatmentQueries, PreparedStatementPacket} {
648+
if fkState != nil && (queryFormat != SQLQueries || tt.concurrency != 1) {
649+
continue
630650
}
631-
// Ensure that the Vitess database is originally empty
632-
ensureDatabaseState(t, mcmp.VtConn, true)
633-
ensureDatabaseState(t, mcmp.MySQLConn, true)
634-
635-
// Create the fuzzer.
636-
fz := newFuzzer(tt.concurrency, tt.maxValForId, tt.maxValForCol, tt.insertShare, tt.deleteShare, tt.updateShare, queryFormat)
637-
638-
// Start the fuzzer.
639-
fz.start(t, testSharded)
640-
641-
// Wait for the timeForTesting so that the threads continue to run.
642-
totalTime := time.After(tt.timeForTesting)
643-
done := false
644-
for !done {
645-
select {
646-
case <-totalTime:
647-
done = true
648-
case <-time.After(10 * time.Millisecond):
649-
validateReplication(t)
651+
t.Run(getTestName(tt.name, testSharded)+fmt.Sprintf(" FkState - %v QueryFormat - %v", sqlparser.FkChecksStateString(fkState), queryFormat), func(t *testing.T) {
652+
mcmp, closer := start(t)
653+
defer closer()
654+
// Set the correct keyspace to use from VtGates.
655+
if testSharded {
656+
t.Skip("Skip test since we don't have sharded foreign key support yet")
657+
_ = utils.Exec(t, mcmp.VtConn, "use `ks`")
658+
} else {
659+
_ = utils.Exec(t, mcmp.VtConn, "use `uks`")
660+
}
661+
662+
// Ensure that the Vitess database is originally empty
663+
ensureDatabaseState(t, mcmp.VtConn, true)
664+
ensureDatabaseState(t, mcmp.MySQLConn, true)
665+
666+
// Create the fuzzer.
667+
fz := newFuzzer(tt.concurrency, tt.maxValForId, tt.maxValForCol, tt.insertShare, tt.deleteShare, tt.updateShare, queryFormat, fkState)
668+
669+
// Start the fuzzer.
670+
fz.start(t, testSharded)
671+
672+
// Wait for the timeForTesting so that the threads continue to run.
673+
totalTime := time.After(tt.timeForTesting)
674+
done := false
675+
for !done {
676+
select {
677+
case <-totalTime:
678+
done = true
679+
case <-time.After(10 * time.Millisecond):
680+
validateReplication(t)
681+
}
650682
}
651-
}
652683

653-
fz.stop()
684+
fz.stop()
654685

655-
// We encountered an error while running the fuzzer. Let's print out the information!
656-
if fz.firstFailureInfo != nil {
657-
log.Errorf("Failing query - %v", fz.firstFailureInfo.queryToFail)
658-
for idx, table := range fkTables {
659-
log.Errorf("MySQL data for %v -\n%v", table, fz.firstFailureInfo.mysqlState[idx].Rows)
660-
log.Errorf("Vitess data for %v -\n%v", table, fz.firstFailureInfo.vitessState[idx].Rows)
686+
// We encountered an error while running the fuzzer. Let's print out the information!
687+
if fz.firstFailureInfo != nil {
688+
log.Errorf("Failing query - %v", fz.firstFailureInfo.queryToFail)
689+
for idx, table := range fkTables {
690+
log.Errorf("MySQL data for %v -\n%v", table, fz.firstFailureInfo.mysqlState[idx].Rows)
691+
log.Errorf("Vitess data for %v -\n%v", table, fz.firstFailureInfo.vitessState[idx].Rows)
692+
}
661693
}
662-
}
663694

664-
// ensure Vitess database has some data. This ensures not all the commands failed.
665-
ensureDatabaseState(t, mcmp.VtConn, false)
666-
// Verify the consistency of the data.
667-
verifyDataIsCorrect(t, mcmp, tt.concurrency)
668-
})
695+
// ensure Vitess database has some data. This ensures not all the commands failed.
696+
ensureDatabaseState(t, mcmp.VtConn, false)
697+
// Verify the consistency of the data.
698+
verifyDataIsCorrect(t, mcmp, tt.concurrency)
699+
})
700+
}
669701
}
670702
}
671703
}

0 commit comments

Comments
 (0)