mirror of
https://github.com/americanexpress/baton.git
synced 2025-07-10 14:22:30 +00:00
Switch to calculating statistics only when a request count is specified
This commit is contained in:
parent
a16f6402bf
commit
a191eada28
@ -94,7 +94,7 @@ Number of 5xx responses: 0
|
|||||||
* Testing REST endpoints with dynamically generated keys
|
* Testing REST endpoints with dynamically generated keys
|
||||||
|
|
||||||
## Caveats
|
## Caveats
|
||||||
* The `min`,`max`,`avg` stats are only accurate when a request count is specified. When in timed mode only the first `100,000` requests are tracked
|
* Statistics are only provided when a fixed number of requests is provided (instead of providing a duration).
|
||||||
|
|
||||||
## Dependency Management
|
## Dependency Management
|
||||||
[Dep](https://github.com/golang/dep) is currently being utilized as the dependency manager for Baton.
|
[Dep](https://github.com/golang/dep) is currently being utilized as the dependency manager for Baton.
|
||||||
|
1
baton.go
1
baton.go
@ -157,6 +157,7 @@ func processResults(baton *Baton, preparedRunConfiguration runConfiguration) {
|
|||||||
timeSum += result.timeSum
|
timeSum += result.timeSum
|
||||||
requestCount += result.totalSuccess
|
requestCount += result.totalSuccess
|
||||||
}
|
}
|
||||||
|
baton.result.hasStats = baton.configuration.duration == 0
|
||||||
baton.result.averageTime = float32(timeSum) / float32(requestCount)
|
baton.result.averageTime = float32(timeSum) / float32(requestCount)
|
||||||
baton.result.totalRequests = baton.result.httpResult.total()
|
baton.result.totalRequests = baton.result.httpResult.total()
|
||||||
baton.result.requestsPerSecond = int(float64(baton.result.totalRequests)/baton.result.timeTaken.Seconds() + 0.5)
|
baton.result.requestsPerSecond = int(float64(baton.result.totalRequests)/baton.result.timeTaken.Seconds() + 0.5)
|
||||||
|
@ -23,11 +23,13 @@ import (
|
|||||||
// CountWorker implements a worker which sends a fixed number of requests
|
// CountWorker implements a worker which sends a fixed number of requests
|
||||||
type countWorker struct {
|
type countWorker struct {
|
||||||
*worker
|
*worker
|
||||||
|
timings chan int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCountWorker(requests <-chan bool, results chan<- HTTPResult, done chan<- bool) *countWorker {
|
func newCountWorker(requests <-chan bool, results chan<- HTTPResult, done chan<- bool) *countWorker {
|
||||||
worker := newWorker(requests, results, done)
|
worker := newWorker(requests, results, done)
|
||||||
return &countWorker{worker}
|
timings := make(chan int, len(requests))
|
||||||
|
return &countWorker{worker, timings}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (worker *countWorker) sendRequest(request preLoadedRequest) {
|
func (worker *countWorker) sendRequest(request preLoadedRequest) {
|
||||||
@ -38,9 +40,10 @@ func (worker *countWorker) sendRequest(request preLoadedRequest) {
|
|||||||
resp := fasthttp.AcquireResponse()
|
resp := fasthttp.AcquireResponse()
|
||||||
|
|
||||||
for range worker.requests {
|
for range worker.requests {
|
||||||
worker.performRequest(req, resp)
|
worker.performRequestWithStats(req, resp, worker.timings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
worker.collectStatistics(worker.timings)
|
||||||
worker.finish()
|
worker.finish()
|
||||||
}
|
}
|
||||||
func (worker *countWorker) sendRequests(requests []preLoadedRequest) {
|
func (worker *countWorker) sendRequests(requests []preLoadedRequest) {
|
||||||
@ -48,8 +51,9 @@ func (worker *countWorker) sendRequests(requests []preLoadedRequest) {
|
|||||||
|
|
||||||
for range worker.requests {
|
for range worker.requests {
|
||||||
req, resp := buildRequest(requests, totalPremadeRequests)
|
req, resp := buildRequest(requests, totalPremadeRequests)
|
||||||
worker.performRequest(req, resp)
|
worker.performRequestWithStats(req, resp, worker.timings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
worker.collectStatistics(worker.timings)
|
||||||
worker.finish()
|
worker.finish()
|
||||||
}
|
}
|
||||||
|
11
result.go
11
result.go
@ -27,13 +27,14 @@ type Result struct {
|
|||||||
totalRequests int
|
totalRequests int
|
||||||
timeTaken time.Duration
|
timeTaken time.Duration
|
||||||
requestsPerSecond int
|
requestsPerSecond int
|
||||||
|
hasStats bool
|
||||||
averageTime float32
|
averageTime float32
|
||||||
minTime int
|
minTime int
|
||||||
maxTime int
|
maxTime int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResult() *Result {
|
func newResult() *Result {
|
||||||
return &Result{*newHTTPResult(), 0, 0, 0, 0, 0, 0}
|
return &Result{*newHTTPResult(), 0, 0, 0, false, 0, 0, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (result *Result) printResults() {
|
func (result *Result) printResults() {
|
||||||
@ -43,9 +44,11 @@ func (result *Result) printResults() {
|
|||||||
fmt.Printf("Total requests: %10d\n", result.totalRequests)
|
fmt.Printf("Total requests: %10d\n", result.totalRequests)
|
||||||
fmt.Printf("Time taken to complete requests: %15s\n", result.timeTaken.String())
|
fmt.Printf("Time taken to complete requests: %15s\n", result.timeTaken.String())
|
||||||
fmt.Printf("Requests per second: %10d\n", result.requestsPerSecond)
|
fmt.Printf("Requests per second: %10d\n", result.requestsPerSecond)
|
||||||
fmt.Printf("Max response time (ms): %10d\n", result.maxTime)
|
if result.hasStats {
|
||||||
fmt.Printf("Min response time (ms): %10d\n", result.minTime)
|
fmt.Printf("Max response time (ms): %10d\n", result.maxTime)
|
||||||
fmt.Printf("Avg response time (ms): %6.2f\n", result.averageTime)
|
fmt.Printf("Min response time (ms): %10d\n", result.minTime)
|
||||||
|
fmt.Printf("Avg response time (ms): %6.2f\n", result.averageTime)
|
||||||
|
}
|
||||||
fmt.Printf("===================== Breakdown =====================\n")
|
fmt.Printf("===================== Breakdown =====================\n")
|
||||||
fmt.Printf("Number of connection errors: %10d\n", result.httpResult.connectionErrorCount)
|
fmt.Printf("Number of connection errors: %10d\n", result.httpResult.connectionErrorCount)
|
||||||
fmt.Printf("Number of 1xx responses: %10d\n", result.httpResult.status1xxCount)
|
fmt.Printf("Number of 1xx responses: %10d\n", result.httpResult.status1xxCount)
|
||||||
|
56
worker.go
56
worker.go
@ -22,15 +22,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TimedBufferSize = 100000
|
|
||||||
|
|
||||||
type worker struct {
|
type worker struct {
|
||||||
httpResult HTTPResult
|
httpResult HTTPResult
|
||||||
client *fasthttp.Client
|
client *fasthttp.Client
|
||||||
requests <-chan bool
|
requests <-chan bool
|
||||||
httpResults chan<- HTTPResult
|
httpResults chan<- HTTPResult
|
||||||
done chan<- bool
|
done chan<- bool
|
||||||
timings chan int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type workable interface {
|
type workable interface {
|
||||||
@ -44,39 +41,20 @@ func (worker *worker) setCustomClient(client *fasthttp.Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newWorker(requests <-chan bool, httpResults chan<- HTTPResult, done chan<- bool) *worker {
|
func newWorker(requests <-chan bool, httpResults chan<- HTTPResult, done chan<- bool) *worker {
|
||||||
requestCount := len(requests)
|
return &worker{*newHTTPResult(), &fasthttp.Client{}, requests, httpResults, done}
|
||||||
|
|
||||||
// If this is running in timed mode we cannot predict the number of requests being
|
|
||||||
// sent, to allocate a large enough channel. As a compromise we fill in the
|
|
||||||
// buffered channel up to a fixed size and ignore the rest
|
|
||||||
if requestCount <= 1 {
|
|
||||||
requestCount = TimedBufferSize
|
|
||||||
}
|
|
||||||
|
|
||||||
timings := make(chan int, requestCount)
|
|
||||||
return &worker{*newHTTPResult(), &fasthttp.Client{}, requests, httpResults, done, timings}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (worker *worker) performRequest(req *fasthttp.Request, resp *fasthttp.Response) bool {
|
func (worker *worker) performRequest(req *fasthttp.Request, resp *fasthttp.Response) bool {
|
||||||
timeNow := time.Now().UnixNano()
|
|
||||||
if err := worker.client.Do(req, resp); err != nil {
|
if err := worker.client.Do(req, resp); err != nil {
|
||||||
worker.httpResult.connectionErrorCount++
|
worker.httpResult.connectionErrorCount++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
timeAfter := time.Now().UnixNano()
|
|
||||||
|
|
||||||
i := int(timeAfter - timeNow)
|
|
||||||
// The select is needed to avoid blocking the thread
|
|
||||||
// if the channel is full
|
|
||||||
select {
|
|
||||||
case worker.timings <- i:
|
|
||||||
// Send the timing via the channel in non-blocking mode
|
|
||||||
default:
|
|
||||||
// If the channel is full (which it will in case of timed mode) then
|
|
||||||
// just proceed
|
|
||||||
break
|
|
||||||
}
|
|
||||||
status := resp.StatusCode()
|
status := resp.StatusCode()
|
||||||
|
|
||||||
|
worker.recordCount(status)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (worker *worker) recordCount(status int) {
|
||||||
if status >= 100 && status < 200 {
|
if status >= 100 && status < 200 {
|
||||||
worker.httpResult.status1xxCount++
|
worker.httpResult.status1xxCount++
|
||||||
} else if status >= 200 && status < 300 {
|
} else if status >= 200 && status < 300 {
|
||||||
@ -88,6 +66,21 @@ func (worker *worker) performRequest(req *fasthttp.Request, resp *fasthttp.Respo
|
|||||||
} else if status >= 500 && status < 600 {
|
} else if status >= 500 && status < 600 {
|
||||||
worker.httpResult.status5xxCount++
|
worker.httpResult.status5xxCount++
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
func (worker *worker) performRequestWithStats(req *fasthttp.Request, resp *fasthttp.Response, timings chan int) bool {
|
||||||
|
timeNow := time.Now().UnixNano()
|
||||||
|
if err := worker.client.Do(req, resp); err != nil {
|
||||||
|
worker.httpResult.connectionErrorCount++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
timeAfter := time.Now().UnixNano()
|
||||||
|
|
||||||
|
i := int(timeAfter - timeNow)
|
||||||
|
// Record the timing into a channel
|
||||||
|
timings <- i
|
||||||
|
|
||||||
|
status := resp.StatusCode()
|
||||||
|
worker.recordCount(status)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -108,18 +101,17 @@ func buildRequest(requests []preLoadedRequest, totalPremadeRequests int) (*fasth
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (worker *worker) finish() {
|
func (worker *worker) finish() {
|
||||||
worker.collectStatistics()
|
|
||||||
worker.httpResults <- worker.httpResult
|
worker.httpResults <- worker.httpResult
|
||||||
worker.done <- true
|
worker.done <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (worker *worker) collectStatistics() {
|
func (worker *worker) collectStatistics(timings chan int) {
|
||||||
close(worker.timings)
|
close(timings)
|
||||||
|
|
||||||
first := true
|
first := true
|
||||||
sum, total := int64(0), 0
|
sum, total := int64(0), 0
|
||||||
|
|
||||||
for timing := range worker.timings {
|
for timing := range timings {
|
||||||
timing = timing / 1000
|
timing = timing / 1000
|
||||||
// The first request is associated with overhead
|
// The first request is associated with overhead
|
||||||
// in setting up the client so we ignore it's result
|
// in setting up the client so we ignore it's result
|
||||||
|
Loading…
x
Reference in New Issue
Block a user