mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-08 22:23:11 +00:00
working basic report generator
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/informalsystems/tm-load-test/pkg/loadtest"
|
||||
"github.com/tendermint/tendermint/test/loadtime/payload"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// Ensure all of the interfaces are correctly satisfied.
|
||||
@@ -23,10 +20,9 @@ type ClientFactory struct{}
|
||||
// TxGenerator holds the set of information that will be used to generate
|
||||
// each transaction.
|
||||
type TxGenerator struct {
|
||||
conns uint64
|
||||
rate uint64
|
||||
size uint64
|
||||
payloadSizeBytes uint64
|
||||
conns uint64
|
||||
rate uint64
|
||||
size uint64
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -42,11 +38,7 @@ func main() {
|
||||
}
|
||||
|
||||
func (f *ClientFactory) ValidateConfig(cfg loadtest.Config) error {
|
||||
psb, err := payload.CalculateUnpaddedSizeBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
psb := payload.UnpaddedSizeBytes()
|
||||
if psb > cfg.Size {
|
||||
return fmt.Errorf("payload size exceeds configured size")
|
||||
}
|
||||
@@ -54,36 +46,17 @@ func (f *ClientFactory) ValidateConfig(cfg loadtest.Config) error {
|
||||
}
|
||||
|
||||
func (f *ClientFactory) NewClient(cfg loadtest.Config) (loadtest.Client, error) {
|
||||
psb, err := payload.CalculateUnpaddedSizeBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TxGenerator{
|
||||
conns: uint64(cfg.Connections),
|
||||
rate: uint64(cfg.Rate),
|
||||
size: uint64(cfg.Size),
|
||||
payloadSizeBytes: uint64(psb),
|
||||
conns: uint64(cfg.Connections),
|
||||
rate: uint64(cfg.Rate),
|
||||
size: uint64(cfg.Size),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *TxGenerator) GenerateTx() ([]byte, error) {
|
||||
p := &payload.Payload{
|
||||
Time: timestamppb.Now(),
|
||||
Connections: c.conns,
|
||||
Rate: c.rate,
|
||||
Size: c.size,
|
||||
Padding: make([]byte, c.size-c.payloadSizeBytes),
|
||||
}
|
||||
_, 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("a="), b...), nil
|
||||
return payload.NewBytes(payload.Options{
|
||||
Conns: c.conns,
|
||||
Rate: c.rate,
|
||||
Size: c.size,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package payload
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func CalculateUnpaddedSizeBytes() (int, error) {
|
||||
func init() {
|
||||
p := &Payload{
|
||||
Time: timestamppb.Now(),
|
||||
Connections: math.MaxUint64,
|
||||
@@ -17,7 +20,57 @@ func CalculateUnpaddedSizeBytes() (int, error) {
|
||||
}
|
||||
b, err := proto.Marshal(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
panic(err)
|
||||
}
|
||||
return len(b), nil
|
||||
payloadSizeBytes = len(b)
|
||||
}
|
||||
|
||||
const keyPrefix = "a="
|
||||
|
||||
var payloadSizeBytes int
|
||||
|
||||
type Options struct {
|
||||
Conns uint64
|
||||
Rate uint64
|
||||
Size uint64
|
||||
}
|
||||
|
||||
func UnpaddedSizeBytes() int {
|
||||
return payloadSizeBytes
|
||||
}
|
||||
|
||||
func NewBytes(o Options) ([]byte, error) {
|
||||
if o.Size < uint64(UnpaddedSizeBytes()) {
|
||||
return nil, fmt.Errorf("configured size %d not large enough to fit unpadded transaction size %d", o.Size, UnpaddedSizeBytes())
|
||||
}
|
||||
p := &Payload{
|
||||
Time: timestamppb.Now(),
|
||||
Connections: o.Conns,
|
||||
Rate: o.Rate,
|
||||
Size: o.Size,
|
||||
Padding: make([]byte, o.Size-uint64(UnpaddedSizeBytes())),
|
||||
}
|
||||
_, 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
|
||||
// return b, nil
|
||||
}
|
||||
|
||||
func FromBytes(b []byte) (*Payload, error) {
|
||||
p := &Payload{}
|
||||
tr := bytes.TrimPrefix(b, []byte(keyPrefix))
|
||||
err := proto.Unmarshal(tr, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@@ -8,12 +8,25 @@ import (
|
||||
|
||||
const payloadSizeTarget = 1024 // 1kb
|
||||
|
||||
func TestCalculateSize(t *testing.T) {
|
||||
s, err := payload.CalculateUnpaddedSizeBytes()
|
||||
if err != nil {
|
||||
t.Fatalf("calculating unpadded size %s", err)
|
||||
}
|
||||
func TestSize(t *testing.T) {
|
||||
s := payload.UnpaddedSizeBytes()
|
||||
if s > payloadSizeTarget {
|
||||
t.Fatalf("unpadded payload size %d exceeds target %d", s, payloadSizeTarget)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTrip(t *testing.T) {
|
||||
b, err := payload.NewBytes(payload.Options{
|
||||
Size: 1024,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("generating payload %s", err)
|
||||
}
|
||||
p, err := payload.FromBytes(b)
|
||||
if err != nil {
|
||||
t.Fatalf("reading payload %s", err)
|
||||
}
|
||||
if p.Size != 1024 {
|
||||
t.Fatalf("payload size value %d does not match expected %d", p.Size, 1024)
|
||||
}
|
||||
}
|
||||
|
||||
36
test/loadtime/report/report.go
Normal file
36
test/loadtime/report/report.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/test/loadtime/payload"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
type blockStore interface {
|
||||
Height() int64
|
||||
Base() int64
|
||||
LoadBlock(int64) *types.Block
|
||||
}
|
||||
|
||||
type Report struct {
|
||||
Max, Min, Avg time.Duration
|
||||
StdDev int64
|
||||
All []time.Duration
|
||||
}
|
||||
|
||||
func GenerateFromBlockStore(s blockStore) (Report, error) {
|
||||
r := Report{}
|
||||
for i := s.Base(); i < s.Height(); i++ {
|
||||
b := s.LoadBlock(i)
|
||||
for _, tx := range b.Data.Txs {
|
||||
p, err := payload.FromBytes(tx)
|
||||
if err != nil {
|
||||
return Report{}, err
|
||||
}
|
||||
t := p.Time.AsTime().Sub(b.Time)
|
||||
r.All = append(r.All, t)
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
61
test/loadtime/report/report_test.go
Normal file
61
test/loadtime/report/report_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package report_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/test/loadtime/payload"
|
||||
"github.com/tendermint/tendermint/test/loadtime/report"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
type mockBlockStore struct {
|
||||
base int64
|
||||
blocks []*types.Block
|
||||
}
|
||||
|
||||
func (m *mockBlockStore) Height() int64 {
|
||||
return m.base + int64(len(m.blocks))
|
||||
}
|
||||
|
||||
func (m *mockBlockStore) Base() int64 {
|
||||
return m.base
|
||||
}
|
||||
|
||||
func (m *mockBlockStore) LoadBlock(i int64) *types.Block {
|
||||
return m.blocks[i-m.base]
|
||||
}
|
||||
|
||||
func TestGenerateReport(t *testing.T) {
|
||||
b1, err := payload.NewBytes(payload.Options{
|
||||
Size: 1024,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("generating payload %s", err)
|
||||
}
|
||||
b2, err := payload.NewBytes(payload.Options{
|
||||
Size: 1024,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("generating payload %s", err)
|
||||
}
|
||||
s := &mockBlockStore{
|
||||
blocks: []*types.Block{
|
||||
{
|
||||
Header: types.Header{
|
||||
Time: time.Now(),
|
||||
},
|
||||
Data: types.Data{
|
||||
Txs: []types.Tx{b1, b2},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
r, err := report.GenerateFromBlockStore(s)
|
||||
if err != nil {
|
||||
t.Fatalf("generating report %s", err)
|
||||
}
|
||||
if len(r.All) != 2 {
|
||||
t.Fatalf("report contained different number of data points from expected. Expected %d but contained %d", 1, len(r.All))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user