github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/balancer/clusterresolver/priority_test.go (about) 1 /* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package clusterresolver 19 20 import ( 21 "context" 22 "testing" 23 "time" 24 25 "github.com/google/go-cmp/cmp" 26 corepb "github.com/hxx258456/ccgo/go-control-plane/envoy/api/v2/core" 27 "github.com/hxx258456/ccgo/grpc/balancer" 28 "github.com/hxx258456/ccgo/grpc/connectivity" 29 "github.com/hxx258456/ccgo/grpc/internal/testutils" 30 "github.com/hxx258456/ccgo/grpc/resolver" 31 "github.com/hxx258456/ccgo/grpc/xds/internal/balancer/priority" 32 xdstestutils "github.com/hxx258456/ccgo/grpc/xds/internal/testutils" 33 ) 34 35 // When a high priority is ready, adding/removing lower locality doesn't cause 36 // changes. 37 // 38 // Init 0 and 1; 0 is up, use 0; add 2, use 0; remove 2, use 0. 39 func (s) TestEDSPriority_HighPriorityReady(t *testing.T) { 40 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 41 defer cleanup() 42 43 // Two localities, with priorities [0, 1], each with one backend. 44 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 45 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 46 clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 47 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 48 49 addrs1 := <-cc.NewSubConnAddrsCh 50 if got, want := addrs1[0].Addr, testEndpointAddrs[0]; got != want { 51 t.Fatalf("sc is created with addr %v, want %v", got, want) 52 } 53 sc1 := <-cc.NewSubConnCh 54 55 // p0 is ready. 56 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 57 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 58 59 // Test roundrobin with only p0 subconns. 60 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 61 t.Fatal(err) 62 } 63 64 // Add p2, it shouldn't cause any updates. 65 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 66 clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 67 clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 68 clab2.AddLocality(testSubZones[2], 1, 2, testEndpointAddrs[2:3], nil) 69 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 70 71 select { 72 case <-cc.NewPickerCh: 73 t.Fatalf("got unexpected new picker") 74 case <-cc.NewSubConnCh: 75 t.Fatalf("got unexpected new SubConn") 76 case <-cc.RemoveSubConnCh: 77 t.Fatalf("got unexpected remove SubConn") 78 case <-time.After(defaultTestShortTimeout): 79 } 80 81 // Remove p2, no updates. 82 clab3 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 83 clab3.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 84 clab3.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 85 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab3.Build()), nil) 86 87 select { 88 case <-cc.NewPickerCh: 89 t.Fatalf("got unexpected new picker") 90 case <-cc.NewSubConnCh: 91 t.Fatalf("got unexpected new SubConn") 92 case <-cc.RemoveSubConnCh: 93 t.Fatalf("got unexpected remove SubConn") 94 case <-time.After(defaultTestShortTimeout): 95 } 96 } 97 98 // Lower priority is used when higher priority is not ready. 99 // 100 // Init 0 and 1; 0 is up, use 0; 0 is down, 1 is up, use 1; add 2, use 1; 1 is 101 // down, use 2; remove 2, use 1. 102 func (s) TestEDSPriority_SwitchPriority(t *testing.T) { 103 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 104 defer cleanup() 105 106 // Two localities, with priorities [0, 1], each with one backend. 107 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 108 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 109 clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 110 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 111 112 addrs0 := <-cc.NewSubConnAddrsCh 113 if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want { 114 t.Fatalf("sc is created with addr %v, want %v", got, want) 115 } 116 sc0 := <-cc.NewSubConnCh 117 118 // p0 is ready. 119 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 120 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 121 122 // Test roundrobin with only p0 subconns. 123 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil { 124 t.Fatal(err) 125 } 126 127 // Turn down 0, 1 is used. 128 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 129 addrs1 := <-cc.NewSubConnAddrsCh 130 if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want { 131 t.Fatalf("sc is created with addr %v, want %v", got, want) 132 } 133 sc1 := <-cc.NewSubConnCh 134 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 135 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 136 137 // Test pick with 1. 138 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 139 t.Fatal(err) 140 } 141 142 // Add p2, it shouldn't cause any updates. 143 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 144 clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 145 clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 146 clab2.AddLocality(testSubZones[2], 1, 2, testEndpointAddrs[2:3], nil) 147 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 148 149 select { 150 case <-cc.NewPickerCh: 151 t.Fatalf("got unexpected new picker") 152 case <-cc.NewSubConnCh: 153 t.Fatalf("got unexpected new SubConn") 154 case <-cc.RemoveSubConnCh: 155 t.Fatalf("got unexpected remove SubConn") 156 case <-time.After(defaultTestShortTimeout): 157 } 158 159 // Turn down 1, use 2 160 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 161 addrs2 := <-cc.NewSubConnAddrsCh 162 if got, want := addrs2[0].Addr, testEndpointAddrs[2]; got != want { 163 t.Fatalf("sc is created with addr %v, want %v", got, want) 164 } 165 sc2 := <-cc.NewSubConnCh 166 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 167 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 168 169 // Test pick with 2. 170 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil { 171 t.Fatal(err) 172 } 173 174 // Remove 2, use 1. 175 clab3 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 176 clab3.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 177 clab3.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 178 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab3.Build()), nil) 179 180 // p2 SubConns are removed. 181 scToRemove := <-cc.RemoveSubConnCh 182 if !cmp.Equal(scToRemove, sc2, cmp.AllowUnexported(testutils.TestSubConn{})) { 183 t.Fatalf("RemoveSubConn, want %v, got %v", sc2, scToRemove) 184 } 185 186 // Should get an update with 1's old picker, to override 2's old picker. 187 if err := testErrPickerFromCh(cc.NewPickerCh, balancer.ErrTransientFailure); err != nil { 188 t.Fatal(err) 189 } 190 191 } 192 193 // Add a lower priority while the higher priority is down. 194 // 195 // Init 0 and 1; 0 and 1 both down; add 2, use 2. 196 func (s) TestEDSPriority_HigherDownWhileAddingLower(t *testing.T) { 197 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 198 defer cleanup() 199 // Two localities, with different priorities, each with one backend. 200 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 201 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 202 clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 203 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 204 addrs0 := <-cc.NewSubConnAddrsCh 205 if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want { 206 t.Fatalf("sc is created with addr %v, want %v", got, want) 207 } 208 sc0 := <-cc.NewSubConnCh 209 210 // Turn down 0, 1 is used. 211 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 212 addrs1 := <-cc.NewSubConnAddrsCh 213 if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want { 214 t.Fatalf("sc is created with addr %v, want %v", got, want) 215 } 216 sc1 := <-cc.NewSubConnCh 217 // Turn down 1, pick should error. 218 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 219 220 // Test pick failure. 221 if err := testErrPickerFromCh(cc.NewPickerCh, balancer.ErrTransientFailure); err != nil { 222 t.Fatal(err) 223 } 224 225 // Add p2, it should create a new SubConn. 226 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 227 clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 228 clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 229 clab2.AddLocality(testSubZones[2], 1, 2, testEndpointAddrs[2:3], nil) 230 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 231 addrs2 := <-cc.NewSubConnAddrsCh 232 if got, want := addrs2[0].Addr, testEndpointAddrs[2]; got != want { 233 t.Fatalf("sc is created with addr %v, want %v", got, want) 234 } 235 sc2 := <-cc.NewSubConnCh 236 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 237 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 238 239 // Test pick with 2. 240 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil { 241 t.Fatal(err) 242 } 243 244 } 245 246 // When a higher priority becomes available, all lower priorities are closed. 247 // 248 // Init 0,1,2; 0 and 1 down, use 2; 0 up, close 1 and 2. 249 func (s) TestEDSPriority_HigherReadyCloseAllLower(t *testing.T) { 250 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 251 defer cleanup() 252 // Two localities, with priorities [0,1,2], each with one backend. 253 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 254 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 255 clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 256 clab1.AddLocality(testSubZones[2], 1, 2, testEndpointAddrs[2:3], nil) 257 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 258 addrs0 := <-cc.NewSubConnAddrsCh 259 if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want { 260 t.Fatalf("sc is created with addr %v, want %v", got, want) 261 } 262 sc0 := <-cc.NewSubConnCh 263 264 // Turn down 0, 1 is used. 265 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 266 addrs1 := <-cc.NewSubConnAddrsCh 267 if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want { 268 t.Fatalf("sc is created with addr %v, want %v", got, want) 269 } 270 sc1 := <-cc.NewSubConnCh 271 // Turn down 1, 2 is used. 272 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 273 addrs2 := <-cc.NewSubConnAddrsCh 274 if got, want := addrs2[0].Addr, testEndpointAddrs[2]; got != want { 275 t.Fatalf("sc is created with addr %v, want %v", got, want) 276 } 277 sc2 := <-cc.NewSubConnCh 278 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 279 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 280 281 // Test pick with 2. 282 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil { 283 t.Fatal(err) 284 } 285 286 // When 0 becomes ready, 0 should be used, 1 and 2 should all be closed. 287 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 288 var ( 289 scToRemove []balancer.SubConn 290 scToRemoveMap = make(map[balancer.SubConn]struct{}) 291 ) 292 // Each subconn is removed twice. This is OK in production, but it makes 293 // testing harder. 294 // 295 // The sub-balancer to be closed is priority's child, clusterimpl, who has 296 // weightedtarget as children. 297 // 298 // - When clusterimpl is removed from priority's balancergroup, all its 299 // subconns are removed once. 300 // - When clusterimpl is closed, it closes weightedtarget, and this 301 // weightedtarget's balancer removes all the same subconns again. 302 for i := 0; i < 4; i++ { 303 // We expect 2 subconns, so we recv from channel 4 times. 304 scToRemoveMap[<-cc.RemoveSubConnCh] = struct{}{} 305 } 306 for sc := range scToRemoveMap { 307 scToRemove = append(scToRemove, sc) 308 } 309 310 // sc1 and sc2 should be removed. 311 // 312 // With localities caching, the lower priorities are closed after a timeout, 313 // in goroutines. The order is no longer guaranteed. 314 if !(cmp.Equal(scToRemove[0], sc1, cmp.AllowUnexported(testutils.TestSubConn{})) && 315 cmp.Equal(scToRemove[1], sc2, cmp.AllowUnexported(testutils.TestSubConn{}))) && 316 !(cmp.Equal(scToRemove[0], sc2, cmp.AllowUnexported(testutils.TestSubConn{})) && 317 cmp.Equal(scToRemove[1], sc1, cmp.AllowUnexported(testutils.TestSubConn{}))) { 318 t.Errorf("RemoveSubConn, want [%v, %v], got %v", sc1, sc2, scToRemove) 319 } 320 321 // Test pick with 0. 322 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil { 323 t.Fatal(err) 324 } 325 } 326 327 // At init, start the next lower priority after timeout if the higher priority 328 // doesn't get ready. 329 // 330 // Init 0,1; 0 is not ready (in connecting), after timeout, use 1. 331 func (s) TestEDSPriority_InitTimeout(t *testing.T) { 332 const testPriorityInitTimeout = time.Second 333 defer func() func() { 334 old := priority.DefaultPriorityInitTimeout 335 priority.DefaultPriorityInitTimeout = testPriorityInitTimeout 336 return func() { 337 priority.DefaultPriorityInitTimeout = old 338 } 339 }()() 340 341 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 342 defer cleanup() 343 // Two localities, with different priorities, each with one backend. 344 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 345 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 346 clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 347 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 348 addrs0 := <-cc.NewSubConnAddrsCh 349 if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want { 350 t.Fatalf("sc is created with addr %v, want %v", got, want) 351 } 352 sc0 := <-cc.NewSubConnCh 353 354 // Keep 0 in connecting, 1 will be used after init timeout. 355 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 356 357 // Make sure new SubConn is created before timeout. 358 select { 359 case <-time.After(testPriorityInitTimeout * 3 / 4): 360 case <-cc.NewSubConnAddrsCh: 361 t.Fatalf("Got a new SubConn too early (Within timeout). Expect a new SubConn only after timeout") 362 } 363 364 addrs1 := <-cc.NewSubConnAddrsCh 365 if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want { 366 t.Fatalf("sc is created with addr %v, want %v", got, want) 367 } 368 sc1 := <-cc.NewSubConnCh 369 370 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 371 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 372 373 // Test pick with 1. 374 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 375 t.Fatal(err) 376 } 377 } 378 379 // Add localities to existing priorities. 380 // 381 // - start with 2 locality with p0 and p1 382 // - add localities to existing p0 and p1 383 func (s) TestEDSPriority_MultipleLocalities(t *testing.T) { 384 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 385 defer cleanup() 386 // Two localities, with different priorities, each with one backend. 387 clab0 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 388 clab0.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 389 clab0.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 390 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab0.Build()), nil) 391 addrs0 := <-cc.NewSubConnAddrsCh 392 if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want { 393 t.Fatalf("sc is created with addr %v, want %v", got, want) 394 } 395 sc0 := <-cc.NewSubConnCh 396 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 397 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 398 399 // Test roundrobin with only p0 subconns. 400 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil { 401 t.Fatal(err) 402 } 403 404 // Turn down p0 subconns, p1 subconns will be created. 405 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 406 407 addrs1 := <-cc.NewSubConnAddrsCh 408 if got, want := addrs1[0].Addr, testEndpointAddrs[1]; got != want { 409 t.Fatalf("sc is created with addr %v, want %v", got, want) 410 } 411 sc1 := <-cc.NewSubConnCh 412 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 413 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 414 415 // Test roundrobin with only p1 subconns. 416 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 417 t.Fatal(err) 418 } 419 420 // Reconnect p0 subconns, p1 subconn will be closed. 421 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 422 423 scToRemove := <-cc.RemoveSubConnCh 424 if !cmp.Equal(scToRemove, sc1, cmp.AllowUnexported(testutils.TestSubConn{})) { 425 t.Fatalf("RemoveSubConn, want %v, got %v", sc1, scToRemove) 426 } 427 428 // Test roundrobin with only p0 subconns. 429 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil { 430 t.Fatal(err) 431 } 432 433 // Add two localities, with two priorities, with one backend. 434 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 435 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 436 clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 437 clab1.AddLocality(testSubZones[2], 1, 0, testEndpointAddrs[2:3], nil) 438 clab1.AddLocality(testSubZones[3], 1, 1, testEndpointAddrs[3:4], nil) 439 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 440 addrs2 := <-cc.NewSubConnAddrsCh 441 if got, want := addrs2[0].Addr, testEndpointAddrs[2]; got != want { 442 t.Fatalf("sc is created with addr %v, want %v", got, want) 443 } 444 sc2 := <-cc.NewSubConnCh 445 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 446 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 447 448 // Test roundrobin with only two p0 subconns. 449 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0, sc2}); err != nil { 450 t.Fatal(err) 451 } 452 453 // Turn down p0 subconns, p1 subconns will be created. 454 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 455 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 456 457 sc3 := <-cc.NewSubConnCh 458 edsb.UpdateSubConnState(sc3, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 459 edsb.UpdateSubConnState(sc3, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 460 sc4 := <-cc.NewSubConnCh 461 edsb.UpdateSubConnState(sc4, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 462 edsb.UpdateSubConnState(sc4, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 463 464 // Test roundrobin with only p1 subconns. 465 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc3, sc4}); err != nil { 466 t.Fatal(err) 467 } 468 } 469 470 // EDS removes all localities, and re-adds them. 471 func (s) TestEDSPriority_RemovesAllLocalities(t *testing.T) { 472 const testPriorityInitTimeout = time.Second 473 defer func() func() { 474 old := priority.DefaultPriorityInitTimeout 475 priority.DefaultPriorityInitTimeout = testPriorityInitTimeout 476 return func() { 477 priority.DefaultPriorityInitTimeout = old 478 } 479 }()() 480 481 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 482 defer cleanup() 483 // Two localities, with different priorities, each with one backend. 484 clab0 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 485 clab0.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 486 clab0.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 487 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab0.Build()), nil) 488 addrs0 := <-cc.NewSubConnAddrsCh 489 if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want { 490 t.Fatalf("sc is created with addr %v, want %v", got, want) 491 } 492 sc0 := <-cc.NewSubConnCh 493 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 494 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 495 496 // Test roundrobin with only p0 subconns. 497 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil { 498 t.Fatal(err) 499 } 500 501 // Remove all priorities. 502 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 503 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 504 // p0 subconn should be removed. 505 scToRemove := <-cc.RemoveSubConnCh 506 <-cc.RemoveSubConnCh // Drain the duplicate subconn removed. 507 if !cmp.Equal(scToRemove, sc0, cmp.AllowUnexported(testutils.TestSubConn{})) { 508 t.Fatalf("RemoveSubConn, want %v, got %v", sc0, scToRemove) 509 } 510 511 // time.Sleep(time.Second) 512 513 // Test pick return TransientFailure. 514 if err := testErrPickerFromCh(cc.NewPickerCh, priority.ErrAllPrioritiesRemoved); err != nil { 515 t.Fatal(err) 516 } 517 518 // Re-add two localities, with previous priorities, but different backends. 519 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 520 clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[2:3], nil) 521 clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[3:4], nil) 522 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 523 addrs01 := <-cc.NewSubConnAddrsCh 524 if got, want := addrs01[0].Addr, testEndpointAddrs[2]; got != want { 525 t.Fatalf("sc is created with addr %v, want %v", got, want) 526 } 527 sc01 := <-cc.NewSubConnCh 528 529 // Don't send any update to p0, so to not override the old state of p0. 530 // Later, connect to p1 and then remove p1. This will fallback to p0, and 531 // will send p0's old picker if they are not correctly removed. 532 533 // p1 will be used after priority init timeout. 534 addrs11 := <-cc.NewSubConnAddrsCh 535 if got, want := addrs11[0].Addr, testEndpointAddrs[3]; got != want { 536 t.Fatalf("sc is created with addr %v, want %v", got, want) 537 } 538 sc11 := <-cc.NewSubConnCh 539 edsb.UpdateSubConnState(sc11, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 540 edsb.UpdateSubConnState(sc11, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 541 542 // Test roundrobin with only p1 subconns. 543 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc11}); err != nil { 544 t.Fatal(err) 545 } 546 547 // Remove p1 from EDS, to fallback to p0. 548 clab3 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 549 clab3.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[2:3], nil) 550 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab3.Build()), nil) 551 552 // p1 subconn should be removed. 553 scToRemove1 := <-cc.RemoveSubConnCh 554 <-cc.RemoveSubConnCh // Drain the duplicate subconn removed. 555 if !cmp.Equal(scToRemove1, sc11, cmp.AllowUnexported(testutils.TestSubConn{})) { 556 t.Fatalf("RemoveSubConn, want %v, got %v", sc11, scToRemove1) 557 } 558 559 // Test pick return TransientFailure. 560 if err := testErrPickerFromCh(cc.NewPickerCh, balancer.ErrNoSubConnAvailable); err != nil { 561 t.Fatal(err) 562 } 563 564 // Send an ready update for the p0 sc that was received when re-adding 565 // localities to EDS. 566 edsb.UpdateSubConnState(sc01, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 567 edsb.UpdateSubConnState(sc01, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 568 569 // Test roundrobin with only p0 subconns. 570 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc01}); err != nil { 571 t.Fatal(err) 572 } 573 574 select { 575 case <-cc.NewPickerCh: 576 t.Fatalf("got unexpected new picker") 577 case <-cc.NewSubConnCh: 578 t.Fatalf("got unexpected new SubConn") 579 case <-cc.RemoveSubConnCh: 580 t.Fatalf("got unexpected remove SubConn") 581 case <-time.After(defaultTestShortTimeout): 582 } 583 } 584 585 // Test the case where the high priority contains no backends. The low priority 586 // will be used. 587 func (s) TestEDSPriority_HighPriorityNoEndpoints(t *testing.T) { 588 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 589 defer cleanup() 590 // Two localities, with priorities [0, 1], each with one backend. 591 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 592 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 593 clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 594 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 595 addrs1 := <-cc.NewSubConnAddrsCh 596 if got, want := addrs1[0].Addr, testEndpointAddrs[0]; got != want { 597 t.Fatalf("sc is created with addr %v, want %v", got, want) 598 } 599 sc1 := <-cc.NewSubConnCh 600 601 // p0 is ready. 602 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 603 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 604 605 // Test roundrobin with only p0 subconns. 606 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 607 t.Fatal(err) 608 } 609 610 // Remove addresses from priority 0, should use p1. 611 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 612 clab2.AddLocality(testSubZones[0], 1, 0, nil, nil) 613 clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 614 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 615 // p0 will remove the subconn, and ClientConn will send a sc update to 616 // shutdown. 617 scToRemove := <-cc.RemoveSubConnCh 618 edsb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown}) 619 620 addrs2 := <-cc.NewSubConnAddrsCh 621 if got, want := addrs2[0].Addr, testEndpointAddrs[1]; got != want { 622 t.Fatalf("sc is created with addr %v, want %v", got, want) 623 } 624 sc2 := <-cc.NewSubConnCh 625 626 // p1 is ready. 627 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 628 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 629 630 // Test roundrobin with only p1 subconns. 631 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil { 632 t.Fatal(err) 633 } 634 } 635 636 // Test the case where the high priority contains no healthy backends. The low 637 // priority will be used. 638 func (s) TestEDSPriority_HighPriorityAllUnhealthy(t *testing.T) { 639 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 640 defer cleanup() 641 // Two localities, with priorities [0, 1], each with one backend. 642 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 643 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 644 clab1.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 645 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 646 addrs1 := <-cc.NewSubConnAddrsCh 647 if got, want := addrs1[0].Addr, testEndpointAddrs[0]; got != want { 648 t.Fatalf("sc is created with addr %v, want %v", got, want) 649 } 650 sc1 := <-cc.NewSubConnCh 651 652 // p0 is ready. 653 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 654 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 655 656 // Test roundrobin with only p0 subconns. 657 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 658 t.Fatal(err) 659 } 660 661 // Set priority 0 endpoints to all unhealthy, should use p1. 662 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 663 clab2.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], &xdstestutils.AddLocalityOptions{ 664 Health: []corepb.HealthStatus{corepb.HealthStatus_UNHEALTHY}, 665 }) 666 clab2.AddLocality(testSubZones[1], 1, 1, testEndpointAddrs[1:2], nil) 667 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 668 // p0 will remove the subconn, and ClientConn will send a sc update to 669 // transient failure. 670 scToRemove := <-cc.RemoveSubConnCh 671 edsb.UpdateSubConnState(scToRemove, balancer.SubConnState{ConnectivityState: connectivity.Shutdown}) 672 673 addrs2 := <-cc.NewSubConnAddrsCh 674 if got, want := addrs2[0].Addr, testEndpointAddrs[1]; got != want { 675 t.Fatalf("sc is created with addr %v, want %v", got, want) 676 } 677 sc2 := <-cc.NewSubConnCh 678 679 // p1 is ready. 680 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 681 edsb.UpdateSubConnState(sc2, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 682 683 // Test roundrobin with only p1 subconns. 684 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc2}); err != nil { 685 t.Fatal(err) 686 } 687 } 688 689 // Test the case where the first and only priority is removed. 690 func (s) TestEDSPriority_FirstPriorityRemoved(t *testing.T) { 691 const testPriorityInitTimeout = time.Second 692 defer func() func() { 693 old := priority.DefaultPriorityInitTimeout 694 priority.DefaultPriorityInitTimeout = testPriorityInitTimeout 695 return func() { 696 priority.DefaultPriorityInitTimeout = old 697 } 698 }()() 699 700 _, cc, xdsC, cleanup := setupTestEDS(t, nil) 701 defer cleanup() 702 // One localities, with priorities [0], each with one backend. 703 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 704 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 705 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 706 // Remove the only localities. 707 clab2 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 708 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab2.Build()), nil) 709 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 710 defer cancel() 711 if err := cc.WaitForErrPicker(ctx); err != nil { 712 t.Fatal(err) 713 } 714 } 715 716 // Watch resources from EDS and DNS, with EDS as the higher priority. Lower 717 // priority is used when higher priority is not ready. 718 func (s) TestFallbackToDNS(t *testing.T) { 719 const testDNSEndpointAddr = "3.1.4.1:5" 720 // dnsTargetCh, dnsCloseCh, resolveNowCh, dnsR, cleanup := setupDNS() 721 dnsTargetCh, _, resolveNowCh, dnsR, cleanupDNS := setupDNS() 722 defer cleanupDNS() 723 edsb, cc, xdsC, cleanup := setupTestEDS(t, nil) 724 defer cleanup() 725 726 if err := edsb.UpdateClientConnState(balancer.ClientConnState{ 727 BalancerConfig: &LBConfig{ 728 DiscoveryMechanisms: []DiscoveryMechanism{ 729 { 730 Type: DiscoveryMechanismTypeEDS, 731 Cluster: testClusterName, 732 }, 733 { 734 Type: DiscoveryMechanismTypeLogicalDNS, 735 DNSHostname: testDNSTarget, 736 }, 737 }, 738 }, 739 }); err != nil { 740 t.Fatal(err) 741 } 742 743 ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout) 744 defer ctxCancel() 745 select { 746 case target := <-dnsTargetCh: 747 if diff := cmp.Diff(target, resolver.Target{Scheme: "dns", Endpoint: testDNSTarget}); diff != "" { 748 t.Fatalf("got unexpected DNS target to watch, diff (-got, +want): %v", diff) 749 } 750 case <-ctx.Done(): 751 t.Fatal("Timed out waiting for building DNS resolver") 752 } 753 754 // One locality with one backend. 755 clab1 := xdstestutils.NewClusterLoadAssignmentBuilder(testClusterNames[0], nil) 756 clab1.AddLocality(testSubZones[0], 1, 0, testEndpointAddrs[:1], nil) 757 xdsC.InvokeWatchEDSCallback("", parseEDSRespProtoForTesting(clab1.Build()), nil) 758 759 // Also send a DNS update, because the balancer needs both updates from all 760 // resources to move on. 761 dnsR.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: testDNSEndpointAddr}}}) 762 763 addrs0 := <-cc.NewSubConnAddrsCh 764 if got, want := addrs0[0].Addr, testEndpointAddrs[0]; got != want { 765 t.Fatalf("sc is created with addr %v, want %v", got, want) 766 } 767 sc0 := <-cc.NewSubConnCh 768 769 // p0 is ready. 770 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 771 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 772 773 // Test roundrobin with only p0 subconns. 774 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc0}); err != nil { 775 t.Fatal(err) 776 } 777 778 // Turn down 0, p1 (DNS) will be used. 779 edsb.UpdateSubConnState(sc0, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 780 781 // The transient failure above should not trigger a re-resolve to the DNS 782 // resolver. Need to read to clear the channel, to avoid potential deadlock 783 // writing to the channel later. 784 shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout) 785 defer shortCancel() 786 select { 787 case <-resolveNowCh: 788 t.Fatal("unexpected re-resolve trigger by transient failure from EDS endpoint") 789 case <-shortCtx.Done(): 790 } 791 792 // The addresses used to create new SubConn should be the DNS endpoint. 793 addrs1 := <-cc.NewSubConnAddrsCh 794 if got, want := addrs1[0].Addr, testDNSEndpointAddr; got != want { 795 t.Fatalf("sc is created with addr %v, want %v", got, want) 796 } 797 sc1 := <-cc.NewSubConnCh 798 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Connecting}) 799 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.Ready}) 800 801 // Test pick with 1. 802 if err := testRoundRobinPickerFromCh(cc.NewPickerCh, []balancer.SubConn{sc1}); err != nil { 803 t.Fatal(err) 804 } 805 806 // Turn down the DNS endpoint, this should trigger an re-resolve in the DNS 807 // resolver. 808 edsb.UpdateSubConnState(sc1, balancer.SubConnState{ConnectivityState: connectivity.TransientFailure}) 809 810 // The transient failure above should trigger a re-resolve to the DNS 811 // resolver. Need to read to clear the channel, to avoid potential deadlock 812 // writing to the channel later. 813 select { 814 case <-resolveNowCh: 815 case <-ctx.Done(): 816 t.Fatal("Timed out waiting for re-resolve") 817 } 818 }