vitess.io/vitess@v0.16.2/go/vt/vttablet/grpctmclient/client.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 grpctmclient 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "sync" 24 "time" 25 26 "github.com/spf13/pflag" 27 "google.golang.org/grpc" 28 29 "vitess.io/vitess/go/netutil" 30 "vitess.io/vitess/go/vt/callerid" 31 "vitess.io/vitess/go/vt/grpcclient" 32 "vitess.io/vitess/go/vt/hook" 33 "vitess.io/vitess/go/vt/log" 34 "vitess.io/vitess/go/vt/logutil" 35 "vitess.io/vitess/go/vt/mysqlctl/tmutils" 36 "vitess.io/vitess/go/vt/servenv" 37 "vitess.io/vitess/go/vt/topo/topoproto" 38 "vitess.io/vitess/go/vt/vttablet/tmclient" 39 40 logutilpb "vitess.io/vitess/go/vt/proto/logutil" 41 querypb "vitess.io/vitess/go/vt/proto/query" 42 replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" 43 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 44 tabletmanagerservicepb "vitess.io/vitess/go/vt/proto/tabletmanagerservice" 45 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 46 ) 47 48 var ( 49 concurrency = 8 50 cert string 51 key string 52 ca string 53 crl string 54 name string 55 ) 56 57 func registerFlags(fs *pflag.FlagSet) { 58 fs.IntVar(&concurrency, "tablet_manager_grpc_concurrency", concurrency, "concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App})") 59 fs.StringVar(&cert, "tablet_manager_grpc_cert", cert, "the cert to use to connect") 60 fs.StringVar(&key, "tablet_manager_grpc_key", key, "the key to use to connect") 61 fs.StringVar(&ca, "tablet_manager_grpc_ca", ca, "the server ca to use to validate servers when connecting") 62 fs.StringVar(&crl, "tablet_manager_grpc_crl", crl, "the server crl to use to validate server certificates when connecting") 63 fs.StringVar(&name, "tablet_manager_grpc_server_name", name, "the server name to use to validate server certificate") 64 } 65 66 var _binaries = []string{ // binaries that require the flags in this package 67 "vtbackup", 68 "vtcombo", 69 "vtctl", 70 "vtctld", 71 "vtctldclient", 72 "vtgr", 73 "vtorc", 74 "vttablet", 75 "vttestserver", 76 } 77 78 func init() { 79 tmclient.RegisterTabletManagerClientFactory("grpc", func() tmclient.TabletManagerClient { 80 return NewClient() 81 }) 82 tmclient.RegisterTabletManagerClientFactory("grpc-oneshot", func() tmclient.TabletManagerClient { 83 return NewClient() 84 }) 85 86 for _, cmd := range _binaries { 87 servenv.OnParseFor(cmd, registerFlags) 88 } 89 } 90 91 type tmc struct { 92 cc *grpc.ClientConn 93 client tabletmanagerservicepb.TabletManagerClient 94 } 95 96 // grpcClient implements both dialer and poolDialer. 97 type grpcClient struct { 98 // This cache of connections is to maximize QPS for ExecuteFetch. 99 // Note we'll keep the clients open and close them upon Close() only. 100 // But that's OK because usually the tasks that use them are 101 // one-purpose only. 102 // The map is protected by the mutex. 103 mu sync.Mutex 104 rpcClientMap map[string]chan *tmc 105 } 106 107 type dialer interface { 108 dial(ctx context.Context, tablet *topodatapb.Tablet) (tabletmanagerservicepb.TabletManagerClient, io.Closer, error) 109 Close() 110 } 111 112 type poolDialer interface { 113 dialPool(ctx context.Context, tablet *topodatapb.Tablet) (tabletmanagerservicepb.TabletManagerClient, error) 114 } 115 116 // Client implements tmclient.TabletManagerClient. 117 // 118 // Connections are produced by the dialer implementation, which is either the 119 // grpcClient implementation, which reuses connections only for ExecuteFetch and 120 // otherwise makes single-purpose connections that are closed after use. 121 // 122 // In order to more efficiently use the underlying tcp connections, you can 123 // instead use the cachedConnDialer implementation by specifying 124 // 125 // -tablet_manager_protocol "grpc-cached" 126 // 127 // The cachedConnDialer keeps connections to up to -tablet_manager_grpc_connpool_size distinct 128 // tablets open at any given time, for faster per-RPC call time, and less 129 // connection churn. 130 type Client struct { 131 dialer dialer 132 } 133 134 // NewClient returns a new gRPC client. 135 func NewClient() *Client { 136 return &Client{ 137 dialer: &grpcClient{}, 138 } 139 } 140 141 // dial returns a client to use 142 func (client *grpcClient) dial(ctx context.Context, tablet *topodatapb.Tablet) (tabletmanagerservicepb.TabletManagerClient, io.Closer, error) { 143 addr := netutil.JoinHostPort(tablet.Hostname, int32(tablet.PortMap["grpc"])) 144 opt, err := grpcclient.SecureDialOption(cert, key, ca, crl, name) 145 if err != nil { 146 return nil, nil, err 147 } 148 cc, err := grpcclient.Dial(addr, grpcclient.FailFast(false), opt) 149 if err != nil { 150 return nil, nil, err 151 } 152 153 return tabletmanagerservicepb.NewTabletManagerClient(cc), cc, nil 154 } 155 156 func (client *grpcClient) dialPool(ctx context.Context, tablet *topodatapb.Tablet) (tabletmanagerservicepb.TabletManagerClient, error) { 157 addr := netutil.JoinHostPort(tablet.Hostname, int32(tablet.PortMap["grpc"])) 158 opt, err := grpcclient.SecureDialOption(cert, key, ca, crl, name) 159 if err != nil { 160 return nil, err 161 } 162 163 client.mu.Lock() 164 if client.rpcClientMap == nil { 165 client.rpcClientMap = make(map[string]chan *tmc) 166 } 167 c, ok := client.rpcClientMap[addr] 168 if !ok { 169 c = make(chan *tmc, concurrency) 170 client.rpcClientMap[addr] = c 171 client.mu.Unlock() 172 173 for i := 0; i < cap(c); i++ { 174 cc, err := grpcclient.Dial(addr, grpcclient.FailFast(false), opt) 175 if err != nil { 176 return nil, err 177 } 178 c <- &tmc{ 179 cc: cc, 180 client: tabletmanagerservicepb.NewTabletManagerClient(cc), 181 } 182 } 183 } else { 184 client.mu.Unlock() 185 } 186 187 result := <-c 188 c <- result 189 return result.client, nil 190 } 191 192 // Close is part of the tmclient.TabletManagerClient interface. 193 func (client *grpcClient) Close() { 194 client.mu.Lock() 195 defer client.mu.Unlock() 196 for _, c := range client.rpcClientMap { 197 close(c) 198 for ch := range c { 199 ch.cc.Close() 200 } 201 } 202 client.rpcClientMap = nil 203 } 204 205 // 206 // Various read-only methods 207 // 208 209 // Ping is part of the tmclient.TabletManagerClient interface. 210 func (client *Client) Ping(ctx context.Context, tablet *topodatapb.Tablet) error { 211 c, closer, err := client.dialer.dial(ctx, tablet) 212 if err != nil { 213 return err 214 } 215 defer closer.Close() 216 result, err := c.Ping(ctx, &tabletmanagerdatapb.PingRequest{ 217 Payload: "payload", 218 }) 219 if err != nil { 220 return err 221 } 222 if result.Payload != "payload" { 223 return fmt.Errorf("bad ping result: %v", result.Payload) 224 } 225 return nil 226 } 227 228 // Sleep is part of the tmclient.TabletManagerClient interface. 229 func (client *Client) Sleep(ctx context.Context, tablet *topodatapb.Tablet, duration time.Duration) error { 230 c, closer, err := client.dialer.dial(ctx, tablet) 231 if err != nil { 232 return err 233 } 234 defer closer.Close() 235 _, err = c.Sleep(ctx, &tabletmanagerdatapb.SleepRequest{ 236 Duration: int64(duration), 237 }) 238 return err 239 } 240 241 // ExecuteHook is part of the tmclient.TabletManagerClient interface. 242 func (client *Client) ExecuteHook(ctx context.Context, tablet *topodatapb.Tablet, hk *hook.Hook) (*hook.HookResult, error) { 243 c, closer, err := client.dialer.dial(ctx, tablet) 244 if err != nil { 245 return nil, err 246 } 247 defer closer.Close() 248 hr, err := c.ExecuteHook(ctx, &tabletmanagerdatapb.ExecuteHookRequest{ 249 Name: hk.Name, 250 Parameters: hk.Parameters, 251 ExtraEnv: hk.ExtraEnv, 252 }) 253 if err != nil { 254 return nil, err 255 } 256 return &hook.HookResult{ 257 ExitStatus: int(hr.ExitStatus), 258 Stdout: hr.Stdout, 259 Stderr: hr.Stderr, 260 }, nil 261 } 262 263 // GetSchema is part of the tmclient.TabletManagerClient interface. 264 func (client *Client) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) { 265 c, closer, err := client.dialer.dial(ctx, tablet) 266 if err != nil { 267 return nil, err 268 } 269 defer closer.Close() 270 response, err := c.GetSchema(ctx, request) 271 if err != nil { 272 return nil, err 273 } 274 return response.SchemaDefinition, nil 275 } 276 277 // GetPermissions is part of the tmclient.TabletManagerClient interface. 278 func (client *Client) GetPermissions(ctx context.Context, tablet *topodatapb.Tablet) (*tabletmanagerdatapb.Permissions, error) { 279 c, closer, err := client.dialer.dial(ctx, tablet) 280 if err != nil { 281 return nil, err 282 } 283 defer closer.Close() 284 response, err := c.GetPermissions(ctx, &tabletmanagerdatapb.GetPermissionsRequest{}) 285 if err != nil { 286 return nil, err 287 } 288 return response.Permissions, nil 289 } 290 291 // 292 // Various read-write methods 293 // 294 295 // SetReadOnly is part of the tmclient.TabletManagerClient interface. 296 func (client *Client) SetReadOnly(ctx context.Context, tablet *topodatapb.Tablet) error { 297 c, closer, err := client.dialer.dial(ctx, tablet) 298 if err != nil { 299 return err 300 } 301 defer closer.Close() 302 _, err = c.SetReadOnly(ctx, &tabletmanagerdatapb.SetReadOnlyRequest{}) 303 return err 304 } 305 306 // SetReadWrite is part of the tmclient.TabletManagerClient interface. 307 func (client *Client) SetReadWrite(ctx context.Context, tablet *topodatapb.Tablet) error { 308 c, closer, err := client.dialer.dial(ctx, tablet) 309 if err != nil { 310 return err 311 } 312 defer closer.Close() 313 _, err = c.SetReadWrite(ctx, &tabletmanagerdatapb.SetReadWriteRequest{}) 314 return err 315 } 316 317 // ChangeType is part of the tmclient.TabletManagerClient interface. 318 func (client *Client) ChangeType(ctx context.Context, tablet *topodatapb.Tablet, dbType topodatapb.TabletType, semiSync bool) error { 319 c, closer, err := client.dialer.dial(ctx, tablet) 320 if err != nil { 321 return err 322 } 323 defer closer.Close() 324 _, err = c.ChangeType(ctx, &tabletmanagerdatapb.ChangeTypeRequest{ 325 TabletType: dbType, 326 SemiSync: semiSync, 327 }) 328 return err 329 } 330 331 // RefreshState is part of the tmclient.TabletManagerClient interface. 332 func (client *Client) RefreshState(ctx context.Context, tablet *topodatapb.Tablet) error { 333 c, closer, err := client.dialer.dial(ctx, tablet) 334 if err != nil { 335 return err 336 } 337 defer closer.Close() 338 _, err = c.RefreshState(ctx, &tabletmanagerdatapb.RefreshStateRequest{}) 339 return err 340 } 341 342 // RunHealthCheck is part of the tmclient.TabletManagerClient interface. 343 func (client *Client) RunHealthCheck(ctx context.Context, tablet *topodatapb.Tablet) error { 344 c, closer, err := client.dialer.dial(ctx, tablet) 345 if err != nil { 346 return err 347 } 348 defer closer.Close() 349 _, err = c.RunHealthCheck(ctx, &tabletmanagerdatapb.RunHealthCheckRequest{}) 350 return err 351 } 352 353 // ReloadSchema is part of the tmclient.TabletManagerClient interface. 354 func (client *Client) ReloadSchema(ctx context.Context, tablet *topodatapb.Tablet, waitPosition string) error { 355 c, closer, err := client.dialer.dial(ctx, tablet) 356 if err != nil { 357 return err 358 } 359 defer closer.Close() 360 _, err = c.ReloadSchema(ctx, &tabletmanagerdatapb.ReloadSchemaRequest{ 361 WaitPosition: waitPosition, 362 }) 363 return err 364 } 365 366 // PreflightSchema is part of the tmclient.TabletManagerClient interface. 367 func (client *Client) PreflightSchema(ctx context.Context, tablet *topodatapb.Tablet, changes []string) ([]*tabletmanagerdatapb.SchemaChangeResult, error) { 368 c, closer, err := client.dialer.dial(ctx, tablet) 369 if err != nil { 370 return nil, err 371 } 372 defer closer.Close() 373 374 response, err := c.PreflightSchema(ctx, &tabletmanagerdatapb.PreflightSchemaRequest{ 375 Changes: changes, 376 }) 377 if err != nil { 378 return nil, err 379 } 380 381 return response.ChangeResults, nil 382 } 383 384 // ApplySchema is part of the tmclient.TabletManagerClient interface. 385 func (client *Client) ApplySchema(ctx context.Context, tablet *topodatapb.Tablet, change *tmutils.SchemaChange) (*tabletmanagerdatapb.SchemaChangeResult, error) { 386 c, closer, err := client.dialer.dial(ctx, tablet) 387 if err != nil { 388 return nil, err 389 } 390 defer closer.Close() 391 response, err := c.ApplySchema(ctx, &tabletmanagerdatapb.ApplySchemaRequest{ 392 Sql: change.SQL, 393 Force: change.Force, 394 AllowReplication: change.AllowReplication, 395 BeforeSchema: change.BeforeSchema, 396 AfterSchema: change.AfterSchema, 397 SqlMode: change.SQLMode, 398 }) 399 if err != nil { 400 return nil, err 401 } 402 return &tabletmanagerdatapb.SchemaChangeResult{ 403 BeforeSchema: response.BeforeSchema, 404 AfterSchema: response.AfterSchema, 405 }, nil 406 } 407 408 // LockTables is part of the tmclient.TabletManagerClient interface. 409 func (client *Client) LockTables(ctx context.Context, tablet *topodatapb.Tablet) error { 410 c, closer, err := client.dialer.dial(ctx, tablet) 411 if err != nil { 412 return err 413 } 414 defer closer.Close() 415 416 _, err = c.LockTables(ctx, &tabletmanagerdatapb.LockTablesRequest{}) 417 return err 418 } 419 420 // UnlockTables is part of the tmclient.TabletManagerClient interface. 421 func (client *Client) UnlockTables(ctx context.Context, tablet *topodatapb.Tablet) error { 422 c, closer, err := client.dialer.dial(ctx, tablet) 423 if err != nil { 424 return err 425 } 426 defer closer.Close() 427 428 _, err = c.UnlockTables(ctx, &tabletmanagerdatapb.UnlockTablesRequest{}) 429 return err 430 } 431 432 // ExecuteQuery is part of the tmclient.TabletManagerClient interface. 433 func (client *Client) ExecuteQuery(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.ExecuteQueryRequest) (*querypb.QueryResult, error) { 434 c, closer, err := client.dialer.dial(ctx, tablet) 435 if err != nil { 436 return nil, err 437 } 438 defer closer.Close() 439 440 cid := req.CallerId 441 if cid == nil { 442 cid = callerid.EffectiveCallerIDFromContext(ctx) 443 } 444 445 response, err := c.ExecuteQuery(ctx, &tabletmanagerdatapb.ExecuteQueryRequest{ 446 Query: req.Query, 447 DbName: topoproto.TabletDbName(tablet), 448 MaxRows: req.MaxRows, 449 CallerId: cid, 450 }) 451 if err != nil { 452 return nil, err 453 } 454 return response.Result, nil 455 } 456 457 // ExecuteFetchAsDba is part of the tmclient.TabletManagerClient interface. 458 func (client *Client) ExecuteFetchAsDba(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsDbaRequest) (*querypb.QueryResult, error) { 459 var c tabletmanagerservicepb.TabletManagerClient 460 var err error 461 if usePool { 462 if poolDialer, ok := client.dialer.(poolDialer); ok { 463 c, err = poolDialer.dialPool(ctx, tablet) 464 if err != nil { 465 return nil, err 466 } 467 } 468 } 469 470 if !usePool || c == nil { 471 var closer io.Closer 472 c, closer, err = client.dialer.dial(ctx, tablet) 473 if err != nil { 474 return nil, err 475 } 476 defer closer.Close() 477 } 478 479 response, err := c.ExecuteFetchAsDba(ctx, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ 480 Query: req.Query, 481 DbName: topoproto.TabletDbName(tablet), 482 MaxRows: req.MaxRows, 483 DisableBinlogs: req.DisableBinlogs, 484 ReloadSchema: req.DisableBinlogs, 485 }) 486 if err != nil { 487 return nil, err 488 } 489 return response.Result, nil 490 } 491 492 // ExecuteFetchAsAllPrivs is part of the tmclient.TabletManagerClient interface. 493 func (client *Client) ExecuteFetchAsAllPrivs(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.ExecuteFetchAsAllPrivsRequest) (*querypb.QueryResult, error) { 494 c, closer, err := client.dialer.dial(ctx, tablet) 495 if err != nil { 496 return nil, err 497 } 498 defer closer.Close() 499 500 response, err := c.ExecuteFetchAsAllPrivs(ctx, &tabletmanagerdatapb.ExecuteFetchAsAllPrivsRequest{ 501 Query: req.Query, 502 DbName: topoproto.TabletDbName(tablet), 503 MaxRows: req.MaxRows, 504 ReloadSchema: req.ReloadSchema, 505 }) 506 if err != nil { 507 return nil, err 508 } 509 return response.Result, nil 510 } 511 512 // ExecuteFetchAsApp is part of the tmclient.TabletManagerClient interface. 513 func (client *Client) ExecuteFetchAsApp(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsAppRequest) (*querypb.QueryResult, error) { 514 var c tabletmanagerservicepb.TabletManagerClient 515 var err error 516 if usePool { 517 if poolDialer, ok := client.dialer.(poolDialer); ok { 518 c, err = poolDialer.dialPool(ctx, tablet) 519 if err != nil { 520 return nil, err 521 } 522 } 523 } 524 525 if !usePool || c == nil { 526 var closer io.Closer 527 c, closer, err = client.dialer.dial(ctx, tablet) 528 if err != nil { 529 return nil, err 530 } 531 defer closer.Close() 532 } 533 534 response, err := c.ExecuteFetchAsApp(ctx, req) 535 if err != nil { 536 return nil, err 537 } 538 return response.Result, nil 539 } 540 541 // 542 // Replication related methods 543 // 544 545 // ReplicationStatus is part of the tmclient.TabletManagerClient interface. 546 func (client *Client) ReplicationStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.Status, error) { 547 c, closer, err := client.dialer.dial(ctx, tablet) 548 if err != nil { 549 return nil, err 550 } 551 defer closer.Close() 552 response, err := c.ReplicationStatus(ctx, &tabletmanagerdatapb.ReplicationStatusRequest{}) 553 if err != nil { 554 return nil, err 555 } 556 return response.Status, nil 557 } 558 559 // FullStatus is part of the tmclient.TabletManagerClient interface. 560 func (client *Client) FullStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.FullStatus, error) { 561 c, closer, err := client.dialer.dial(ctx, tablet) 562 if err != nil { 563 return nil, err 564 } 565 defer closer.Close() 566 response, err := c.FullStatus(ctx, &tabletmanagerdatapb.FullStatusRequest{}) 567 if err != nil { 568 return nil, err 569 } 570 return response.Status, nil 571 } 572 573 // PrimaryStatus is part of the tmclient.TabletManagerClient interface. 574 func (client *Client) PrimaryStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.PrimaryStatus, error) { 575 c, closer, err := client.dialer.dial(ctx, tablet) 576 if err != nil { 577 return nil, err 578 } 579 defer closer.Close() 580 response, err := c.PrimaryStatus(ctx, &tabletmanagerdatapb.PrimaryStatusRequest{}) 581 if err != nil { 582 return nil, err 583 } 584 return response.Status, nil 585 } 586 587 // PrimaryPosition is part of the tmclient.TabletManagerClient interface. 588 func (client *Client) PrimaryPosition(ctx context.Context, tablet *topodatapb.Tablet) (string, error) { 589 c, closer, err := client.dialer.dial(ctx, tablet) 590 if err != nil { 591 return "", err 592 } 593 defer closer.Close() 594 response, err := c.PrimaryPosition(ctx, &tabletmanagerdatapb.PrimaryPositionRequest{}) 595 if err != nil { 596 return "", err 597 } 598 return response.Position, nil 599 } 600 601 // WaitForPosition is part of the tmclient.TabletManagerClient interface. 602 func (client *Client) WaitForPosition(ctx context.Context, tablet *topodatapb.Tablet, pos string) error { 603 c, closer, err := client.dialer.dial(ctx, tablet) 604 if err != nil { 605 return err 606 } 607 defer closer.Close() 608 _, err = c.WaitForPosition(ctx, &tabletmanagerdatapb.WaitForPositionRequest{Position: pos}) 609 return err 610 } 611 612 // StopReplication is part of the tmclient.TabletManagerClient interface. 613 func (client *Client) StopReplication(ctx context.Context, tablet *topodatapb.Tablet) error { 614 c, closer, err := client.dialer.dial(ctx, tablet) 615 if err != nil { 616 return err 617 } 618 defer closer.Close() 619 _, err = c.StopReplication(ctx, &tabletmanagerdatapb.StopReplicationRequest{}) 620 return err 621 } 622 623 // StopReplicationMinimum is part of the tmclient.TabletManagerClient interface. 624 func (client *Client) StopReplicationMinimum(ctx context.Context, tablet *topodatapb.Tablet, minPos string, waitTime time.Duration) (string, error) { 625 c, closer, err := client.dialer.dial(ctx, tablet) 626 if err != nil { 627 return "", err 628 } 629 defer closer.Close() 630 631 response, err := c.StopReplicationMinimum(ctx, &tabletmanagerdatapb.StopReplicationMinimumRequest{ 632 Position: minPos, 633 WaitTimeout: int64(waitTime), 634 }) 635 if err != nil { 636 return "", err 637 } 638 return response.Position, nil 639 } 640 641 // StartReplication is part of the tmclient.TabletManagerClient interface. 642 func (client *Client) StartReplication(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) error { 643 c, closer, err := client.dialer.dial(ctx, tablet) 644 if err != nil { 645 return err 646 } 647 defer closer.Close() 648 _, err = c.StartReplication(ctx, &tabletmanagerdatapb.StartReplicationRequest{ 649 SemiSync: semiSync, 650 }) 651 return err 652 } 653 654 // StartReplicationUntilAfter is part of the tmclient.TabletManagerClient interface. 655 func (client *Client) StartReplicationUntilAfter(ctx context.Context, tablet *topodatapb.Tablet, position string, waitTime time.Duration) error { 656 c, closer, err := client.dialer.dial(ctx, tablet) 657 if err != nil { 658 return err 659 } 660 defer closer.Close() 661 _, err = c.StartReplicationUntilAfter(ctx, &tabletmanagerdatapb.StartReplicationUntilAfterRequest{ 662 Position: position, 663 WaitTimeout: int64(waitTime), 664 }) 665 return err 666 } 667 668 // GetReplicas is part of the tmclient.TabletManagerClient interface. 669 func (client *Client) GetReplicas(ctx context.Context, tablet *topodatapb.Tablet) ([]string, error) { 670 c, closer, err := client.dialer.dial(ctx, tablet) 671 if err != nil { 672 return nil, err 673 } 674 defer closer.Close() 675 response, err := c.GetReplicas(ctx, &tabletmanagerdatapb.GetReplicasRequest{}) 676 if err != nil { 677 return nil, err 678 } 679 return response.Addrs, nil 680 } 681 682 // VExec is part of the tmclient.TabletManagerClient interface. 683 func (client *Client) VExec(ctx context.Context, tablet *topodatapb.Tablet, query, workflow, keyspace string) (*querypb.QueryResult, error) { 684 c, closer, err := client.dialer.dial(ctx, tablet) 685 if err != nil { 686 return nil, err 687 } 688 defer closer.Close() 689 response, err := c.VExec(ctx, &tabletmanagerdatapb.VExecRequest{Query: query, Workflow: workflow, Keyspace: keyspace}) 690 if err != nil { 691 return nil, err 692 } 693 return response.Result, nil 694 } 695 696 // VReplicationExec is part of the tmclient.TabletManagerClient interface. 697 func (client *Client) VReplicationExec(ctx context.Context, tablet *topodatapb.Tablet, query string) (*querypb.QueryResult, error) { 698 c, closer, err := client.dialer.dial(ctx, tablet) 699 if err != nil { 700 return nil, err 701 } 702 defer closer.Close() 703 response, err := c.VReplicationExec(ctx, &tabletmanagerdatapb.VReplicationExecRequest{Query: query}) 704 if err != nil { 705 return nil, err 706 } 707 return response.Result, nil 708 } 709 710 // VReplicationWaitForPos is part of the tmclient.TabletManagerClient interface. 711 func (client *Client) VReplicationWaitForPos(ctx context.Context, tablet *topodatapb.Tablet, id int, pos string) error { 712 c, closer, err := client.dialer.dial(ctx, tablet) 713 if err != nil { 714 return err 715 } 716 defer closer.Close() 717 if _, err = c.VReplicationWaitForPos(ctx, &tabletmanagerdatapb.VReplicationWaitForPosRequest{Id: int64(id), Position: pos}); err != nil { 718 return err 719 } 720 return nil 721 } 722 723 // VDiff is part of the tmclient.TabletManagerClient interface. 724 func (client *Client) VDiff(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.VDiffRequest) (*tabletmanagerdatapb.VDiffResponse, error) { 725 log.Infof("VDiff for tablet %s, request %+v", tablet.Alias.String(), req) 726 c, closer, err := client.dialer.dial(ctx, tablet) 727 if err != nil { 728 return nil, err 729 } 730 defer closer.Close() 731 response, err := c.VDiff(ctx, req) 732 if err != nil { 733 return nil, err 734 } 735 return response, nil 736 } 737 738 // 739 // Reparenting related functions 740 // 741 742 // ResetReplication is part of the tmclient.TabletManagerClient interface. 743 func (client *Client) ResetReplication(ctx context.Context, tablet *topodatapb.Tablet) error { 744 c, closer, err := client.dialer.dial(ctx, tablet) 745 if err != nil { 746 return err 747 } 748 defer closer.Close() 749 _, err = c.ResetReplication(ctx, &tabletmanagerdatapb.ResetReplicationRequest{}) 750 return err 751 } 752 753 // InitPrimary is part of the tmclient.TabletManagerClient interface. 754 func (client *Client) InitPrimary(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) (string, error) { 755 c, closer, err := client.dialer.dial(ctx, tablet) 756 if err != nil { 757 return "", err 758 } 759 defer closer.Close() 760 761 response, err := c.InitPrimary(ctx, &tabletmanagerdatapb.InitPrimaryRequest{ 762 SemiSync: semiSync, 763 }) 764 if err != nil { 765 return "", err 766 } 767 return response.Position, nil 768 } 769 770 // PopulateReparentJournal is part of the tmclient.TabletManagerClient interface. 771 func (client *Client) PopulateReparentJournal(ctx context.Context, tablet *topodatapb.Tablet, timeCreatedNS int64, actionName string, tabletAlias *topodatapb.TabletAlias, pos string) error { 772 c, closer, err := client.dialer.dial(ctx, tablet) 773 if err != nil { 774 return err 775 } 776 defer closer.Close() 777 _, err = c.PopulateReparentJournal(ctx, &tabletmanagerdatapb.PopulateReparentJournalRequest{ 778 TimeCreatedNs: timeCreatedNS, 779 ActionName: actionName, 780 PrimaryAlias: tabletAlias, 781 ReplicationPosition: pos, 782 }) 783 return err 784 } 785 786 // InitReplica is part of the tmclient.TabletManagerClient interface. 787 func (client *Client) InitReplica(ctx context.Context, tablet *topodatapb.Tablet, parent *topodatapb.TabletAlias, replicationPosition string, timeCreatedNS int64, semiSync bool) error { 788 c, closer, err := client.dialer.dial(ctx, tablet) 789 if err != nil { 790 return err 791 } 792 defer closer.Close() 793 _, err = c.InitReplica(ctx, &tabletmanagerdatapb.InitReplicaRequest{ 794 Parent: parent, 795 ReplicationPosition: replicationPosition, 796 TimeCreatedNs: timeCreatedNS, 797 SemiSync: semiSync, 798 }) 799 return err 800 } 801 802 // DemotePrimary is part of the tmclient.TabletManagerClient interface. 803 func (client *Client) DemotePrimary(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.PrimaryStatus, error) { 804 c, closer, err := client.dialer.dial(ctx, tablet) 805 if err != nil { 806 return nil, err 807 } 808 defer closer.Close() 809 response, err := c.DemotePrimary(ctx, &tabletmanagerdatapb.DemotePrimaryRequest{}) 810 if err != nil { 811 return nil, err 812 } 813 return response.PrimaryStatus, nil 814 } 815 816 // UndoDemotePrimary is part of the tmclient.TabletManagerClient interface. 817 func (client *Client) UndoDemotePrimary(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) error { 818 c, closer, err := client.dialer.dial(ctx, tablet) 819 if err != nil { 820 return err 821 } 822 defer closer.Close() 823 _, err = c.UndoDemotePrimary(ctx, &tabletmanagerdatapb.UndoDemotePrimaryRequest{ 824 SemiSync: semiSync, 825 }) 826 return err 827 } 828 829 // ReplicaWasPromoted is part of the tmclient.TabletManagerClient interface. 830 func (client *Client) ReplicaWasPromoted(ctx context.Context, tablet *topodatapb.Tablet) error { 831 c, closer, err := client.dialer.dial(ctx, tablet) 832 if err != nil { 833 return err 834 } 835 defer closer.Close() 836 _, err = c.ReplicaWasPromoted(ctx, &tabletmanagerdatapb.ReplicaWasPromotedRequest{}) 837 return err 838 } 839 840 // ResetReplicationParameters is part of the tmclient.TabletManagerClient interface. 841 func (client *Client) ResetReplicationParameters(ctx context.Context, tablet *topodatapb.Tablet) error { 842 c, closer, err := client.dialer.dial(ctx, tablet) 843 if err != nil { 844 return err 845 } 846 defer closer.Close() 847 _, err = c.ResetReplicationParameters(ctx, &tabletmanagerdatapb.ResetReplicationParametersRequest{}) 848 return err 849 } 850 851 // SetReplicationSource is part of the tmclient.TabletManagerClient interface. 852 func (client *Client) SetReplicationSource(ctx context.Context, tablet *topodatapb.Tablet, parent *topodatapb.TabletAlias, timeCreatedNS int64, waitPosition string, forceStartReplication bool, semiSync bool) error { 853 c, closer, err := client.dialer.dial(ctx, tablet) 854 if err != nil { 855 return err 856 } 857 defer closer.Close() 858 859 _, err = c.SetReplicationSource(ctx, &tabletmanagerdatapb.SetReplicationSourceRequest{ 860 Parent: parent, 861 TimeCreatedNs: timeCreatedNS, 862 WaitPosition: waitPosition, 863 ForceStartReplication: forceStartReplication, 864 SemiSync: semiSync, 865 }) 866 return err 867 } 868 869 // ReplicaWasRestarted is part of the tmclient.TabletManagerClient interface. 870 func (client *Client) ReplicaWasRestarted(ctx context.Context, tablet *topodatapb.Tablet, parent *topodatapb.TabletAlias) error { 871 c, closer, err := client.dialer.dial(ctx, tablet) 872 if err != nil { 873 return err 874 } 875 defer closer.Close() 876 _, err = c.ReplicaWasRestarted(ctx, &tabletmanagerdatapb.ReplicaWasRestartedRequest{ 877 Parent: parent, 878 }) 879 return err 880 } 881 882 // StopReplicationAndGetStatus is part of the tmclient.TabletManagerClient interface. 883 func (client *Client) StopReplicationAndGetStatus(ctx context.Context, tablet *topodatapb.Tablet, stopReplicationMode replicationdatapb.StopReplicationMode) (status *replicationdatapb.StopReplicationStatus, err error) { 884 c, closer, err := client.dialer.dial(ctx, tablet) 885 if err != nil { 886 return nil, err 887 } 888 defer closer.Close() 889 response, err := c.StopReplicationAndGetStatus(ctx, &tabletmanagerdatapb.StopReplicationAndGetStatusRequest{ 890 StopReplicationMode: stopReplicationMode, 891 }) 892 if err != nil { 893 return nil, err 894 } 895 return &replicationdatapb.StopReplicationStatus{ //nolint 896 Before: response.Status.Before, 897 After: response.Status.After, 898 }, nil 899 } 900 901 // PromoteReplica is part of the tmclient.TabletManagerClient interface. 902 func (client *Client) PromoteReplica(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) (string, error) { 903 c, closer, err := client.dialer.dial(ctx, tablet) 904 if err != nil { 905 return "", err 906 } 907 defer closer.Close() 908 909 response, err := c.PromoteReplica(ctx, &tabletmanagerdatapb.PromoteReplicaRequest{ 910 SemiSync: semiSync, 911 }) 912 if err != nil { 913 return "", err 914 } 915 return response.Position, nil 916 } 917 918 // Backup related methods 919 type backupStreamAdapter struct { 920 stream tabletmanagerservicepb.TabletManager_BackupClient 921 closer io.Closer 922 } 923 924 func (e *backupStreamAdapter) Recv() (*logutilpb.Event, error) { 925 br, err := e.stream.Recv() 926 if err != nil { 927 e.closer.Close() 928 return nil, err 929 } 930 return br.Event, nil 931 } 932 933 // Backup is part of the tmclient.TabletManagerClient interface. 934 func (client *Client) Backup(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.BackupRequest) (logutil.EventStream, error) { 935 c, closer, err := client.dialer.dial(ctx, tablet) 936 if err != nil { 937 return nil, err 938 } 939 940 stream, err := c.Backup(ctx, req) 941 if err != nil { 942 closer.Close() 943 return nil, err 944 } 945 return &backupStreamAdapter{ 946 stream: stream, 947 closer: closer, 948 }, nil 949 } 950 951 type restoreFromBackupStreamAdapter struct { 952 stream tabletmanagerservicepb.TabletManager_RestoreFromBackupClient 953 closer io.Closer 954 } 955 956 func (e *restoreFromBackupStreamAdapter) Recv() (*logutilpb.Event, error) { 957 br, err := e.stream.Recv() 958 if err != nil { 959 e.closer.Close() 960 return nil, err 961 } 962 return br.Event, nil 963 } 964 965 // RestoreFromBackup is part of the tmclient.TabletManagerClient interface. 966 func (client *Client) RestoreFromBackup(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.RestoreFromBackupRequest) (logutil.EventStream, error) { 967 c, closer, err := client.dialer.dial(ctx, tablet) 968 if err != nil { 969 return nil, err 970 } 971 972 stream, err := c.RestoreFromBackup(ctx, req) 973 if err != nil { 974 closer.Close() 975 return nil, err 976 } 977 return &restoreFromBackupStreamAdapter{ 978 stream: stream, 979 closer: closer, 980 }, nil 981 } 982 983 // Close is part of the tmclient.TabletManagerClient interface. 984 func (client *Client) Close() { 985 client.dialer.Close() 986 }