snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / vet-proto (push) Failing after 1s
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 0s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / vet-proto (push) Failing after 1s
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
Stale bot / stale (push) Failing after 0s
This commit is contained in:
175
internal/xds/clients/lrsclient/lrsclient.go
Normal file
175
internal/xds/clients/lrsclient/lrsclient.go
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2025 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package lrsclient provides an LRS (Load Reporting Service) client.
|
||||
//
|
||||
// See: https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/load_stats/v3/lrs.proto
|
||||
package lrsclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
igrpclog "google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/internal/xds/clients"
|
||||
clientsinternal "google.golang.org/grpc/internal/xds/clients/internal"
|
||||
"google.golang.org/grpc/internal/xds/clients/internal/backoff"
|
||||
)
|
||||
|
||||
const (
|
||||
clientFeatureNoOverprovisioning = "envoy.lb.does_not_support_overprovisioning"
|
||||
clientFeatureResourceWrapper = "xds.config.resource-in-sotw"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultExponentialBackoff = backoff.DefaultExponential.Backoff
|
||||
)
|
||||
|
||||
// LRSClient is an LRS (Load Reporting Service) client.
|
||||
type LRSClient struct {
|
||||
transportBuilder clients.TransportBuilder
|
||||
node clients.Node
|
||||
backoff func(int) time.Duration // Backoff for LRS stream failures.
|
||||
logger *igrpclog.PrefixLogger
|
||||
|
||||
// The LRSClient owns a bunch of streams to individual LRS servers.
|
||||
//
|
||||
// Once all references to a stream are dropped, the stream is closed.
|
||||
mu sync.Mutex
|
||||
lrsStreams map[clients.ServerIdentifier]*streamImpl // Map from server config to in-use streamImpls.
|
||||
lrsRefs map[clients.ServerIdentifier]int // Map from server config to number of references.
|
||||
}
|
||||
|
||||
// New returns a new LRS Client configured with the provided config.
|
||||
func New(config Config) (*LRSClient, error) {
|
||||
if config.TransportBuilder == nil {
|
||||
return nil, errors.New("lrsclient: transport builder is nil")
|
||||
}
|
||||
|
||||
c := &LRSClient{
|
||||
transportBuilder: config.TransportBuilder,
|
||||
node: config.Node,
|
||||
backoff: defaultExponentialBackoff,
|
||||
lrsStreams: make(map[clients.ServerIdentifier]*streamImpl),
|
||||
lrsRefs: make(map[clients.ServerIdentifier]int),
|
||||
}
|
||||
c.logger = prefixLogger(c)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// ReportLoad creates and returns a LoadStore for the caller to report loads
|
||||
// using a LoadReportingStream.
|
||||
//
|
||||
// Caller must call Stop on the returned LoadStore when they are done reporting
|
||||
// load to this server.
|
||||
func (c *LRSClient) ReportLoad(si clients.ServerIdentifier) (*LoadStore, error) {
|
||||
lrs, err := c.getOrCreateLRSStream(si)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return lrs.loadStore, nil
|
||||
}
|
||||
|
||||
// getOrCreateLRSStream returns an lrs stream for the given server identifier.
|
||||
//
|
||||
// If an active lrs stream exists for the given server identifier, it is
|
||||
// returned. Otherwise, a new lrs stream is created and returned.
|
||||
func (c *LRSClient) getOrCreateLRSStream(serverIdentifier clients.ServerIdentifier) (*streamImpl, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.logger.V(2) {
|
||||
c.logger.Infof("Received request for a reference to an lrs stream for server identifier %q", serverIdentifier)
|
||||
}
|
||||
|
||||
// Use an existing stream, if one exists for this server identifier.
|
||||
if s, ok := c.lrsStreams[serverIdentifier]; ok {
|
||||
if c.logger.V(2) {
|
||||
c.logger.Infof("Reusing an existing lrs stream for server identifier %q", serverIdentifier)
|
||||
}
|
||||
c.lrsRefs[serverIdentifier]++
|
||||
return s, nil
|
||||
}
|
||||
|
||||
if c.logger.V(2) {
|
||||
c.logger.Infof("Creating a new lrs stream for server identifier %q", serverIdentifier)
|
||||
}
|
||||
|
||||
// Create a new transport and create a new lrs stream, and add it to the
|
||||
// map of lrs streams.
|
||||
tr, err := c.transportBuilder.Build(serverIdentifier)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("lrsclient: failed to create transport for server identifier %s: %v", serverIdentifier, err)
|
||||
}
|
||||
|
||||
nodeProto := clientsinternal.NodeProto(c.node)
|
||||
nodeProto.ClientFeatures = []string{clientFeatureNoOverprovisioning, clientFeatureResourceWrapper}
|
||||
lrs := newStreamImpl(streamOpts{
|
||||
transport: tr,
|
||||
backoff: c.backoff,
|
||||
nodeProto: nodeProto,
|
||||
logPrefix: clientPrefix(c),
|
||||
})
|
||||
|
||||
// Register a stop function that decrements the reference count, stops
|
||||
// the LRS stream when the last reference is removed and closes the
|
||||
// transport and removes the lrs stream and its references from the
|
||||
// respective maps. Before closing the stream, it waits for the provided
|
||||
// context to be done (timeout or cancellation).
|
||||
stop := func(ctx context.Context) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if r, ok := c.lrsRefs[serverIdentifier]; !ok || r == 0 {
|
||||
c.logger.Errorf("Attempting to stop already stopped StreamImpl")
|
||||
return
|
||||
}
|
||||
c.lrsRefs[serverIdentifier]--
|
||||
if c.lrsRefs[serverIdentifier] != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
lrs.finalSendRequest <- struct{}{}
|
||||
|
||||
select {
|
||||
case err := <-lrs.finalSendDone:
|
||||
if err != nil {
|
||||
c.logger.Warningf("Final send attempt failed: %v", err)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
c.logger.Warningf("Context canceled before finishing the final send attempt: %v", err)
|
||||
}
|
||||
|
||||
lrs.cancelStream()
|
||||
lrs.cancelStream = nil
|
||||
lrs.logger.Infof("Stopping LRS stream")
|
||||
<-lrs.doneCh
|
||||
|
||||
delete(c.lrsStreams, serverIdentifier)
|
||||
tr.Close()
|
||||
}
|
||||
lrs.loadStore.stop = stop
|
||||
|
||||
c.lrsStreams[serverIdentifier] = lrs
|
||||
c.lrsRefs[serverIdentifier] = 1
|
||||
|
||||
return lrs, nil
|
||||
}
|
||||
Reference in New Issue
Block a user