vitess.io/vitess@v0.16.2/go/vt/vtctl/schematools/reload_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 schematools 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 27 "vitess.io/vitess/go/sync2" 28 "vitess.io/vitess/go/vt/logutil" 29 "vitess.io/vitess/go/vt/topo/memorytopo" 30 "vitess.io/vitess/go/vt/topo/topoproto" 31 "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" 32 "vitess.io/vitess/go/vt/vttablet/tmclient" 33 34 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 35 ) 36 37 type reloadSchemaTMC struct { 38 tmclient.TabletManagerClient 39 40 // tablet alias => positionStr => error 41 results map[string]map[string]error 42 43 m sync.Mutex 44 calls []*reloadSchemaCall 45 errCount int 46 } 47 48 type reloadSchemaCall struct { 49 Alias *topodatapb.TabletAlias 50 Position string 51 } 52 53 func (tmc *reloadSchemaTMC) ReloadSchema(ctx context.Context, tablet *topodatapb.Tablet, position string) (err error) { 54 rpcErr := err 55 defer func() { 56 tmc.m.Lock() 57 defer tmc.m.Unlock() 58 59 tmc.calls = append(tmc.calls, &reloadSchemaCall{ 60 Alias: tablet.Alias, 61 Position: position, 62 }) 63 64 if rpcErr != nil { 65 tmc.errCount++ 66 } 67 }() 68 69 if tmc.results == nil { 70 return fmt.Errorf("no results set on reloadSchemaTMC") 71 } 72 73 key := topoproto.TabletAliasString(tablet.Alias) 74 posMap, ok := tmc.results[key] 75 if !ok { 76 return fmt.Errorf("no ReloadSchema fakes set for %s", key) 77 } 78 79 rpcErr, ok = posMap[position] 80 if !ok { 81 return fmt.Errorf("no ReloadSchema fake set for (%s, %s)", key, position) 82 } 83 84 return rpcErr 85 } 86 87 func TestReloadShard(t *testing.T) { 88 t.Parallel() 89 ctx := context.Background() 90 91 tests := []struct { 92 name string 93 cells []string 94 tablets []*topodatapb.Tablet 95 results map[string]map[string]error 96 req *struct { 97 Keyspace string 98 Shard string 99 Position string 100 IncludePrimary bool 101 } 102 expected *struct { 103 IsPartial bool 104 Ok bool 105 } 106 expectedCalls []*reloadSchemaCall 107 expectedErrCount int 108 }{ 109 { 110 name: "ok", 111 cells: []string{"zone1", "zone2"}, 112 tablets: []*topodatapb.Tablet{ 113 { 114 Alias: &topodatapb.TabletAlias{ 115 Cell: "zone1", 116 Uid: 100, 117 }, 118 Keyspace: "ks", 119 Shard: "-", 120 Type: topodatapb.TabletType_PRIMARY, 121 }, 122 { 123 Alias: &topodatapb.TabletAlias{ 124 Cell: "zone1", 125 Uid: 101, 126 }, 127 Keyspace: "ks", 128 Shard: "-", 129 Type: topodatapb.TabletType_REPLICA, 130 }, 131 { 132 Alias: &topodatapb.TabletAlias{ 133 Cell: "zone2", 134 Uid: 200, 135 }, 136 Keyspace: "ks", 137 Shard: "-", 138 Type: topodatapb.TabletType_REPLICA, 139 }, 140 }, 141 results: map[string]map[string]error{ 142 "zone1-0000000101": { 143 "pos1": nil, 144 }, 145 "zone2-0000000200": { 146 "pos1": nil, 147 }, 148 }, 149 req: &struct { 150 Keyspace string 151 Shard string 152 Position string 153 IncludePrimary bool 154 }{ 155 Keyspace: "ks", 156 Shard: "-", 157 Position: "pos1", 158 }, 159 expected: &struct { 160 IsPartial bool 161 Ok bool 162 }{ 163 IsPartial: false, 164 Ok: true, 165 }, 166 expectedCalls: []*reloadSchemaCall{ 167 { 168 Alias: &topodatapb.TabletAlias{ 169 Cell: "zone1", 170 Uid: 101, 171 }, 172 Position: "pos1", 173 }, 174 { 175 Alias: &topodatapb.TabletAlias{ 176 Cell: "zone2", 177 Uid: 200, 178 }, 179 Position: "pos1", 180 }, 181 }, 182 expectedErrCount: 0, 183 }, 184 { 185 name: "best effort", 186 cells: []string{"zone1", "zone2"}, 187 tablets: []*topodatapb.Tablet{ 188 { 189 Alias: &topodatapb.TabletAlias{ 190 Cell: "zone1", 191 Uid: 100, 192 }, 193 Keyspace: "ks", 194 Shard: "-", 195 Type: topodatapb.TabletType_PRIMARY, 196 }, 197 { 198 Alias: &topodatapb.TabletAlias{ 199 Cell: "zone1", 200 Uid: 101, 201 }, 202 Keyspace: "ks", 203 Shard: "-", 204 Type: topodatapb.TabletType_REPLICA, 205 }, 206 { 207 Alias: &topodatapb.TabletAlias{ 208 Cell: "zone2", 209 Uid: 200, 210 }, 211 Keyspace: "ks", 212 Shard: "-", 213 Type: topodatapb.TabletType_REPLICA, 214 }, 215 }, 216 results: map[string]map[string]error{ 217 "zone1-0000000101": { 218 "pos1": nil, 219 }, 220 "zone2-0000000200": { 221 "pos1": assert.AnError, 222 }, 223 }, 224 req: &struct { 225 Keyspace string 226 Shard string 227 Position string 228 IncludePrimary bool 229 }{ 230 Keyspace: "ks", 231 Shard: "-", 232 Position: "pos1", 233 }, 234 expected: &struct { 235 IsPartial bool 236 Ok bool 237 }{ 238 IsPartial: false, 239 Ok: true, 240 }, 241 expectedCalls: []*reloadSchemaCall{ 242 { 243 Alias: &topodatapb.TabletAlias{ 244 Cell: "zone1", 245 Uid: 101, 246 }, 247 Position: "pos1", 248 }, 249 { 250 Alias: &topodatapb.TabletAlias{ 251 Cell: "zone2", 252 Uid: 200, 253 }, 254 Position: "pos1", 255 }, 256 }, 257 expectedErrCount: 1, 258 }, 259 { 260 name: "include_primary", 261 cells: []string{"zone1"}, 262 tablets: []*topodatapb.Tablet{ 263 { 264 Alias: &topodatapb.TabletAlias{ 265 Cell: "zone1", 266 Uid: 100, 267 }, 268 Keyspace: "ks", 269 Shard: "-", 270 Type: topodatapb.TabletType_PRIMARY, 271 }, 272 }, 273 results: map[string]map[string]error{ 274 "zone1-0000000100": { 275 "": nil, 276 "some position": assert.AnError, 277 }, 278 }, 279 req: &struct { 280 Keyspace string 281 Shard string 282 Position string 283 IncludePrimary bool 284 }{ 285 Keyspace: "ks", 286 Shard: "-", 287 Position: "some position", 288 IncludePrimary: true, 289 }, 290 expected: &struct { 291 IsPartial bool 292 Ok bool 293 }{ 294 IsPartial: false, 295 Ok: true, 296 }, 297 expectedCalls: []*reloadSchemaCall{ 298 { 299 Alias: &topodatapb.TabletAlias{ 300 Cell: "zone1", 301 Uid: 100, 302 }, 303 Position: "", 304 }, 305 }, 306 }, 307 { 308 name: "fail to load tabletmap", 309 req: &struct { 310 Keyspace string 311 Shard string 312 Position string 313 IncludePrimary bool 314 }{ 315 Keyspace: "doesnotexist", 316 Shard: "-", 317 }, 318 expected: &struct { 319 IsPartial bool 320 Ok bool 321 }{ 322 IsPartial: false, 323 Ok: false, 324 }, 325 }, 326 } 327 328 for _, tt := range tests { 329 tt := tt 330 t.Run(tt.name, func(t *testing.T) { 331 t.Parallel() 332 333 ts := memorytopo.NewServer(tt.cells...) 334 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 335 AlsoSetShardPrimary: true, 336 }, tt.tablets...) 337 tmc := &reloadSchemaTMC{ 338 results: tt.results, 339 } 340 341 isPartial, ok := ReloadShard(ctx, ts, tmc, logutil.NewMemoryLogger(), tt.req.Keyspace, tt.req.Shard, tt.req.Position, sync2.NewSemaphore(1, 0), tt.req.IncludePrimary) 342 assert.Equal(t, tt.expected.IsPartial, isPartial, "incorrect value for isPartial") 343 assert.Equal(t, tt.expected.Ok, ok, "incorrect value for ok") 344 345 tmc.m.Lock() 346 defer tmc.m.Unlock() 347 348 assert.ElementsMatch(t, tt.expectedCalls, tmc.calls) 349 assert.Equal(t, tt.expectedErrCount, tmc.errCount, "rpc error count incorrect") 350 }) 351 } 352 }