Skip to content

Commit 29fb9e0

Browse files
committed
initial commit: pick a tablet
Signed-off-by: Nick Van Wiggeren <[email protected]>
1 parent 8fc596b commit 29fb9e0

File tree

7 files changed

+88
-20
lines changed

7 files changed

+88
-20
lines changed

go/test/vschemawrapper/vschema_wrapper.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ func (vw *VSchemaWrapper) ShardDestination() key.ShardDestination {
236236
}
237237

238238
func (vw *VSchemaWrapper) FindTable(tab sqlparser.TableName) (*vindexes.BaseTable, string, topodatapb.TabletType, key.ShardDestination, error) {
239-
destKeyspace, destTabletType, destTarget, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY)
239+
destKeyspace, destTabletType, destTarget, _, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY)
240240
if err != nil {
241241
return nil, destKeyspace, destTabletType, destTarget, err
242242
}
@@ -248,15 +248,15 @@ func (vw *VSchemaWrapper) FindTable(tab sqlparser.TableName) (*vindexes.BaseTabl
248248
}
249249

250250
func (vw *VSchemaWrapper) FindView(tab sqlparser.TableName) sqlparser.TableStatement {
251-
destKeyspace, _, _, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY)
251+
destKeyspace, _, _, _, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY)
252252
if err != nil {
253253
return nil
254254
}
255255
return vw.V.FindView(destKeyspace, tab.Name.String())
256256
}
257257

258258
func (vw *VSchemaWrapper) FindViewTarget(name sqlparser.TableName) (*vindexes.Keyspace, error) {
259-
destKeyspace, _, _, err := topoproto.ParseDestination(name.Qualifier.String(), topodatapb.TabletType_PRIMARY)
259+
destKeyspace, _, _, _, err := topoproto.ParseDestination(name.Qualifier.String(), topodatapb.TabletType_PRIMARY)
260260
if err != nil {
261261
return nil, err
262262
}
@@ -336,7 +336,7 @@ func (vw *VSchemaWrapper) IsViewsEnabled() bool {
336336
// FindMirrorRule finds the mirror rule for the requested keyspace, table
337337
// name, and the tablet type in the VSchema.
338338
func (vw *VSchemaWrapper) FindMirrorRule(tab sqlparser.TableName) (*vindexes.MirrorRule, error) {
339-
destKeyspace, destTabletType, _, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY)
339+
destKeyspace, destTabletType, _, _, err := topoproto.ParseDestination(tab.Qualifier.String(), topodatapb.TabletType_PRIMARY)
340340
if err != nil {
341341
return nil, err
342342
}

go/vt/topo/topoproto/destination.go

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,37 @@ import (
2929

3030
// ParseDestination parses the string representation of a ShardDestination
3131
// of the form keyspace:shard@tablet_type. You can use a / instead of a :.
32-
func ParseDestination(targetString string, defaultTabletType topodatapb.TabletType) (string, topodatapb.TabletType, key.ShardDestination, error) {
32+
// It also supports tablet-specific routing with keyspace@tablet-alias where
33+
// tablet-alias is in the format cell-uid (e.g., zone1-0000000100).
34+
func ParseDestination(targetString string, defaultTabletType topodatapb.TabletType) (string, topodatapb.TabletType, key.ShardDestination, *topodatapb.TabletAlias, error) {
3335
var dest key.ShardDestination
3436
var keyspace string
37+
var tabletAlias *topodatapb.TabletAlias
3538
tabletType := defaultTabletType
3639

3740
last := strings.LastIndexAny(targetString, "@")
3841
if last != -1 {
39-
// No need to check the error. UNKNOWN will be returned on
40-
// error and it will fail downstream.
41-
tabletType, _ = ParseTabletType(targetString[last+1:])
42+
afterAt := targetString[last+1:]
43+
// Try parsing as tablet type first (backward compatible)
44+
parsedTabletType, err := ParseTabletType(afterAt)
45+
// If tablet type parsing fails or returns UNKNOWN, try parsing as tablet alias
46+
if err != nil || parsedTabletType == topodatapb.TabletType_UNKNOWN {
47+
// Check if it looks like a tablet alias (contains a dash)
48+
if strings.Contains(afterAt, "-") {
49+
alias, aliasErr := ParseTabletAlias(afterAt)
50+
if aliasErr == nil {
51+
tabletAlias = alias
52+
// Keep tabletType as defaultTabletType when using tablet alias
53+
} else {
54+
// If both tablet type and tablet alias parsing fail, keep the UNKNOWN tablet type
55+
// which will cause appropriate error handling downstream
56+
tabletType = topodatapb.TabletType_UNKNOWN
57+
}
58+
}
59+
} else {
60+
// Successfully parsed as tablet type
61+
tabletType = parsedTabletType
62+
}
4263
targetString = targetString[:last]
4364
}
4465
last = strings.LastIndexAny(targetString, "/:")
@@ -51,29 +72,29 @@ func ParseDestination(targetString string, defaultTabletType topodatapb.TabletTy
5172
if last != -1 {
5273
rangeEnd := strings.LastIndexAny(targetString, "]")
5374
if rangeEnd == -1 {
54-
return keyspace, tabletType, dest, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid key range provided. Couldn't find range end ']'")
75+
return keyspace, tabletType, dest, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid key range provided. Couldn't find range end ']'")
5576
}
5677
rangeString := targetString[last+1 : rangeEnd]
5778
if strings.Contains(rangeString, "-") {
5879
// Parse as range
5980
keyRange, err := key.ParseShardingSpec(rangeString)
6081
if err != nil {
61-
return keyspace, tabletType, dest, err
82+
return keyspace, tabletType, dest, nil, err
6283
}
6384
if len(keyRange) != 1 {
64-
return keyspace, tabletType, dest, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "single keyrange expected in %s", rangeString)
85+
return keyspace, tabletType, dest, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "single keyrange expected in %s", rangeString)
6586
}
6687
dest = key.DestinationExactKeyRange{KeyRange: keyRange[0]}
6788
} else {
6889
// Parse as keyspace id
6990
destBytes, err := hex.DecodeString(rangeString)
7091
if err != nil {
71-
return keyspace, tabletType, dest, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "expected valid hex in keyspace id %s", rangeString)
92+
return keyspace, tabletType, dest, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "expected valid hex in keyspace id %s", rangeString)
7293
}
7394
dest = key.DestinationKeyspaceID(destBytes)
7495
}
7596
targetString = targetString[:last]
7697
}
7798
keyspace = targetString
78-
return keyspace, tabletType, dest, nil
99+
return keyspace, tabletType, dest, tabletAlias, nil
79100
}

