Skip to content

Commit 4dcf563

Browse files
authored
Adding 5K tables integration test for schema migration (#1324)
1 parent 03e1b89 commit 4dcf563

9 files changed

Lines changed: 15798 additions & 488 deletions

File tree

.github/workflows/integration-tests-against-omni.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jobs:
4040
MYSQLDB_FKACTION: test_foreign_key_action_data
4141
MYSQLDB_CHECK_CONSTRAINT: test_mysql_checkconstraint
4242
MYSQLDB_DATA_TYPES: test_mysql_data_types
43+
MYSQLDB_5000_TABLES: test_mysql_5000_tables
4344
MYSQLPWD: root
4445

4546
# sql server envs
@@ -109,6 +110,8 @@ jobs:
109110
- run: mysql -v -P 3306 --protocol=tcp -u root -proot < test_data/mysql_foreignkeyaction_dump.test.out
110111
- run: mysql -v -P 3306 --protocol=tcp -u root -proot < test_data/mysql_checkconstraint_dump.test.out
111112
- run: mysql -v -P 3306 --protocol=tcp -u root -proot < test_data/mysql_data_types_dump.test.out
113+
- run: mysql -v -P 3306 --protocol=tcp -u root -proot -e "CREATE DATABASE test_mysql_5000_tables;"
114+
- run: mysql -v -P 3306 --protocol=tcp -u root -proot test_mysql_5000_tables < test_data/mysql_5000_tables.sql
112115

113116
# init sql server with test_data
114117
# since we use ubuntu-latest container, we should ensure that the path matches the latest from https://packages.microsoft.com/config/ubuntu/

import_file/csv_data_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func getCommonInfoSchemaMock(tableCount int) *MockInfoSchemaInterface {
136136
MockProcessData: func(conv *internal.Conv, infoSchema common.InfoSchema, additionalAttributes internal.AdditionalDataAttributes) {
137137
},
138138
MockSetRowStats: func(conv *internal.Conv, infoSchema common.InfoSchema) {},
139-
MockProcessTable: func(conv *internal.Conv, table common.SchemaAndName, infoSchema common.InfoSchema) (schema.Table, error) {
139+
MockProcessTable: func(conv *internal.Conv, table common.SchemaAndName, infoSchema common.StandardInfoSchema) (schema.Table, error) {
140140
return schema.Table{}, nil
141141
},
142142
MockGetIncludedSrcTablesFromConv: func(conv *internal.Conv) (schemaToTablesMap map[string]internal.SchemaDetails, err error) {
@@ -149,7 +149,7 @@ type MockInfoSchemaInterface struct {
149149
MockGenerateSrcSchema func(conv *internal.Conv, infoSchema common.InfoSchema, numWorkers int) (int, error)
150150
MockProcessData func(conv *internal.Conv, infoSchema common.InfoSchema, additionalAttributes internal.AdditionalDataAttributes)
151151
MockSetRowStats func(conv *internal.Conv, infoSchema common.InfoSchema)
152-
MockProcessTable func(conv *internal.Conv, table common.SchemaAndName, infoSchema common.InfoSchema) (schema.Table, error)
152+
MockProcessTable func(conv *internal.Conv, table common.SchemaAndName, infoSchema common.StandardInfoSchema) (schema.Table, error)
153153
MockGetIncludedSrcTablesFromConv func(conv *internal.Conv) (schemaToTablesMap map[string]internal.SchemaDetails, err error)
154154
}
155155

@@ -182,7 +182,7 @@ func (m *MockInfoSchemaInterface) ProcessData(conv *internal.Conv, infoSchema co
182182
func (m *MockInfoSchemaInterface) SetRowStats(conv *internal.Conv, infoSchema common.InfoSchema) {
183183
}
184184

185-
func (m *MockInfoSchemaInterface) ProcessTable(conv *internal.Conv, table common.SchemaAndName, infoSchema common.InfoSchema) (schema.Table, error) {
185+
func (m *MockInfoSchemaInterface) ProcessTable(conv *internal.Conv, table common.SchemaAndName, infoSchema common.StandardInfoSchema) (schema.Table, error) {
186186
return m.MockProcessTable(conv, table, infoSchema)
187187
}
188188

internal/test_util.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ func assertSrcColDef(t *testing.T, conv *Conv, tableId string, expectedColDef, a
157157
assert.NotEqual(t, colId, "")
158158
actualCol := actualColDef[colId]
159159
actualCol.Id = ""
160+
col.DefaultValue.Value.ExpressionId = ""
161+
col.GeneratedColumn.Value.ExpressionId = ""
162+
actualCol.DefaultValue.Value.ExpressionId = ""
163+
actualCol.GeneratedColumn.Value.ExpressionId = ""
160164
assert.Equal(t, col, actualCol)
161165
}
162166
}

sources/common/infoschema.go

Lines changed: 135 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,58 @@ type InfoSchema interface {
3434
GetToDdl() ToDdl
3535
GetTableName(schema string, tableName string) string
3636
GetTables() ([]SchemaAndName, error)
37-
GetColumns(conv *internal.Conv, table SchemaAndName, constraints map[string][]string, primaryKeys []string) (map[string]schema.Column, []string, error)
3837
GetRowsFromTable(conv *internal.Conv, srcTable string) (interface{}, error)
3938
GetRowCount(table SchemaAndName) (int64, error)
39+
ProcessData(conv *internal.Conv, tableId string, srcSchema schema.Table, spCols []string, spSchema ddl.CreateTable, additionalAttributes internal.AdditionalDataAttributes) error
40+
41+
}
42+
43+
// StandardInfoSchema supports per-table fetching of metadata.
44+
type StandardInfoSchema interface {
45+
InfoSchema
46+
// GetColumns fetches column definitions for a single table.
47+
// Returns a map of column ID to schema.Column, a list of column IDs in ordinal order, and any error.
48+
GetColumns(conv *internal.Conv, table SchemaAndName, constraints map[string][]string, primaryKeys []string) (map[string]schema.Column, []string, error)
49+
// GetConstraints fetches constraints (primary keys, check constraints, etc.) for a single table.
50+
// Returns a list of primary key column names, a list of check constraints, a map of column name to list of constraint types, and any error.
4051
GetConstraints(conv *internal.Conv, table SchemaAndName) ([]string, []schema.CheckConstraint, map[string][]string, error)
52+
// GetForeignKeys fetches foreign key constraints for a single table.
53+
// Returns a list of schema.ForeignKey and any error.
4154
GetForeignKeys(conv *internal.Conv, table SchemaAndName) (foreignKeys []schema.ForeignKey, err error)
55+
// GetIndexes fetches indexes for a single table.
56+
// Returns a list of schema.Index and any error.
4257
GetIndexes(conv *internal.Conv, table SchemaAndName, colNameIdMp map[string]string) ([]schema.Index, error)
43-
ProcessData(conv *internal.Conv, tableId string, srcSchema schema.Table, spCols []string, spSchema ddl.CreateTable, additionalAttributes internal.AdditionalDataAttributes) error
58+
}
4459

60+
// TableColumns contains column definitions and IDs for a table.
61+
type TableColumns struct {
62+
ColDefs map[string]schema.Column
63+
ColIds []string
64+
}
65+
66+
// TableConstraints contains constraints for a table.
67+
type TableConstraints struct {
68+
PrimaryKeys []string
69+
CheckConstraints []schema.CheckConstraint
70+
ColumnConstraints map[string][]string // colName -> constraintTypes
71+
}
72+
73+
// TableForeignKeys contains foreign keys for a table.
74+
type TableForeignKeys struct {
75+
ForeignKeys []schema.ForeignKey
76+
}
77+
78+
// BatchedInfoSchema supports bulk fetching of metadata for multiple tables.
79+
type BatchedInfoSchema interface {
80+
InfoSchema
81+
// GetColumnsBatch fetches column definitions for a batch of tables.
82+
GetColumnsBatch(conv *internal.Conv, tables []SchemaAndName) (map[string]TableColumns, error)
83+
// GetConstraintsBatch fetches constraints for a batch of tables.
84+
GetConstraintsBatch(conv *internal.Conv, tables []SchemaAndName) (map[string]TableConstraints, error)
85+
// GetForeignKeysBatch fetches foreign key constraints for a batch of tables.
86+
GetForeignKeysBatch(conv *internal.Conv, tables []SchemaAndName) (map[string]TableForeignKeys, error)
87+
// GetIndexesBatch fetches indexes for a batch of tables.
88+
GetIndexesBatch(conv *internal.Conv, tables []SchemaAndName, colDefs map[string]TableColumns) (map[string][]schema.Index, error)
4589
}
4690

4791
// SchemaAndName contains the schema and name for a table
@@ -65,7 +109,7 @@ type InfoSchemaInterface interface {
65109
GenerateSrcSchema(conv *internal.Conv, infoSchema InfoSchema, numWorkers int) (int, error)
66110
ProcessData(conv *internal.Conv, infoSchema InfoSchema, additionalAttributes internal.AdditionalDataAttributes)
67111
SetRowStats(conv *internal.Conv, infoSchema InfoSchema)
68-
ProcessTable(conv *internal.Conv, table SchemaAndName, infoSchema InfoSchema) (schema.Table, error)
112+
ProcessTable(conv *internal.Conv, table SchemaAndName, infoSchema StandardInfoSchema) (schema.Table, error)
69113
GetIncludedSrcTablesFromConv(conv *internal.Conv) (schemaToTablesMap map[string]internal.SchemaDetails, err error)
70114
}
71115
type InfoSchemaImpl struct{}
@@ -110,8 +154,81 @@ func (is *InfoSchemaImpl) GenerateSrcSchema(conv *internal.Conv, infoSchema Info
110154
numWorkers = DefaultWorkers
111155
}
112156

157+
var tableCount int
158+
var e error
159+
160+
if bis, ok := infoSchema.(BatchedInfoSchema); ok {
161+
tableCount, e = is.generateSrcSchemaBatched(conv, bis, tables, numWorkers)
162+
} else if sis, ok := infoSchema.(StandardInfoSchema); ok {
163+
tableCount, e = is.generateSrcSchemaStandard(conv, sis, tables, numWorkers)
164+
} else {
165+
return 0, fmt.Errorf("source does not support schema fetching")
166+
}
167+
168+
if e != nil {
169+
return 0, e
170+
}
171+
172+
internal.ResolveForeignKeyIds(conv.SrcSchema)
173+
return tableCount, nil
174+
}
175+
176+
func (is *InfoSchemaImpl) generateSrcSchemaBatched(conv *internal.Conv, bis BatchedInfoSchema, tables []SchemaAndName, numWorkers int) (int, error) {
177+
batchSize := 50
178+
var batches [][]SchemaAndName
179+
for i := 0; i < len(tables); i += batchSize {
180+
end := i + batchSize
181+
if end > len(tables) {
182+
end = len(tables)
183+
}
184+
batches = append(batches, tables[i:end])
185+
}
186+
187+
asyncProcessBatch := func(batch []SchemaAndName, mutex *sync.Mutex) task.TaskResult[[]SchemaAndName] {
188+
tableCols, err := bis.GetColumnsBatch(conv, batch)
189+
if err != nil {
190+
return task.TaskResult[[]SchemaAndName]{Result: batch, Err: err}
191+
}
192+
tableConstraints, err := bis.GetConstraintsBatch(conv, batch)
193+
if err != nil {
194+
return task.TaskResult[[]SchemaAndName]{Result: batch, Err: err}
195+
}
196+
tableForeignKeys, err := bis.GetForeignKeysBatch(conv, batch)
197+
if err != nil {
198+
return task.TaskResult[[]SchemaAndName]{Result: batch, Err: err}
199+
}
200+
indexes, err := bis.GetIndexesBatch(conv, batch, tableCols)
201+
if err != nil {
202+
return task.TaskResult[[]SchemaAndName]{Result: batch, Err: err}
203+
}
204+
205+
mutex.Lock()
206+
for _, t := range batch {
207+
name := bis.GetTableName(t.Schema, t.Name)
208+
cols := tableCols[t.Name]
209+
constraints := tableConstraints[t.Name]
210+
fks := tableForeignKeys[t.Name]
211+
212+
tableObj := BuildSchemaTable(t, name, cols.ColDefs, cols.ColIds, constraints.PrimaryKeys, constraints.CheckConstraints, indexes[t.Name], fks.ForeignKeys)
213+
conv.SrcSchema[tableObj.Id] = tableObj
214+
}
215+
mutex.Unlock()
216+
217+
return task.TaskResult[[]SchemaAndName]{Result: batch, Err: nil}
218+
}
219+
220+
r := task.RunParallelTasksImpl[[]SchemaAndName, []SchemaAndName]{}
221+
_, e := r.RunParallelTasks(batches, numWorkers, asyncProcessBatch, true)
222+
223+
if e != nil {
224+
return 0, e
225+
}
226+
return len(tables), nil
227+
}
228+
229+
func (is *InfoSchemaImpl) generateSrcSchemaStandard(conv *internal.Conv, sis StandardInfoSchema, tables []SchemaAndName, numWorkers int) (int, error) {
113230
asyncProcessTable := func(t SchemaAndName, mutex *sync.Mutex) task.TaskResult[SchemaAndName] {
114-
table, e := is.ProcessTable(conv, t, infoSchema)
231+
table, e := is.ProcessTable(conv, t, sis)
115232
mutex.Lock()
116233
conv.SrcSchema[table.Id] = table
117234
mutex.Unlock()
@@ -120,13 +237,10 @@ func (is *InfoSchemaImpl) GenerateSrcSchema(conv *internal.Conv, infoSchema Info
120237
}
121238

122239
r := task.RunParallelTasksImpl[SchemaAndName, SchemaAndName]{}
123-
res, e := r.RunParallelTasks(tables, numWorkers, asyncProcessTable, false)
240+
_, e := r.RunParallelTasks(tables, numWorkers, asyncProcessTable, false)
124241
if e != nil {
125-
logger.Log.Info(fmt.Sprintf("exiting due to error: %s , while processing schema for table %s\n", e, res))
126242
return 0, e
127243
}
128-
129-
internal.ResolveForeignKeyIds(conv.SrcSchema)
130244
return len(tables), nil
131245
}
132246

@@ -180,10 +294,9 @@ func (is *InfoSchemaImpl) SetRowStats(conv *internal.Conv, infoSchema InfoSchema
180294
}
181295
}
182296

183-
func (is *InfoSchemaImpl) ProcessTable(conv *internal.Conv, table SchemaAndName, infoSchema InfoSchema) (schema.Table, error) {
297+
func (is *InfoSchemaImpl) ProcessTable(conv *internal.Conv, table SchemaAndName, infoSchema StandardInfoSchema) (schema.Table, error) {
184298
var t schema.Table
185299
logger.Log.Info(fmt.Sprintf("processing schema for table %s", table))
186-
tblId := internal.GenerateTableId()
187300
primaryKeys, checkConstraints, constraints, err := infoSchema.GetConstraints(conv, table)
188301
if err != nil {
189302
return t, fmt.Errorf("couldn't get constraints for table %s.%s: %s", table.Schema, table.Name, err)
@@ -193,7 +306,6 @@ func (is *InfoSchemaImpl) ProcessTable(conv *internal.Conv, table SchemaAndName,
193306
return t, fmt.Errorf("couldn't get foreign key constraints for table %s.%s: %s", table.Schema, table.Name, err)
194307
}
195308

196-
table.Id = tblId
197309
colDefs, colIds, err := infoSchema.GetColumns(conv, table, constraints, primaryKeys)
198310
if err != nil {
199311
return t, fmt.Errorf("couldn't get schema for table %s.%s: %s", table.Schema, table.Name, err)
@@ -209,11 +321,21 @@ func (is *InfoSchemaImpl) ProcessTable(conv *internal.Conv, table SchemaAndName,
209321
}
210322

211323
name := infoSchema.GetTableName(table.Schema, table.Name)
324+
return BuildSchemaTable(table, name, colDefs, colIds, primaryKeys, checkConstraints, indexes, foreignKeys), nil
325+
}
326+
327+
// BuildSchemaTable constructs a schema.Table struct from fetched metadata.
328+
func BuildSchemaTable(table SchemaAndName, name string, colDefs map[string]schema.Column, colIds []string, primaryKeys []string, checkConstraints []schema.CheckConstraint, indexes []schema.Index, foreignKeys []schema.ForeignKey) schema.Table {
329+
tblId := internal.GenerateTableId()
330+
colNameIdMap := make(map[string]string)
331+
for k, v := range colDefs {
332+
colNameIdMap[v.Name] = k
333+
}
212334
var schemaPKeys []schema.Key
213335
for _, k := range primaryKeys {
214336
schemaPKeys = append(schemaPKeys, schema.Key{ColId: colNameIdMap[k]})
215337
}
216-
t = schema.Table{
338+
return schema.Table{
217339
Id: tblId,
218340
Name: name,
219341
Schema: table.Schema,
@@ -224,9 +346,9 @@ func (is *InfoSchemaImpl) ProcessTable(conv *internal.Conv, table SchemaAndName,
224346
CheckConstraints: checkConstraints,
225347
Indexes: indexes,
226348
ForeignKeys: foreignKeys}
227-
return t, nil
228349
}
229350

351+
230352
// getIncludedSrcTablesFromConv fetches the list of tables
231353
// from the source database that need to be migrated.
232354
func (is *InfoSchemaImpl) GetIncludedSrcTablesFromConv(conv *internal.Conv) (schemaToTablesMap map[string]internal.SchemaDetails, err error) {

sources/common/mocks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func (mis *MockInfoSchema) GenerateSrcSchema(conv *internal.Conv, infoSchema Inf
3535
func (mis *MockInfoSchema) ProcessData(conv *internal.Conv, infoSchema InfoSchema, additionalAttributes internal.AdditionalDataAttributes) {
3636
}
3737
func (mis *MockInfoSchema) SetRowStats(conv *internal.Conv, infoSchema InfoSchema) {}
38-
func (mis *MockInfoSchema) processTable(conv *internal.Conv, table SchemaAndName, infoSchema InfoSchema) (schema.Table, error) {
38+
func (mis *MockInfoSchema) ProcessTable(conv *internal.Conv, table SchemaAndName, infoSchema StandardInfoSchema) (schema.Table, error) {
3939
args := mis.Called(conv, table, infoSchema)
4040
return args.Get(0).(schema.Table), args.Error(1)
4141
}

0 commit comments

Comments
 (0)