Skip to content

Commit e41e894

Browse files
authored
orca: create ORCA producer for LB policies to use to receive OOB load reports (#5669)
1 parent 36d14db commit e41e894

File tree

8 files changed

+880
-3
lines changed

8 files changed

+880
-3
lines changed

balancer/balancer.go

+23
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ type SubConn interface {
110110
UpdateAddresses([]resolver.Address)
111111
// Connect starts the connecting for this SubConn.
112112
Connect()
113+
// GetOrBuildProducer returns a reference to the existing Producer for this
114+
// ProducerBuilder in this SubConn, or, if one does not currently exist,
115+
// creates a new one and returns it. Returns a close function which must
116+
// be called when the Producer is no longer needed.
117+
GetOrBuildProducer(ProducerBuilder) (p Producer, close func())
113118
}
114119

115120
// NewSubConnOptions contains options to create new SubConn.
@@ -371,3 +376,21 @@ type ClientConnState struct {
371376
// ErrBadResolverState may be returned by UpdateClientConnState to indicate a
372377
// problem with the provided name resolver data.
373378
var ErrBadResolverState = errors.New("bad resolver state")
379+
380+
// A ProducerBuilder is a simple constructor for a Producer. It is used by the
381+
// SubConn to create producers when needed.
382+
type ProducerBuilder interface {
383+
// Build creates a Producer. The first parameter is always a
384+
// grpc.ClientConnInterface (a type to allow creating RPCs/streams on the
385+
// associated SubConn), but is declared as interface{} to avoid a
386+
// dependency cycle. Should also return a close function that will be
387+
// called when all references to the Producer have been given up.
388+
Build(grpcClientConnInterface interface{}) (p Producer, close func())
389+
}
390+
391+
// A Producer is a type shared among potentially many consumers. It is
392+
// associated with a SubConn, and an implementation will typically contain
393+
// other methods to provide additional functionality, e.g. configuration or
394+
// subscription registration.
395+
type Producer interface {
396+
}

balancer/base/balancer_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ func (sc *testSubConn) UpdateAddresses(addresses []resolver.Address) {}
4444

4545
func (sc *testSubConn) Connect() {}
4646

47+
func (sc *testSubConn) GetOrBuildProducer(balancer.ProducerBuilder) (balancer.Producer, func()) {
48+
return nil, nil
49+
}
50+
4751
// testPickBuilder creates balancer.Picker for test.
4852
type testPickBuilder struct {
4953
validate func(info PickerBuildInfo)

balancer_conn_wrappers.go

+68-3
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,20 @@
1919
package grpc
2020

2121
import (
22+
"context"
2223
"fmt"
2324
"strings"
2425
"sync"
2526

2627
"google.golang.org/grpc/balancer"
28+
"google.golang.org/grpc/codes"
2729
"google.golang.org/grpc/connectivity"
2830
"google.golang.org/grpc/internal/balancer/gracefulswitch"
2931
"google.golang.org/grpc/internal/buffer"
3032
"google.golang.org/grpc/internal/channelz"
3133
"google.golang.org/grpc/internal/grpcsync"
3234
"google.golang.org/grpc/resolver"
35+
"google.golang.org/grpc/status"
3336
)
3437

3538
// ccBalancerWrapper sits between the ClientConn and the Balancer.
@@ -305,7 +308,7 @@ func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer
305308
channelz.Warningf(logger, ccb.cc.channelzID, "acBalancerWrapper: NewSubConn: failed to newAddrConn: %v", err)
306309
return nil, err
307310
}
308-
acbw := &acBalancerWrapper{ac: ac}
311+
acbw := &acBalancerWrapper{ac: ac, producers: make(map[balancer.ProducerBuilder]*refCountedProducer)}
309312
acbw.ac.mu.Lock()
310313
ac.acbw = acbw
311314
acbw.ac.mu.Unlock()
@@ -359,8 +362,9 @@ func (ccb *ccBalancerWrapper) Target() string {
359362
// acBalancerWrapper is a wrapper on top of ac for balancers.
360363
// It implements balancer.SubConn interface.
361364
type acBalancerWrapper struct {
362-
mu sync.Mutex
363-
ac *addrConn
365+
mu sync.Mutex
366+
ac *addrConn
367+
producers map[balancer.ProducerBuilder]*refCountedProducer
364368
}
365369

366370
func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
@@ -414,3 +418,64 @@ func (acbw *acBalancerWrapper) getAddrConn() *addrConn {
414418
defer acbw.mu.Unlock()
415419
return acbw.ac
416420
}
421+
422+
var errSubConnNotReady = status.Error(codes.Unavailable, "SubConn not currently connected")
423+
424+
// NewStream begins a streaming RPC on the addrConn. If the addrConn is not
425+
// ready, returns errSubConnNotReady.
426+
func (acbw *acBalancerWrapper) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) {
427+
transport := acbw.ac.getReadyTransport()
428+
if transport == nil {
429+
return nil, errSubConnNotReady
430+
}
431+
return newNonRetryClientStream(ctx, desc, method, transport, acbw.ac, opts...)
432+
}
433+
434+
// Invoke performs a unary RPC. If the addrConn is not ready, returns
435+
// errSubConnNotReady.
436+
func (acbw *acBalancerWrapper) Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...CallOption) error {
437+
cs, err := acbw.NewStream(ctx, unaryStreamDesc, method, opts...)
438+
if err != nil {
439+
return err
440+
}
441+
if err := cs.SendMsg(args); err != nil {
442+
return err
443+
}
444+
return cs.RecvMsg(reply)
445+
}
446+
447+
type refCountedProducer struct {
448+
producer balancer.Producer
449+
refs int // number of current refs to the producer
450+
close func() // underlying producer's close function
451+
}
452+
453+
func (acbw *acBalancerWrapper) GetOrBuildProducer(pb balancer.ProducerBuilder) (balancer.Producer, func()) {
454+
acbw.mu.Lock()
455+
defer acbw.mu.Unlock()
456+
457+
// Look up existing producer from this builder.
458+
pData := acbw.producers[pb]
459+
if pData == nil {
460+
// Not found; create a new one and add it to the producers map.
461+
p, close := pb.Build(acbw)
462+
pData = &refCountedProducer{producer: p, close: close}
463+
acbw.producers[pb] = pData
464+
}
465+
// Account for this new reference.
466+
pData.refs++
467+
468+
// Return a cleanup function wrapped in a OnceFunc to remove this reference
469+
// and delete the refCountedProducer from the map if the total reference
470+
// count goes to zero.
471+
unref := func() {
472+
acbw.mu.Lock()
473+
pData.refs--
474+
if pData.refs == 0 {
475+
defer pData.close() // Run outside the acbw mutex
476+
delete(acbw.producers, pb)
477+
}
478+
acbw.mu.Unlock()
479+
}
480+
return pData.producer, grpcsync.OnceFunc(unref)
481+
}

internal/testutils/balancer.go

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ func (tsc *TestSubConn) Connect() {
6868
}
6969
}
7070

71+
// GetOrBuildProducer is a no-op.
72+
func (tsc *TestSubConn) GetOrBuildProducer(balancer.ProducerBuilder) (balancer.Producer, func()) {
73+
return nil, nil
74+
}
75+
7176
// String implements stringer to print human friendly error message.
7277
func (tsc *TestSubConn) String() string {
7378
return tsc.id

orca/internal/internal.go

+7
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,15 @@
2020
// avoid polluting the godoc of the top-level orca package.
2121
package internal
2222

23+
import ibackoff "google.golang.org/grpc/internal/backoff"
24+
2325
// AllowAnyMinReportingInterval prevents clamping of the MinReportingInterval
2426
// configured via ServiceOptions, to a minimum of 30s.
2527
//
2628
// For testing purposes only.
2729
var AllowAnyMinReportingInterval interface{} // func(*ServiceOptions)
30+
31+
// DefaultBackoffFunc is used by the producer to control its backoff behavior.
32+
//
33+
// For testing purposes only.
34+
var DefaultBackoffFunc = ibackoff.DefaultExponential.Backoff

0 commit comments

Comments
 (0)