vitess.io/vitess@v0.16.2/go/vt/wrangler/testlib/external_reparent_test.go (about) 1 /* 2 Copyright 2018 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 testlib 18 19 import ( 20 "context" 21 "flag" 22 "testing" 23 "time" 24 25 "vitess.io/vitess/go/vt/discovery" 26 27 "github.com/stretchr/testify/assert" 28 29 "vitess.io/vitess/go/vt/logutil" 30 "vitess.io/vitess/go/vt/topo/memorytopo" 31 "vitess.io/vitess/go/vt/topo/topoproto" 32 "vitess.io/vitess/go/vt/topotools" 33 "vitess.io/vitess/go/vt/vttablet/tmclient" 34 "vitess.io/vitess/go/vt/wrangler" 35 36 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 37 ) 38 39 // The tests in this package test the wrangler version of TabletExternallyReparented 40 // This is the one that is now called by the vtctl command 41 42 // TestTabletExternallyReparentedBasic tests the base cases for TER 43 func TestTabletExternallyReparentedBasic(t *testing.T) { 44 delay := discovery.GetTabletPickerRetryDelay() 45 defer func() { 46 discovery.SetTabletPickerRetryDelay(delay) 47 }() 48 discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) 49 50 ctx := context.Background() 51 ts := memorytopo.NewServer("cell1") 52 wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) 53 vp := NewVtctlPipe(t, ts) 54 defer vp.Close() 55 56 // Create an old primary, a new primary, two good replicas, one bad replica 57 oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) 58 newPrimary := NewFakeTablet(t, wr, "cell1", 1, topodatapb.TabletType_REPLICA, nil) 59 60 // Build keyspace graph 61 err := topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, oldPrimary.Tablet.Keyspace, []string{"cell1"}, false) 62 if err != nil { 63 t.Fatalf("RebuildKeyspaceLocked failed: %v", err) 64 } 65 66 // On the elected primary, we will respond to 67 // TabletActionReplicaWasPromoted 68 newPrimary.StartActionLoop(t, wr) 69 defer newPrimary.StopActionLoop(t) 70 71 // On the old primary, we will only respond to 72 // TabletActionReplicaWasRestarted. 73 oldPrimary.StartActionLoop(t, wr) 74 defer oldPrimary.StopActionLoop(t) 75 76 // First test: reparent to the same primary, make sure it works 77 // as expected. 78 if err := vp.Run([]string{"TabletExternallyReparented", topoproto.TabletAliasString(oldPrimary.Tablet.Alias)}); err != nil { 79 t.Fatalf("TabletExternallyReparented(same primary) should have worked: %v", err) 80 } 81 82 // check the old primary is still primary 83 tablet, err := ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 84 if err != nil { 85 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 86 } 87 if tablet.Type != topodatapb.TabletType_PRIMARY { 88 t.Fatalf("old primary should be PRIMARY but is: %v", tablet.Type) 89 } 90 91 oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) 92 oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ 93 "RESET SLAVE ALL", 94 "FAKE SET MASTER", 95 "START Replica", 96 } 97 98 // This tests the good case, where everything works as planned 99 t.Logf("TabletExternallyReparented(new primary) expecting success") 100 if err := wr.TabletExternallyReparented(ctx, newPrimary.Tablet.Alias); err != nil { 101 t.Fatalf("TabletExternallyReparented(replica) failed: %v", err) 102 } 103 104 // check the new primary is primary 105 tablet, err = ts.GetTablet(ctx, newPrimary.Tablet.Alias) 106 if err != nil { 107 t.Fatalf("GetTablet(%v) failed: %v", newPrimary.Tablet.Alias, err) 108 } 109 if tablet.Type != topodatapb.TabletType_PRIMARY { 110 t.Fatalf("new primary should be PRIMARY but is: %v", tablet.Type) 111 } 112 113 // We have to wait for shard sync to do its magic in the background 114 startTime := time.Now() 115 for { 116 if time.Since(startTime) > 10*time.Second /* timeout */ { 117 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 118 if err != nil { 119 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 120 } 121 t.Fatalf("old primary (%v) should be replica but is: %v", topoproto.TabletAliasString(oldPrimary.Tablet.Alias), tablet.Type) 122 } 123 // check the old primary was converted to replica 124 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 125 if err != nil { 126 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 127 } 128 if tablet.Type == topodatapb.TabletType_REPLICA { 129 break 130 } else { 131 time.Sleep(100 * time.Millisecond /* interval at which to check again */) 132 } 133 } 134 } 135 136 func TestTabletExternallyReparentedToReplica(t *testing.T) { 137 delay := discovery.GetTabletPickerRetryDelay() 138 defer func() { 139 discovery.SetTabletPickerRetryDelay(delay) 140 }() 141 discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) 142 143 ctx := context.Background() 144 ts := memorytopo.NewServer("cell1") 145 wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) 146 147 // Create an old primary, a new primary, two good replicas, one bad replica 148 oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) 149 newPrimary := NewFakeTablet(t, wr, "cell1", 1, topodatapb.TabletType_REPLICA, nil) 150 newPrimary.FakeMysqlDaemon.ReadOnly = true 151 newPrimary.FakeMysqlDaemon.Replicating = true 152 153 // Build keyspace graph 154 err := topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, oldPrimary.Tablet.Keyspace, []string{"cell1"}, false) 155 if err != nil { 156 t.Fatalf("RebuildKeyspaceLocked failed: %v", err) 157 } 158 159 // On the elected primary, we will respond to 160 // TabletActionReplicaWasPromoted 161 newPrimary.StartActionLoop(t, wr) 162 defer newPrimary.StopActionLoop(t) 163 164 // On the old primary, we will only respond to 165 // TabletActionReplicaWasRestarted. 166 oldPrimary.StartActionLoop(t, wr) 167 defer oldPrimary.StopActionLoop(t) 168 169 // Second test: reparent to a replica, and pretend the old 170 // primary is still good to go. 171 oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) 172 oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ 173 "RESET SLAVE ALL", 174 "FAKE SET MASTER", 175 "START Replica", 176 } 177 178 // This tests a bad case: the new designated primary is a replica at mysql level, 179 // but we should do what we're told anyway. 180 if err := wr.TabletExternallyReparented(ctx, newPrimary.Tablet.Alias); err != nil { 181 t.Fatalf("TabletExternallyReparented(replica) error: %v", err) 182 } 183 184 // check that newPrimary is primary 185 tablet, err := ts.GetTablet(ctx, newPrimary.Tablet.Alias) 186 if err != nil { 187 t.Fatalf("GetTablet(%v) failed: %v", newPrimary.Tablet.Alias, err) 188 } 189 if tablet.Type != topodatapb.TabletType_PRIMARY { 190 t.Fatalf("new primary should be PRIMARY but is: %v", tablet.Type) 191 } 192 193 // We have to wait for shard sync to do its magic in the background 194 startTime := time.Now() 195 for { 196 if time.Since(startTime) > 10*time.Second /* timeout */ { 197 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 198 if err != nil { 199 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 200 } 201 t.Fatalf("old primary (%v) should be replica but is: %v", topoproto.TabletAliasString(oldPrimary.Tablet.Alias), tablet.Type) 202 } 203 // check the old primary was converted to replica 204 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 205 if err != nil { 206 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 207 } 208 if tablet.Type == topodatapb.TabletType_REPLICA { 209 break 210 } else { 211 time.Sleep(100 * time.Millisecond /* interval at which to check again */) 212 } 213 } 214 } 215 216 // TestTabletExternallyReparentedWithDifferentMysqlPort makes sure 217 // that if mysql is restarted on the primary-elect tablet and has a different 218 // port, we pick it up correctly. 219 func TestTabletExternallyReparentedWithDifferentMysqlPort(t *testing.T) { 220 delay := discovery.GetTabletPickerRetryDelay() 221 defer func() { 222 discovery.SetTabletPickerRetryDelay(delay) 223 }() 224 discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) 225 226 ctx := context.Background() 227 ts := memorytopo.NewServer("cell1") 228 wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) 229 230 // Create an old primary, a new primary, two good replicas, one bad replica 231 oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) 232 newPrimary := NewFakeTablet(t, wr, "cell1", 1, topodatapb.TabletType_REPLICA, nil) 233 goodReplica := NewFakeTablet(t, wr, "cell1", 2, topodatapb.TabletType_REPLICA, nil) 234 235 // Build keyspace graph 236 err := topotools.RebuildKeyspace(context.Background(), logutil.NewConsoleLogger(), ts, oldPrimary.Tablet.Keyspace, []string{"cell1"}, false) 237 if err != nil { 238 t.Fatalf("RebuildKeyspaceLocked failed: %v", err) 239 } 240 // Now we're restarting mysql on a different port, 3301->3303 241 // but without updating the Tablet record in topology. 242 243 // On the elected primary, we will respond to 244 // TabletActionReplicaWasPromoted, so we need a MysqlDaemon 245 // that returns no primary, and the new port (as returned by mysql) 246 newPrimary.FakeMysqlDaemon.MysqlPort.Set(3303) 247 newPrimary.StartActionLoop(t, wr) 248 defer newPrimary.StopActionLoop(t) 249 250 oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) 251 oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ 252 "RESET SLAVE ALL", 253 "FAKE SET MASTER", 254 "START Replica", 255 } 256 // On the old primary, we will only respond to 257 // TabletActionReplicaWasRestarted and point to the new mysql port 258 oldPrimary.StartActionLoop(t, wr) 259 defer oldPrimary.StopActionLoop(t) 260 261 // On the good replicas, we will respond to 262 // TabletActionReplicaWasRestarted and point to the new mysql port 263 goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(oldPrimary.Tablet)) 264 goodReplica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ 265 // These 4 statements come from tablet startup 266 "STOP SLAVE", 267 "RESET SLAVE ALL", 268 "FAKE SET MASTER", 269 "START SLAVE", 270 } 271 goodReplica.StartActionLoop(t, wr) 272 defer goodReplica.StopActionLoop(t) 273 274 // This tests the good case, where everything works as planned 275 t.Logf("TabletExternallyReparented(new primary) expecting success") 276 if err := wr.TabletExternallyReparented(ctx, newPrimary.Tablet.Alias); err != nil { 277 t.Fatalf("TabletExternallyReparented(replica) failed: %v", err) 278 } 279 // check the new primary is primary 280 tablet, err := ts.GetTablet(ctx, newPrimary.Tablet.Alias) 281 if err != nil { 282 t.Fatalf("GetTablet(%v) failed: %v", newPrimary.Tablet.Alias, err) 283 } 284 if tablet.Type != topodatapb.TabletType_PRIMARY { 285 t.Fatalf("new primary should be PRIMARY but is: %v", tablet.Type) 286 } 287 288 // We have to wait for shard sync to do its magic in the background 289 startTime := time.Now() 290 for { 291 if time.Since(startTime) > 10*time.Second /* timeout */ { 292 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 293 if err != nil { 294 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 295 } 296 t.Fatalf("old primary (%v) should be replica but is: %v", topoproto.TabletAliasString(oldPrimary.Tablet.Alias), tablet.Type) 297 } 298 // check the old primary was converted to replica 299 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 300 if err != nil { 301 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 302 } 303 if tablet.Type == topodatapb.TabletType_REPLICA { 304 break 305 } else { 306 time.Sleep(100 * time.Millisecond /* interval at which to check again */) 307 } 308 } 309 } 310 311 // TestTabletExternallyReparentedContinueOnUnexpectedPrimary makes sure 312 // that we ignore mysql's primary if the flag is set 313 func TestTabletExternallyReparentedContinueOnUnexpectedPrimary(t *testing.T) { 314 delay := discovery.GetTabletPickerRetryDelay() 315 defer func() { 316 discovery.SetTabletPickerRetryDelay(delay) 317 }() 318 discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) 319 320 ctx := context.Background() 321 ts := memorytopo.NewServer("cell1") 322 wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) 323 324 // Create an old primary, a new primary, two good replicas, one bad replica 325 oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) 326 newPrimary := NewFakeTablet(t, wr, "cell1", 1, topodatapb.TabletType_REPLICA, nil) 327 goodReplica := NewFakeTablet(t, wr, "cell1", 2, topodatapb.TabletType_REPLICA, nil) 328 329 // Build keyspace graph 330 err := topotools.RebuildKeyspace(context.Background(), logutil.NewConsoleLogger(), ts, oldPrimary.Tablet.Keyspace, []string{"cell1"}, false) 331 if err != nil { 332 t.Fatalf("RebuildKeyspaceLocked failed: %v", err) 333 } 334 // On the elected primary, we will respond to 335 // TabletActionReplicaWasPromoted, so we need a MysqlDaemon 336 // that returns no primary, and the new port (as returned by mysql) 337 newPrimary.StartActionLoop(t, wr) 338 defer newPrimary.StopActionLoop(t) 339 340 oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) 341 oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ 342 "RESET SLAVE ALL", 343 "FAKE SET MASTER", 344 "START Replica", 345 } 346 // On the old primary, we will only respond to 347 // TabletActionReplicaWasRestarted and point to a bad host 348 oldPrimary.StartActionLoop(t, wr) 349 defer oldPrimary.StopActionLoop(t) 350 351 // On the good replica, we will respond to 352 // TabletActionReplicaWasRestarted and point to a bad host 353 goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(oldPrimary.Tablet)) 354 goodReplica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ 355 // These 4 statements come from tablet startup 356 "STOP SLAVE", 357 "RESET SLAVE ALL", 358 "FAKE SET MASTER", 359 "START SLAVE", 360 } 361 goodReplica.StartActionLoop(t, wr) 362 defer goodReplica.StopActionLoop(t) 363 364 // This tests the good case, where everything works as planned 365 t.Logf("TabletExternallyReparented(new primary) expecting success") 366 if err := wr.TabletExternallyReparented(ctx, newPrimary.Tablet.Alias); err != nil { 367 t.Fatalf("TabletExternallyReparented(replica) failed: %v", err) 368 } 369 // check the new primary is primary 370 tablet, err := ts.GetTablet(ctx, newPrimary.Tablet.Alias) 371 if err != nil { 372 t.Fatalf("GetTablet(%v) failed: %v", newPrimary.Tablet.Alias, err) 373 } 374 if tablet.Type != topodatapb.TabletType_PRIMARY { 375 t.Fatalf("new primary should be PRIMARY but is: %v", tablet.Type) 376 } 377 // We have to wait for shard sync to do its magic in the background 378 startTime := time.Now() 379 for { 380 if time.Since(startTime) > 10*time.Second /* timeout */ { 381 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 382 if err != nil { 383 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 384 } 385 t.Fatalf("old primary (%v) should be replica but is: %v", topoproto.TabletAliasString(oldPrimary.Tablet.Alias), tablet.Type) 386 } 387 // check the old primary was converted to replica 388 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 389 if err != nil { 390 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 391 } 392 if tablet.Type == topodatapb.TabletType_REPLICA { 393 break 394 } else { 395 time.Sleep(100 * time.Millisecond /* interval at which to check again */) 396 } 397 } 398 } 399 400 func TestTabletExternallyReparentedRerun(t *testing.T) { 401 delay := discovery.GetTabletPickerRetryDelay() 402 defer func() { 403 discovery.SetTabletPickerRetryDelay(delay) 404 }() 405 discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) 406 407 ctx := context.Background() 408 ts := memorytopo.NewServer("cell1") 409 wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) 410 411 // Create an old primary, a new primary, and a good replica. 412 oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_PRIMARY, nil) 413 newPrimary := NewFakeTablet(t, wr, "cell1", 1, topodatapb.TabletType_REPLICA, nil) 414 goodReplica := NewFakeTablet(t, wr, "cell1", 2, topodatapb.TabletType_REPLICA, nil) 415 416 // Build keyspace graph 417 err := topotools.RebuildKeyspace(context.Background(), logutil.NewConsoleLogger(), ts, oldPrimary.Tablet.Keyspace, []string{"cell1"}, false) 418 if err != nil { 419 t.Fatalf("RebuildKeyspaceLocked failed: %v", err) 420 } 421 // On the elected primary, we will respond to 422 // TabletActionReplicaWasPromoted. 423 newPrimary.StartActionLoop(t, wr) 424 defer newPrimary.StopActionLoop(t) 425 426 oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) 427 oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ 428 "RESET SLAVE ALL", 429 "FAKE SET MASTER", 430 "START Replica", 431 } 432 // On the old primary, we will only respond to 433 // TabletActionReplicaWasRestarted. 434 oldPrimary.StartActionLoop(t, wr) 435 defer oldPrimary.StopActionLoop(t) 436 437 goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) 438 // On the good replica, we will respond to 439 // TabletActionReplicaWasRestarted. 440 goodReplica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ 441 // These 4 statements come from tablet startup 442 "STOP SLAVE", 443 "RESET SLAVE ALL", 444 "FAKE SET MASTER", 445 "START SLAVE", 446 } 447 goodReplica.StartActionLoop(t, wr) 448 defer goodReplica.StopActionLoop(t) 449 450 // The reparent should work as expected here 451 if err := wr.TabletExternallyReparented(ctx, newPrimary.Tablet.Alias); err != nil { 452 t.Fatalf("TabletExternallyReparented(replica) failed: %v", err) 453 } 454 455 // check the new primary is primary 456 tablet, err := ts.GetTablet(ctx, newPrimary.Tablet.Alias) 457 if err != nil { 458 t.Fatalf("GetTablet(%v) failed: %v", newPrimary.Tablet.Alias, err) 459 } 460 if tablet.Type != topodatapb.TabletType_PRIMARY { 461 t.Fatalf("new primary should be PRIMARY but is: %v", tablet.Type) 462 } 463 464 // We have to wait for shard sync to do its magic in the background 465 startTime := time.Now() 466 for { 467 if time.Since(startTime) > 10*time.Second /* timeout */ { 468 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 469 if err != nil { 470 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 471 } 472 t.Fatalf("old primary (%v) should be replica but is: %v", topoproto.TabletAliasString(oldPrimary.Tablet.Alias), tablet.Type) 473 } 474 // check the old primary was converted to replica 475 tablet, err = ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 476 if err != nil { 477 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 478 } 479 if tablet.Type == topodatapb.TabletType_REPLICA { 480 break 481 } else { 482 time.Sleep(100 * time.Millisecond /* interval at which to check again */) 483 } 484 } 485 486 // run TER again and make sure the primary is still correct 487 if err := wr.TabletExternallyReparented(ctx, newPrimary.Tablet.Alias); err != nil { 488 t.Fatalf("TabletExternallyReparented(replica) failed: %v", err) 489 } 490 491 // check the new primary is still primary 492 tablet, err = ts.GetTablet(ctx, newPrimary.Tablet.Alias) 493 if err != nil { 494 t.Fatalf("GetTablet(%v) failed: %v", newPrimary.Tablet.Alias, err) 495 } 496 if tablet.Type != topodatapb.TabletType_PRIMARY { 497 t.Fatalf("new primary should be PRIMARY but is: %v", tablet.Type) 498 } 499 500 } 501 502 func TestRPCTabletExternallyReparentedDemotesPrimaryToConfiguredTabletType(t *testing.T) { 503 delay := discovery.GetTabletPickerRetryDelay() 504 defer func() { 505 discovery.SetTabletPickerRetryDelay(delay) 506 }() 507 discovery.SetTabletPickerRetryDelay(5 * time.Millisecond) 508 509 flag.Set("disable_active_reparents", "true") 510 defer flag.Set("disable_active_reparents", "false") 511 512 ctx := context.Background() 513 ts := memorytopo.NewServer("cell1") 514 wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient()) 515 516 // Create an old primary and a new primary 517 oldPrimary := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_SPARE, nil) 518 newPrimary := NewFakeTablet(t, wr, "cell1", 1, topodatapb.TabletType_SPARE, nil) 519 520 oldPrimary.StartActionLoop(t, wr) 521 newPrimary.StartActionLoop(t, wr) 522 defer oldPrimary.StopActionLoop(t) 523 defer newPrimary.StopActionLoop(t) 524 525 // Build keyspace graph 526 err := topotools.RebuildKeyspace(context.Background(), logutil.NewConsoleLogger(), ts, oldPrimary.Tablet.Keyspace, []string{"cell1"}, false) 527 assert.NoError(t, err, "RebuildKeyspaceLocked failed: %v", err) 528 529 // Reparent to new primary 530 ti, err := ts.GetTablet(ctx, newPrimary.Tablet.Alias) 531 if err != nil { 532 t.Fatalf("GetTablet failed: %v", err) 533 } 534 535 if err := wr.TabletExternallyReparented(context.Background(), ti.Tablet.Alias); err != nil { 536 t.Fatalf("TabletExternallyReparented failed: %v", err) 537 } 538 539 // We have to wait for shard sync to do its magic in the background 540 startTime := time.Now() 541 for { 542 if time.Since(startTime) > 10*time.Second /* timeout */ { 543 tablet, err := ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 544 if err != nil { 545 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 546 } 547 t.Fatalf("old primary (%v) should be spare but is: %v", topoproto.TabletAliasString(oldPrimary.Tablet.Alias), tablet.Type) 548 } 549 // check the old primary was converted to replica 550 tablet, err := ts.GetTablet(ctx, oldPrimary.Tablet.Alias) 551 if err != nil { 552 t.Fatalf("GetTablet(%v) failed: %v", oldPrimary.Tablet.Alias, err) 553 } 554 if tablet.Type == topodatapb.TabletType_SPARE { 555 break 556 } else { 557 time.Sleep(100 * time.Millisecond /* interval at which to check again */) 558 } 559 } 560 561 shardInfo, err := ts.GetShard(context.Background(), newPrimary.Tablet.Keyspace, newPrimary.Tablet.Shard) 562 assert.NoError(t, err) 563 564 assert.True(t, topoproto.TabletAliasEqual(newPrimary.Tablet.Alias, shardInfo.PrimaryAlias)) 565 assert.Equal(t, topodatapb.TabletType_PRIMARY, newPrimary.TM.Tablet().Type) 566 }