Skip to content

Commit

Permalink
add ability to set custom max time range for benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
umputun committed May 31, 2022
1 parent 949aa03 commit d0d63d6
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 8 deletions.
29 changes: 21 additions & 8 deletions benchmarks.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import (
"time"
)

var maxTimeRange = time.Duration(15) * time.Minute
var maxTimeRangeDefault = time.Duration(15) * time.Minute

// Benchmarks is a basic benchmarking middleware collecting and reporting performance metrics
// It keeps track of the requests speeds and counts in 1s benchData buckets ,limiting the number of buckets
// to maxTimeRange. User can request the benchmark for any time duration. This is intended to be used
// for retrieving the benchmark data for the last minute, 5 minutes and up to maxTimeRange.
type Benchmarks struct {
st time.Time
data *list.List
lock sync.RWMutex
st time.Time
data *list.List
lock sync.RWMutex
maxTimeRange time.Duration

nowFn func() time.Time // for testing only
}
Expand All @@ -42,13 +43,25 @@ type BenchmarkStats struct {
// NewBenchmarks creates a new benchmark middleware
func NewBenchmarks() *Benchmarks {
res := &Benchmarks{
st: time.Now(),
data: list.New(),
nowFn: time.Now,
st: time.Now(),
data: list.New(),
nowFn: time.Now,
maxTimeRange: maxTimeRangeDefault,
}
return res
}

// WithTimeRange sets the maximum time range for the benchmark to keep data for.
// Default is 15 minutes. The increase of this range will change memory utilization as each second of the range
// kept as benchData aggregate. The default means 15*60 = 900 seconds of data aggregate.
// Larger range allows for longer time periods to be benchmarked.
func (b *Benchmarks) WithTimeRange(max time.Duration) *Benchmarks {
b.lock.Lock()
defer b.lock.Unlock()
b.maxTimeRange = max
return b
}

// Handler calculates 1/5/10m request per second and allows to access those values
func (b *Benchmarks) Handler(next http.Handler) http.Handler {

Expand All @@ -70,7 +83,7 @@ func (b *Benchmarks) update(reqDuration time.Duration) {

// keep maxTimeRange in the list, drop the rest
for e := b.data.Front(); e != nil; e = e.Next() {
if b.data.Front().Value.(benchData).ts.After(b.nowFn().Add(-maxTimeRange)) {
if b.data.Front().Value.(benchData).ts.After(b.nowFn().Add(-b.maxTimeRange)) {
break
}
b.data.Remove(b.data.Front())
Expand Down
53 changes: 53 additions & 0 deletions benchmarks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,59 @@ func TestBenchmark_Stats2s(t *testing.T) {
MinRespTime: (time.Millisecond * 50).Microseconds(), MaxRespTime: (time.Millisecond * 250).Microseconds()}, res)
}

func TestBenchmark_WithTimeRange(t *testing.T) {

nowFn := func(dt time.Time) func() time.Time {
return func() time.Time { return dt }
}

{
bench := NewBenchmarks().WithTimeRange(time.Minute)

bench.nowFn = nowFn(time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC))
bench.update(time.Millisecond * 50)
bench.update(time.Millisecond * 150)
bench.update(time.Millisecond * 250)
bench.update(time.Millisecond * 100)

bench.nowFn = nowFn(time.Date(2018, time.January, 1, 1, 0, 0, 0, time.UTC)) // 1 hour later
bench.update(time.Millisecond * 1000)

res := bench.Stats(time.Minute)
t.Logf("%+v", res)
assert.Equal(t, BenchmarkStats{Requests: 1, RequestsSec: 1, AverageRespTime: 1000000,
MinRespTime: (time.Millisecond * 1000).Microseconds(), MaxRespTime: (time.Millisecond * 1000).Microseconds()}, res)

res = bench.Stats(time.Hour)
t.Logf("%+v", res)
assert.Equal(t, BenchmarkStats{Requests: 1, RequestsSec: 1, AverageRespTime: 1000000,
MinRespTime: (time.Millisecond * 1000).Microseconds(), MaxRespTime: (time.Millisecond * 1000).Microseconds()}, res)
}

{
bench := NewBenchmarks().WithTimeRange(time.Hour * 2)

bench.nowFn = nowFn(time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC))
bench.update(time.Millisecond * 50)
bench.update(time.Millisecond * 150)
bench.update(time.Millisecond * 250)
bench.update(time.Millisecond * 100)

bench.nowFn = nowFn(time.Date(2018, time.January, 1, 1, 0, 0, 0, time.UTC)) // 1 hour later
bench.update(time.Millisecond * 1000)

res := bench.Stats(time.Minute)
t.Logf("%+v", res)
assert.Equal(t, BenchmarkStats{Requests: 1, RequestsSec: 1, AverageRespTime: 1000000,
MinRespTime: (time.Millisecond * 1000).Microseconds(), MaxRespTime: (time.Millisecond * 1000).Microseconds()}, res)

res = bench.Stats(time.Hour)
t.Logf("%+v", res)
assert.Equal(t, BenchmarkStats{Requests: 5, RequestsSec: 0.0013885031935573452, AverageRespTime: 310000,
MinRespTime: (time.Millisecond * 50).Microseconds(), MaxRespTime: (time.Millisecond * 1000).Microseconds()}, res)
}
}

func TestBenchmark_Cleanup(t *testing.T) {
bench := NewBenchmarks()
for i := 0; i < 1000; i++ {
Expand Down

0 comments on commit d0d63d6

Please sign in to comment.