Skip to content

Circuit breaker playground #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions _examples/circuit-breaker-playground/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Circuit Breaker Playground

This application simulates circuit breaker reaction on an unhealthy application.

You can control server latency with Up/Down buttons in real time.
149 changes: 149 additions & 0 deletions _examples/circuit-breaker-playground/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package main

Check notice on line 1 in _examples/circuit-breaker-playground/main.go

View workflow job for this annotation

GitHub Actions / test (1.22.x)

File is not covered by tests.

import (
"context"
"fmt"
"math/rand/v2"
"net/http"
"net/http/httptest"
"os"
"sync"
"sync/atomic"
"time"

"github.com/alecthomas/kingpin/v2"
"github.com/gizak/termui/v3/widgets"
"github.com/sony/gobreaker"
"github.com/vearutop/plt/curl"
"github.com/vearutop/plt/loadgen"
"github.com/vearutop/plt/nethttp"
)

func main() {
lf := loadgen.Flags{}
lf.Register()

var (
// Response time window, normally distributed.
minResp = int64(300 * time.Millisecond)
maxResp = int64(510 * time.Millisecond)

// Atomic counters.
cbFailed int64
cbPassed int64
cbState int64

// Response time considered a timeout by HTTP client.
timeout = 500 * time.Millisecond

// ReadyToTrip params.
requestsThreshold = uint32(100)
errorThreshold = 0.03

mu sync.Mutex
readyToTripMsg string
)

cbSettings := gobreaker.Settings{
// Name is the name of the CircuitBreaker.
Name: "acme",

// MaxRequests is the maximum number of requests allowed to pass through
// when the CircuitBreaker is half-open.
// If MaxRequests is 0, the CircuitBreaker allows only 1 request.
MaxRequests: 500,

// Interval is the cyclic period of the closed state
// for the CircuitBreaker to clear the internal Counts.
// If Interval is less than or equal to 0, the CircuitBreaker doesn't clear internal Counts during the closed state.
Interval: 2000 * time.Millisecond,

// Timeout is the period of the open state,
// after which the state of the CircuitBreaker becomes half-open.
// If Timeout is less than or equal to 0, the timeout value of the CircuitBreaker is set to 60 seconds.
Timeout: 3 * time.Second,

// OnStateChange is called whenever the state of the CircuitBreaker changes.
OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
atomic.StoreInt64(&cbState, int64(to))
},

// ReadyToTrip is called with a copy of Counts whenever a request fails in the closed state.
// If ReadyToTrip returns true, the CircuitBreaker will be placed into the open state.
// If ReadyToTrip is nil, default ReadyToTrip is used.
// Default ReadyToTrip returns true when the number of consecutive failures is more than 5.
ReadyToTrip: func(counts gobreaker.Counts) bool {
enoughRequests := counts.Requests > requestsThreshold
errorRate := float64(counts.TotalFailures) / float64(counts.Requests)
reachedFailureLvl := errorRate >= errorThreshold

mu.Lock()
defer mu.Unlock()

readyToTripMsg = fmt.Sprintf("%d/%d failed (%.2f%%), %s",
counts.TotalFailures, counts.Requests, 100*errorRate, time.Now().Format(time.TimeOnly))
return enoughRequests && reachedFailureLvl
},
}

// This handler returns 200 OK after a random delay between minResp and maxResp.
h := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
r := rand.Float64()
t := float64(atomic.LoadInt64(&minResp)) + float64(atomic.LoadInt64(&maxResp)-atomic.LoadInt64(&minResp))*r

time.Sleep(time.Duration(t))
})

srv := httptest.NewServer(h)

// Customizing Live UI.
lf.PrepareLoadLimitsWidget = func(paragraph *widgets.Paragraph) {
mu.Lock()
defer mu.Unlock()

paragraph.Title = "Response Time"
paragraph.Text = fmt.Sprintf("Max resp: %s, <Up>/<Down>: ±5%%", time.Duration(atomic.LoadInt64(&maxResp)).Truncate(time.Millisecond).String())
paragraph.Text += fmt.Sprintf("\nCB %s, f: %d, p: %d\nReady to trip: %s",
gobreaker.State(atomic.LoadInt64(&cbState)).String(),
atomic.LoadInt64(&cbFailed),
atomic.LoadInt64(&cbPassed),
readyToTripMsg,
)
}

lf.KeyPressed["<Up>"] = func() {
atomic.StoreInt64(&maxResp, int64(float64(atomic.LoadInt64(&maxResp))*1.05))
}

lf.KeyPressed["<Down>"] = func() {
atomic.StoreInt64(&maxResp, int64(float64(atomic.LoadInt64(&maxResp))*0.95))
}

// Applying transport middleware.
curl.AddCommand(&lf, func(lf *loadgen.Flags, f *nethttp.Flags, j loadgen.JobProducer) {
if nj, ok := j.(*nethttp.JobProducer); ok {
nj.PrepareRoundTripper = func(tr http.RoundTripper) http.RoundTripper {
return CircuitBreakerMiddleware(cbSettings, &cbFailed)(
roundTripperFunc(func(r *http.Request) (*http.Response, error) {
ctx, cancel := context.WithTimeout(r.Context(), timeout)
defer cancel()

atomic.AddInt64(&cbPassed, 1)

return tr.RoundTrip(r.WithContext(ctx))
}),
)
}
}
})

// Preparing command line arguments.
os.Args = append(os.Args,
"--live-ui",
"--rate-limit=100",
"--number=1000000",
"curl", srv.URL)

