google.golang.org/grpc@v1.74.2/xds/internal/balancer/loadstore/load_store_wrapper.go (about) 1 /* 2 * 3 * Copyright 2020 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 loadstore contains the loadStoreWrapper shared by the balancers. 20 package loadstore 21 22 import ( 23 "sync" 24 25 "google.golang.org/grpc/xds/internal/clients" 26 "google.golang.org/grpc/xds/internal/clients/lrsclient" 27 ) 28 29 // NewWrapper creates a Wrapper. 30 func NewWrapper() *Wrapper { 31 return &Wrapper{} 32 } 33 34 // Wrapper wraps a load store with cluster and edsService. 35 // 36 // It's store and cluster/edsService can be updated separately. And it will 37 // update its internal perCluster store so that new stats will be added to the 38 // correct perCluster. 39 // 40 // Note that this struct is a temporary workaround before we implement graceful 41 // switch for EDS. Any update to the clusterName and serviceName is too early, 42 // the perfect timing is when the picker is updated with the new connection. 43 // This early update could cause picks for the old SubConn being reported to the 44 // new services. 45 // 46 // When the graceful switch in EDS is done, there should be no need for this 47 // struct. The policies that record/report load shouldn't need to handle update 48 // of lrsServerName/cluster/edsService. Its parent should do a graceful switch 49 // of the whole tree when one of that changes. 50 type Wrapper struct { 51 mu sync.RWMutex 52 cluster string 53 edsService string 54 // store and perCluster are initialized as nil. They are only set by the 55 // balancer when LRS is enabled. Before that, all functions to record loads 56 // are no-op. 57 store *lrsclient.LoadStore 58 perCluster *lrsclient.PerClusterReporter 59 } 60 61 // UpdateClusterAndService updates the cluster name and eds service for this 62 // wrapper. If any one of them is changed from before, the perCluster store in 63 // this wrapper will also be updated. 64 func (lsw *Wrapper) UpdateClusterAndService(cluster, edsService string) { 65 lsw.mu.Lock() 66 defer lsw.mu.Unlock() 67 if cluster == lsw.cluster && edsService == lsw.edsService { 68 return 69 } 70 lsw.cluster = cluster 71 lsw.edsService = edsService 72 if lsw.store == nil { 73 return 74 } 75 lsw.perCluster = lsw.store.ReporterForCluster(lsw.cluster, lsw.edsService) 76 } 77 78 // UpdateLoadStore updates the load store for this wrapper. If it is changed 79 // from before, the perCluster store in this wrapper will also be updated. 80 func (lsw *Wrapper) UpdateLoadStore(store *lrsclient.LoadStore) { 81 lsw.mu.Lock() 82 defer lsw.mu.Unlock() 83 if store == lsw.store { 84 return 85 } 86 lsw.store = store 87 if lsw.store == nil { 88 lsw.perCluster = nil 89 return 90 } 91 lsw.perCluster = lsw.store.ReporterForCluster(lsw.cluster, lsw.edsService) 92 } 93 94 // CallStarted records a call started in the store. 95 func (lsw *Wrapper) CallStarted(locality clients.Locality) { 96 lsw.mu.RLock() 97 defer lsw.mu.RUnlock() 98 if lsw.perCluster != nil { 99 lsw.perCluster.CallStarted(locality) 100 } 101 } 102 103 // CallFinished records a call finished in the store. 104 func (lsw *Wrapper) CallFinished(locality clients.Locality, err error) { 105 lsw.mu.RLock() 106 defer lsw.mu.RUnlock() 107 if lsw.perCluster != nil { 108 lsw.perCluster.CallFinished(locality, err) 109 } 110 } 111 112 // CallServerLoad records the server load in the store. 113 func (lsw *Wrapper) CallServerLoad(locality clients.Locality, name string, val float64) { 114 lsw.mu.RLock() 115 defer lsw.mu.RUnlock() 116 if lsw.perCluster != nil { 117 lsw.perCluster.CallServerLoad(locality, name, val) 118 } 119 } 120 121 // CallDropped records a call dropped in the store. 122 func (lsw *Wrapper) CallDropped(category string) { 123 lsw.mu.RLock() 124 defer lsw.mu.RUnlock() 125 if lsw.perCluster != nil { 126 lsw.perCluster.CallDropped(category) 127 } 128 }