-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathclient_kvstore.go
120 lines (108 loc) · 3.76 KB
/
client_kvstore.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package loadtest
import (
"fmt"
)
// The CometBFT common.RandStr method can effectively generate human-readable
// (alphanumeric) strings from a set of 62 characters. We aim here with the
// KVStore client to generate unique client IDs as well as totally unique keys
// for all transactions. Values are not so important.
const (
KVStoreClientIDLen int = 5 // Allows for 6,471,002 random client IDs (62C5)
kvstoreMinValueLen int = 1 // We at least need 1 character in a key/value pair's value.
)
// This is a map of nCr where n=62 and r varies from 0 through 15. It gives the
// maximum number of unique transaction IDs that can be accommodated with a
// given key suffix length.
var kvstoreMaxTxsByKeySuffixLen = []uint64{
0, // 0
62, // 1
1891, // 2
37820, // 3
557845, // 4
6471002, // 5
61474519, // 6
491796152, // 7
3381098545, // 8
20286591270, // 9
107518933731, // 10
508271323092, // 11
2160153123141, // 12
8308281242850, // 13
29078984349975, // 14
93052749919920, // 15
}
// KVStoreClientFactory creates load testing clients to interact with the
// built-in CometBFT kvstore ABCI application.
type KVStoreClientFactory struct{}
// KVStoreClient generates arbitrary transactions (random key=value pairs) to
// be sent to the kvstore ABCI application. The keys are structured as follows:
//
// `[client_id][tx_id]=[tx_id]`
//
// where each value (`client_id` and `tx_id`) is padded with 0s to meet the
// transaction size requirement.
type KVStoreClient struct {
keyPrefix []byte // Contains the client ID
keySuffixLen int
valueLen int
}
var (
_ ClientFactory = (*KVStoreClientFactory)(nil)
_ Client = (*KVStoreClient)(nil)
)
func init() {
if err := RegisterClientFactory("kvstore", NewKVStoreClientFactory()); err != nil {
panic(err)
}
}
func NewKVStoreClientFactory() *KVStoreClientFactory {
return &KVStoreClientFactory{}
}
func (f *KVStoreClientFactory) ValidateConfig(cfg Config) error {
maxTxsPerEndpoint := cfg.MaxTxsPerEndpoint()
if maxTxsPerEndpoint < 1 {
return fmt.Errorf("cannot calculate an appropriate maximum number of transactions per endpoint (got %d)", maxTxsPerEndpoint)
}
minKeySuffixLen, err := requiredKVStoreSuffixLen(maxTxsPerEndpoint)
if err != nil {
return err
}
// "[client_id][random_suffix]=[value]"
minTxSize := KVStoreClientIDLen + minKeySuffixLen + 1 + kvstoreMinValueLen
if cfg.Size < minTxSize {
return fmt.Errorf("transaction size %d is too small for given parameters (should be at least %d bytes)", cfg.Size, minTxSize)
}
return nil
}
func (f *KVStoreClientFactory) NewClient(cfg Config) (Client, error) {
keyPrefix := []byte(randStr(KVStoreClientIDLen))
keySuffixLen, err := requiredKVStoreSuffixLen(cfg.MaxTxsPerEndpoint())
if err != nil {
return nil, err
}
keyLen := len(keyPrefix) + keySuffixLen
// value length = key length - 1 (to cater for "=" symbol)
valueLen := cfg.Size - keyLen - 1
return &KVStoreClient{
keyPrefix: keyPrefix,
keySuffixLen: keySuffixLen,
valueLen: valueLen,
}, nil
}
func requiredKVStoreSuffixLen(maxTxCount uint64) (int, error) {
for l, maxTxs := range kvstoreMaxTxsByKeySuffixLen {
if maxTxCount < maxTxs {
if l+1 > len(kvstoreMaxTxsByKeySuffixLen) {
return -1, fmt.Errorf("cannot cater for maximum tx count of %d (too many unique transactions, suffix length %d)", maxTxCount, l+1)
}
// we use l+1 to minimize collision probability
return l + 1, nil
}
}
return -1, fmt.Errorf("cannot cater for maximum tx count of %d (too many unique transactions)", maxTxCount)
}
func (c *KVStoreClient) GenerateTx() ([]byte, error) {
k := append(c.keyPrefix, []byte(randStr(c.keySuffixLen))...)
v := []byte(randStr(c.valueLen))
return append(k, append([]byte("="), v...)...), nil
}