Skip to content

Commit

Permalink
various improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Ondřej Benkovský committed Apr 17, 2024
1 parent e6100dc commit 1bd3739
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 40 deletions.
3 changes: 3 additions & 0 deletions cmd/benchmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,9 @@ func (b *Benchmark) Run(ctx context.Context) ([]*ResultStats, error) {
st.Codes = make(map[int]int64)
}
st.Qtypes = make(map[string]int64)
if b.useDoH {
st.DoHStatusCodes = make(map[int]int64)
}
st.Counters = &Counters{}

var err error
Expand Down
28 changes: 15 additions & 13 deletions cmd/jsonreporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,19 @@ type histogramPoint struct {
}

type jsonResult struct {
TotalRequests int64 `json:"totalRequests"`
TotalSuccessCodes int64 `json:"totalSuccessCodes"`
TotalErrors int64 `json:"totalErrors"`
TotalIDmismatch int64 `json:"TotalIDmismatch"`
TotalTruncatedResponses int64 `json:"totalTruncatedResponses"`
ResponseRcodes map[string]int64 `json:"responseRcodes,omitempty"`
QuestionTypes map[string]int64 `json:"questionTypes"`
QueriesPerSecond float64 `json:"queriesPerSecond"`
BenchmarkDurationSeconds float64 `json:"benchmarkDurationSeconds"`
LatencyStats latencyStats `json:"latencyStats"`
LatencyDistribution []histogramPoint `json:"latencyDistribution,omitempty"`
TotalDNSSECSecuredDomains *int `json:"totalDNSSECSecuredDomains,omitempty"`
TotalRequests int64 `json:"totalRequests"`
TotalSuccessCodes int64 `json:"totalSuccessCodes"`
TotalErrors int64 `json:"totalErrors"`
TotalIDmismatch int64 `json:"TotalIDmismatch"`
TotalTruncatedResponses int64 `json:"totalTruncatedResponses"`
ResponseRcodes map[string]int64 `json:"responseRcodes,omitempty"`
QuestionTypes map[string]int64 `json:"questionTypes"`
QueriesPerSecond float64 `json:"queriesPerSecond"`
BenchmarkDurationSeconds float64 `json:"benchmarkDurationSeconds"`
LatencyStats latencyStats `json:"latencyStats"`
LatencyDistribution []histogramPoint `json:"latencyDistribution,omitempty"`
TotalDNSSECSecuredDomains *int `json:"totalDNSSECSecuredDomains,omitempty"`
DohHTTPResponseStatusCodes map[int]int64 `json:"dohHTTPResponseStatusCodes,omitempty"`
}

