google.golang.org/grpc@v1.72.2/picker_wrapper_test.go (about) 1 /* 2 * 3 * Copyright 2017 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 19 package grpc 20 21 import ( 22 "context" 23 "fmt" 24 "sync" 25 "sync/atomic" 26 "testing" 27 "time" 28 29 "google.golang.org/grpc/balancer" 30 "google.golang.org/grpc/codes" 31 "google.golang.org/grpc/connectivity" 32 "google.golang.org/grpc/internal/transport" 33 "google.golang.org/grpc/status" 34 ) 35 36 const goroutineCount = 5 37 38 var ( 39 testT = &testTransport{} 40 testSC = &acBalancerWrapper{ac: &addrConn{ 41 state: connectivity.Ready, 42 transport: testT, 43 }} 44 testSCNotReady = &acBalancerWrapper{ac: &addrConn{ 45 state: connectivity.TransientFailure, 46 }} 47 ) 48 49 type testTransport struct { 50 transport.ClientTransport 51 } 52 53 type testingPicker struct { 54 err error 55 sc balancer.SubConn 56 maxCalled int64 57 } 58 59 func (p *testingPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { 60 if atomic.AddInt64(&p.maxCalled, -1) < 0 { 61 return balancer.PickResult{}, fmt.Errorf("pick called to many times (> goroutineCount)") 62 } 63 if p.err != nil { 64 return balancer.PickResult{}, p.err 65 } 66 return balancer.PickResult{SubConn: p.sc}, nil 67 } 68 69 func (s) TestBlockingPickTimeout(t *testing.T) { 70 bp := newPickerWrapper(nil) 71 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 72 defer cancel() 73 if _, _, err := bp.pick(ctx, true, balancer.PickInfo{}); status.Code(err) != codes.DeadlineExceeded { 74 t.Errorf("bp.pick returned error %v, want DeadlineExceeded", err) 75 } 76 } 77 78 func (s) TestBlockingPick(t *testing.T) { 79 bp := newPickerWrapper(nil) 80 // All goroutines should block because picker is nil in bp. 81 var finishedCount uint64 82 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 83 defer cancel() 84 wg := sync.WaitGroup{} 85 wg.Add(goroutineCount) 86 for i := goroutineCount; i > 0; i-- { 87 go func() { 88 if tr, _, err := bp.pick(ctx, true, balancer.PickInfo{}); err != nil || tr != testT { 89 t.Errorf("bp.pick returned transport: %v, error: %v, want transport: %v, error: nil", tr, err, testT) 90 } 91 atomic.AddUint64(&finishedCount, 1) 92 wg.Done() 93 }() 94 } 95 time.Sleep(50 * time.Millisecond) 96 if c := atomic.LoadUint64(&finishedCount); c != 0 { 97 t.Errorf("finished goroutines count: %v, want 0", c) 98 } 99 bp.updatePicker(&testingPicker{sc: testSC, maxCalled: goroutineCount}) 100 // Wait for all pickers to finish before the context is cancelled. 101 wg.Wait() 102 } 103 104 func (s) TestBlockingPickNoSubAvailable(t *testing.T) { 105 bp := newPickerWrapper(nil) 106 var finishedCount uint64 107 bp.updatePicker(&testingPicker{err: balancer.ErrNoSubConnAvailable, maxCalled: goroutineCount}) 108 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 109 defer cancel() 110 // All goroutines should block because picker returns no subConn available. 111 wg := sync.WaitGroup{} 112 wg.Add(goroutineCount) 113 for i := goroutineCount; i > 0; i-- { 114 go func() { 115 if tr, _, err := bp.pick(ctx, true, balancer.PickInfo{}); err != nil || tr != testT { 116 t.Errorf("bp.pick returned transport: %v, error: %v, want transport: %v, error: nil", tr, err, testT) 117 } 118 atomic.AddUint64(&finishedCount, 1) 119 wg.Done() 120 }() 121 } 122 time.Sleep(50 * time.Millisecond) 123 if c := atomic.LoadUint64(&finishedCount); c != 0 { 124 t.Errorf("finished goroutines count: %v, want 0", c) 125 } 126 bp.updatePicker(&testingPicker{sc: testSC, maxCalled: goroutineCount}) 127 // Wait for all pickers to finish before the context is cancelled. 128 wg.Wait() 129 } 130 131 func (s) TestBlockingPickTransientWaitforready(t *testing.T) { 132 bp := newPickerWrapper(nil) 133 bp.updatePicker(&testingPicker{err: balancer.ErrTransientFailure, maxCalled: goroutineCount}) 134 var finishedCount uint64 135 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 136 defer cancel() 137 // All goroutines should block because picker returns transientFailure and 138 // picks are not failfast. 139 wg := sync.WaitGroup{} 140 wg.Add(goroutineCount) 141 for i := goroutineCount; i > 0; i-- { 142 go func() { 143 if tr, _, err := bp.pick(ctx, false, balancer.PickInfo{}); err != nil || tr != testT { 144 t.Errorf("bp.pick returned transport: %v, error: %v, want transport: %v, error: nil", tr, err, testT) 145 } 146 atomic.AddUint64(&finishedCount, 1) 147 wg.Done() 148 }() 149 } 150 time.Sleep(time.Millisecond) 151 if c := atomic.LoadUint64(&finishedCount); c != 0 { 152 t.Errorf("finished goroutines count: %v, want 0", c) 153 } 154 bp.updatePicker(&testingPicker{sc: testSC, maxCalled: goroutineCount}) 155 // Wait for all pickers to finish before the context is cancelled. 156 wg.Wait() 157 } 158 159 func (s) TestBlockingPickSCNotReady(t *testing.T) { 160 bp := newPickerWrapper(nil) 161 bp.updatePicker(&testingPicker{sc: testSCNotReady, maxCalled: goroutineCount}) 162 var finishedCount uint64 163 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 164 defer cancel() 165 // All goroutines should block because subConn is not ready. 166 wg := sync.WaitGroup{} 167 wg.Add(goroutineCount) 168 for i := goroutineCount; i > 0; i-- { 169 go func() { 170 if tr, _, err := bp.pick(ctx, true, balancer.PickInfo{}); err != nil || tr != testT { 171 t.Errorf("bp.pick returned transport: %v, error: %v, want transport: %v, error: nil", tr, err, testT) 172 } 173 atomic.AddUint64(&finishedCount, 1) 174 wg.Done() 175 }() 176 } 177 time.Sleep(time.Millisecond) 178 if c := atomic.LoadUint64(&finishedCount); c != 0 { 179 t.Errorf("finished goroutines count: %v, want 0", c) 180 } 181 bp.updatePicker(&testingPicker{sc: testSC, maxCalled: goroutineCount}) 182 // Wait for all pickers to finish before the context is cancelled. 183 wg.Wait() 184 }