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  }