func (s *jsonReporter) print(params reportParameters) error {
Expand Down Expand Up @@ -104,7 +105,8 @@ func (s *jsonReporter) print(params reportParameters) error {
P75Ms: time.Duration(params.timings.ValueAtQuantile(75)).Milliseconds(),
P50Ms: time.Duration(params.timings.ValueAtQuantile(50)).Milliseconds(),
},
LatencyDistribution: res,
LatencyDistribution: res,
DohHTTPResponseStatusCodes: params.dohResponseStatusesTotals,
}
if params.benchmark.DNSSEC {
totalDNSSECSecuredDomains := len(params.authenticatedDomains)
Expand Down
45 changes: 27 additions & 18 deletions cmd/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ type orderedMap struct {
}

type reportParameters struct {
benchmark *Benchmark
outputWriter io.Writer
timings *hdrhistogram.Histogram
codeTotals map[int]int64
totalCounters Counters
qtypeTotals map[string]int64
topErrs orderedMap
authenticatedDomains map[string]struct{}
benchmarkDuration time.Duration
benchmark *Benchmark
outputWriter io.Writer
timings *hdrhistogram.Histogram
codeTotals map[int]int64
totalCounters Counters
qtypeTotals map[string]int64
topErrs orderedMap
authenticatedDomains map[string]struct{}
benchmarkDuration time.Duration
dohResponseStatusesTotals map[int]int64
}

// PrintReport prints formatted benchmark result to stdout, exports graphs and generates CSV output if configured.
Expand All @@ -43,6 +44,8 @@ func (b *Benchmark) PrintReport(w io.Writer, stats []*ResultStats, benchStart ti
timings := hdrhistogram.New(b.HistMin.Nanoseconds(), b.HistMax.Nanoseconds(), b.HistPre)
codeTotals := make(map[int]int64)
qtypeTotals := make(map[string]int64)
dohResponseStatusesTotals := make(map[int]int64)

times := make([]Datapoint, 0)
errTimes := make([]ErrorDatapoint, 0)

Expand Down Expand Up @@ -77,6 +80,11 @@ func (b *Benchmark) PrintReport(w io.Writer, stats []*ResultStats, benchStart ti
qtypeTotals[k] += v
}
}
if s.DoHStatusCodes != nil {
for k, v := range s.DoHStatusCodes {
dohResponseStatusesTotals[k] += v
}
}
if s.Counters != nil {
totalCounters = Counters{
Total: totalCounters.Total + s.Counters.Total,
Expand Down Expand Up @@ -161,15 +169,16 @@ func (b *Benchmark) PrintReport(w io.Writer, stats []*ResultStats, benchStart ti
}
topErrs := orderedMap{m: top3errs, order: top3errorsInOrder}
params := reportParameters{
benchmark: b,
outputWriter: w,
timings: timings,
codeTotals: codeTotals,
totalCounters: totalCounters,
qtypeTotals: qtypeTotals,
topErrs: topErrs,
authenticatedDomains: authenticatedDomains,
benchmarkDuration: benchDuration,
benchmark: b,
outputWriter: w,
timings: timings,
codeTotals: codeTotals,
totalCounters: totalCounters,
qtypeTotals: qtypeTotals,
topErrs: topErrs,
authenticatedDomains: authenticatedDomains,
benchmarkDuration: benchDuration,
dohResponseStatusesTotals: dohResponseStatusesTotals,
}
if b.JSON {
j := jsonReporter{}
Expand Down
60 changes: 60 additions & 0 deletions cmd/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,51 @@ func ExampleBenchmark_PrintReport_dnssec() {
// test 1 (16.67)%
}

func ExampleBenchmark_PrintReport_DoH() {

Check failure on line 91 in cmd/report_test.go

View workflow job for this annotation

GitHub Actions / golangci

tests: ExampleBenchmark_PrintReport_DoH has malformed example suffix: DoH (govet)
b, rs := testReportData()
rs.DoHStatusCodes = map[int]int64{
200: 2,
500: 1,
}

b.PrintReport(os.Stdout, []*ResultStats{&rs}, time.Now(), time.Second)

// Output: Total requests: 1
// Read/Write errors: 3
// ID mismatch errors: 6
// DNS success codes: 4
// Truncated responses: 7
//
// DNS response codes:
// NOERROR: 2
//
// DoH HTTP response status codes:
// 200: 2
// 500: 1
//
// DNS question types:
// A: 2
//
// Time taken for tests: 1s
// Questions per second: 1.0
// DNS timings, 2 datapoints
// min: 5ns
// mean: 7ns
// [+/-sd]: 2ns
// max: 10ns
// p99: 10ns
// p95: 10ns
// p90: 10ns
// p75: 10ns
// p50: 5ns
//
// Total Errors: 6
// Top errors:
// test2 3 (50.00)%
// read udp 8.8.8.8:53 2 (33.33)%
// test 1 (16.67)%
}

func ExampleBenchmark_PrintReport_json() {
b, rs := testReportData()
b.JSON = true
Expand All @@ -112,6 +157,20 @@ func ExampleBenchmark_PrintReport_json_dnssec() {
// Output: {"totalRequests":1,"totalSuccessCodes":4,"totalErrors":6,"TotalIDmismatch":6,"totalTruncatedResponses":7,"responseRcodes":{"NOERROR":2},"questionTypes":{"A":2},"queriesPerSecond":1,"benchmarkDurationSeconds":1,"latencyStats":{"minMs":0,"meanMs":0,"stdMs":0,"maxMs":0,"p99Ms":0,"p95Ms":0,"p90Ms":0,"p75Ms":0,"p50Ms":0},"latencyDistribution":[{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":1},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":1}],"totalDNSSECSecuredDomains":1}
}

func ExampleBenchmark_PrintReport_json_DoH() {
b, rs := testReportData()
b.JSON = true
b.Rcodes = true
b.HistDisplay = true
rs.DoHStatusCodes = map[int]int64{
200: 2,
}

b.PrintReport(os.Stdout, []*ResultStats{&rs}, time.Now(), time.Second)

// Output: {"totalRequests":1,"totalSuccessCodes":4,"totalErrors":6,"TotalIDmismatch":6,"totalTruncatedResponses":7,"responseRcodes":{"NOERROR":2},"questionTypes":{"A":2},"queriesPerSecond":1,"benchmarkDurationSeconds":1,"latencyStats":{"minMs":0,"meanMs":0,"stdMs":0,"maxMs":0,"p99Ms":0,"p95Ms":0,"p90Ms":0,"p75Ms":0,"p50Ms":0},"latencyDistribution":[{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":1},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":0},{"latencyMs":0,"count":1}],"dohHTTPResponseStatusCodes":{"200":2}}
}

func ExampleBenchmark_PrintReport_server_dns_errors() {
b, rs := testReportDataWithServerDNSErrors()

Expand All @@ -125,6 +184,7 @@ func ExampleBenchmark_PrintReport_server_dns_errors() {
//
// Time taken for tests: 1s
// Questions per second: 3.0
//
// Total Errors: 3
// Top errors:
// no such host unknown.host.com 3 (100.00)%
Expand Down
21 changes: 18 additions & 3 deletions cmd/result.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package cmd

import (
"errors"
"time"

"github.com/HdrHistogram/hdrhistogram-go"
"github.com/miekg/dns"
"github.com/tantalor93/doh-go/doh"
)

// Counters represents various counters of benchmark results.
Expand Down Expand Up @@ -38,10 +40,26 @@ type ResultStats struct {
Counters *Counters
Errors []ErrorDatapoint
AuthenticatedDomains map[string]struct{}
DoHStatusCodes map[int]int64
}

func (rs *ResultStats) record(req *dns.Msg, resp *dns.Msg, err error, time time.Time, duration time.Duration) {
rs.Counters.Total++

if rs.DoHStatusCodes != nil {
statusError := doh.UnexpectedServerHTTPStatusError{}
if err != nil && errors.As(err, &statusError) {
rs.DoHStatusCodes[statusError.HTTPStatus()]++
}
if err == nil {
rs.DoHStatusCodes[200]++
}
}

if rs.Qtypes != nil {
rs.Qtypes[dns.TypeToString[req.Question[0].Qtype]]++
}

if err != nil {
rs.Counters.IOError++
rs.Errors = append(rs.Errors, ErrorDatapoint{Start: time, Err: err})
Expand All @@ -68,9 +86,6 @@ func (rs *ResultStats) record(req *dns.Msg, resp *dns.Msg, err error, time time.
c++
rs.Codes[resp.Rcode] = c
}
if rs.Qtypes != nil {
rs.Qtypes[dns.TypeToString[req.Question[0].Qtype]]++
}
if resp.AuthenticatedData {
if rs.AuthenticatedDomains == nil {
rs.AuthenticatedDomains = make(map[string]struct{})
Expand Down
24 changes: 21 additions & 3 deletions cmd/stdreporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"io"
"sort"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -31,6 +32,24 @@ func (s *standardReporter) print(params reportParameters) error {
}
}

var dohResponseStatuses []int
for key := range params.dohResponseStatusesTotals {
dohResponseStatuses = append(dohResponseStatuses, key)
}
sort.Ints(dohResponseStatuses)

if len(params.dohResponseStatusesTotals) > 0 {
fmt.Println()
fmt.Println("DoH HTTP response status codes:")
for _, st := range dohResponseStatuses {
if st == 200 {
successPrint(params.outputWriter, "\t%d:\t%d\n", st, params.dohResponseStatusesTotals[st])
} else {
errPrint(params.outputWriter, "\t%d:\t%d\n", st, params.dohResponseStatusesTotals[st])
}
}
}

if len(params.qtypeTotals) > 0 {
fmt.Println()
fmt.Println("DNS question types:")
Expand All @@ -48,7 +67,7 @@ func (s *standardReporter) print(params reportParameters) error {

fmt.Println("Time taken for tests:\t", highlightStr(roundDuration(params.benchmarkDuration).String()))
fmt.Printf("Questions per second:\t %s", highlightStr(fmt.Sprintf("%0.1f", float64(params.totalCounters.Total)/params.benchmarkDuration.Seconds())))

fmt.Println()
min := time.Duration(params.timings.Min())
mean := time.Duration(params.timings.Mean())
sd := time.Duration(params.timings.StdDev())
Expand All @@ -60,7 +79,6 @@ func (s *standardReporter) print(params reportParameters) error {
p50 := time.Duration(params.timings.ValueAtQuantile(50))

if tc := params.timings.TotalCount(); tc > 0 {
fmt.Println()
fmt.Println("DNS timings,", highlightStr(tc), "datapoints")
fmt.Println("\t min:\t\t", highlightStr(roundDuration(min)))
fmt.Println("\t mean:\t\t", highlightStr(roundDuration(mean)))
Expand Down Expand Up @@ -98,7 +116,7 @@ func (s *standardReporter) print(params reportParameters) error {
}

func (b *Benchmark) printProgress(w io.Writer, c Counters) {
fmt.Printf("\n\nTotal requests:\t\t%s\n", highlightStr(c.Total))
fmt.Printf("\nTotal requests:\t\t%s\n", highlightStr(c.Total))

if c.IOError > 0 {
errPrint(w, "Read/Write errors:\t%d\n", c.IOError)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/quic-go/quic-go v0.42.0
github.com/schollz/progressbar/v3 v3.14.2
github.com/stretchr/testify v1.9.0
github.com/tantalor93/doh-go v0.1.1
github.com/tantalor93/doh-go v0.2.0
github.com/tantalor93/doq-go v0.10.0
go-hep.org/x/hep v0.34.1
go.uber.org/ratelimit v0.3.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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/tantalor93/doh-go v0.1.1 h1:yhdu+FfK54w6Wv/XGyZM5rFuofnZCxiGYPbJXY5vAkg=
github.com/tantalor93/doh-go v0.1.1/go.mod h1:/BofzWjI0lSfO4dANJrHN/6opCJjlpHkdm/p5XkbQSM=
github.com/tantalor93/doh-go v0.2.0 h1:yXFf7DM3Pij2r+fS6E/RFF/iO8trks/sZa3tZqPZpKw=
github.com/tantalor93/doh-go v0.2.0/go.mod h1:dQ224IhiJ3xD9LIvYxdBd0Brjp7kdOuh535dppqwSb4=
github.com/tantalor93/doq-go v0.10.0 h1:wQ2DYyIapOzTz/GvfrUHTdvE+mlzV8pdgNeVqsA5zm8=
github.com/tantalor93/doq-go v0.10.0/go.mod h1:v0Li1QQ8uu2c//7RQu+P0bkna1yBC6h5hjnhNUyCdBg=
github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRVe/J8=
Expand Down

0 comments on commit 1bd3739

Please sign in to comment.