Skip to content

Commit bdc2ba5

Browse files
committed
remove tablet from old ring if its target changes
Signed-off-by: Mohamed Hamza <[email protected]>
1 parent 0a8ce4b commit bdc2ba5

File tree

2 files changed

+167
-3
lines changed

2 files changed

+167
-3
lines changed

go/vt/vtgate/balancer/session.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"sync"
2727

2828
"vitess.io/vitess/go/vt/discovery"
29-
"vitess.io/vitess/go/vt/log"
3029
querypb "vitess.io/vitess/go/vt/proto/query"
3130
"vitess.io/vitess/go/vt/proto/topodata"
3231
"vitess.io/vitess/go/vt/srvtopo"
@@ -54,17 +53,21 @@ type SessionBalancer struct {
5453
// externalRings are the hash rings created for each target. It contains only tablets
5554
// external to localCell.
5655
externalRings map[discovery.KeyspaceShardTabletType]*hashRing
56+
57+
// tablets keeps track of the latest state of each tablet, keyed by tablet alias. This is
58+
// used to check whether a tablet's target has changed, and needs to be removed from its old
59+
// hash ring.
60+
tablets map[string]*discovery.TabletHealth
5761
}
5862

5963
// NewSessionBalancer creates a new session balancer.
6064
func NewSessionBalancer(ctx context.Context, localCell string, topoServer srvtopo.Server, hc discovery.HealthCheck) (TabletBalancer, error) {
61-
log.Info("session balancer: creating new session balancer")
62-
6365
b := &SessionBalancer{
6466
localCell: localCell,
6567
hc: hc,
6668
localRings: make(map[discovery.KeyspaceShardTabletType]*hashRing),
6769
externalRings: make(map[discovery.KeyspaceShardTabletType]*hashRing),
70+
tablets: make(map[string]*discovery.TabletHealth),
6871
}
6972

7073
// Set up health check subscription
@@ -140,6 +143,8 @@ func (b *SessionBalancer) watchHealthCheck(ctx context.Context, hcChan chan *dis
140143
continue
141144
}
142145

146+
b.removeOldTablet(tablet)
147+
143148
// Ignore tablets we aren't supposed to watch
144149
if _, ok := tabletTypesToWatch[tablet.Target.TabletType]; !ok {
145150
continue
@@ -150,6 +155,39 @@ func (b *SessionBalancer) watchHealthCheck(ctx context.Context, hcChan chan *dis
150155
}
151156
}
152157

158+
// removeOldTablet removes the entry for a tablet in its old hash ring if its target has changed. For example, if a
159+
// reparent happens and a replica is now a primary, we need to remove it from the replica hash ring.
160+
func (b *SessionBalancer) removeOldTablet(tablet *discovery.TabletHealth) {
161+
alias := tabletAlias(tablet)
162+
prevTablet, ok := b.tablets[alias]
163+
if !ok {
164+
return
165+
}
166+
167+
prevTarget := prevTablet.Target
168+
169+
// If this tablet's target changed, remove it from its old hash ring.
170+
targetChanged := prevTarget.TabletType != tablet.Target.TabletType || prevTarget.Keyspace != tablet.Target.Keyspace || prevTarget.Shard != tablet.Target.Shard
171+
if !targetChanged {
172+
return
173+
}
174+
175+
prevKey := discovery.KeyFromTarget(prevTablet.Target)
176+
177+
var ring map[discovery.KeyspaceShardTabletType]*hashRing
178+
if tablet.Tablet.Alias.Cell == b.localCell {
179+
ring = b.localRings
180+
} else {
181+
ring = b.externalRings
182+
}
183+
184+
if ring == nil || ring[prevKey] == nil {
185+
return
186+
}
187+
188+
ring[prevKey].remove(prevTablet)
189+
}
190+
153191
// onTabletHealthChange is the handler for tablet health events. If a tablet goes into serving,
154192
// it is added to the appropriate (local or external) hash ring for its target. If it goes out
155193
// of serving, it is removed from the hash ring.
@@ -166,8 +204,12 @@ func (b *SessionBalancer) onTabletHealthChange(tablet *discovery.TabletHealth) {
166204

167205
if tablet.Serving {
168206
ring.add(tablet)
207+
208+
alias := tabletAlias(tablet)
209+
b.tablets[alias] = tablet
169210
} else {
170211
ring.remove(tablet)
212+
delete(b.tablets, tabletAlias(tablet))
171213
}
172214
}
173215

go/vt/vtgate/balancer/session_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,128 @@ func TestTabletTypesToWatch(t *testing.T) {
681681
}
682682
}
683683

