vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/vstreamer/engine_test.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 vstreamer 18 19 import ( 20 "context" 21 "encoding/json" 22 "testing" 23 "time" 24 25 "github.com/stretchr/testify/require" 26 27 "vitess.io/vitess/go/mysql/fakesqldb" 28 "vitess.io/vitess/go/sqltypes" 29 "vitess.io/vitess/go/vt/dbconfigs" 30 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 31 "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" 32 ) 33 34 var ( 35 shardedVSchema = `{ 36 "sharded": true, 37 "vindexes": { 38 "hash": { 39 "type": "hash" 40 } 41 }, 42 "tables": { 43 "t1": { 44 "column_vindexes": [ 45 { 46 "column": "id1", 47 "name": "hash" 48 } 49 ] 50 } 51 } 52 }` 53 54 multicolumnVSchema = `{ 55 "sharded": true, 56 "vindexes": { 57 "region_vdx": { 58 "type": "region_experimental", 59 "params": { 60 "region_bytes": "1" 61 } 62 } 63 }, 64 "tables": { 65 "t1": { 66 "column_vindexes": [ 67 { 68 "columns": [ 69 "region", 70 "id" 71 ], 72 "name": "region_vdx" 73 } 74 ] 75 } 76 } 77 }` 78 ) 79 80 func TestUpdateVSchema(t *testing.T) { 81 if testing.Short() { 82 t.Skip() 83 } 84 85 defer env.SetVSchema("{}") 86 87 // We have to start at least one stream to start the vschema watcher. 88 ctx, cancel := context.WithCancel(context.Background()) 89 cancel() 90 filter := &binlogdatapb.Filter{ 91 Rules: []*binlogdatapb.Rule{{ 92 Match: "/.*/", 93 }}, 94 } 95 // Stream should terminate immediately due to canceled context. 96 _ = engine.Stream(ctx, "current", nil, filter, func(_ []*binlogdatapb.VEvent) error { 97 return nil 98 }) 99 100 startCount := expectUpdateCount(t, 1) 101 102 if err := env.SetVSchema(shardedVSchema); err != nil { 103 t.Fatal(err) 104 } 105 expectUpdateCount(t, startCount+1) 106 107 want := `{ 108 "routing_rules": {}, 109 "keyspaces": { 110 "vttest": { 111 "sharded": true, 112 "tables": { 113 "dual": { 114 "type": "reference", 115 "name": "dual" 116 }, 117 "t1": { 118 "name": "t1", 119 "column_vindexes": [ 120 { 121 "columns": [ 122 "id1" 123 ], 124 "type": "hash", 125 "name": "hash", 126 "vindex": {} 127 } 128 ], 129 "ordered": [ 130 { 131 "columns": [ 132 "id1" 133 ], 134 "type": "hash", 135 "name": "hash", 136 "vindex": {} 137 } 138 ] 139 } 140 }, 141 "vindexes": { 142 "hash": {} 143 } 144 } 145 }, 146 "shard_routing_rules": null 147 }` 148 b, err := json.MarshalIndent(engine.vschema(), "", " ") 149 if err != nil { 150 t.Fatal(err) 151 } 152 got := string(b) 153 require.Equal(t, want, got) 154 } 155 156 func expectUpdateCount(t *testing.T, wantCount int64) int64 { 157 for i := 0; i < 10; i++ { 158 gotCount := engine.vschemaUpdates.Get() 159 if gotCount >= wantCount { 160 return gotCount 161 } 162 if i == 9 { 163 t.Fatalf("update count: %d, want %d", gotCount, wantCount) 164 } 165 time.Sleep(10 * time.Millisecond) 166 } 167 panic("unreachable") 168 } 169 170 func TestVStreamerWaitForMySQL(t *testing.T) { 171 tableName := "test" 172 type fields struct { 173 vse *Engine 174 cp dbconfigs.Connector 175 se *schema.Engine 176 ReplicationLagSeconds int64 177 maxInnoDBTrxHistLen int64 178 maxMySQLReplLagSecs int64 179 } 180 tests := []struct { 181 name string 182 fields fields 183 wantErr bool 184 }{ 185 { 186 name: "Small InnoDB MVCC impact limit", 187 fields: fields{ 188 vse: engine, 189 se: engine.se, 190 maxInnoDBTrxHistLen: 100, 191 maxMySQLReplLagSecs: 5000, 192 }, 193 wantErr: true, 194 }, 195 { 196 name: "Small Repl Lag impact limit", 197 fields: fields{ 198 vse: engine, 199 se: engine.se, 200 maxInnoDBTrxHistLen: 10000, 201 maxMySQLReplLagSecs: 5, 202 }, 203 wantErr: true, 204 }, 205 { 206 name: "Large impact limits", 207 fields: fields{ 208 vse: engine, 209 se: engine.se, 210 maxInnoDBTrxHistLen: 10000, 211 maxMySQLReplLagSecs: 200, 212 }, 213 wantErr: false, 214 }, 215 } 216 testDB := fakesqldb.New(t) 217 hostres := sqltypes.MakeTestResult(sqltypes.MakeTestFields( 218 "hostname|port", 219 "varchar|int64"), 220 "localhost|3306", 221 ) 222 thlres := sqltypes.MakeTestResult(sqltypes.MakeTestFields( 223 "history_len", 224 "int64"), 225 "1000", 226 ) 227 sbmres := sqltypes.MakeTestResult(sqltypes.MakeTestFields( 228 "Seconds_Behind_Master", 229 "int64"), 230 "10", 231 ) 232 testDB.AddQuery(hostQuery, hostres) 233 testDB.AddQuery(trxHistoryLenQuery, thlres) 234 testDB.AddQuery(replicaLagQuery, sbmres) 235 for _, tt := range tests { 236 tt.fields.cp = testDB.ConnParams() 237 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 238 defer cancel() 239 t.Run(tt.name, func(t *testing.T) { 240 uvs := &uvstreamer{ 241 ctx: ctx, 242 cancel: cancel, 243 vse: tt.fields.vse, 244 cp: tt.fields.cp, 245 se: tt.fields.se, 246 ReplicationLagSeconds: tt.fields.ReplicationLagSeconds, 247 } 248 env.TabletEnv.Config().RowStreamer.MaxInnoDBTrxHistLen = tt.fields.maxInnoDBTrxHistLen 249 env.TabletEnv.Config().RowStreamer.MaxMySQLReplLagSecs = tt.fields.maxMySQLReplLagSecs 250 if err := uvs.vse.waitForMySQL(ctx, uvs.cp, tableName); (err != nil) != tt.wantErr { 251 t.Errorf("vstreamer.waitForMySQL() error = %v, wantErr %v", err, tt.wantErr) 252 } 253 }) 254 } 255 256 require.Equal(t, engine.rowStreamerWaits.Counts()["VStreamerTest.waitForMySQL"], int64(2)) 257 require.Equal(t, engine.vstreamerPhaseTimings.Counts()["VStreamerTest."+tableName+":waitForMySQL"], int64(2)) 258 }