vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/shard_sync_test.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 tabletmanager 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "testing" 24 "time" 25 26 "vitess.io/vitess/go/protoutil" 27 "vitess.io/vitess/go/vt/proto/vttime" 28 29 "github.com/stretchr/testify/assert" 30 31 "vitess.io/vitess/go/vt/proto/topodata" 32 "vitess.io/vitess/go/vt/topo" 33 34 "github.com/stretchr/testify/require" 35 36 "vitess.io/vitess/go/vt/topo/topoproto" 37 38 "vitess.io/vitess/go/vt/topo/memorytopo" 39 ) 40 41 const ( 42 keyspace = "ks" 43 shard = "0" 44 ) 45 46 func TestShardSync(t *testing.T) { 47 ctx := context.Background() 48 ts := memorytopo.NewServer("cell1") 49 statsTabletTypeCount.ResetAll() 50 tm := newTestTM(t, ts, 100, keyspace, shard) 51 defer tm.Stop() 52 53 // update the primary info in the shard record and set it to nil 54 originalTime := time.Now() 55 updatePrimaryInfoInShardRecord(ctx, t, tm, nil, originalTime) 56 57 // now try to promote the tablet to primary 58 err := tm.tmState.ChangeTabletType(ctx, topodata.TabletType_PRIMARY, DBActionSetReadWrite) 59 require.NoError(t, err) 60 // verify that the tablet record has been updated 61 ti, err := ts.GetTablet(ctx, tm.tabletAlias) 62 require.NoError(t, err) 63 assert.Equal(t, topodata.TabletType_PRIMARY, ti.Type) 64 assert.NotNil(t, ti.PrimaryTermStartTime) 65 66 // wait for syncing to work correctly 67 // this should also have updated the shard record since it is a more recent operation 68 // We check here that the shard record and the tablet record are in sync 69 checkShardRecordWithTimeout(ctx, t, ts, ti.Alias, ti.PrimaryTermStartTime, 1*time.Second) 70 71 // even if try to update the shard record with the old timestamp, it should be reverted again 72 updatePrimaryInfoInShardRecord(ctx, t, tm, nil, originalTime) 73 74 // this should have also updated the shard record because of the timestamp. 75 checkShardRecordWithTimeout(ctx, t, ts, ti.Alias, ti.PrimaryTermStartTime, 1*time.Second) 76 77 // updating the shard record with the latest time should trigger an update in the tablet 78 newTime := time.Now() 79 updatePrimaryInfoInShardRecord(ctx, t, tm, nil, newTime) 80 81 // this should not have updated. 82 checkShardRecordWithTimeout(ctx, t, ts, nil, protoutil.TimeToProto(newTime), 1*time.Second) 83 84 // verify that the tablet record has been updated 85 checkTabletRecordWithTimeout(ctx, t, ts, tm.tabletAlias, topodata.TabletType_REPLICA, nil, 1*time.Second) 86 } 87 88 func checkShardRecordWithTimeout(ctx context.Context, t *testing.T, ts *topo.Server, tabletAlias *topodata.TabletAlias, expectedStartTime *vttime.Time, timeToWait time.Duration) { 89 timeOut := time.After(timeToWait) 90 for { 91 select { 92 case <-timeOut: 93 t.Fatalf("timed out: waiting for shard record to update") 94 default: 95 si, err := ts.GetShard(ctx, keyspace, shard) 96 require.NoError(t, err) 97 if reflect.DeepEqual(tabletAlias, si.PrimaryAlias) && reflect.DeepEqual(expectedStartTime, si.PrimaryTermStartTime) { 98 return 99 } 100 time.Sleep(100 * time.Millisecond) 101 } 102 } 103 } 104 105 func checkTabletRecordWithTimeout(ctx context.Context, t *testing.T, ts *topo.Server, tabletAlias *topodata.TabletAlias, tabletType topodata.TabletType, expectedStartTime *vttime.Time, timeToWait time.Duration) { 106 timeOut := time.After(timeToWait) 107 for { 108 select { 109 case <-timeOut: 110 t.Fatalf("timed out: waiting for tablet record to update") 111 default: 112 ti, err := ts.GetTablet(ctx, tabletAlias) 113 require.NoError(t, err) 114 if reflect.DeepEqual(tabletType, ti.Type) && reflect.DeepEqual(expectedStartTime, ti.PrimaryTermStartTime) { 115 return 116 } 117 time.Sleep(100 * time.Millisecond) 118 } 119 } 120 } 121 122 func updatePrimaryInfoInShardRecord(ctx context.Context, t *testing.T, tm *TabletManager, alias *topodata.TabletAlias, time time.Time) { 123 ctx, unlock, lockErr := tm.TopoServer.LockShard(ctx, keyspace, shard, fmt.Sprintf("updatePrimaryInfoInShardRecord(%v)", topoproto.TabletAliasString(tm.tabletAlias))) 124 require.NoError(t, lockErr) 125 defer unlock(&lockErr) 126 127 _, err := tm.TopoServer.UpdateShardFields(ctx, keyspace, shard, func(si *topo.ShardInfo) error { 128 si.PrimaryAlias = alias 129 si.SetPrimaryTermStartTime(time) 130 return nil 131 }) 132 require.NoError(t, err) 133 }