vitess.io/vitess@v0.16.2/go/vt/schemamanager/schemamanager_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package schemamanager 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "strings" 24 "testing" 25 26 "vitess.io/vitess/go/vt/logutil" 27 "vitess.io/vitess/go/vt/mysqlctl/tmutils" 28 "vitess.io/vitess/go/vt/topo" 29 "vitess.io/vitess/go/vt/topo/memorytopo" 30 "vitess.io/vitess/go/vt/topo/topoproto" 31 "vitess.io/vitess/go/vt/vttablet/faketmclient" 32 "vitess.io/vitess/go/vt/vttablet/tmclient" 33 "vitess.io/vitess/go/vt/vttablet/tmclienttest" 34 35 querypb "vitess.io/vitess/go/vt/proto/query" 36 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 37 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 38 39 // import the gRPC client implementation for tablet manager 40 _ "vitess.io/vitess/go/vt/vttablet/grpctmclient" 41 ) 42 43 var ( 44 errControllerOpen = errors.New("Open Fail") 45 errControllerRead = errors.New("Read Fail") 46 ) 47 48 func init() { 49 // enforce we will use the right protocol (gRPC) (note the 50 // client is unused, but it is initialized, so it needs to exist) 51 tmclienttest.SetProtocol("go.vt.schemamanager", "grpc") 52 } 53 54 func TestSchemaManagerControllerOpenFail(t *testing.T) { 55 controller := newFakeController( 56 []string{"select * from test_db"}, true, false, false) 57 ctx := context.Background() 58 59 _, err := Run(ctx, controller, newFakeExecutor(t)) 60 if err != errControllerOpen { 61 t.Fatalf("controller.Open fail, should get error: %v, but get error: %v", 62 errControllerOpen, err) 63 } 64 } 65 66 func TestSchemaManagerControllerReadFail(t *testing.T) { 67 controller := newFakeController( 68 []string{"select * from test_db"}, false, true, false) 69 ctx := context.Background() 70 _, err := Run(ctx, controller, newFakeExecutor(t)) 71 if err != errControllerRead { 72 t.Fatalf("controller.Read fail, should get error: %v, but get error: %v", 73 errControllerRead, err) 74 } 75 if !controller.onReadFailTriggered { 76 t.Fatalf("OnReadFail should be called") 77 } 78 } 79 80 func TestSchemaManagerValidationFail(t *testing.T) { 81 controller := newFakeController( 82 []string{"invalid sql"}, false, false, false) 83 ctx := context.Background() 84 85 _, err := Run(ctx, controller, newFakeExecutor(t)) 86 if err == nil || !strings.Contains(err.Error(), "failed to parse sql") { 87 t.Fatalf("run schema change should fail due to executor.Validate fail, but got: %v", err) 88 } 89 } 90 91 func TestSchemaManagerExecutorOpenFail(t *testing.T) { 92 controller := newFakeController( 93 []string{"create table test_table (pk int);"}, false, false, false) 94 controller.SetKeyspace("unknown_keyspace") 95 executor := NewTabletExecutor("TestSchemaManagerExecutorOpenFail", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout) 96 ctx := context.Background() 97 98 _, err := Run(ctx, controller, executor) 99 if err == nil || !strings.Contains(err.Error(), "unknown_keyspace") { 100 t.Fatalf("run schema change should fail due to executor.Open fail, but got: %v", err) 101 } 102 } 103 104 func TestSchemaManagerExecutorExecuteFail(t *testing.T) { 105 controller := newFakeController( 106 []string{"create table test_table (pk int);"}, false, false, false) 107 executor := NewTabletExecutor("TestSchemaManagerExecutorExecuteFail", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout) 108 ctx := context.Background() 109 110 _, err := Run(ctx, controller, executor) 111 if err == nil || !strings.Contains(err.Error(), "unknown database: vt_test_keyspace") { 112 t.Fatalf("run schema change should fail due to executor.Execute fail, but got: %v", err) 113 } 114 } 115 116 func TestSchemaManagerRun(t *testing.T) { 117 sql := "create table test_table (pk int)" 118 controller := newFakeController( 119 []string{sql}, false, false, false) 120 fakeTmc := newFakeTabletManagerClient() 121 fakeTmc.AddSchemaChange(sql, &tabletmanagerdatapb.SchemaChangeResult{ 122 BeforeSchema: &tabletmanagerdatapb.SchemaDefinition{}, 123 AfterSchema: &tabletmanagerdatapb.SchemaDefinition{ 124 DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */", 125 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 126 { 127 Name: "test_table", 128 Schema: sql, 129 Type: tmutils.TableBaseTable, 130 }, 131 }, 132 }, 133 }) 134 135 fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{}) 136 executor := NewTabletExecutor("TestSchemaManagerRun", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout) 137 138 ctx := context.Background() 139 resp, err := Run(ctx, controller, executor) 140 141 if len(resp.UUIDs) > 0 { 142 t.Fatalf("response should contain an empty list of UUIDs, found %v", len(resp.UUIDs)) 143 } 144 145 if err != nil { 146 t.Fatalf("schema change should success but get error: %v", err) 147 } 148 if !controller.onReadSuccessTriggered { 149 t.Fatalf("OnReadSuccess should be called") 150 } 151 if controller.onReadFailTriggered { 152 t.Fatalf("OnReadFail should not be called") 153 } 154 if !controller.onValidationSuccessTriggered { 155 t.Fatalf("OnValidateSuccess should be called") 156 } 157 if controller.onValidationFailTriggered { 158 t.Fatalf("OnValidationFail should not be called") 159 } 160 if !controller.onExecutorCompleteTriggered { 161 t.Fatalf("OnExecutorComplete should be called") 162 } 163 } 164 165 func TestSchemaManagerExecutorFail(t *testing.T) { 166 sql := "create table test_table (pk int)" 167 controller := newFakeController([]string{sql}, false, false, false) 168 fakeTmc := newFakeTabletManagerClient() 169 fakeTmc.AddSchemaChange(sql, &tabletmanagerdatapb.SchemaChangeResult{ 170 BeforeSchema: &tabletmanagerdatapb.SchemaDefinition{}, 171 AfterSchema: &tabletmanagerdatapb.SchemaDefinition{ 172 DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */", 173 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 174 { 175 Name: "test_table", 176 Schema: sql, 177 Type: tmutils.TableBaseTable, 178 }, 179 }, 180 }, 181 }) 182 183 fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{}) 184 fakeTmc.EnableExecuteFetchAsDbaError = true 185 executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout) 186 187 ctx := context.Background() 188 resp, err := Run(ctx, controller, executor) 189 if len(resp.UUIDs) > 0 { 190 t.Fatalf("response should contain an empty list of UUIDs, found %v", len(resp.UUIDs)) 191 } 192 193 if err == nil || !strings.Contains(err.Error(), "schema change failed") { 194 t.Fatalf("schema change should fail, but got err: %v", err) 195 } 196 } 197 198 func TestSchemaManagerRegisterControllerFactory(t *testing.T) { 199 sql := "create table test_table (pk int)" 200 RegisterControllerFactory( 201 "test_controller", 202 func(params map[string]string) (Controller, error) { 203 return newFakeController([]string{sql}, false, false, false), nil 204 205 }) 206 207 _, err := GetControllerFactory("unknown") 208 if err == nil || !strings.Contains(err.Error(), "there is no data sourcer factory") { 209 t.Fatalf("controller factory is not registered, GetControllerFactory should return an error, but got: %v", err) 210 } 211 _, err = GetControllerFactory("test_controller") 212 if err != nil { 213 t.Fatalf("GetControllerFactory should succeed, but get an error: %v", err) 214 } 215 func() { 216 defer func() { 217 err := recover() 218 if err == nil { 219 t.Fatalf("RegisterControllerFactory should fail, it registers a registered ControllerFactory") 220 } 221 }() 222 RegisterControllerFactory( 223 "test_controller", 224 func(params map[string]string) (Controller, error) { 225 return newFakeController([]string{sql}, false, false, false), nil 226 227 }) 228 }() 229 } 230 231 func newFakeExecutor(t *testing.T) *TabletExecutor { 232 return NewTabletExecutor("newFakeExecutor", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout) 233 } 234 235 func newFakeTabletManagerClient() *fakeTabletManagerClient { 236 return &fakeTabletManagerClient{ 237 TabletManagerClient: faketmclient.NewFakeTabletManagerClient(), 238 preflightSchemas: make(map[string]*tabletmanagerdatapb.SchemaChangeResult), 239 schemaDefinitions: make(map[string]*tabletmanagerdatapb.SchemaDefinition), 240 } 241 } 242 243 type fakeTabletManagerClient struct { 244 tmclient.TabletManagerClient 245 EnableExecuteFetchAsDbaError bool 246 preflightSchemas map[string]*tabletmanagerdatapb.SchemaChangeResult 247 schemaDefinitions map[string]*tabletmanagerdatapb.SchemaDefinition 248 } 249 250 func (client *fakeTabletManagerClient) AddSchemaChange(sql string, schemaResult *tabletmanagerdatapb.SchemaChangeResult) { 251 client.preflightSchemas[sql] = schemaResult 252 } 253 254 func (client *fakeTabletManagerClient) AddSchemaDefinition( 255 dbName string, schemaDefinition *tabletmanagerdatapb.SchemaDefinition) { 256 client.schemaDefinitions[dbName] = schemaDefinition 257 } 258 259 func (client *fakeTabletManagerClient) PreflightSchema(ctx context.Context, tablet *topodatapb.Tablet, changes []string) ([]*tabletmanagerdatapb.SchemaChangeResult, error) { 260 var result []*tabletmanagerdatapb.SchemaChangeResult 261 for _, change := range changes { 262 scr, ok := client.preflightSchemas[change] 263 if ok { 264 result = append(result, scr) 265 } else { 266 result = append(result, &tabletmanagerdatapb.SchemaChangeResult{}) 267 } 268 } 269 return result, nil 270 } 271 272 func (client *fakeTabletManagerClient) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) { 273 result, ok := client.schemaDefinitions[topoproto.TabletDbName(tablet)] 274 if !ok { 275 return nil, fmt.Errorf("unknown database: %s", topoproto.TabletDbName(tablet)) 276 } 277 return result, nil 278 } 279 280 func (client *fakeTabletManagerClient) ExecuteFetchAsDba(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsDbaRequest) (*querypb.QueryResult, error) { 281 if client.EnableExecuteFetchAsDbaError { 282 return nil, fmt.Errorf("ExecuteFetchAsDba occur an unknown error") 283 } 284 return client.TabletManagerClient.ExecuteFetchAsDba(ctx, tablet, usePool, req) 285 } 286 287 // newFakeTopo returns a topo with: 288 // - a keyspace named 'test_keyspace'. 289 // - 3 shards named '1', '2', '3'. 290 // - A primary tablet for each shard. 291 func newFakeTopo(t *testing.T) *topo.Server { 292 ts := memorytopo.NewServer("test_cell") 293 ctx := context.Background() 294 if err := ts.CreateKeyspace(ctx, "test_keyspace", &topodatapb.Keyspace{}); err != nil { 295 t.Fatalf("CreateKeyspace failed: %v", err) 296 } 297 for i, shard := range []string{"0", "1", "2"} { 298 if err := ts.CreateShard(ctx, "test_keyspace", shard); err != nil { 299 t.Fatalf("CreateShard(%v) failed: %v", shard, err) 300 } 301 tablet := &topodatapb.Tablet{ 302 Alias: &topodatapb.TabletAlias{ 303 Cell: "test_cell", 304 Uid: uint32(i + 1), 305 }, 306 Keyspace: "test_keyspace", 307 Shard: shard, 308 } 309 if err := ts.CreateTablet(ctx, tablet); err != nil { 310 t.Fatalf("CreateTablet failed: %v", err) 311 } 312 if _, err := ts.UpdateShardFields(ctx, "test_keyspace", shard, func(si *topo.ShardInfo) error { 313 si.Shard.PrimaryAlias = tablet.Alias 314 return nil 315 }); err != nil { 316 t.Fatalf("UpdateShardFields failed: %v", err) 317 } 318 } 319 if err := ts.CreateKeyspace(ctx, "unsharded_keyspace", &topodatapb.Keyspace{}); err != nil { 320 t.Fatalf("CreateKeyspace failed: %v", err) 321 } 322 if err := ts.CreateShard(ctx, "unsharded_keyspace", "0"); err != nil { 323 t.Fatalf("CreateShard(%v) failed: %v", "0", err) 324 } 325 tablet := &topodatapb.Tablet{ 326 Alias: &topodatapb.TabletAlias{ 327 Cell: "test_cell", 328 Uid: uint32(4), 329 }, 330 Keyspace: "test_keyspace", 331 Shard: "0", 332 } 333 if err := ts.CreateTablet(ctx, tablet); err != nil { 334 t.Fatalf("CreateTablet failed: %v", err) 335 } 336 if _, err := ts.UpdateShardFields(ctx, "unsharded_keyspace", "0", func(si *topo.ShardInfo) error { 337 si.Shard.PrimaryAlias = tablet.Alias 338 return nil 339 }); err != nil { 340 t.Fatalf("UpdateShardFields failed: %v", err) 341 } 342 return ts 343 } 344 345 type fakeController struct { 346 sqls []string 347 keyspace string 348 openFail bool 349 readFail bool 350 closeFail bool 351 onReadSuccessTriggered bool 352 onReadFailTriggered bool 353 onValidationSuccessTriggered bool 354 onValidationFailTriggered bool 355 onExecutorCompleteTriggered bool 356 } 357 358 func newFakeController( 359 sqls []string, openFail bool, readFail bool, closeFail bool) *fakeController { 360 return &fakeController{ 361 sqls: sqls, 362 keyspace: "test_keyspace", 363 openFail: openFail, 364 readFail: readFail, 365 closeFail: closeFail, 366 } 367 } 368 369 func (controller *fakeController) SetKeyspace(keyspace string) { 370 controller.keyspace = keyspace 371 } 372 373 func (controller *fakeController) Open(ctx context.Context) error { 374 if controller.openFail { 375 return errControllerOpen 376 } 377 return nil 378 } 379 380 func (controller *fakeController) Read(ctx context.Context) ([]string, error) { 381 if controller.readFail { 382 return nil, errControllerRead 383 } 384 return controller.sqls, nil 385 } 386 387 func (controller *fakeController) Close() { 388 } 389 390 func (controller *fakeController) Keyspace() string { 391 return controller.keyspace 392 } 393 394 func (controller *fakeController) OnReadSuccess(ctx context.Context) error { 395 controller.onReadSuccessTriggered = true 396 return nil 397 } 398 399 func (controller *fakeController) OnReadFail(ctx context.Context, err error) error { 400 controller.onReadFailTriggered = true 401 return err 402 } 403 404 func (controller *fakeController) OnValidationSuccess(ctx context.Context) error { 405 controller.onValidationSuccessTriggered = true 406 return nil 407 } 408 409 func (controller *fakeController) OnValidationFail(ctx context.Context, err error) error { 410 controller.onValidationFailTriggered = true 411 return err 412 } 413 414 func (controller *fakeController) OnExecutorComplete(ctx context.Context, result *ExecuteResult) error { 415 controller.onExecutorCompleteTriggered = true 416 return nil 417 } 418 419 var _ Controller = (*fakeController)(nil)