File: tracker.go

package info (click to toggle)
golang-github-cowsql-go-cowsql 1.22.0-2
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid, trixie
  • size: 716 kB
  • sloc: sh: 373; makefile: 5
file content (129 lines) | stat: -rw-r--r-- 2,796 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package benchmark

import (
	"fmt"
	"math"
	"strings"
	"sync"
	"time"
)

func durToMs(d time.Duration) string {
	ms := int64(d / time.Millisecond)
	rest := int64(d % time.Millisecond)
	return fmt.Sprintf("%d.%06d", ms, rest)
}

type measurement struct {
	start    time.Time
	duration time.Duration
}

func (m measurement) String() string {
	return fmt.Sprintf("%v %v", m.start.UnixNano(), durToMs(m.duration))
}

type measurementErr struct {
	start time.Time
	err   error
}

func (m measurementErr) String() string {
	return fmt.Sprintf("%v %v", m.start.UnixNano(), m.err)
}

type tracker struct {
	lock         sync.RWMutex
	measurements map[work][]measurement
	errors       map[work][]measurementErr
}

type report struct {
	n             int
	nErr          int
	totalDuration time.Duration
	avgDuration   time.Duration
	maxDuration   time.Duration
	minDuration   time.Duration
	measurements  []measurement
	errors        []measurementErr
}

func (r report) String() string {
	var msb strings.Builder
	for _, m := range r.measurements {
		fmt.Fprintf(&msb, "%s\n", m)
	}

	var esb strings.Builder
	for _, e := range r.errors {
		fmt.Fprintf(&esb, "%s\n", e)
	}

	return fmt.Sprintf("n %d\n"+
		"n_err %d\n"+
		"avg [ms] %s\n"+
		"max [ms] %s\n"+
		"min [ms] %s\n"+
		"measurements [timestamp in ns] [ms]\n%s\n"+
		"errors\n%s\n",
		r.n, r.nErr, durToMs(r.avgDuration),
		durToMs(r.maxDuration), durToMs(r.minDuration),
		msb.String(), esb.String())
}

func (t *tracker) measure(start time.Time, work work, err *error) {
	t.lock.Lock()
	defer t.lock.Unlock()
	duration := time.Since(start)
	if *err == nil {
		m := measurement{start, duration}
		t.measurements[work] = append(t.measurements[work], m)
	} else {
		e := measurementErr{start, *err}
		t.errors[work] = append(t.errors[work], e)
	}
}

func (t *tracker) report() map[work]report {
	t.lock.RLock()
	defer t.lock.RUnlock()
	reports := make(map[work]report)
	for w := range t.measurements {
		report := report{
			n:             len(t.measurements[w]),
			nErr:          len(t.errors[w]),
			totalDuration: 0,
			avgDuration:   0,
			maxDuration:   0,
			minDuration:   time.Duration(math.MaxInt64),
			measurements:  t.measurements[w],
			errors:        t.errors[w],
		}

		for _, m := range t.measurements[w] {
			report.totalDuration += m.duration
			if m.duration < report.minDuration {
				report.minDuration = m.duration
			}
			if m.duration > report.maxDuration {
				report.maxDuration = m.duration
			}
		}

		if report.n > 0 {
			report.avgDuration = report.totalDuration / time.Duration(report.n)
		}
		reports[w] = report
	}

	return reports
}

func newTracker() *tracker {
	return &tracker{
		lock:         sync.RWMutex{},
		measurements: make(map[work][]measurement),
		errors:       make(map[work][]measurementErr),
	}
}