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 }