vitess.io/vitess@v0.16.2/go/vt/wrangler/reparent.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 wrangler
    18  
    19  /*
    20  This file handles the reparenting operations.
    21  */
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	"time"
    27  
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  
    30  	"vitess.io/vitess/go/event"
    31  	"vitess.io/vitess/go/vt/log"
    32  	"vitess.io/vitess/go/vt/topo/topoproto"
    33  	"vitess.io/vitess/go/vt/topotools/events"
    34  	"vitess.io/vitess/go/vt/vtctl/grpcvtctldserver"
    35  	"vitess.io/vitess/go/vt/vtctl/reparentutil"
    36  
    37  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    38  	vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
    39  )
    40  
    41  // ReparentTablet tells a tablet to reparent this tablet to the current
    42  // primary, based on the current replication position. If there is no
    43  // match, it will fail.
    44  func (wr *Wrangler) ReparentTablet(ctx context.Context, tabletAlias *topodatapb.TabletAlias) error {
    45  	_, err := wr.vtctld.ReparentTablet(ctx, &vtctldatapb.ReparentTabletRequest{
    46  		Tablet: tabletAlias,
    47  	})
    48  	return err
    49  }
    50  
    51  // InitShardPrimary will make the provided tablet the primary for the shard.
    52  func (wr *Wrangler) InitShardPrimary(ctx context.Context, keyspace, shard string, primaryElectTabletAlias *topodatapb.TabletAlias, force bool, waitReplicasTimeout time.Duration) (err error) {
    53  	// lock the shard
    54  	ctx, unlock, lockErr := wr.ts.LockShard(ctx, keyspace, shard, fmt.Sprintf("InitShardPrimary(%v)", topoproto.TabletAliasString(primaryElectTabletAlias)))
    55  	if lockErr != nil {
    56  		return lockErr
    57  	}
    58  	defer unlock(&err)
    59  
    60  	// Create reusable Reparent event with available info
    61  	ev := &events.Reparent{}
    62  
    63  	// do the work
    64  	err = grpcvtctldserver.NewVtctldServer(wr.ts).InitShardPrimaryLocked(ctx, ev, &vtctldatapb.InitShardPrimaryRequest{
    65  		Keyspace:                keyspace,
    66  		Shard:                   shard,
    67  		PrimaryElectTabletAlias: primaryElectTabletAlias,
    68  		Force:                   force,
    69  	}, waitReplicasTimeout, wr.tmc, wr.logger)
    70  	if err != nil {
    71  		event.DispatchUpdate(ev, "failed InitShardPrimary: "+err.Error())
    72  	} else {
    73  		event.DispatchUpdate(ev, "finished InitShardPrimary")
    74  	}
    75  	return err
    76  }
    77  
    78  // PlannedReparentShard will make the provided tablet the primary for the shard,
    79  // when both the current and new primary are reachable and in good shape.
    80  func (wr *Wrangler) PlannedReparentShard(ctx context.Context, keyspace, shard string, primaryElectTabletAlias, avoidTabletAlias *topodatapb.TabletAlias, waitReplicasTimeout time.Duration) (err error) {
    81  	_, err = reparentutil.NewPlannedReparenter(wr.ts, wr.tmc, wr.logger).ReparentShard(
    82  		ctx,
    83  		keyspace,
    84  		shard,
    85  		reparentutil.PlannedReparentOptions{
    86  			AvoidPrimaryAlias:   avoidTabletAlias,
    87  			NewPrimaryAlias:     primaryElectTabletAlias,
    88  			WaitReplicasTimeout: waitReplicasTimeout,
    89  		},
    90  	)
    91  
    92  	return err
    93  }
    94  
    95  // EmergencyReparentShard will make the provided tablet the primary for
    96  // the shard, when the old primary is completely unreachable.
    97  func (wr *Wrangler) EmergencyReparentShard(ctx context.Context, keyspace, shard string, primaryElectTabletAlias *topodatapb.TabletAlias, waitReplicasTimeout time.Duration, ignoredTablets sets.Set[string], preventCrossCellPromotion bool) (err error) {
    98  	_, err = reparentutil.NewEmergencyReparenter(wr.ts, wr.tmc, wr.logger).ReparentShard(
    99  		ctx,
   100  		keyspace,
   101  		shard,
   102  		reparentutil.EmergencyReparentOptions{
   103  			NewPrimaryAlias:           primaryElectTabletAlias,
   104  			WaitReplicasTimeout:       waitReplicasTimeout,
   105  			IgnoreReplicas:            ignoredTablets,
   106  			PreventCrossCellPromotion: preventCrossCellPromotion,
   107  		},
   108  	)
   109  
   110  	return err
   111  }
   112  
   113  // TabletExternallyReparented changes the type of new primary for this shard to PRIMARY
   114  // and updates it's tablet record in the topo. Updating the shard record is handled
   115  // by the new primary tablet
   116  func (wr *Wrangler) TabletExternallyReparented(ctx context.Context, newPrimaryAlias *topodatapb.TabletAlias) error {
   117  
   118  	tabletInfo, err := wr.ts.GetTablet(ctx, newPrimaryAlias)
   119  	if err != nil {
   120  		log.Warningf("TabletExternallyReparented: failed to read tablet record for %v: %v", newPrimaryAlias, err)
   121  		return err
   122  	}
   123  
   124  	// Check the global shard record.
   125  	tablet := tabletInfo.Tablet
   126  	si, err := wr.ts.GetShard(ctx, tablet.Keyspace, tablet.Shard)
   127  	if err != nil {
   128  		log.Warningf("TabletExternallyReparented: failed to read global shard record for %v/%v: %v", tablet.Keyspace, tablet.Shard, err)
   129  		return err
   130  	}
   131  
   132  	// We update the tablet only if it is not currently primary
   133  	if tablet.Type != topodatapb.TabletType_PRIMARY {
   134  		log.Infof("TabletExternallyReparented: executing tablet type change to PRIMARY")
   135  
   136  		durabilityName, err := wr.ts.GetKeyspaceDurability(ctx, tablet.Keyspace)
   137  		if err != nil {
   138  			return err
   139  		}
   140  		log.Infof("Getting a new durability policy for %v", durabilityName)
   141  		durability, err := reparentutil.GetDurabilityPolicy(durabilityName)
   142  		if err != nil {
   143  			return err
   144  		}
   145  
   146  		// Create a reusable Reparent event with available info.
   147  		ev := &events.Reparent{
   148  			ShardInfo:  *si,
   149  			NewPrimary: tablet,
   150  			OldPrimary: &topodatapb.Tablet{
   151  				Alias: si.PrimaryAlias,
   152  				Type:  topodatapb.TabletType_PRIMARY,
   153  			},
   154  		}
   155  		defer func() {
   156  			if err != nil {
   157  				event.DispatchUpdate(ev, "failed: "+err.Error())
   158  			}
   159  		}()
   160  		event.DispatchUpdate(ev, "starting external reparent")
   161  
   162  		if err := wr.tmc.ChangeType(ctx, tablet, topodatapb.TabletType_PRIMARY, reparentutil.SemiSyncAckers(durability, tablet) > 0); err != nil {
   163  			log.Warningf("Error calling ChangeType on new primary %v: %v", topoproto.TabletAliasString(newPrimaryAlias), err)
   164  			return err
   165  		}
   166  		event.DispatchUpdate(ev, "finished")
   167  	}
   168  	return nil
   169  }