go/vt/topo/topoproto/destination_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func TestParseDestination(t *testing.T) {
9090
}}
9191

9292
for _, tcase := range testcases {
93-
if targetKeyspace, targetTabletType, targetDest, _ := ParseDestination(tcase.targetString, topodatapb.TabletType_PRIMARY); !reflect.DeepEqual(targetDest, tcase.dest) || targetKeyspace != tcase.keyspace || targetTabletType != tcase.tabletType {
93+
if targetKeyspace, targetTabletType, targetDest, _, _ := ParseDestination(tcase.targetString, topodatapb.TabletType_PRIMARY); !reflect.DeepEqual(targetDest, tcase.dest) || targetKeyspace != tcase.keyspace || targetTabletType != tcase.tabletType {
9494
t.Errorf("ParseDestination(%s) - got: (%v, %v, %v), want (%v, %v, %v)",
9595
tcase.targetString,
9696
targetDest,
@@ -103,19 +103,19 @@ func TestParseDestination(t *testing.T) {
103103
}
104104
}
105105

106-
_, _, _, err := ParseDestination("ks[20-40-60]", topodatapb.TabletType_PRIMARY)
106+
_, _, _, _, err := ParseDestination("ks[20-40-60]", topodatapb.TabletType_PRIMARY)
107107
want := "single keyrange expected in 20-40-60"
108108
if err == nil || err.Error() != want {
109109
t.Errorf("executorExec error: %v, want %s", err, want)
110110
}
111111

112-
_, _, _, err = ParseDestination("ks[--60]", topodatapb.TabletType_PRIMARY)
112+
_, _, _, _, err = ParseDestination("ks[--60]", topodatapb.TabletType_PRIMARY)
113113
want = "malformed spec: MinKey/MaxKey cannot be in the middle of the spec: \"--60\""
114114
if err == nil || err.Error() != want {
115115
t.Errorf("executorExec error: %v, want %s", err, want)
116116
}
117117

118-
_, _, _, err = ParseDestination("ks[qrnqorrs]@primary", topodatapb.TabletType_PRIMARY)
118+
_, _, _, _, err = ParseDestination("ks[qrnqorrs]@primary", topodatapb.TabletType_PRIMARY)
119119
want = "expected valid hex in keyspace id qrnqorrs"
120120
if err == nil || err.Error() != want {
121121
t.Errorf("executorExec error: %v, want %s", err, want)

go/vt/vtgate/executorcontext/safe_session.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ type (
6565

6666
logging *ExecuteLogger
6767

68+
// targetTabletAlias is set when using tablet-specific routing via USE keyspace@tablet-alias.
69+
// This causes all queries to route to the specified tablet until cleared.
70+
// Note: This is stored in the Go wrapper, not in the protobuf Session.
71+
targetTabletAlias *topodatapb.TabletAlias
72+
6873
*vtgatepb.Session
6974
}
7075

@@ -1143,3 +1148,19 @@ func (l *ExecuteLogger) GetLogs() []engine.ExecuteEntry {
11431148
copy(result, l.entries)
11441149
return result
11451150
}
1151+
1152+
// SetTargetTabletAlias sets the tablet alias for tablet-specific routing.
1153+
// When set, all queries will route to the specified tablet until cleared.
1154+
func (session *SafeSession) SetTargetTabletAlias(alias *topodatapb.TabletAlias) {
1155+
session.mu.Lock()
1156+
defer session.mu.Unlock()
1157+
session.targetTabletAlias = alias
1158+
}
1159+
1160+
// GetTargetTabletAlias returns the current tablet alias for tablet-specific routing,
1161+
// or nil if not set.
1162+
func (session *SafeSession) GetTargetTabletAlias() *topodatapb.TabletAlias {
1163+
session.mu.Lock()
1164+
defer session.mu.Unlock()
1165+
return session.targetTabletAlias
1166+
}

go/vt/vtgate/executorcontext/vcursor_impl.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,8 @@ func (vc *VCursorImpl) parseDestinationTarget(targetString string) (string, topo
538538

539539
// ParseDestinationTarget parses destination target string and provides a keyspace if possible.
540540
func ParseDestinationTarget(targetString string, tablet topodatapb.TabletType, vschema *vindexes.VSchema) (string, topodatapb.TabletType, key.ShardDestination, error) {
541-
destKeyspace, destTabletType, dest, err := topoprotopb.ParseDestination(targetString, tablet)
541+
destKeyspace, destTabletType, dest, _, err := topoprotopb.ParseDestination(targetString, tablet)
542+
// Note: We ignore the tablet alias here as it's handled separately in SetTarget
542543
// If the keyspace is not specified, and there is only one keyspace in the VSchema, use that.
543544
if destKeyspace == "" && len(vschema.Keyspaces) == 1 {
544545
for k := range vschema.Keyspaces {
@@ -1024,10 +1025,24 @@ func (vc *VCursorImpl) Session() engine.SessionActions {
10241025
}
10251026

10261027
func (vc *VCursorImpl) SetTarget(target string) error {
1027-
keyspace, tabletType, _, err := topoprotopb.ParseDestination(target, vc.config.DefaultTabletType)
1028+
keyspace, tabletType, _, tabletAlias, err := topoprotopb.ParseDestination(target, vc.config.DefaultTabletType)
10281029
if err != nil {
10291030
return err
10301031
}
1032+
1033+
// Clear any existing tablet-specific routing if not specified
1034+
if tabletAlias == nil {
1035+
vc.SafeSession.SetTargetTabletAlias(nil)
1036+
} else {
1037+
// Store tablet alias in session for routing
1038+
// Note: We don't validate the tablet exists here - if it doesn't exist or is
1039+
// unreachable, the query will fail during execution with a clear error message
1040+
vc.SafeSession.SetTargetTabletAlias(tabletAlias)
1041+
1042+
// Keep tabletType as determined by ParseDestination (defaultTabletType)
1043+
// The actual routing uses the tablet alias, so the type is only for VCursor state
1044+
}
1045+
10311046
if _, ok := vc.vschema.Keyspaces[keyspace]; !ignoreKeyspace(keyspace) && !ok {
10321047
return vterrors.VT05003(keyspace)
10331048
}

go/vt/vtgate/scatter_conn.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,13 @@ func requireNewQS(err error, target *querypb.Target) bool {
862862
// actionInfo looks at the current session, and returns information about what needs to be done for this tablet
863863
func actionInfo(ctx context.Context, target *querypb.Target, session *econtext.SafeSession, autocommit bool, txMode vtgatepb.TransactionMode) (*shardActionInfo, *vtgatepb.Session_ShardSession, error) {
864864
if !(session.InTransaction() || session.InReservedConn()) {
865+
// Check for tablet-specific routing for non-transactional queries
866+
if alias := session.GetTargetTabletAlias(); alias != nil {
867+
return &shardActionInfo{
868+
actionNeeded: nothing,
869+
alias: alias,
870+
}, nil, nil
871+
}
865872
return &shardActionInfo{}, nil, nil
866873
}
867874
ignoreSession := ctx.Value(engine.IgnoreReserveTxn)
@@ -899,6 +906,10 @@ func actionInfo(ctx context.Context, target *querypb.Target, session *econtext.S
899906
info.alias = shardSession.TabletAlias
900907
info.rowsAffected = shardSession.RowsAffected
901908
}
909+
// Override alias if tablet-specific routing is set
910+
if targetAlias := session.GetTargetTabletAlias(); targetAlias != nil {
911+
info.alias = targetAlias
912+
}
902913
return info, shardSession, nil
903914
}
904915

go/vtbench/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ func (c *grpcVttabletConn) connect(ctx context.Context, cp ConnParams) error {
124124
})
125125

126126
// parse the "db" into the keyspace/shard target
127-
keyspace, tabletType, dest, err := topoproto.ParseDestination(cp.DB, topodatapb.TabletType_PRIMARY)
127+
keyspace, tabletType, dest, _, err := topoproto.ParseDestination(cp.DB, topodatapb.TabletType_PRIMARY)
128128
if err != nil {
129129
return err
130130
}

0 commit comments

Comments
 (0)