@@ -103,17 +103,25 @@ type tmc struct {
103103 client tabletmanagerservicepb.TabletManagerClient
104104}
105105
106- type addrTmcMap map [string ]* tmc
106+ type tmcEntry struct {
107+ once sync.Once
108+ tmc * tmc
109+ err error
110+ }
111+
112+ type addrTmcMap map [string ]* tmcEntry
107113
108114// grpcClient implements both dialer and poolDialer.
109115type grpcClient struct {
110116 // This cache of connections is to maximize QPS for ExecuteFetchAs{Dba,App},
111117 // CheckThrottler and FullStatus. Note we'll keep the clients open and close them upon Close() only.
112118 // But that's OK because usually the tasks that use them are one-purpose only.
113- // The map is protected by the mutex .
114- mu sync.Mutex
119+ // rpcClientMapMu protects rpcClientMap .
120+ rpcClientMapMu sync.Mutex
115121 rpcClientMap map [string ]chan * tmc
116- rpcDialPoolMap map [DialPoolGroup ]addrTmcMap
122+ // rpcDialPoolMapMu protects rpcDialPoolMap.
123+ rpcDialPoolMapMu sync.Mutex
124+ rpcDialPoolMap map [DialPoolGroup ]addrTmcMap
117125}
118126
119127type dialer interface {
@@ -185,25 +193,32 @@ func (client *grpcClient) dialPool(ctx context.Context, tablet *topodatapb.Table
185193 return nil , vterrors .FromGRPC (err )
186194 }
187195
188- client .mu .Lock ()
189- if client .rpcClientMap == nil {
190- client .rpcClientMap = make (map [string ]chan * tmc )
191- }
192- c , ok := client .rpcClientMap [addr ]
193- if ! ok {
196+ c , isEmpty := func () (chan * tmc , bool ) {
197+ client .rpcClientMapMu .Lock ()
198+ defer client .rpcClientMapMu .Unlock ()
199+
200+ if client .rpcClientMap == nil {
201+ client .rpcClientMap = make (map [string ]chan * tmc )
202+ }
203+ c , ok := client .rpcClientMap [addr ]
204+ if ok {
205+ return c , false
206+ }
207+
194208 c = make (chan * tmc , concurrency )
195209 client .rpcClientMap [addr ] = c
196- client .mu .Unlock ()
210+ return c , true
211+ }()
197212
213+ // If the channel is empty, populate it with connections.
214+ if isEmpty {
198215 for i := 0 ; i < cap (c ); i ++ {
199216 tm , err := client .createTmc (ctx , addr , opt )
200217 if err != nil {
201218 return nil , vterrors .FromGRPC (err )
202219 }
203220 c <- tm
204221 }
205- } else {
206- client .mu .Unlock ()
207222 }
208223
209224 result := <- c
@@ -218,44 +233,81 @@ func (client *grpcClient) dialDedicatedPool(ctx context.Context, dialPoolGroup D
218233 return nil , nil , err
219234 }
220235
221- client .mu .Lock ()
222- defer client .mu .Unlock ()
223- if client .rpcDialPoolMap == nil {
224- client .rpcDialPoolMap = make (map [DialPoolGroup ]addrTmcMap )
225- }
226- if _ , ok := client .rpcDialPoolMap [dialPoolGroup ]; ! ok {
227- client .rpcDialPoolMap [dialPoolGroup ] = make (addrTmcMap )
228- }
229- m := client .rpcDialPoolMap [dialPoolGroup ]
230- if _ , ok := m [addr ]; ! ok {
231- tm , err := client .createTmc (ctx , addr , opt )
232- if err != nil {
233- return nil , nil , err
236+ entry := func () * tmcEntry {
237+ client .rpcDialPoolMapMu .Lock ()
238+ defer client .rpcDialPoolMapMu .Unlock ()
239+
240+ if client .rpcDialPoolMap == nil {
241+ client .rpcDialPoolMap = make (map [DialPoolGroup ]addrTmcMap )
242+ }
243+ if _ , ok := client .rpcDialPoolMap [dialPoolGroup ]; ! ok {
244+ client .rpcDialPoolMap [dialPoolGroup ] = make (addrTmcMap )
245+ }
246+
247+ poolEntries := client .rpcDialPoolMap [dialPoolGroup ]
248+ entry , ok := poolEntries [addr ]
249+ if ok {
250+ return entry
234251 }
235- m [addr ] = tm
252+
253+ entry = & tmcEntry {}
254+ poolEntries [addr ] = entry
255+ return entry
256+ }()
257+
258+ // Initialize connection exactly once, without holding the mutex
259+ entry .once .Do (func () {
260+ entry .tmc , entry .err = client .createTmc (ctx , addr , opt )
261+ })
262+
263+ if entry .err != nil {
264+ return nil , nil , entry .err
236265 }
266+
237267 invalidator := func () {
238- client .mu .Lock ()
239- defer client .mu .Unlock ()
240- if tm := m [addr ]; tm != nil && tm .cc != nil {
241- tm .cc .Close ()
268+ client .rpcDialPoolMapMu .Lock ()
269+ defer client .rpcDialPoolMapMu .Unlock ()
270+
271+ if entry .tmc != nil && entry .tmc .cc != nil {
272+ entry .tmc .cc .Close ()
273+ }
274+
275+ if poolEntries , ok := client .rpcDialPoolMap [dialPoolGroup ]; ok {
276+ delete (poolEntries , addr )
242277 }
243- delete (m , addr )
244278 }
245- return m [ addr ] .client , invalidator , nil
279+ return entry . tmc .client , invalidator , nil
246280}
247281
248282// Close is part of the tmclient.TabletManagerClient interface.
249283func (client * grpcClient ) Close () {
250- client .mu .Lock ()
251- defer client .mu .Unlock ()
252- for _ , c := range client .rpcClientMap {
253- close (c )
254- for ch := range c {
255- ch .cc .Close ()
284+ func () {
285+ client .rpcClientMapMu .Lock ()
286+ defer client .rpcClientMapMu .Unlock ()
287+
288+ for _ , c := range client .rpcClientMap {
289+ close (c )
290+ for ch := range c {
291+ ch .cc .Close ()
292+ }
256293 }
257- }
258- client .rpcClientMap = nil
294+ client .rpcClientMap = nil
295+ }()
296+
297+ // Close dedicated pools
298+ func () {
299+ client .rpcDialPoolMapMu .Lock ()
300+ defer client .rpcDialPoolMapMu .Unlock ()
301+
302+ for _ , addrMap := range client .rpcDialPoolMap {
303+ for _ , tm := range addrMap {
304+ if tm != nil && tm .tmc != nil && tm .tmc .cc != nil {
305+ tm .tmc .cc .Close ()
306+ }
307+ }
308+ }
309+ client .rpcDialPoolMap = nil
310+ }()
259311}
260312
261313//
0 commit comments