mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 00:36:38 +00:00
238 lines
6.9 KiB
Markdown
238 lines
6.9 KiB
Markdown
# Signal Server Load Test
|
|
|
|
Load testing tool for the NetBird signal server.
|
|
|
|
## Features
|
|
|
|
- **Rate-based peer pair creation**: Spawn peer pairs at configurable rates (e.g., 10, 20 pairs/sec)
|
|
- **Two exchange modes**:
|
|
- **Single message**: Each pair exchanges one message for validation
|
|
- **Continuous exchange**: Pairs continuously exchange messages for a specified duration (e.g., 30 seconds, 10 minutes)
|
|
- **Configurable message interval**: Control message send rate in continuous mode
|
|
- **Message exchange validation**: Validates encrypted body size > 0
|
|
- **Comprehensive metrics**: Tracks throughput, success/failure rates, and latency statistics
|
|
- **Local server testing**: Tests include embedded signal server for easy development
|
|
- **Worker pool pattern**: Efficient concurrent execution
|
|
- **Graceful shutdown**: Context-based cancellation
|
|
|
|
## Usage
|
|
|
|
### Standalone Binary
|
|
|
|
Build and run the load test as a standalone binary:
|
|
|
|
```bash
|
|
# Build the binary
|
|
cd signal/loadtest/cmd/signal-loadtest
|
|
go build -o signal-loadtest
|
|
|
|
# Single message exchange
|
|
./signal-loadtest \
|
|
-server http://localhost:10000 \
|
|
-pairs-per-sec 10 \
|
|
-total-pairs 100 \
|
|
-message-size 100
|
|
|
|
# Continuous exchange for 30 seconds
|
|
./signal-loadtest \
|
|
-server http://localhost:10000 \
|
|
-pairs-per-sec 10 \
|
|
-total-pairs 20 \
|
|
-message-size 200 \
|
|
-exchange-duration 30s \
|
|
-message-interval 200ms
|
|
|
|
# Long-running test (10 minutes)
|
|
./signal-loadtest \
|
|
-server http://localhost:10000 \
|
|
-pairs-per-sec 20 \
|
|
-total-pairs 50 \
|
|
-message-size 500 \
|
|
-exchange-duration 10m \
|
|
-message-interval 100ms \
|
|
-test-duration 15m \
|
|
-log-level debug
|
|
|
|
# Show help
|
|
./signal-loadtest -h
|
|
```
|
|
|
|
**Available Flags:**
|
|
- `-server`: Signal server URL (default: `http://localhost:10000`)
|
|
- `-pairs-per-sec`: Peer pairs created per second (default: 10)
|
|
- `-total-pairs`: Total number of peer pairs (default: 100)
|
|
- `-message-size`: Message size in bytes (default: 100)
|
|
- `-test-duration`: Maximum test duration, 0 = unlimited (default: 0)
|
|
- `-exchange-duration`: Continuous exchange duration per pair, 0 = single message (default: 0)
|
|
- `-message-interval`: Interval between messages in continuous mode (default: 100ms)
|
|
- `-log-level`: Log level: trace, debug, info, warn, error (default: info)
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
# Run all tests (includes load tests)
|
|
go test -v -timeout 2m
|
|
|
|
# Run specific single-message load tests
|
|
go test -v -run TestLoadTest_10PairsPerSecond -timeout 40s
|
|
go test -v -run TestLoadTest_20PairsPerSecond -timeout 40s
|
|
go test -v -run TestLoadTest_SmallBurst -timeout 30s
|
|
|
|
# Run continuous exchange tests
|
|
go test -v -run TestLoadTest_ContinuousExchange_ShortBurst -timeout 30s
|
|
go test -v -run TestLoadTest_ContinuousExchange_30Seconds -timeout 2m
|
|
go test -v -run TestLoadTest_ContinuousExchange_10Minutes -timeout 15m
|
|
|
|
# Skip long-running tests in quick runs
|
|
go test -short
|
|
```
|
|
|
|
### Programmatic Usage
|
|
|
|
#### Single Message Exchange
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"github.com/netbirdio/netbird/signal/loadtest"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
config := loadtest.LoadTestConfig{
|
|
ServerURL: "http://localhost:10000",
|
|
PairsPerSecond: 10,
|
|
TotalPairs: 100,
|
|
MessageSize: 100,
|
|
TestDuration: 30 * time.Second,
|
|
}
|
|
|
|
lt := loadtest.NewLoadTest(config)
|
|
if err := lt.Run(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
metrics := lt.GetMetrics()
|
|
metrics.PrintReport()
|
|
}
|
|
```
|
|
|
|
#### Continuous Message Exchange
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"github.com/netbirdio/netbird/signal/loadtest"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
config := loadtest.LoadTestConfig{
|
|
ServerURL: "http://localhost:10000",
|
|
PairsPerSecond: 10,
|
|
TotalPairs: 20,
|
|
MessageSize: 200,
|
|
ExchangeDuration: 10 * time.Minute, // Each pair exchanges messages for 10 minutes
|
|
MessageInterval: 200 * time.Millisecond, // Send message every 200ms
|
|
TestDuration: 15 * time.Minute, // Overall test timeout
|
|
}
|
|
|
|
lt := loadtest.NewLoadTest(config)
|
|
if err := lt.Run(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
metrics := lt.GetMetrics()
|
|
metrics.PrintReport()
|
|
}
|
|
```
|
|
|
|
## Configuration Options
|
|
|
|
- **ServerURL**: Signal server URL (e.g., `http://localhost:10000` or `https://signal.example.com:443`)
|
|
- **PairsPerSecond**: Rate at which peer pairs are created (e.g., 10, 20)
|
|
- **TotalPairs**: Total number of peer pairs to create
|
|
- **MessageSize**: Size of test message payload in bytes
|
|
- **TestDuration**: Maximum test duration (optional, 0 = no limit)
|
|
- **ExchangeDuration**: Duration for continuous message exchange per pair (0 = single message)
|
|
- **MessageInterval**: Interval between messages in continuous mode (default: 100ms)
|
|
- **RampUpDuration**: Gradual ramp-up period (not yet implemented)
|
|
|
|
## Metrics
|
|
|
|
The load test collects and reports:
|
|
|
|
- **Total Pairs Sent**: Number of peer pairs attempted
|
|
- **Successful Exchanges**: Completed message exchanges
|
|
- **Failed Exchanges**: Failed message exchanges
|
|
- **Total Messages Exchanged**: Count of successfully exchanged messages
|
|
- **Total Errors**: Cumulative error count
|
|
- **Throughput**: Pairs per second (actual)
|
|
- **Latency Statistics**: Min, Max, Avg message exchange latency
|
|
|
|
## Test Results
|
|
|
|
Example output from a 20 pairs/sec test:
|
|
|
|
```
|
|
=== Load Test Report ===
|
|
Test Duration: 5.055249917s
|
|
Total Pairs Sent: 100
|
|
Successful Exchanges: 100
|
|
Failed Exchanges: 0
|
|
Total Messages Exchanged: 100
|
|
Total Errors: 0
|
|
Throughput: 19.78 pairs/sec
|
|
|
|
Latency Statistics:
|
|
Min: 170.375µs
|
|
Max: 5.176916ms
|
|
Avg: 441.566µs
|
|
========================
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Client (`client.go`)
|
|
- Manages gRPC connection to signal server
|
|
- Establishes bidirectional stream for receiving messages
|
|
- Sends messages via `Send` RPC method
|
|
- Handles message reception asynchronously
|
|
|
|
### Load Test Engine (`rate_loadtest.go`)
|
|
- Worker pool pattern for concurrent peer pairs
|
|
- Rate-limited pair creation using ticker
|
|
- Atomic counters for thread-safe metrics collection
|
|
- Graceful shutdown on context cancellation
|
|
|
|
### Test Suite
|
|
- `loadtest_test.go`: Single pair validation test
|
|
- `rate_loadtest_test.go`: Multiple rate-based load tests and benchmarks
|
|
|
|
## Implementation Details
|
|
|
|
### Message Flow
|
|
1. Create sender and receiver clients with unique IDs
|
|
2. Both clients connect to signal server via bidirectional stream
|
|
3. Sender sends encrypted message using `Send` RPC
|
|
4. Signal server forwards message to receiver's stream
|
|
5. Receiver reads message from stream
|
|
6. Validate encrypted body size > 0
|
|
7. Record latency and success metrics
|
|
|
|
### Concurrency
|
|
- Worker pool size = `PairsPerSecond`
|
|
- Each worker handles multiple peer pairs sequentially
|
|
- Atomic operations for metrics to avoid lock contention
|
|
- Channel-based work distribution
|
|
|
|
## Future Enhancements
|
|
|
|
- [ ] TLS/HTTPS support for production servers
|
|
- [ ] Ramp-up period implementation
|
|
- [ ] Percentile latency metrics (p50, p95, p99)
|
|
- [ ] Connection reuse for multiple messages per pair
|
|
- [ ] Support for custom message payloads
|
|
- [ ] CSV/JSON metrics export
|
|
- [ ] Real-time metrics dashboard
|