Repository: bojand/ghz
Pull request #242 (2020-11-17 14:42:39)

[feature] add client side load balancing
Adds the option to define a client side load balancer.
There are only 2 available balancers by default: pick_first and round_robin. Default is pick_first.

This option can be defined on the client and passed as an option e.g.
```go
. WithClientLoadBalancing("round_robin") // for default round robin instead of first value
```


fixes: https://github.com/bojand/ghz/issues/103


## Note
This PR uses an old grpc deprecated [option](https://github.com/grpc/grpc-go/issues/3003) because some clients are not up to date to use the newest experimental api and therefore don't even have that option.

Linting fails because of ^
```
Error: runner/requester.go:326:23: SA1019: grpc.WithBalancerName is deprecated: use WithDefaultServiceConfig and WithDisableServiceConfig instead.  Will be removed in a future 1.x release.  (staticcheck)
		opts = append(opts, grpc.WithBalancerName(b.config.lbStrategy))
		                    ^
Error: Process completed with exit code 1.
```


Patch:
diff --git a/runner/options.go b/runner/options.go
index c5a1bf6..72afe9b 100644
--- a/runner/options.go
+++ b/runner/options.go
@@ -66,6 +66,8 @@ type RunConfig struct {
 	// data
 	data []byte
 
+	// lbStrategy
+	lbStrategy string
 	// data func
 	dataFunc BinaryDataFunc
 
@@ -310,6 +312,16 @@ func WithBinaryData(data []byte) Option {
 	}
 }
 
+// WithClientLoadBalancing specifies the LB strategy to use
+// The strategies has to be self written and pre defined
+func WithClientLoadBalancing(strategy string) Option {
+	return func(o *RunConfig) error {
+		o.lbStrategy = strategy
+
+		return nil
+	}
+}
+
 // WithBinaryDataFunc specifies the binary data func which will be called on each request
 //  WithBinaryDataFunc(changeFunc)
 func WithBinaryDataFunc(data func(mtd *desc.MethodDescriptor, callData *CallData) []byte) Option {
diff --git a/runner/requester.go b/runner/requester.go
index ca4894e..5231a9e 100644
--- a/runner/requester.go
+++ b/runner/requester.go
@@ -322,6 +322,10 @@ func (b *Requester) newClientConn(withStatsHandler bool) (*grpc.ClientConn, erro
 			grpc.MaxCallSendMsgSize(math.MaxInt32),
 		))
 
+	if b.config.lbStrategy != "" {
+		opts = append(opts, grpc.WithBalancerName(b.config.lbStrategy))
+	}
+
 	// create client connection
 	return grpc.DialContext(ctx, b.config.host, opts...)
 }


Test patch:
diff --git a/runner/options_test.go b/runner/options_test.go
index b459821..5472f13 100644
--- a/runner/options_test.go
+++ b/runner/options_test.go
@@ -125,8 +125,9 @@ func TestRunConfig_newRunConfig(t *testing.T) {
 			WithDialTimeout(time.Duration(30*time.Second)),
 			WithName("asdf"),
 			WithCPUs(4),
-			WithBinaryDataFunc(changeFunc),
 			WithBinaryData([]byte("asdf1234foobar")),
+			WithBinaryDataFunc(changeFunc),
+			WithClientLoadBalancing(`{"loadBalancingPolicy":"round_robin"}`),
 			WithMetadataFromFile("../testdata/metadata.json"),
 			WithProtoset("testdata/bundle.protoset"),
 		)
@@ -152,6 +153,7 @@ func TestRunConfig_newRunConfig(t *testing.T) {
 		assert.Equal(t, 4, c.cpus)
 		assert.Equal(t, "asdf", c.name)
 		assert.Equal(t, []byte("asdf1234foobar"), c.data)
+		assert.Equal(t, `{"loadBalancingPolicy":"round_robin"}`, c.lbStrategy)
 		funcName1 := runtime.FuncForPC(reflect.ValueOf(changeFunc).Pointer()).Name()
 		funcName2 := runtime.FuncForPC(reflect.ValueOf(c.dataFunc).Pointer()).Name()
 		assert.Equal(t, funcName1, funcName2)