Skip to content

Commit 88e1c73

Browse files
Add config based settings (#4)
1 parent 95d2b55 commit 88e1c73

4 files changed

Lines changed: 206 additions & 40 deletions

File tree

README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ Edit `my-config.json`:
2828
"accounts": {
2929
"count": 100,
3030
"newAccountRate": 0.1
31+
},
32+
"settings": {
33+
"workers": 5,
34+
"tps": 100,
35+
"statsInterval": "10s",
36+
"bufferSize": 1000,
37+
"trackUserLatency": true
3138
}
3239
}
3340
```
@@ -89,7 +96,8 @@ Edit `my-config.json`:
8996
"endpoints": ["http://localhost:8545"],
9097
"chainId": 1329,
9198
"scenarios": [...],
92-
"accounts": {...}
99+
"accounts": {...},
100+
"settings": {...}
93101
}
94102
```
95103

@@ -110,6 +118,31 @@ Edit `my-config.json`:
110118
}
111119
```
112120

121+
### Settings
122+
```json
123+
"settings": {
124+
"workers": 5,
125+
"tps": 100,
126+
"statsInterval": "10s",
127+
"bufferSize": 1000,
128+
"trackUserLatency": true
129+
}
130+
```
131+
132+
**Settings Precedence**: CLI flags > Config file settings > Default values
133+
134+
Available settings:
135+
- `workers`: Number of workers per endpoint
136+
- `tps`: Transactions per second (0 = unlimited)
137+
- `statsInterval`: Stats logging interval (e.g., "10s", "5m")
138+
- `bufferSize`: Buffer size per worker
139+
- `dryRun`: Simulate without sending transactions
140+
- `debug`: Enable debug logging
141+
- `trackReceipts`: Track transaction receipts
142+
- `trackBlocks`: Track block statistics
143+
- `trackUserLatency`: Track user latency metrics
144+
- `prewarm`: Prewarm accounts before test
145+
113146
## Available Scenarios
114147

115148
- **EVMTransfer**: Simple ETH transfers

config/config.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
package config
22

3-
import "math/big"
3+
import (
4+
"math/big"
5+
"time"
6+
)
47

58
// LoadConfig stores the configuration for load-related settings.
69
type LoadConfig struct {
7-
ChainID int64 `json:"chain_id,omitempty"`
10+
ChainID int64 `json:"chainId,omitempty"`
811
Endpoints []string `json:"endpoints"`
912
Accounts *AccountConfig `json:"accounts,omitempty"`
1013
Scenarios []Scenario `json:"scenarios,omitempty"`
11-
MockDeploy bool `json:"mock_deploy,omitempty"`
14+
MockDeploy bool `json:"mockDeploy,omitempty"`
15+
Settings *Settings `json:"settings,omitempty"`
16+
}
17+
18+
// Settings stores CLI-configurable settings that can be specified in config file
19+
type Settings struct {
20+
Workers *int `json:"workers,omitempty"`
21+
TPS *float64 `json:"tps,omitempty"`
22+
StatsInterval *time.Duration `json:"statsInterval,omitempty"`
23+
BufferSize *int `json:"bufferSize,omitempty"`
24+
DryRun *bool `json:"dryRun,omitempty"`
25+
Debug *bool `json:"debug,omitempty"`
26+
TrackReceipts *bool `json:"trackReceipts,omitempty"`
27+
TrackBlocks *bool `json:"trackBlocks,omitempty"`
28+
TrackUserLatency *bool `json:"trackUserLatency,omitempty"`
29+
Prewarm *bool `json:"prewarm,omitempty"`
1230
}
1331

1432
// GetChainID returns the chain ID as a big.Int.
@@ -18,8 +36,8 @@ func (c *LoadConfig) GetChainID() *big.Int {
1836

1937
// AccountConfig stores the configuration for account generation.
2038
type AccountConfig struct {
21-
NewAccountRate float64 `json:"new_account_rate,omitempty"`
22-
Accounts int `json:"accounts,omitempty"`
39+
NewAccountRate float64 `json:"newAccountRate,omitempty"`
40+
Accounts int `json:"count,omitempty"`
2341
}
2442

2543
// Scenario represents each scenario in the load configuration.

main.go

Lines changed: 133 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,105 @@ var (
3535
trackUserLatency bool
3636
)
3737

38+
// ResolvedSettings holds the final resolved settings after applying precedence
39+
type ResolvedSettings struct {
40+
Workers int
41+
TPS float64
42+
StatsInterval time.Duration
43+
BufferSize int
44+
DryRun bool
45+
Debug bool
46+
TrackReceipts bool
47+
TrackBlocks bool
48+
TrackUserLatency bool
49+
Prewarm bool
50+
}
51+
52+
// resolveSettings applies precedence: CLI > Config > Default
53+
func resolveSettings(cfg *config.LoadConfig, cmd *cobra.Command) ResolvedSettings {
54+
settings := ResolvedSettings{
55+
// Default values
56+
Workers: 1,
57+
TPS: 0,
58+
StatsInterval: 10 * time.Second,
59+
BufferSize: 1000,
60+
DryRun: false,
61+
Debug: false,
62+
TrackReceipts: false,
63+
TrackBlocks: false,
64+
TrackUserLatency: false,
65+
Prewarm: false,
66+
}
67+
68+
// Apply config values if present
69+
if cfg.Settings != nil {
70+
if cfg.Settings.Workers != nil {
71+
settings.Workers = *cfg.Settings.Workers
72+
}
73+
if cfg.Settings.TPS != nil {
74+
settings.TPS = *cfg.Settings.TPS
75+
}
76+
if cfg.Settings.StatsInterval != nil {
77+
settings.StatsInterval = *cfg.Settings.StatsInterval
78+
}
79+
if cfg.Settings.BufferSize != nil {
80+
settings.BufferSize = *cfg.Settings.BufferSize
81+
}
82+
if cfg.Settings.DryRun != nil {
83+
settings.DryRun = *cfg.Settings.DryRun
84+
}
85+
if cfg.Settings.Debug != nil {
86+
settings.Debug = *cfg.Settings.Debug
87+
}
88+
if cfg.Settings.TrackReceipts != nil {
89+
settings.TrackReceipts = *cfg.Settings.TrackReceipts
90+
}
91+
if cfg.Settings.TrackBlocks != nil {
92+
settings.TrackBlocks = *cfg.Settings.TrackBlocks
93+
}
94+
if cfg.Settings.TrackUserLatency != nil {
95+
settings.TrackUserLatency = *cfg.Settings.TrackUserLatency
96+
}
97+
if cfg.Settings.Prewarm != nil {
98+
settings.Prewarm = *cfg.Settings.Prewarm
99+
}
100+
}
101+
102+
// Apply CLI values if explicitly set (CLI wins over config)
103+
if cmd.Flags().Changed("workers") {
104+
settings.Workers = workers
105+
}
106+
if cmd.Flags().Changed("tps") {
107+
settings.TPS = tps
108+
}
109+
if cmd.Flags().Changed("stats-interval") {
110+
settings.StatsInterval = statsInterval
111+
}
112+
if cmd.Flags().Changed("buffer-size") {
113+
settings.BufferSize = bufferSize
114+
}
115+
if cmd.Flags().Changed("dry-run") {
116+
settings.DryRun = dryRun
117+
}
118+
if cmd.Flags().Changed("debug") {
119+
settings.Debug = debug
120+
}
121+
if cmd.Flags().Changed("track-receipts") {
122+
settings.TrackReceipts = trackReceipts
123+
}
124+
if cmd.Flags().Changed("track-blocks") {
125+
settings.TrackBlocks = trackBlocks
126+
}
127+
if cmd.Flags().Changed("track-user-latency") {
128+
settings.TrackUserLatency = trackUserLatency
129+
}
130+
if cmd.Flags().Changed("prewarm") {
131+
settings.Prewarm = prewarm
132+
}
133+
134+
return settings
135+
}
136+
38137
var rootCmd = &cobra.Command{
39138
Use: "seiload",
40139
Short: "Sei Chain Load Test v2",
@@ -88,42 +187,45 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
88187
return fmt.Errorf("failed to load config: %w", err)
89188
}
90189

190+
// Resolve settings with precedence: CLI > Config > Default
191+
settings := resolveSettings(cfg, cmd)
192+
91193
log.Printf("🚀 Starting Sei Chain Load Test v2")
92194
log.Printf("📁 Config file: %s", configFile)
93195
log.Printf("🎯 Endpoints: %d", len(cfg.Endpoints))
94-
log.Printf("👥 Workers per endpoint: %d", workers)
95-
log.Printf("🔧 Total workers: %d", len(cfg.Endpoints)*workers)
196+
log.Printf("👥 Workers per endpoint: %d", settings.Workers)
197+
log.Printf("🔧 Total workers: %d", len(cfg.Endpoints)*settings.Workers)
96198
log.Printf("📊 Scenarios: %d", len(cfg.Scenarios))
97-
log.Printf("⏱️ Stats interval: %v", statsInterval)
98-
log.Printf("📦 Buffer size per worker: %d", bufferSize)
99-
if tps > 0 {
100-
log.Printf("📈 Transactions per second: %.2f", tps)
199+
log.Printf("⏱️ Stats interval: %v", settings.StatsInterval)
200+
log.Printf("📦 Buffer size per worker: %d", settings.BufferSize)
201+
if settings.TPS > 0 {
202+
log.Printf("📈 Transactions per second: %.2f", settings.TPS)
101203
}
102-
if dryRun {
204+
if settings.DryRun {
103205
log.Printf("📝 Dry run: enabled")
104206
}
105-
if trackReceipts {
207+
if settings.TrackReceipts {
106208
log.Printf("📝 Track receipts: enabled")
107209
}
108-
if trackBlocks {
210+
if settings.TrackBlocks {
109211
log.Printf("📝 Track blocks: enabled")
110212
}
111-
if prewarm {
213+
if settings.Prewarm {
112214
log.Printf("📝 Prewarm: enabled")
113215
}
114-
if trackUserLatency {
216+
if settings.TrackUserLatency {
115217
log.Printf("📝 Track user latency: enabled")
116218
}
117219
log.Println()
118220

119221
// Enable mock deployment in dry-run mode
120-
if dryRun {
222+
if settings.DryRun {
121223
cfg.MockDeploy = true
122224
}
123225

124226
// Create statistics collector and logger
125227
collector := stats.NewCollector()
126-
logger := stats.NewLogger(collector, statsInterval, debug)
228+
logger := stats.NewLogger(collector, settings.StatsInterval, settings.Debug)
127229

128230
err = service.Run(ctx, func(ctx context.Context, s service.Scope) error {
129231
// Create the generator from the config struct
@@ -133,14 +235,14 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
133235
}
134236

135237
// Create the sender from the config struct
136-
snd, err := sender.NewShardedSender(cfg, bufferSize, workers)
238+
snd, err := sender.NewShardedSender(cfg, settings.BufferSize, settings.Workers)
137239
if err != nil {
138240
return fmt.Errorf("failed to create sender: %w", err)
139241
}
140242

141243
// Create and start block collector if endpoints are available
142244
var blockCollector *stats.BlockCollector
143-
if len(cfg.Endpoints) > 0 && trackBlocks {
245+
if len(cfg.Endpoints) > 0 && settings.TrackBlocks {
144246
blockCollector = stats.NewBlockCollector()
145247
collector.SetBlockCollector(blockCollector)
146248
s.SpawnBgNamed("block collector", func() error {
@@ -149,24 +251,24 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
149251
}
150252

151253
// Create and start user latency tracker if endpoints are available
152-
if len(cfg.Endpoints) > 0 && trackUserLatency {
153-
userLatencyTracker := stats.NewUserLatencyTracker(statsInterval)
254+
if len(cfg.Endpoints) > 0 && settings.TrackUserLatency {
255+
userLatencyTracker := stats.NewUserLatencyTracker(settings.StatsInterval)
154256
s.SpawnBgNamed("user latency tracker", func() error {
155257
return userLatencyTracker.Run(ctx, cfg.Endpoints[0])
156258
})
157259
}
158260

159261
// Enable dry-run mode in sender if specified
160-
if dryRun {
262+
if settings.DryRun {
161263
snd.SetDryRun(true)
162264
}
163-
if debug {
265+
if settings.Debug {
164266
snd.SetDebug(true)
165267
}
166-
if trackReceipts {
268+
if settings.TrackReceipts {
167269
snd.SetTrackReceipts(true)
168270
}
169-
if trackBlocks {
271+
if settings.TrackBlocks {
170272
snd.SetTrackBlocks(true)
171273
}
172274

@@ -175,17 +277,17 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
175277

176278
// Create dispatcher
177279
dispatcher := sender.NewDispatcher(gen, snd)
178-
if tps > 0 {
280+
if settings.TPS > 0 {
179281
// Convert TPS to interval: 1/tps seconds = (1/tps) * 1e9 nanoseconds
180-
intervalNs := int64((1.0 / tps) * 1e9)
282+
intervalNs := int64((1.0 / settings.TPS) * 1e9)
181283
dispatcher.SetRateLimit(time.Duration(intervalNs))
182284
}
183285

184286
// Set statistics collector for dispatcher
185287
dispatcher.SetStatsCollector(collector)
186288

187289
// Set up prewarming if enabled
188-
if prewarm {
290+
if settings.Prewarm {
189291
log.Printf("🔥 Creating prewarm generator...")
190292
prewarmGen := generator.NewPrewarmGenerator(cfg, gen)
191293
dispatcher.SetPrewarmGenerator(prewarmGen)
@@ -198,7 +300,7 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
198300
log.Printf("✅ Connected to %d endpoints", snd.GetNumShards())
199301

200302
// Perform prewarming if enabled (before starting logger to avoid logging prewarm transactions)
201-
if prewarm {
303+
if settings.Prewarm {
202304
if err := dispatcher.Prewarm(ctx); err != nil {
203305
return fmt.Errorf("failed to prewarm accounts: %w", err)
204306
}
@@ -216,20 +318,20 @@ func runLoadTest(ctx context.Context, cmd *cobra.Command, args []string) error {
216318
sigChan := make(chan os.Signal, 1)
217319
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
218320

219-
log.Printf("📈 Logging statistics every %v (Press Ctrl+C to stop)", statsInterval)
220-
if dryRun {
321+
log.Printf("📈 Logging statistics every %v (Press Ctrl+C to stop)", settings.StatsInterval)
322+
if settings.DryRun {
221323
log.Printf("📝 Dry-run mode: Simulating requests without sending")
222324
}
223-
if debug {
325+
if settings.Debug {
224326
log.Printf("🐛 Debug mode: Each transaction will be logged")
225327
}
226-
if trackReceipts {
328+
if settings.TrackReceipts {
227329
log.Printf("📝 Track receipts mode: Receipts will be tracked")
228330
}
229-
if trackBlocks {
331+
if settings.TrackBlocks {
230332
log.Printf("📝 Track blocks mode: Block data will be collected")
231333
}
232-
if trackUserLatency {
334+
if settings.TrackUserLatency {
233335
log.Printf("📝 Track user latency mode: User latency will be tracked")
234336
}
235337
log.Print(strings.Repeat("=", 60))

0 commit comments

Comments
 (0)