684+
func TestTabletTargetChanges(t *testing.T) {
685+
b, hcChan := newSessionBalancer(t)
686+
687+
replica := &discovery.TabletHealth{
688+
Tablet: &topodatapb.Tablet{
689+
Alias: &topodatapb.TabletAlias{
690+
Cell: "local",
691+
Uid: 100,
692+
},
693+
Keyspace: "keyspace",
694+
Shard: "0",
695+
},
696+
Target: &querypb.Target{
697+
Keyspace: "keyspace",
698+
Shard: "0",
699+
TabletType: topodatapb.TabletType_REPLICA,
700+
Cell: "local",
701+
},
702+
Serving: true,
703+
}
704+
705+
primary := &discovery.TabletHealth{
706+
Tablet: &topodatapb.Tablet{
707+
Alias: &topodatapb.TabletAlias{
708+
Cell: "local",
709+
Uid: 100,
710+
},
711+
Keyspace: "keyspace",
712+
Shard: "0",
713+
},
714+
Target: &querypb.Target{
715+
Keyspace: "keyspace",
716+
Shard: "0",
717+
TabletType: topodatapb.TabletType_PRIMARY,
718+
Cell: "local",
719+
},
720+
Serving: true,
721+
}
722+
723+
hcChan <- replica
724+
725+
// Give a moment for the worker to process the tablets
726+
time.Sleep(100 * time.Millisecond)
727+
728+
require.Len(t, b.localRings, 1, b.print())
729+
require.Len(t, b.localRings[discovery.KeyFromTarget(replica.Target)].tablets, 1, b.print())
730+
731+
require.Len(t, b.externalRings, 0, b.print())
732+
733+
// Reparent happens, tablet is now a primary
734+
hcChan <- primary
735+
736+
// Give a moment for the worker to process the tablets
737+
time.Sleep(100 * time.Millisecond)
738+
739+
require.Len(t, b.localRings, 1, b.print())
740+
require.Len(t, b.localRings[discovery.KeyFromTarget(replica.Target)].tablets, 0, b.print())
741+
742+
require.Len(t, b.externalRings, 0, b.print())
743+
}
744+
745+
func TestExternalTabletTargetChanges(t *testing.T) {
746+
b, hcChan := newSessionBalancer(t)
747+
748+
replica := &discovery.TabletHealth{
749+
Tablet: &topodatapb.Tablet{
750+
Alias: &topodatapb.TabletAlias{
751+
Cell: "external",
752+
Uid: 100,
753+
},
754+
Keyspace: "keyspace",
755+
Shard: "0",
756+
},
757+
Target: &querypb.Target{
758+
Keyspace: "keyspace",
759+
Shard: "0",
760+
TabletType: topodatapb.TabletType_REPLICA,
761+
Cell: "external",
762+
},
763+
Serving: true,
764+
}
765+
766+
primary := &discovery.TabletHealth{
767+
Tablet: &topodatapb.Tablet{
768+
Alias: &topodatapb.TabletAlias{
769+
Cell: "external",
770+
Uid: 100,
771+
},
772+
Keyspace: "keyspace",
773+
Shard: "0",
774+
},
775+
Target: &querypb.Target{
776+
Keyspace: "keyspace",
777+
Shard: "0",
778+
TabletType: topodatapb.TabletType_PRIMARY,
779+
Cell: "external",
780+
},
781+
Serving: true,
782+
}
783+
784+
hcChan <- replica
785+
786+
// Give a moment for the worker to process the tablets
787+
time.Sleep(100 * time.Millisecond)
788+
789+
require.Len(t, b.externalRings, 1, b.print())
790+
require.Len(t, b.externalRings[discovery.KeyFromTarget(replica.Target)].tablets, 1, b.print())
791+
792+
require.Len(t, b.localRings, 0, b.print())
793+
794+
// Reparent happens, tablet is now a primary
795+
hcChan <- primary
796+
797+
// Give a moment for the worker to process the tablets
798+
time.Sleep(100 * time.Millisecond)
799+
800+
require.Len(t, b.externalRings, 1, b.print())
801+
require.Len(t, b.externalRings[discovery.KeyFromTarget(replica.Target)].tablets, 0, b.print())
802+
803+
require.Len(t, b.localRings, 0, b.print())
804+
}
805+
684806
func buildOpts(uuid string) PickOpts {
685807
return PickOpts{SessionUUID: uuid}
686808
}

0 commit comments

Comments
 (0)