vitess.io/vitess@v0.16.2/go/vt/key/destination.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 key
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/hex"
    22  	"math/rand"
    23  	"sort"
    24  	"strings"
    25  
    26  	"vitess.io/vitess/go/vt/vterrors"
    27  
    28  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    29  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    30  )
    31  
    32  // AnyShardPicker makes a choice on what shard to use when any shard will do. Used for testing.
    33  var AnyShardPicker DestinationAnyShardPicker = DestinationAnyShardPickerRandomShard{}
    34  
    35  // Destination is an interface definition for a query destination,
    36  // within a given Keyspace / Tablet Type. It is meant to be an internal
    37  // data structure, with multiple possible implementations.
    38  // The srvtopo package can resolve Destinations into actual Targets.
    39  type Destination interface {
    40  	// Resolve calls the callback for every shard Destination
    41  	// resolves into, given the shards list.
    42  	// The returned error must be generated by vterrors.
    43  	Resolve([]*topodatapb.ShardReference, func(shard string) error) error
    44  
    45  	// String returns a printable version of the Destination.
    46  	String() string
    47  }
    48  
    49  // DestinationsString returns a printed version of the destination array.
    50  func DestinationsString(destinations []Destination) string {
    51  	var buffer bytes.Buffer
    52  	buffer.WriteString("Destinations:")
    53  	for i, d := range destinations {
    54  		if i > 0 {
    55  			buffer.WriteByte(',')
    56  		}
    57  		buffer.WriteString(d.String())
    58  	}
    59  	return buffer.String()
    60  }
    61  
    62  //
    63  // DestinationShard
    64  //
    65  
    66  // DestinationShard is the destination for a single Shard.
    67  // It implements the Destination interface.
    68  type DestinationShard string
    69  
    70  // Resolve is part of the Destination interface.
    71  func (d DestinationShard) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
    72  	return addShard(string(d))
    73  }
    74  
    75  // String is part of the Destination interface.
    76  func (d DestinationShard) String() string {
    77  	return "DestinationShard(" + string(d) + ")"
    78  }
    79  
    80  //
    81  // DestinationShards
    82  //
    83  
    84  // DestinationShards is the destination for multiple shards.
    85  // It implements the Destination interface.
    86  type DestinationShards []string
    87  
    88  // Resolve is part of the Destination interface.
    89  func (d DestinationShards) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
    90  	for _, shard := range d {
    91  		if err := addShard(shard); err != nil {
    92  			return err
    93  		}
    94  	}
    95  	return nil
    96  }
    97  
    98  // String is part of the Destination interface.
    99  func (d DestinationShards) String() string {
   100  	return "DestinationShards(" + strings.Join(d, ",") + ")"
   101  }
   102  
   103  //
   104  // DestinationExactKeyRange
   105  //
   106  
   107  // DestinationExactKeyRange is the destination for a single KeyRange.
   108  // The KeyRange must map exactly to one or more shards, and cannot
   109  // start or end in the middle of a shard.
   110  // It implements the Destination interface.
   111  // (it cannot be just a type *topodatapb.KeyRange, as then the receiver
   112  // methods don't work. And it can't be topodatapb.KeyRange either,
   113  // as then the methods are on *DestinationExactKeyRange, and the original
   114  // KeyRange cannot be returned).
   115  type DestinationExactKeyRange struct {
   116  	KeyRange *topodatapb.KeyRange
   117  }
   118  
   119  // Resolve is part of the Destination interface.
   120  func (d DestinationExactKeyRange) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   121  	return processExactKeyRange(allShards, d.KeyRange, addShard)
   122  }
   123  
   124  // String is part of the Destination interface.
   125  func (d DestinationExactKeyRange) String() string {
   126  	return "DestinationExactKeyRange(" + KeyRangeString(d.KeyRange) + ")"
   127  }
   128  
   129  func processExactKeyRange(allShards []*topodatapb.ShardReference, kr *topodatapb.KeyRange, addShard func(shard string) error) error {
   130  	sort.SliceStable(allShards, func(i, j int) bool {
   131  		return KeyRangeStartSmaller(allShards[i].GetKeyRange(), allShards[j].GetKeyRange())
   132  	})
   133  
   134  	shardnum := 0
   135  	for shardnum < len(allShards) {
   136  		if KeyRangeStartEqual(kr, allShards[shardnum].KeyRange) {
   137  			break
   138  		}
   139  		shardnum++
   140  	}
   141  	for shardnum < len(allShards) {
   142  		if !KeyRangesIntersect(kr, allShards[shardnum].KeyRange) {
   143  			// If we are over the requested keyrange, we
   144  			// can stop now, we won't find more.
   145  			break
   146  		}
   147  		if err := addShard(allShards[shardnum].Name); err != nil {
   148  			return err
   149  		}
   150  		if KeyRangeEndEqual(kr, allShards[shardnum].KeyRange) {
   151  			return nil
   152  		}
   153  		shardnum++
   154  	}
   155  	return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "keyrange %v does not exactly match shards", KeyRangeString(kr))
   156  }
   157  
   158  //
   159  // DestinationExactKeyRanges
   160  //
   161  
   162  // DestinationExactKeyRanges is the destination for multiple KeyRanges.
   163  // The KeyRanges must map exactly to one or more shards, and cannot
   164  // start or end in the middle of a shard.
   165  // It implements the Destination interface.
   166  type DestinationExactKeyRanges []*topodatapb.KeyRange
   167  
   168  // Resolve is part of the Destination interface.
   169  func (d DestinationExactKeyRanges) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   170  	for _, kr := range d {
   171  		if err := processExactKeyRange(allShards, kr, addShard); err != nil {
   172  			return err
   173  		}
   174  	}
   175  	return nil
   176  }
   177  
   178  // String is part of the Destination interface.
   179  func (d DestinationExactKeyRanges) String() string {
   180  	var buffer bytes.Buffer
   181  	buffer.WriteString("DestinationExactKeyRanges(")
   182  	for i, kr := range d {
   183  		if i > 0 {
   184  			buffer.WriteByte(',')
   185  		}
   186  		buffer.WriteString(KeyRangeString(kr))
   187  	}
   188  	buffer.WriteByte(')')
   189  	return buffer.String()
   190  }
   191  
   192  //
   193  // DestinationKeyRange
   194  //
   195  
   196  // DestinationKeyRange is the destination for a single KeyRange.
   197  // It implements the Destination interface.
   198  // (it cannot be just a type *topodatapb.KeyRange, as then the receiver
   199  // methods don't work. And it can't be topodatapb.KeyRange either,
   200  // as then the methods are on *DestinationKeyRange, and the original
   201  // KeyRange cannot be returned).
   202  type DestinationKeyRange struct {
   203  	KeyRange *topodatapb.KeyRange
   204  }
   205  
   206  // Resolve is part of the Destination interface.
   207  func (d DestinationKeyRange) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   208  	return processKeyRange(allShards, d.KeyRange, addShard)
   209  }
   210  
   211  // String is part of the Destination interface.
   212  func (d DestinationKeyRange) String() string {
   213  	return "DestinationKeyRange(" + KeyRangeString(d.KeyRange) + ")"
   214  }
   215  
   216  func processKeyRange(allShards []*topodatapb.ShardReference, kr *topodatapb.KeyRange, addShard func(shard string) error) error {
   217  	for _, shard := range allShards {
   218  		if !KeyRangesIntersect(kr, shard.KeyRange) {
   219  			// We don't need that shard.
   220  			continue
   221  		}
   222  		if err := addShard(shard.Name); err != nil {
   223  			return err
   224  		}
   225  	}
   226  	return nil
   227  }
   228  
   229  //
   230  // DestinationKeyRanges
   231  //
   232  
   233  // DestinationKeyRanges is the destination for multiple KeyRanges.
   234  // It implements the Destination interface.
   235  type DestinationKeyRanges []*topodatapb.KeyRange
   236  
   237  // Resolve is part of the Destination interface.
   238  func (d DestinationKeyRanges) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   239  	for _, kr := range d {
   240  		if err := processKeyRange(allShards, kr, addShard); err != nil {
   241  			return err
   242  		}
   243  	}
   244  	return nil
   245  }
   246  
   247  // String is part of the Destination interface.
   248  func (d DestinationKeyRanges) String() string {
   249  	var buffer bytes.Buffer
   250  	buffer.WriteString("DestinationKeyRanges(")
   251  	for i, kr := range d {
   252  		if i > 0 {
   253  			buffer.WriteByte(',')
   254  		}
   255  		buffer.WriteString(KeyRangeString(kr))
   256  	}
   257  	buffer.WriteByte(')')
   258  	return buffer.String()
   259  }
   260  
   261  //
   262  // DestinationKeyspaceID
   263  //
   264  
   265  // DestinationKeyspaceID is the destination for a single KeyspaceID.
   266  // It implements the Destination interface.
   267  type DestinationKeyspaceID []byte
   268  
   269  // Resolve is part of the Destination interface.
   270  func (d DestinationKeyspaceID) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   271  	shard, err := GetShardForKeyspaceID(allShards, d)
   272  	if err != nil {
   273  		return err
   274  	}
   275  	return addShard(shard)
   276  }
   277  
   278  // String is part of the Destination interface.
   279  func (d DestinationKeyspaceID) String() string {
   280  	return "DestinationKeyspaceID(" + hex.EncodeToString(d) + ")"
   281  }
   282  
   283  // GetShardForKeyspaceID finds the right shard for a keyspace id.
   284  func GetShardForKeyspaceID(allShards []*topodatapb.ShardReference, keyspaceID []byte) (string, error) {
   285  	if len(allShards) == 0 {
   286  		return "", vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "no shard in keyspace")
   287  	}
   288  
   289  	for _, shardReference := range allShards {
   290  		if KeyRangeContains(shardReference.KeyRange, keyspaceID) {
   291  			return shardReference.Name, nil
   292  		}
   293  	}
   294  	return "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "KeyspaceId %v didn't match any shards %+v", hex.EncodeToString(keyspaceID), allShards)
   295  }
   296  
   297  //
   298  // DestinationKeyspaceIDs
   299  //
   300  
   301  // DestinationKeyspaceIDs is the destination for multiple KeyspaceIDs.
   302  // It implements the Destination interface.
   303  type DestinationKeyspaceIDs [][]byte
   304  
   305  // Resolve is part of the Destination interface.
   306  func (d DestinationKeyspaceIDs) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   307  	for _, ksid := range d {
   308  		shard, err := GetShardForKeyspaceID(allShards, ksid)
   309  		if err != nil {
   310  			return err
   311  		}
   312  		if err := addShard(shard); err != nil {
   313  			return err
   314  		}
   315  	}
   316  	return nil
   317  }
   318  
   319  // String is part of the Destination interface.
   320  func (d DestinationKeyspaceIDs) String() string {
   321  	var buffer bytes.Buffer
   322  	buffer.WriteString("DestinationKeyspaceIDs(")
   323  	for i, ksid := range d {
   324  		if i > 0 {
   325  			buffer.WriteByte(',')
   326  		}
   327  		buffer.WriteString(hex.EncodeToString(ksid))
   328  	}
   329  	buffer.WriteByte(')')
   330  	return buffer.String()
   331  }
   332  
   333  // DestinationAnyShardPicker exposes an interface that will pick an index given a number of available shards.
   334  type DestinationAnyShardPicker interface {
   335  	// PickShard picks a shard given a number of shards
   336  	PickShard(shardCount int) int
   337  }
   338  
   339  // DestinationAnyShardPickerRandomShard picks a random shard.
   340  type DestinationAnyShardPickerRandomShard struct{}
   341  
   342  // PickShard is DestinationAnyShardPickerRandomShard's implementation.
   343  func (dp DestinationAnyShardPickerRandomShard) PickShard(shardCount int) int {
   344  	return rand.Intn(shardCount)
   345  }
   346  
   347  //
   348  // DestinationAnyShard
   349  //
   350  
   351  // DestinationAnyShard is the destination for any one shard in the keyspace.
   352  // It implements the Destination interface.
   353  type DestinationAnyShard struct{}
   354  
   355  // Resolve is part of the Destination interface.
   356  func (d DestinationAnyShard) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   357  	if len(allShards) == 0 {
   358  		return vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "no shard in keyspace")
   359  	}
   360  	return addShard(allShards[AnyShardPicker.PickShard(len(allShards))].Name)
   361  }
   362  
   363  // String is part of the Destination interface.
   364  func (d DestinationAnyShard) String() string {
   365  	return "DestinationAnyShard()"
   366  }
   367  
   368  //
   369  // DestinationAllShards
   370  //
   371  
   372  // DestinationAllShards is the destination for all the shards in the
   373  // keyspace. This usually maps to the first one in the list.
   374  // It implements the Destination interface.
   375  type DestinationAllShards struct{}
   376  
   377  // Resolve is part of the Destination interface.
   378  func (d DestinationAllShards) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   379  	for _, shard := range allShards {
   380  		if err := addShard(shard.Name); err != nil {
   381  			return err
   382  		}
   383  	}
   384  
   385  	return nil
   386  }
   387  
   388  // String is part of the Destination interface.
   389  func (d DestinationAllShards) String() string {
   390  	return "DestinationAllShards()"
   391  }
   392  
   393  //
   394  // DestinationNone
   395  //
   396  
   397  // DestinationNone is a destination that doesn't resolve to any shard.
   398  // It implements the Destination interface.
   399  type DestinationNone struct{}
   400  
   401  // Resolve is part of the Destination interface.
   402  func (d DestinationNone) Resolve(allShards []*topodatapb.ShardReference, addShard func(shard string) error) error {
   403  	return nil
   404  }
   405  
   406  // String is part of the Destination interface.
   407  func (d DestinationNone) String() string {
   408  	return "DestinationNone()"
   409  }