Skip to content

Commit

Permalink
Merge branch 'master' into pace
Browse files Browse the repository at this point in the history
  • Loading branch information
bojand committed Oct 26, 2020
2 parents 7be9e72 + 0a1938f commit 1fbc366
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 27 deletions.
18 changes: 9 additions & 9 deletions runner/call_template_data.go → runner/calldata.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const charset = "abcdefghijklmnopqrstuvwxyz" +
var seededRand *rand.Rand = rand.New(
rand.NewSource(time.Now().UnixNano()))

// call template data
type callTemplateData struct {
// CallData represents contextualized data available for templating
type CallData struct {
WorkerID string // unique worker ID
RequestNumber int64 // unique incremented request number for each request
FullyQualifiedName string // fully-qualified name of the method call
Expand All @@ -42,11 +42,11 @@ var tmplFuncMap = template.FuncMap{
"randomString": randomString,
}

// newCallTemplateData returns new call template data
func newCallTemplateData(
// newCallData returns new CallData
func newCallData(
mtd *desc.MethodDescriptor,
funcs template.FuncMap,
workerID string, reqNum int64) *callTemplateData {
workerID string, reqNum int64) *CallData {
now := time.Now()
newUUID, _ := uuid.NewRandom()

Expand All @@ -61,7 +61,7 @@ func newCallTemplateData(
}
}

return &callTemplateData{
return &CallData{
WorkerID: workerID,
RequestNumber: reqNum,
FullyQualifiedName: mtd.GetFullyQualifiedName(),
Expand All @@ -80,14 +80,14 @@ func newCallTemplateData(
}
}

func (td *callTemplateData) execute(data string) (*bytes.Buffer, error) {
func (td *CallData) execute(data string) (*bytes.Buffer, error) {
t := template.Must(template.New("call_template_data").Funcs(td.templateFuncs).Parse(data))
var tpl bytes.Buffer
err := t.Execute(&tpl, td)
return &tpl, err
}

func (td *callTemplateData) executeData(data string) ([]byte, error) {
func (td *CallData) executeData(data string) ([]byte, error) {
if len(data) > 0 {
input := []byte(data)
tpl, err := td.execute(data)
Expand All @@ -101,7 +101,7 @@ func (td *callTemplateData) executeData(data string) ([]byte, error) {
return []byte{}, nil
}

func (td *callTemplateData) executeMetadata(metadata string) (map[string]string, error) {
func (td *CallData) executeMetadata(metadata string) (map[string]string, error) {
var mdMap map[string]string

if len(metadata) > 0 {
Expand Down
16 changes: 8 additions & 8 deletions runner/call_template_data_test.go → runner/calldata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
"github.com/stretchr/testify/assert"
)

func TestCallTemplateData_New(t *testing.T) {
func TestCallData_New(t *testing.T) {
md, err := protodesc.GetMethodDescFromProto("helloworld.Greeter/SayHello", "../testdata/greeter.proto", []string{})
assert.NoError(t, err)
assert.NotNil(t, md)

ctd := newCallTemplateData(md, nil, "worker_id_123", 100)
ctd := newCallData(md, nil, "worker_id_123", 100)

assert.NotNil(t, ctd)
assert.Equal(t, "worker_id_123", ctd.WorkerID)
Expand All @@ -36,12 +36,12 @@ func TestCallTemplateData_New(t *testing.T) {
assert.Equal(t, 36, len(ctd.UUID))
}

func TestCallTemplateData_ExecuteData(t *testing.T) {
func TestCallData_ExecuteData(t *testing.T) {
md, err := protodesc.GetMethodDescFromProto("helloworld.Greeter/SayHello", "../testdata/greeter.proto", []string{})
assert.NoError(t, err)
assert.NotNil(t, md)

ctd := newCallTemplateData(md, nil, "worker_id_123", 200)
ctd := newCallData(md, nil, "worker_id_123", 200)

assert.NotNil(t, ctd)

Expand Down Expand Up @@ -87,12 +87,12 @@ func TestCallTemplateData_ExecuteData(t *testing.T) {
}
}

func TestCallTemplateData_ExecuteMetadata(t *testing.T) {
func TestCallData_ExecuteMetadata(t *testing.T) {
md, err := protodesc.GetMethodDescFromProto("helloworld.Greeter/SayHello", "../testdata/greeter.proto", []string{})
assert.NoError(t, err)
assert.NotNil(t, md)

ctd := newCallTemplateData(md, nil, "worker_id_123", 200)
ctd := newCallData(md, nil, "worker_id_123", 200)

assert.NotNil(t, ctd)

Expand Down Expand Up @@ -138,7 +138,7 @@ func TestCallTemplateData_ExecuteFuncs(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, md)

ctd := newCallTemplateData(md, nil, "worker_id_123", 200)
ctd := newCallData(md, nil, "worker_id_123", 200)

assert.NotNil(t, ctd)

Expand Down Expand Up @@ -249,7 +249,7 @@ func TestCallTemplateData_ExecuteFuncs(t *testing.T) {
})

t.Run("custom functions", func(t *testing.T) {
ctd = newCallTemplateData(md, template.FuncMap{
ctd = newCallData(md, template.FuncMap{
"getSKU": func() string {
return "custom-sku"
},
Expand Down
25 changes: 23 additions & 2 deletions runner/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ import (
"text/template"
"time"

"github.com/jhump/protoreflect/desc"
"github.com/pkg/errors"
"google.golang.org/grpc/credentials"
)

// BinaryDataFunc is a function that can be used for provide binary data for request programatically.
// MethodDescriptor of the call is passed to the data function.
// CallData for the request is passed and can be used to access worker id, request number, etc...
type BinaryDataFunc func(mtd *desc.MethodDescriptor, callData *CallData) []byte

// ScheduleConst is a constant load schedule
const ScheduleConst = "const"

Expand Down Expand Up @@ -84,7 +90,11 @@ type RunConfig struct {
streamInterval time.Duration

// data
data []byte
data []byte

// data func
dataFunc BinaryDataFunc

binary bool
metadata []byte
rmd map[string]string
Expand Down Expand Up @@ -326,6 +336,17 @@ func WithBinaryData(data []byte) Option {
}
}

// 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 {
return func(o *RunConfig) error {
o.dataFunc = data
o.binary = true

return nil
}
}

// WithBinaryDataFromFile specifies the binary data
// WithBinaryDataFromFile("request_data.bin")
func WithBinaryDataFromFile(path string) Option {
Expand Down Expand Up @@ -583,7 +604,7 @@ func WithLogger(log Logger) Option {
}
}

// WithTemplateFuncs adds additional tempalte functions
// WithTemplateFuncs adds additional template functions
func WithTemplateFuncs(funcMap template.FuncMap) Option {
return func(o *RunConfig) error {
o.funcs = funcMap
Expand Down
5 changes: 5 additions & 0 deletions runner/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"math"
"os"
"reflect"
"runtime"
"testing"
"time"
Expand Down Expand Up @@ -133,6 +134,7 @@ func TestRunConfig_newRunConfig(t *testing.T) {
WithDialTimeout(time.Duration(30*time.Second)),
WithName("asdf"),
WithCPUs(4),
WithBinaryDataFunc(changeFunc),
WithBinaryData([]byte("asdf1234foobar")),
WithMetadataFromFile("../testdata/metadata.json"),
WithProtoset("testdata/bundle.protoset"),
Expand All @@ -159,6 +161,9 @@ 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)
funcName1 := runtime.FuncForPC(reflect.ValueOf(changeFunc).Pointer()).Name()
funcName2 := runtime.FuncForPC(reflect.ValueOf(c.dataFunc).Pointer()).Name()
assert.Equal(t, funcName1, funcName2)
assert.Equal(t, `{"request-id": "{{.RequestNumber}}"}`, string(c.metadata))
assert.Equal(t, "", string(c.proto))
assert.Equal(t, "testdata/bundle.protoset", string(c.protoset))
Expand Down
52 changes: 52 additions & 0 deletions runner/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ import (
"github.com/bojand/ghz/internal"
"github.com/bojand/ghz/internal/helloworld"
"github.com/golang/protobuf/proto"
"github.com/jhump/protoreflect/desc"
"github.com/stretchr/testify/assert"
)

func changeFunc(mtd *desc.MethodDescriptor, cd *CallData) []byte {
msg := &helloworld.HelloRequest{}
msg.Name = "bob"
binData, _ := proto.Marshal(msg)
return binData
}

func TestRunUnary(t *testing.T) {
callType := helloworld.Unary

Expand Down Expand Up @@ -337,6 +345,50 @@ func TestRunUnary(t *testing.T) {
assert.Equal(t, 1, connCount)
})

t.Run("test binary with func", func(t *testing.T) {

gs.ResetCounters()

report, err := Run(
"helloworld.Greeter.SayHello",
internal.TestLocalhost,
WithProtoFile("../testdata/greeter.proto", []string{}),
WithTotalRequests(5),
WithBinaryDataFunc(changeFunc),
WithConcurrency(1),
WithTimeout(time.Duration(20*time.Second)),
WithDialTimeout(time.Duration(20*time.Second)),
WithInsecure(true),
)

assert.NoError(t, err)

assert.NotNil(t, report)

assert.Equal(t, 5, int(report.Count))
assert.NotZero(t, report.Average)
assert.NotZero(t, report.Fastest)
assert.NotZero(t, report.Slowest)
assert.NotZero(t, report.Rps)
assert.Empty(t, report.Name)
assert.NotEmpty(t, report.Date)
assert.NotEmpty(t, report.Options)
assert.NotEmpty(t, report.Details)
assert.NotEmpty(t, report.LatencyDistribution)
assert.Equal(t, ReasonNormalEnd, report.EndReason)
assert.Empty(t, report.ErrorDist)

assert.NotEqual(t, report.Average, report.Slowest)
assert.NotEqual(t, report.Average, report.Fastest)
assert.NotEqual(t, report.Slowest, report.Fastest)

count := gs.GetCount(callType)
assert.Equal(t, 5, count)

connCount := gs.GetConnectionCount()
assert.Equal(t, 1, connCount)
})

t.Run("test connections", func(t *testing.T) {
gs.ResetCounters()

Expand Down
13 changes: 9 additions & 4 deletions runner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (w *Worker) Stop() {
func (w *Worker) makeRequest(tv TickValue) error {
reqNum := int64(tv.reqNumber)

ctd := newCallTemplateData(w.mtd, w.config.funcs, w.workerID, reqNum)
ctd := newCallData(w.mtd, w.config.funcs, w.workerID, reqNum)

var inputs []*dynamic.Message
var err error
Expand Down Expand Up @@ -166,7 +166,7 @@ func (w *Worker) makeRequest(tv TickValue) error {
return err
}

func (w *Worker) getMessages(ctd *callTemplateData, inputData []byte) ([]*dynamic.Message, error) {
func (w *Worker) getMessages(ctd *CallData, inputData []byte) ([]*dynamic.Message, error) {
var inputs []*dynamic.Message

if w.cachedMessages != nil {
Expand All @@ -185,12 +185,17 @@ func (w *Worker) getMessages(ctd *callTemplateData, inputData []byte) ([]*dynami
// Json messages are not cached due to templating
} else {
var err error
if w.config.dataFunc != nil {
inputData = w.config.dataFunc(w.mtd, ctd)
}
inputs, err = createPayloadsFromBin(inputData, w.mtd)
if err != nil {
return nil, err
}

w.cachedMessages = inputs
// We only cache in case we don't dynamically change the binary message
if w.config.dataFunc == nil {
w.cachedMessages = inputs
}
}

return inputs, nil
Expand Down
29 changes: 25 additions & 4 deletions www/docs/calldata.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
id: calldata
title: Call Template Data
title: Call Data
---

Data and metadata can specify [template actions](https://golang.org/pkg/text/template/) that will be parsed and evaluated at every request. Each request gets a new instance of the data. The available variables / actions are:

```go
// call template data
type callTemplateData struct {
// CallData represents contextualized data available for templating
type CallData struct {

// unique worker ID
WorkerID string
Expand Down Expand Up @@ -53,7 +53,7 @@ type callTemplateData struct {
}
```

**Functions**
**Template Functions**

There are also two template functions available:

Expand Down Expand Up @@ -95,3 +95,24 @@ Would result in data with JSON representation:
```

See [example calls](examples.md) for some more usage examples.

### Data Function API

When using the `ghz/runner` package programmatically, we can dynamically create data for each request using `WithBinaryDataFunc()` API:

```go
func dataFunc(mtd *desc.MethodDescriptor, cd *runner.CallData) []byte {
msg := &helloworld.HelloRequest{}
msg.Name = cd.WorkerID
binData, err := proto.Marshal(msg)
return binData
}

report, err := runner.Run(
"helloworld.Greeter.SayHello",
"0.0.0.0:50051",
runner.WithProtoFile("./testdata/greeter.proto", []string{}),
runner.WithInsecure(true),
runner.WithBinaryDataFunc(dataFunc),
)
```

0 comments on commit 1fbc366

Please sign in to comment.