diff --git a/hrp/boomer.go b/hrp/boomer.go index c47db199..8d5e0266 100644 --- a/hrp/boomer.go +++ b/hrp/boomer.go @@ -99,6 +99,9 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend parametersIterator := caseRunner.parametersIterator parametersIterator.SetUnlimitedMode() + // reset start time only once + once := sync.Once{} + return &boomer.Task{ Name: testcase.Config.Name, Weight: testcase.Config.Weight, @@ -115,6 +118,10 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend startTime := time.Now() for _, step := range testcase.TestSteps { + // reset start time only once before step + once.Do(func() { + b.Boomer.ResetStartTime() + }) stepResult, err := step.Run(sessionRunner) if err != nil { // step failed diff --git a/hrp/internal/boomer/boomer.go b/hrp/internal/boomer/boomer.go index 83459a4f..6c67007d 100644 --- a/hrp/internal/boomer/boomer.go +++ b/hrp/internal/boomer/boomer.go @@ -170,3 +170,7 @@ func (b *Boomer) GetSpawnDoneChan() chan struct{} { func (b *Boomer) GetSpawnCount() int { return b.localRunner.spawnCount } + +func (b *Boomer) ResetStartTime() { + b.localRunner.stats.total.resetStartTime() +} diff --git a/hrp/internal/boomer/output.go b/hrp/internal/boomer/output.go index 79284cf7..eb34540a 100644 --- a/hrp/internal/boomer/output.go +++ b/hrp/internal/boomer/output.go @@ -10,6 +10,7 @@ import ( "github.com/google/uuid" "github.com/olekukonko/tablewriter" + "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/push" "github.com/rs/zerolog/log" @@ -264,10 +265,9 @@ func deserializeStatsEntry(stat interface{}) (entryOutput *statsEntryOutput, err var duration float64 if entry.Name == "Total" { - duration = float64(entry.LastRequestTimestamp - entry.StartTime) - // fix: avoid divide by zero - if duration < 1 { - duration = 1 + duration = float64(entry.LastRequestTimestamp-entry.StartTime) / 1e3 + if duration == 0 { + return nil, errors.New("no step specified") } } else { duration = float64(reportStatsInterval / time.Second) diff --git a/hrp/internal/boomer/runner.go b/hrp/internal/boomer/runner.go index 25ad1bbf..68109901 100644 --- a/hrp/internal/boomer/runner.go +++ b/hrp/internal/boomer/runner.go @@ -11,7 +11,6 @@ import ( "time" "github.com/olekukonko/tablewriter" - "github.com/rs/zerolog/log" ) @@ -154,7 +153,7 @@ func (r *runner) reportTestResult() { if err != nil { return } - duration := time.Duration(entryTotalOutput.LastRequestTimestamp-entryTotalOutput.StartTime) * time.Second + duration := time.Duration(entryTotalOutput.LastRequestTimestamp-entryTotalOutput.StartTime) * time.Millisecond currentTime := time.Now() println(fmt.Sprint("=========================================== Statistics Summary ==========================================")) println(fmt.Sprintf("Current time: %s, Users: %v, Duration: %v, Accumulated Transactions: %d Passed, %d Failed", diff --git a/hrp/internal/boomer/stats.go b/hrp/internal/boomer/stats.go index b15c655d..8fdd3897 100644 --- a/hrp/internal/boomer/stats.go +++ b/hrp/internal/boomer/stats.go @@ -1,6 +1,7 @@ package boomer import ( + "sync/atomic" "time" "github.com/httprunner/httprunner/hrp/internal/json" @@ -101,8 +102,6 @@ func (s *requestStats) get(name string, method string) (entry *statsEntry) { newEntry := &statsEntry{ Name: name, Method: method, - NumReqsPerSec: make(map[int64]int64), - NumFailPerSec: make(map[int64]int64), ResponseTimes: make(map[int64]int64), } s.entries[name+method] = newEntry @@ -171,10 +170,6 @@ type statsEntry struct { MinResponseTime int64 `json:"min_response_time"` // Maximum response time MaxResponseTime int64 `json:"max_response_time"` - // A {second => request_count} dict that holds the number of requests made per second - NumReqsPerSec map[int64]int64 `json:"num_reqs_per_sec"` - // A (second => failure_count) dict that hold the number of failures per second - NumFailPerSec map[int64]int64 `json:"num_fail_per_sec"` // A {response_time => count} dict that holds the response time distribution of all the requests // The keys (the response time in ms) are rounded to store 1, 2, ... 9, 10, 20. .. 90, // 100, 200 .. 900, 1000, 2000 ... 9000, in order to save memory. @@ -191,17 +186,19 @@ type statsEntry struct { NumNoneRequests int64 `json:"num_none_requests"` } +func (s *statsEntry) resetStartTime() { + atomic.StoreInt64(&s.StartTime, time.Duration(time.Now().UnixNano()).Milliseconds()) +} + func (s *statsEntry) reset() { - s.StartTime = time.Now().Unix() + atomic.StoreInt64(&s.StartTime, time.Duration(time.Now().UnixNano()).Milliseconds()) s.NumRequests = 0 s.NumFailures = 0 s.TotalResponseTime = 0 s.ResponseTimes = make(map[int64]int64) s.MinResponseTime = 0 s.MaxResponseTime = 0 - s.LastRequestTimestamp = time.Now().Unix() - s.NumReqsPerSec = make(map[int64]int64) - s.NumFailPerSec = make(map[int64]int64) + s.LastRequestTimestamp = time.Duration(time.Now().UnixNano()).Milliseconds() s.TotalContentLength = 0 } @@ -215,15 +212,7 @@ func (s *statsEntry) log(responseTime int64, contentLength int64) { } func (s *statsEntry) logTimeOfRequest() { - key := time.Now().Unix() - _, ok := s.NumReqsPerSec[key] - if !ok { - s.NumReqsPerSec[key] = 1 - } else { - s.NumReqsPerSec[key]++ - } - - s.LastRequestTimestamp = key + s.LastRequestTimestamp = time.Duration(time.Now().UnixNano()).Milliseconds() } func (s *statsEntry) logResponseTime(responseTime int64) { @@ -267,13 +256,6 @@ func (s *statsEntry) logResponseTime(responseTime int64) { func (s *statsEntry) logFailures() { s.NumFailures++ - key := time.Now().Unix() - _, ok := s.NumFailPerSec[key] - if !ok { - s.NumFailPerSec[key] = 1 - } else { - s.NumFailPerSec[key]++ - } } func (s *statsEntry) serialize() map[string]interface{} {