summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/pkg/metrics/unique_counter.go
blob: dfc96126aab58f3b3a1eb8bb55981aae77aa9b23 (plain)
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
// SPDX-License-Identifier: GPL-3.0-or-later

package metrics

import (
	"github.com/axiomhq/hyperloglog"
	"github.com/netdata/netdata/go/go.d.plugin/pkg/stm"
)

type (
	UniqueCounter interface {
		stm.Value
		Insert(s string)
		Value() int
		Reset()
	}

	mapUniqueCounter struct {
		m map[string]bool
	}

	hyperLogLogUniqueCounter struct {
		sketch *hyperloglog.Sketch
	}

	UniqueCounterVec struct {
		useHyperLogLog bool
		Items          map[string]UniqueCounter
	}
)

var (
	_ stm.Value = mapUniqueCounter{}
	_ stm.Value = hyperLogLogUniqueCounter{}
	_ stm.Value = UniqueCounterVec{}
)

func NewUniqueCounter(useHyperLogLog bool) UniqueCounter {
	if useHyperLogLog {
		return &hyperLogLogUniqueCounter{hyperloglog.New()}
	}
	return mapUniqueCounter{map[string]bool{}}
}

func (c mapUniqueCounter) WriteTo(rv map[string]int64, key string, mul, div int) {
	rv[key] = int64(float64(c.Value()*mul) / float64(div))
}

func (c mapUniqueCounter) Insert(s string) {
	c.m[s] = true
}

func (c mapUniqueCounter) Value() int {
	return len(c.m)
}

func (c mapUniqueCounter) Reset() {
	for key := range c.m {
		delete(c.m, key)
	}
}

// WriteTo writes its value into given map.
func (c hyperLogLogUniqueCounter) WriteTo(rv map[string]int64, key string, mul, div int) {
	rv[key] = int64(float64(c.Value()*mul) / float64(div))
}

func (c *hyperLogLogUniqueCounter) Insert(s string) {
	c.sketch.Insert([]byte(s))
}

func (c *hyperLogLogUniqueCounter) Value() int {
	return int(c.sketch.Estimate())
}

func (c *hyperLogLogUniqueCounter) Reset() {
	c.sketch = hyperloglog.New()
}

func NewUniqueCounterVec(useHyperLogLog bool) UniqueCounterVec {
	return UniqueCounterVec{
		Items:          map[string]UniqueCounter{},
		useHyperLogLog: useHyperLogLog,
	}
}

// WriteTo writes its value into given map.
func (c UniqueCounterVec) WriteTo(rv map[string]int64, key string, mul, div int) {
	for name, value := range c.Items {
		value.WriteTo(rv, key+"_"+name, mul, div)
	}
}

// Get gets UniqueCounter instance by name
func (c UniqueCounterVec) Get(name string) UniqueCounter {
	item, ok := c.Items[name]
	if ok {
		return item
	}
	item = NewUniqueCounter(c.useHyperLogLog)
	c.Items[name] = item
	return item
}

func (c UniqueCounterVec) Reset() {
	for _, value := range c.Items {
		value.Reset()
	}
}