test: add the loadtime report tool (#9351) (#9364)

This pull request adds the report tool and modifies the loadtime libraries to better support its use.

(cherry picked from commit 8655080a0f)

Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>
This commit is contained in:
mergify[bot]
2022-09-02 15:29:56 -04:00
committed by GitHub
co-authored by William Banfield
parent 6ed1bb96d7
commit d9fd26ce93
13 changed files with 551 additions and 88 deletions
+87
View File
@@ -0,0 +1,87 @@
package payload
import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"math"
"google.golang.org/protobuf/proto"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)
const keyPrefix = "a="
// NewBytes generates a new payload and returns the encoded representation of
// the payload as a slice of bytes. NewBytes uses the fields on the Options
// to create the payload.
func NewBytes(p *Payload) ([]byte, error) {
p.Padding = make([]byte, 1)
if p.Time == nil {
p.Time = timestamppb.Now()
}
us, err := CalculateUnpaddedSize(p)
if err != nil {
return nil, err
}
if p.Size < uint64(us) {
return nil, fmt.Errorf("configured size %d not large enough to fit unpadded transaction of size %d", p.Size, us)
}
p.Padding = make([]byte, p.Size-uint64(us))
_, err = rand.Read(p.Padding)
if err != nil {
return nil, err
}
b, err := proto.Marshal(p)
if err != nil {
return nil, err
}
// prepend a single key so that the kv store only ever stores a single
// transaction instead of storing all tx and ballooning in size.
return append([]byte(keyPrefix), b...), nil
}
// FromBytes extracts a paylod from the byte representation of the payload.
// FromBytes leaves the padding untouched, returning it to the caller to handle
// or discard per their preference.
func FromBytes(b []byte) (*Payload, error) {
p := &Payload{}
tr := bytes.TrimPrefix(b, []byte(keyPrefix))
if bytes.Equal(b, tr) {
return nil, errors.New("payload bytes missing key prefix")
}
err := proto.Unmarshal(tr, p)
if err != nil {
return nil, err
}
return p, nil
}
// MaxUnpaddedSize returns the maximum size that a payload may be if no padding
// is included.
func MaxUnpaddedSize() (int, error) {
p := &Payload{
Time: timestamppb.Now(),
Connections: math.MaxUint64,
Rate: math.MaxUint64,
Size: math.MaxUint64,
Padding: make([]byte, 1),
}
return CalculateUnpaddedSize(p)
}
// CalculateUnpaddedSize calculates the size of the passed in payload for the
// purpose of determining how much padding to add to add to reach the target size.
// CalculateUnpaddedSize returns an error if the payload Padding field is longer than 1.
func CalculateUnpaddedSize(p *Payload) (int, error) {
if len(p.Padding) != 1 {
return 0, fmt.Errorf("expected length of padding to be 1, received %d", len(p.Padding))
}
b, err := proto.Marshal(p)
if err != nil {
return 0, err
}
return len(b) + len(keyPrefix), nil
}
+50
View File
@@ -0,0 +1,50 @@
package payload_test
import (
"testing"
"github.com/tendermint/tendermint/test/loadtime/payload"
)
const payloadSizeTarget = 1024 // 1kb
func TestSize(t *testing.T) {
s, err := payload.MaxUnpaddedSize()
if err != nil {
t.Fatalf("calculating max unpadded size %s", err)
}
if s > payloadSizeTarget {
t.Fatalf("unpadded payload size %d exceeds target %d", s, payloadSizeTarget)
}
}
func TestRoundTrip(t *testing.T) {
const (
testConns = 512
testRate = 4
)
b, err := payload.NewBytes(&payload.Payload{
Size: payloadSizeTarget,
Connections: testConns,
Rate: testRate,
})
if err != nil {
t.Fatalf("generating payload %s", err)
}
if len(b) < payloadSizeTarget {
t.Fatalf("payload size in bytes %d less than expected %d", len(b), payloadSizeTarget)
}
p, err := payload.FromBytes(b)
if err != nil {
t.Fatalf("reading payload %s", err)
}
if p.Size != payloadSizeTarget {
t.Fatalf("payload size value %d does not match expected %d", p.Size, payloadSizeTarget)
}
if p.Connections != testConns {
t.Fatalf("payload connections value %d does not match expected %d", p.Connections, testConns)
}
if p.Rate != testRate {
t.Fatalf("payload rate value %d does not match expected %d", p.Rate, testRate)
}
}
-23
View File
@@ -1,23 +0,0 @@
package payload
import (
"math"
"google.golang.org/protobuf/proto"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)
func CalculateUnpaddedSizeBytes() (int, error) {
p := &Payload{
Time: timestamppb.Now(),
Connections: math.MaxUint64,
Rate: math.MaxUint64,
Size: math.MaxUint64,
Padding: make([]byte, 1),
}
b, err := proto.Marshal(p)
if err != nil {
return 0, err
}
return len(b), nil
}
-19
View File
@@ -1,19 +0,0 @@
package payload_test
import (
"testing"
"github.com/tendermint/tendermint/test/loadtime/payload"
)
const payloadSizeTarget = 1024 // 1kb
func TestCalculateSize(t *testing.T) {
s, err := payload.CalculateUnpaddedSizeBytes()
if err != nil {
t.Fatalf("calculating unpadded size %s", err)
}
if s > payloadSizeTarget {
t.Fatalf("unpadded payload size %d exceeds target %d", s, payloadSizeTarget)
}
}