vitess.io/vitess@v0.16.2/go/vt/vtctl/reparentutil/durability.go (about)

     1  /*
     2  Copyright 2021 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 reparentutil
    18  
    19  import (
    20  	"fmt"
    21  
    22  	"vitess.io/vitess/go/vt/log"
    23  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    24  	"vitess.io/vitess/go/vt/topo/topoproto"
    25  	"vitess.io/vitess/go/vt/vtctl/reparentutil/promotionrule"
    26  )
    27  
    28  //=======================================================================
    29  
    30  // A NewDurabler is a function that creates a new Durabler based on the
    31  // properties specified in the input map. Every Durabler must
    32  // register a NewDurabler function.
    33  type NewDurabler func() Durabler
    34  
    35  var (
    36  	// durabilityPolicies is a map that stores the functions needed to create a new Durabler
    37  	durabilityPolicies = make(map[string]NewDurabler)
    38  )
    39  
    40  func init() {
    41  	// register all the durability rules with their functions to create them
    42  	RegisterDurability("none", func() Durabler {
    43  		return &durabilityNone{}
    44  	})
    45  	RegisterDurability("semi_sync", func() Durabler {
    46  		return &durabilitySemiSync{}
    47  	})
    48  	RegisterDurability("cross_cell", func() Durabler {
    49  		return &durabilityCrossCell{}
    50  	})
    51  	RegisterDurability("test", func() Durabler {
    52  		return &durabilityTest{}
    53  	})
    54  }
    55  
    56  // Durabler is the interface which is used to get the promotion rules for candidates and the semi sync setup
    57  type Durabler interface {
    58  	// promotionRule represents the precedence in which we want to tablets to be promoted.
    59  	// The higher the promotion rule of a tablet, the more we want it to be promoted in case of a failover
    60  	promotionRule(*topodatapb.Tablet) promotionrule.CandidatePromotionRule
    61  	// semiSyncAckers represents the number of semi-sync ackers required for a given tablet if it were to become the PRIMARY instance
    62  	semiSyncAckers(*topodatapb.Tablet) int
    63  	// isReplicaSemiSync returns whether the "replica" should send semi-sync acks if "primary" were to become the PRIMARY instance
    64  	isReplicaSemiSync(primary, replica *topodatapb.Tablet) bool
    65  }
    66  
    67  func RegisterDurability(name string, newDurablerFunc NewDurabler) {
    68  	if durabilityPolicies[name] != nil {
    69  		log.Fatalf("durability policy %v already registered", name)
    70  	}
    71  	durabilityPolicies[name] = newDurablerFunc
    72  }
    73  
    74  //=======================================================================
    75  
    76  // GetDurabilityPolicy is used to get a new durability policy from the registered policies
    77  func GetDurabilityPolicy(name string) (Durabler, error) {
    78  	newDurabilityCreationFunc, found := durabilityPolicies[name]
    79  	if !found {
    80  		return nil, fmt.Errorf("durability policy %v not found", name)
    81  	}
    82  	return newDurabilityCreationFunc(), nil
    83  }
    84  
    85  // CheckDurabilityPolicyExists is used to check if the durability policy is part of the registered policies
    86  func CheckDurabilityPolicyExists(name string) bool {
    87  	_, found := durabilityPolicies[name]
    88  	return found
    89  }
    90  
    91  // PromotionRule returns the promotion rule for the instance.
    92  func PromotionRule(durability Durabler, tablet *topodatapb.Tablet) promotionrule.CandidatePromotionRule {
    93  	// Prevent panics.
    94  	if tablet == nil || tablet.Alias == nil {
    95  		return promotionrule.MustNot
    96  	}
    97  	return durability.promotionRule(tablet)
    98  }
    99  
   100  // SemiSyncAckers returns the primary semi-sync setting for the instance.
   101  // 0 means none. Non-zero specifies the number of required ackers.
   102  func SemiSyncAckers(durability Durabler, tablet *topodatapb.Tablet) int {
   103  	return durability.semiSyncAckers(tablet)
   104  }
   105  
   106  // IsReplicaSemiSync returns the replica semi-sync setting from the tablet record.
   107  // Prefer using this function if tablet record is available.
   108  func IsReplicaSemiSync(durability Durabler, primary, replica *topodatapb.Tablet) bool {
   109  	// Prevent panics.
   110  	if primary == nil || primary.Alias == nil || replica == nil || replica.Alias == nil {
   111  		return false
   112  	}
   113  	return durability.isReplicaSemiSync(primary, replica)
   114  }
   115  
   116  //=======================================================================
   117  
   118  // durabilityNone has no semi-sync and returns NeutralPromoteRule for Primary and Replica tablet types, MustNotPromoteRule for everything else
   119  type durabilityNone struct{}
   120  
   121  // promotionRule implements the Durabler interface
   122  func (d *durabilityNone) promotionRule(tablet *topodatapb.Tablet) promotionrule.CandidatePromotionRule {
   123  	switch tablet.Type {
   124  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA:
   125  		return promotionrule.Neutral
   126  	}
   127  	return promotionrule.MustNot
   128  }
   129  
   130  // semiSyncAckers implements the Durabler interface
   131  func (d *durabilityNone) semiSyncAckers(tablet *topodatapb.Tablet) int {
   132  	return 0
   133  }
   134  
   135  // isReplicaSemiSync implements the Durabler interface
   136  func (d *durabilityNone) isReplicaSemiSync(primary, replica *topodatapb.Tablet) bool {
   137  	return false
   138  }
   139  
   140  //=======================================================================
   141  
   142  // durabilitySemiSync has 1 semi-sync setup. It only allows Primary and Replica type servers to acknowledge semi sync
   143  // It returns NeutralPromoteRule for Primary and Replica tablet types, MustNotPromoteRule for everything else
   144  type durabilitySemiSync struct{}
   145  
   146  // promotionRule implements the Durabler interface
   147  func (d *durabilitySemiSync) promotionRule(tablet *topodatapb.Tablet) promotionrule.CandidatePromotionRule {
   148  	switch tablet.Type {
   149  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA:
   150  		return promotionrule.Neutral
   151  	}
   152  	return promotionrule.MustNot
   153  }
   154  
   155  // semiSyncAckers implements the Durabler interface
   156  func (d *durabilitySemiSync) semiSyncAckers(tablet *topodatapb.Tablet) int {
   157  	return 1
   158  }
   159  
   160  // isReplicaSemiSync implements the Durabler interface
   161  func (d *durabilitySemiSync) isReplicaSemiSync(primary, replica *topodatapb.Tablet) bool {
   162  	switch replica.Type {
   163  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA:
   164  		return true
   165  	}
   166  	return false
   167  }
   168  
   169  //=======================================================================
   170  
   171  // durabilityCrossCell has 1 semi-sync setup. It only allows Primary and Replica type servers from a different cell to acknowledge semi sync.
   172  // This means that a transaction must be in two cells for it to be acknowledged
   173  // It returns NeutralPromoteRule for Primary and Replica tablet types, MustNotPromoteRule for everything else
   174  type durabilityCrossCell struct{}
   175  
   176  // promotionRule implements the Durabler interface
   177  func (d *durabilityCrossCell) promotionRule(tablet *topodatapb.Tablet) promotionrule.CandidatePromotionRule {
   178  	switch tablet.Type {
   179  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA:
   180  		return promotionrule.Neutral
   181  	}
   182  	return promotionrule.MustNot
   183  }
   184  
   185  // semiSyncAckers implements the Durabler interface
   186  func (d *durabilityCrossCell) semiSyncAckers(tablet *topodatapb.Tablet) int {
   187  	return 1
   188  }
   189  
   190  // isReplicaSemiSync implements the Durabler interface
   191  func (d *durabilityCrossCell) isReplicaSemiSync(primary, replica *topodatapb.Tablet) bool {
   192  	switch replica.Type {
   193  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA:
   194  		return primary.Alias.Cell != replica.Alias.Cell
   195  	}
   196  	return false
   197  }
   198  
   199  //=======================================================================
   200  
   201  // durabilityTest is like durabilityNone. It overrides the type for a specific tablet to prefer. It is only meant to be used for testing purposes!
   202  type durabilityTest struct{}
   203  
   204  // promotionRule implements the Durabler interface
   205  func (d *durabilityTest) promotionRule(tablet *topodatapb.Tablet) promotionrule.CandidatePromotionRule {
   206  	if topoproto.TabletAliasString(tablet.Alias) == "zone2-0000000200" {
   207  		return promotionrule.Prefer
   208  	}
   209  
   210  	switch tablet.Type {
   211  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA:
   212  		return promotionrule.Neutral
   213  	}
   214  	return promotionrule.MustNot
   215  }
   216  
   217  // semiSyncAckers implements the Durabler interface
   218  func (d *durabilityTest) semiSyncAckers(tablet *topodatapb.Tablet) int {
   219  	return 0
   220  }
   221  
   222  // isReplicaSemiSync implements the Durabler interface
   223  func (d *durabilityTest) isReplicaSemiSync(primary, replica *topodatapb.Tablet) bool {
   224  	return false
   225  }