fix: account for queued loadgen rate starts
This commit is contained in:
parent
5ffd87f56b
commit
d9d3a3d6c0
@ -263,7 +263,8 @@ go run ./cmd/proxy-loadgen `
|
||||
连接握手延迟分位上界、吞吐和 Go 运行时内存/GC 快照。它不会输出提取响应中的地址或
|
||||
凭据,也不构成 100,000 QPS 证明。限速场景以 `Requests` 表示实际发起数;当
|
||||
`RateStartsDropped` 非零时,目标速率受压测端并发容量或时间窗限制,报告吞吐不得
|
||||
标注为已达到配置的 `-rate`。
|
||||
标注为已达到配置的 `-rate`。每个生成的限速令牌均会计入实际发起或丢弃,因此
|
||||
`RateStartsGenerated = Requests + RateStartsDropped`。
|
||||
需要保留单次容量证据时,使用 `-output REPORT_FILE` 同时写出 JSON 文件;文件在
|
||||
同目录完整写入后才替换目标,标准输出仍保留相同报告,便于交给日志或指标系统。
|
||||
|
||||
|
||||
@ -21,6 +21,16 @@ data:
|
||||
limits:
|
||||
maxConcurrentConnections: 100000
|
||||
requestsPerMinutePerClient: 60000
|
||||
transport:
|
||||
dialTimeout: 10s
|
||||
handshakeTimeout: 15s
|
||||
responseHeaderTimeout: 30s
|
||||
idleConnTimeout: 90s
|
||||
maxIdleConns: 20000
|
||||
maxIdleConnsPerHost: 32
|
||||
maxConnsPerHost: 32
|
||||
tunnelBufferBytes: 32768
|
||||
tunnelIdleTimeout: 5m
|
||||
retry:
|
||||
maxAttempts: 2
|
||||
retryMethods: [GET, HEAD]
|
||||
|
||||
@ -204,6 +204,7 @@ func TestShippedDeploymentConfigurationsResolveEnvironment(t *testing.T) {
|
||||
wantGatewayUser string
|
||||
wantPostgresURL string
|
||||
wantRedisURL string
|
||||
wantTransport GatewayTransport
|
||||
}{
|
||||
{
|
||||
name: "local",
|
||||
@ -211,6 +212,12 @@ func TestShippedDeploymentConfigurationsResolveEnvironment(t *testing.T) {
|
||||
wantGatewayUser: "local-gateway",
|
||||
wantPostgresURL: "postgres://proxy_pool:local-only-change-me@postgres:5432/proxy_pool?sslmode=disable",
|
||||
wantRedisURL: "redis://redis:6379/0",
|
||||
wantTransport: GatewayTransport{
|
||||
MaxIdleConns: 20000,
|
||||
MaxIdleConnsPerHost: 32,
|
||||
MaxConnsPerHost: 32,
|
||||
TunnelBufferBytes: 32768,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "kubernetes",
|
||||
@ -219,6 +226,17 @@ func TestShippedDeploymentConfigurationsResolveEnvironment(t *testing.T) {
|
||||
wantGatewayUser: "resolved-gateway-user",
|
||||
wantPostgresURL: "postgres://resolved",
|
||||
wantRedisURL: "redis://resolved",
|
||||
wantTransport: GatewayTransport{
|
||||
DialTimeout: Duration(10 * time.Second),
|
||||
HandshakeTimeout: Duration(15 * time.Second),
|
||||
ResponseHeaderTimeout: Duration(30 * time.Second),
|
||||
IdleConnTimeout: Duration(90 * time.Second),
|
||||
MaxIdleConns: 20000,
|
||||
MaxIdleConnsPerHost: 32,
|
||||
MaxConnsPerHost: 32,
|
||||
TunnelBufferBytes: 32768,
|
||||
TunnelIdleTimeout: Duration(5 * time.Minute),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
@ -248,6 +266,7 @@ func TestShippedDeploymentConfigurationsResolveEnvironment(t *testing.T) {
|
||||
cfg.Admin.Auth.Token != "resolved-admin-token" ||
|
||||
cfg.Storage.PostgresURL != test.wantPostgresURL ||
|
||||
cfg.Storage.RedisURL != test.wantRedisURL ||
|
||||
cfg.Gateway.Transport != test.wantTransport ||
|
||||
cfg.Upstreams["provider-a"].API.Auth.Value != "resolved-provider-a-token" ||
|
||||
cfg.Upstreams["provider-b"].API.Auth.Value != "resolved-provider-b-token" {
|
||||
t.Fatalf("deployment values were not resolved: %+v", cfg.Redacted())
|
||||
|
||||
@ -331,7 +331,7 @@ func runForDuration(ctx context.Context, options Options, execute func(context.C
|
||||
for {
|
||||
select {
|
||||
case <-workloadContext.Done():
|
||||
workers.Wait()
|
||||
waitForRateWorkers(&workers, jobs, stats)
|
||||
return
|
||||
case now := <-ticker.C:
|
||||
credit += float64(options.Rate) * now.Sub(last).Seconds()
|
||||
@ -352,7 +352,7 @@ func runForDuration(ctx context.Context, options Options, execute func(context.C
|
||||
case jobs <- struct{}{}:
|
||||
case <-workloadContext.Done():
|
||||
stats.rateStartsDropped.Add(uint64(tokens - index))
|
||||
workers.Wait()
|
||||
waitForRateWorkers(&workers, jobs, stats)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -360,6 +360,11 @@ func runForDuration(ctx context.Context, options Options, execute func(context.C
|
||||
}
|
||||
}
|
||||
|
||||
func waitForRateWorkers(workers *sync.WaitGroup, jobs <-chan struct{}, stats *counters) {
|
||||
workers.Wait()
|
||||
stats.rateStartsDropped.Add(uint64(len(jobs)))
|
||||
}
|
||||
|
||||
type requestBuilder func(context.Context) (*http.Request, responseValidator, error)
|
||||
|
||||
type responseValidator func(*http.Response) error
|
||||
|
||||
@ -66,11 +66,26 @@ func TestRunTimeBoxedRateIsBounded(t *testing.T) {
|
||||
TargetURL: server.URL, Duration: 100 * time.Millisecond, Rate: 100, Concurrency: 2, RequestTimeout: time.Second,
|
||||
})
|
||||
if err != nil || report.Requests == 0 || report.Requests > 20 || report.Requests != report.Completed ||
|
||||
report.Succeeded != report.Completed || requests.Load() != int64(report.Requests) {
|
||||
report.Succeeded+report.Failed != report.Completed || report.RateStartsGenerated != report.Requests+report.RateStartsDropped ||
|
||||
requests.Load() > int64(report.Requests) {
|
||||
t.Fatalf("Run() = (%+v, %v); handler requests=%d", report, err, requests.Load())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunForDurationAccountsQueuedRateStartsAtDeadline(t *testing.T) {
|
||||
stats := &counters{}
|
||||
runForDuration(context.Background(), Options{
|
||||
Duration: 40 * time.Millisecond, Rate: 1000, Concurrency: 1,
|
||||
}, func(ctx context.Context) {
|
||||
stats.requests.Add(1)
|
||||
<-ctx.Done()
|
||||
}, stats)
|
||||
|
||||
if generated, accounted := stats.rateStartsGenerated.Load(), stats.requests.Load()+stats.rateStartsDropped.Load(); generated == 0 || generated != accounted {
|
||||
t.Fatalf("rate starts generated=%d, accounted=%d", generated, accounted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunReportsRateStartsDroppedByConcurrency(t *testing.T) {
|
||||
started := make(chan struct{}, 1)
|
||||
server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user