Skip to content

Commit 803aa58

Browse files
authored
Merge pull request ton-connect#183 from ton-connect/dev/load-test-rework
Fix load testing script to provide a more realistic distribution of listeners and senders.
2 parents 83dc491 + 25be8c4 commit 803aa58

2 files changed

Lines changed: 285 additions & 38 deletions

File tree

benchmark/README.md

Lines changed: 225 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,241 @@ This directory contains performance testing tools for the Ton Connect Bridge Ser
1010
xk6 build --with github.com/phymbert/xk6-sse@latest
1111
```
1212

13-
2. **Start the bridge server:**
13+
2. **Start the bridge server and storage:**
1414
```bash
1515
make build
16-
POSTGRES_URI="postgres://bridge_user:bridge_password@localhost:5432/bridge?sslmode=disable" \
16+
docker-compose -f docker/docker-compose.cluster-valkey.yml up -d
17+
STORAGE=valkey VALKEY_URI=redis://127.0.0.1:6379 \
1718
PORT=8081 CORS_ENABLE=true RPS_LIMIT=100 CONNECTIONS_LIMIT=8000 \
1819
LOG_LEVEL=error ./bridge
1920
```
2021

21-
3. **Run benchmark:**
22+
> **Tip: Bypass Bridge Rate Limits in Local/Test**
23+
24+
If your bridge server is configured with request rate limiting (e.g. `RPS_LIMIT`), you can bypass these limits for benchmark and development purposes by setting the environment variable `RATE_LIMITS_BY_PASS_TOKEN` to a known token, such as `test-token`.
25+
The benchmark script will use this token via its `Authorization: Bearer` header, so you should make sure your bridge instantiation command includes this variable:
26+
27+
```bash
28+
RATE_LIMITS_BY_PASS_TOKEN=test-token \
29+
STORAGE=valkey VALKEY_URI=redis://127.0.0.1:6379 \
30+
PORT=8081 CORS_ENABLE=true RPS_LIMIT=100 CONNECTIONS_LIMIT=8000 \
31+
LOG_LEVEL=error ./bridge
32+
```
33+
34+
This instructs the server to exempt any request with `Authorization: Bearer test-token` from rate limits. **Do not use this configuration in production environments.**
35+
By default, the `bridge_test.js` load script will send this token (unless overridden via the `AUTH_TOKEN` environment variable).
36+
37+
3. **Flush Redis data (important for clean test results):**
38+
39+
Before running benchmarks, flush Redis data locally to avoid collisions with previous test data:
40+
```bash
41+
redis-cli FLUSHALL
42+
```
43+
44+
Or if using a specific Redis database:
45+
```bash
46+
redis-cli -n <database_number> FLUSHDB
47+
```
48+
49+
**Note**: This ensures that test results are not affected by stale data from previous test runs, providing accurate and reproducible benchmark results.
50+
51+
4. **Run benchmark:**
2252
```bash
2353
cd benchmark
2454
./k6 run bridge_test.js
2555
```
2656

57+
## 📋 Test Overview
58+
59+
The `bridge_test.js` script performs end-to-end load testing of the Ton Connect Bridge Server by simulating:
60+
- **SSE Listeners**: Virtual users that maintain persistent SSE connections to receive messages
61+
- **Message Senders**: Virtual users that send messages through the bridge
62+
63+
The test measures message delivery latency, error rates, and system stability under load.
64+
65+
## 🏗️ Test Architecture
66+
67+
The test runs two parallel scenarios:
68+
69+
### 1. SSE Workers Scenario (`sse`)
70+
- Maintains persistent SSE connections to `/events` endpoint
71+
- Listens for incoming messages and measures delivery latency
72+
- Supports multiple client IDs per VU (configurable via `LISTENER_WRITERS_RATIO`)
73+
- Automatically reconnects on connection failures
74+
75+
### 2. Message Senders Scenario (`senders`)
76+
- Sends POST requests to `/message` endpoint
77+
- Uses ramping arrival rate executor to control message sending rate
78+
- Generates random client IDs within the configured ID space
79+
- Sends messages with timestamps for latency measurement
80+
81+
## ⚙️ Configuration Options
82+
83+
All configuration is done via environment variables:
84+
85+
- `AUTH_TOKEN` - Bearer token used to authenticate requests to the bridge server and bypass any rate limiting. For local development or testing, you can use a fixed string such as `test-token`; for production-like testing, set this to a valid token if your bridge enforces authentication.
86+
87+
### Load Configuration
88+
- `SSE_VUS` (default: `100`) - Number of SSE listener virtual users
89+
- `SEND_RATE` (default: `1000`) - Target messages per second to send
90+
- `LISTENER_WRITERS_RATIO` (default: `3`) - Number of listeners per writer (affects ID space calculation)
91+
- `TOTAL_INSTANCES` (default: `1`) - Total number of test instances (for distributed testing)
92+
- `CURRENT_INSTANCE` (default: `0`) - Current instance index (0-based, for distributed testing)
93+
94+
### Timing Configuration
95+
All duration values support `s` (seconds), `m` (minutes), or `h` (hours) suffixes.
96+
97+
**SSE Scenario:**
98+
- `SSE_RAMP_UP` (default: `10s`) - Time to ramp up SSE connections
99+
- `SSE_HOLD` (default: `50s`) - Duration to maintain steady SSE connections
100+
- `SSE_RAMP_DOWN` (default: `10s`) - Time to ramp down SSE connections
101+
- `SSE_DELAY` (default: `0s`) - Delay before starting SSE scenario
102+
103+
**Sender Scenario:**
104+
- `SENDER_RAMP_UP` (default: `10s`) - Time to ramp up message sending rate
105+
- `SENDER_HOLD` (default: `30s`) - Duration to maintain steady sending rate
106+
- `SENDER_RAMP_DOWN` (default: `10s`) - Time to ramp down message sending rate
107+
- `SENDER_DELAY` (default: `10s`) - Delay before starting sender scenario (typically set to allow SSE connections to establish)
108+
109+
### Server Configuration
110+
- `BRIDGE_URL` (default: `http://localhost:8081/bridge`) - Base URL of the bridge server
111+
112+
## 📊 Metrics Collected
113+
114+
The test tracks the following custom metrics:
115+
116+
- **`sse_message_received`** - Counter of messages received via SSE
117+
- **`sse_message_sent`** - Counter of messages successfully sent
118+
- **`sse_errors`** - Counter of SSE connection/communication errors
119+
- **`post_errors`** - Counter of failed POST requests
120+
- **`delivery_latency`** - Trend metric tracking end-to-end message delivery latency (milliseconds)
121+
- **`json_parse_errors`** - Counter of JSON parsing errors
122+
- **`missing_timestamps`** - Counter of messages received without timestamps
123+
124+
Standard k6 metrics are also collected (HTTP request duration, status codes, etc.).
125+
126+
## 🎯 Test Thresholds
127+
128+
The test includes the following pass/fail thresholds:
129+
130+
- `http_req_failed < 0.01%` - HTTP request failure rate must be below 0.01%
131+
- `delivery_latency p(95) < 2000ms` - 95th percentile latency must be below 2 seconds
132+
- `sse_errors < 10` - Total SSE errors must be below 10
133+
- `json_parse_errors < 5` - Total JSON parse errors must be below 5
134+
- `missing_timestamps < 100` - Messages without timestamps must be below 100
135+
- `sse_message_sent > 5` - At least 5 messages must be sent
136+
- `sse_message_received > 5` - At least 5 messages must be received
137+
138+
## 🚀 Usage Examples
139+
140+
### Basic Run
141+
```bash
142+
./k6 run bridge_test.js
143+
```
144+
145+
### Custom Load Configuration
146+
```bash
147+
SSE_VUS=2000 SEND_RATE=20 ./k6 run bridge_test.js
148+
```
149+
150+
### Extended Test Duration
151+
```bash
152+
SSE_RAMP_UP=30s SSE_HOLD=5m SSE_RAMP_DOWN=30s \
153+
SENDER_RAMP_UP=30s SENDER_HOLD=5m SENDER_RAMP_DOWN=30s \
154+
SENDER_DELAY=30s ./k6 run bridge_test.js
155+
```
156+
157+
### High Load Test
158+
```bash
159+
SSE_VUS=5000 SEND_RATE=50 \
160+
SSE_RAMP_UP=10m SSE_HOLD=30m SSE_RAMP_DOWN=10m \
161+
SENDER_RAMP_UP=10m SENDER_HOLD=30m SENDER_RAMP_DOWN=10m \
162+
./k6 run bridge_test.js
163+
```
164+
165+
### Distributed Testing (Multiple Instances)
166+
On instance 0:
167+
```bash
168+
TOTAL_INSTANCES=3 CURRENT_INSTANCE=0 ./k6 run bridge_test.js
169+
```
170+
171+
On instance 1:
172+
```bash
173+
TOTAL_INSTANCES=3 CURRENT_INSTANCE=1 ./k6 run bridge_test.js
174+
```
175+
176+
On instance 2:
177+
```bash
178+
TOTAL_INSTANCES=3 CURRENT_INSTANCE=2 ./k6 run bridge_test.js
179+
```
180+
181+
### Custom Bridge URL
182+
```bash
183+
BRIDGE_URL=http://bridge.example.com:8081/bridge ./k6 run bridge_test.js
184+
```
185+
186+
## 📈 Understanding Results
187+
188+
### Preparation for Accurate Results
189+
190+
**Important**: Always flush Redis data before running tests to ensure clean results:
191+
```bash
192+
redis-cli FLUSHALL
193+
```
194+
195+
Local valkey cluster cleanup
196+
```bash
197+
docker exec -i docker-valkey-shard1-1 redis-cli -p 6379 FLUSHALL
198+
docker exec -i docker-valkey-shard2-1 redis-cli -p 6380 FLUSHALL
199+
docker exec -i docker-valkey-shard3-1 redis-cli -p 6381 FLUSHALL
200+
```
201+
202+
This prevents:
203+
- Stale messages from previous test runs affecting metrics
204+
- Client ID collisions with previous test data
205+
- Inaccurate latency measurements due to cached data
206+
- Message delivery inconsistencies
207+
208+
### Key Metrics to Monitor
209+
210+
1. **Delivery Latency (`delivery_latency`)**
211+
- Measures time from message creation to delivery via SSE
212+
- Lower is better; p(95) should be < 2000ms
213+
214+
2. **Message Throughput**
215+
- Compare `sse_message_sent` vs `sse_message_received`
216+
- Should be approximately equal (accounting for timing differences)
217+
218+
3. **Error Rates**
219+
- Monitor `sse_errors` and `post_errors`
220+
- Should remain low (< 1% failure rate)
221+
222+
4. **Connection Stability**
223+
- Low `sse_errors` indicates stable SSE connections
224+
- High error count suggests connection issues or server overload
225+
226+
### Common Issues
227+
228+
- **High latency**: Server may be overloaded, reduce `SEND_RATE` or increase server resources
229+
- **SSE connection errors**: Check `CONNECTIONS_LIMIT` on bridge server, may need to increase
230+
- **Message delivery failures**: Verify `sse_message_sent` vs `sse_message_received` ratio
231+
- **JSON parse errors**: May indicate malformed messages or encoding issues
232+
233+
## 🔧 ID Space Management
234+
235+
The test generates client IDs within a calculated ID space to ensure:
236+
- Listeners exist for messages being sent
237+
- No collisions between test instances in distributed setups
238+
- Predictable ID distribution
239+
240+
The ID space is calculated as:
241+
```
242+
ID_SPACE_SIZE = TOTAL_INSTANCES × LISTENER_WRITERS_RATIO × SSE_VUS - 1
243+
```
244+
245+
Each instance uses a different offset:
246+
```
247+
START_INDEX_OFFSET = CURRENT_INSTANCE × LISTENER_WRITERS_RATIO × SSE_VUS
248+
```
27249

28250
Happy benchmarking! 🚀

0 commit comments

Comments
 (0)