// Running the app.q
kingpin.Parse()
}
42 changes: 42 additions & 0 deletions _examples/circuit-breaker-playground/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

Check notice on line 1 in _examples/circuit-breaker-playground/middleware.go

View workflow job for this annotation

GitHub Actions / test (1.22.x)

File is not covered by tests.

import (
"fmt"
"net/http"
"sync/atomic"

"github.com/sony/gobreaker"
)

// CircuitBreakerMiddleware is a http.RoundTripper.
func CircuitBreakerMiddleware(
settings gobreaker.Settings,
cbFailed *int64,
) func(tripper http.RoundTripper) http.RoundTripper {
cb := gobreaker.NewTwoStepCircuitBreaker(settings)
return func(tripper http.RoundTripper) http.RoundTripper {
return roundTripperFunc(func(r *http.Request) (*http.Response, error) {
done, err := cb.Allow()
// Error means circuit breaker is open and request should fail immediately.
if err != nil {
atomic.AddInt64(cbFailed, 1)

return nil, fmt.Errorf("circuit breaker %s: %w", settings.Name, err)
}
resp, err := tripper.RoundTrip(r)

// Done is nil if err is not.
if done != nil {
done(err == nil)
}

return resp, err
})
}
}

type roundTripperFunc func(*http.Request) (*http.Response, error)

func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return fn(req)
}
42 changes: 42 additions & 0 deletions _examples/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module examples

go 1.23.1

replace github.com/vearutop/plt => ../

require (
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/gizak/termui/v3 v3.1.0
github.com/sony/gobreaker v1.0.0
github.com/vearutop/plt v0.3.12
)

require (
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/bool64/dev v0.2.36 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/pprof v0.0.0-20240903155634-a8630aee4ab9 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/nsf/termbox-go v1.1.1 // indirect
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.47.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.55.0 // indirect
github.com/vearutop/dynhist-go v1.2.2 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.25.0 // indirect
)
92 changes: 92 additions & 0 deletions _examples/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg=
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/bool64/dev v0.2.36 h1:yU3bbOTujoxhWnt8ig8t94PVmZXIkCaRj9C57OtqJBY=
github.com/bool64/dev v0.2.36/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc=
github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20240903155634-a8630aee4ab9 h1:q5g0N9eal4bmJwXHC5z0QCKs8qhS35hFfq0BAYsIwZI=
github.com/google/pprof v0.0.0-20240903155634-a8630aee4ab9/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY=
github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ=
github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8=
github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
github.com/vearutop/dynhist-go v1.2.2 h1:pKPC2rYHdzpkasztNJmjE7m9DbUJDbMhZi2ZxaUvmS4=
github.com/vearutop/dynhist-go v1.2.2/go.mod h1:liiiYiwAi8ixC3DbkxooEhASTF6ysJSXy+piCrBtxEg=
github.com/vearutop/plt v0.3.12 h1:m1QdFB3zZyZ0rtk8vjVL+jwOCKmMuARVN/uXOEdX2zo=
github.com/vearutop/plt v0.3.12/go.mod h1:tzjjDW70+eb8Lyf+/0KMZ9P5NXZ4PpdI/Lo98mH/gxk=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
12 changes: 2 additions & 10 deletions curl/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,23 +204,15 @@
err error
)

for _, o := range options {
o(&lf, &f, nil)
}

if f.Fast {
if j, err = fasthttp.NewJobProducer(f); err != nil {
if j, err = fasthttp.NewJobProducer(f, lf, options...); err != nil {

Check notice on line 208 in curl/cmd.go

View workflow job for this annotation

GitHub Actions / test (1.22.x)

4 statement(s) on lines 207:210 are not covered by tests.
return fmt.Errorf("failed to init job producer: %w", err)
}
} else {
if j, err = nethttp.NewJobProducer(f, lf); err != nil {
if j, err = nethttp.NewJobProducer(f, lf, options...); err != nil {

Check notice on line 212 in curl/cmd.go

View workflow job for this annotation

GitHub Actions / test (1.22.x)

4 statement(s) on lines 211:214 are not covered by tests.
return fmt.Errorf("failed to init job producer: %w", err)
}
}

for _, o := range options {
o(&lf, &f, j)
}

return loadgen.Run(lf, j)
}
7 changes: 6 additions & 1 deletion fasthttp/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"time"

"github.com/valyala/fasthttp"
"github.com/vearutop/plt/loadgen"
"github.com/vearutop/plt/nethttp"
"github.com/vearutop/plt/report"
)
Expand Down Expand Up @@ -73,7 +74,7 @@
}

// NewJobProducer creates load generator.
func NewJobProducer(f nethttp.Flags) (*JobProducer, error) {
func NewJobProducer(f nethttp.Flags, lf loadgen.Flags, options ...func(lf *loadgen.Flags, f *nethttp.Flags, j loadgen.JobProducer)) (*JobProducer, error) {
u, err := url.Parse(f.URL)
if err != nil {
return nil, fmt.Errorf("failed to parse URL: %w", err)
Expand Down Expand Up @@ -110,6 +111,10 @@
}
j.client.MaxConnsPerHost = 10000

for _, o := range options {
o(&lf, &f, &j)
}

Check notice on line 116 in fasthttp/job.go

View workflow job for this annotation

GitHub Actions / test (1.22.x)

1 statement(s) on lines 114:116 are not covered by tests.

if _, ok := f.HeaderMap["User-Agent"]; !ok {
j.client.Name = "plt"
}
Expand Down
Loading
Loading