[tor-commits] [snowflake/master] Implement binned counts for polling metrics

cohosh at torproject.org cohosh at torproject.org
Mon Apr 26 18:23:28 UTC 2021


commit 92bd900bc57f1d56c21c5abf736deb6ce3a83837
Author: Cecylia Bocovich <cohosh at torproject.org>
Date:   Wed Mar 31 10:52:01 2021 -0400

    Implement binned counts for polling metrics
---
 broker/metrics.go               | 38 +++++++++++--------
 broker/prometheus.go            | 83 +++++++++++++++++++++++++++++++++++++++++
 broker/snowflake-broker_test.go |  8 ----
 go.mod                          |  2 +
 4 files changed, 108 insertions(+), 23 deletions(-)

diff --git a/broker/metrics.go b/broker/metrics.go
index 6939742..24ff9b0 100644
--- a/broker/metrics.go
+++ b/broker/metrics.go
@@ -20,11 +20,11 @@ import (
 
 var (
 	once        sync.Once
-	promMetrics *PromMetrics
+	promMetrics = initPrometheus()
 )
 
 const (
-	PrometheusNamespace = "snowflake"
+	prometheusNamespace = "snowflake"
 	metricsResolution   = 60 * 60 * 24 * time.Second //86400 seconds
 )
 
@@ -147,6 +147,7 @@ func (m *Metrics) UpdateCountryStats(addr string, proxyType string, natType stri
 	} else {
 		m.countryStats.unknown[addr] = true
 	}
+
 	promMetrics.ProxyTotal.With(prometheus.Labels{
 		"nat":  natType,
 		"type": proxyType,
@@ -261,40 +262,47 @@ func binCount(count uint) uint {
 
 type PromMetrics struct {
 	ProxyTotal      *prometheus.CounterVec
-	ProxyPollTotal  *prometheus.CounterVec
-	ClientPollTotal *prometheus.CounterVec
+	ProxyPollTotal  *RoundedCounterVec
+	ClientPollTotal *RoundedCounterVec
 }
 
 //Initialize metrics for prometheus exporter
-func InitPrometheus() {
+func initPrometheus() *PromMetrics {
 
-	promMetrics = &PromMetrics{}
+	promMetrics := &PromMetrics{}
 
 	promMetrics.ProxyTotal = promauto.NewCounterVec(
 		prometheus.CounterOpts{
-			Namespace: PrometheusNamespace,
+			Namespace: prometheusNamespace,
 			Name:      "proxy_total",
 			Help:      "The number of unique snowflake IPs",
 		},
 		[]string{"type", "nat", "cc"},
 	)
 
-	promMetrics.ProxyPollTotal = promauto.NewCounterVec(
+	promMetrics.ProxyPollTotal = NewRoundedCounterVec(
 		prometheus.CounterOpts{
-			Namespace: PrometheusNamespace,
-			Name:      "proxy_poll_total",
-			Help:      "The number of snowflake proxy polls",
+			Namespace: prometheusNamespace,
+			Name:      "rounded_proxy_poll_total",
+			Help:      "The number of snowflake proxy polls, rounded up to a multiple of 8",
 		},
 		[]string{"nat", "status"},
 	)
 
-	promMetrics.ClientPollTotal = promauto.NewCounterVec(
+	promMetrics.ClientPollTotal = NewRoundedCounterVec(
 		prometheus.CounterOpts{
-			Namespace: PrometheusNamespace,
-			Name:      "client_poll_total",
-			Help:      "The number of snowflake client polls",
+			Namespace: prometheusNamespace,
+			Name:      "rounded_client_poll_total",
+			Help:      "The number of snowflake client polls, rounded up to a multiple of 8",
 		},
 		[]string{"nat", "status"},
 	)
 
+	// We need to register this new metric type because there is no constructor
+	// for it in promauto.
+	prometheus.DefaultRegisterer.MustRegister(promMetrics.ClientPollTotal)
+	prometheus.DefaultRegisterer.MustRegister(promMetrics.ProxyPollTotal)
+
+	return promMetrics
+
 }
diff --git a/broker/prometheus.go b/broker/prometheus.go
new file mode 100644
index 0000000..d7592ec
--- /dev/null
+++ b/broker/prometheus.go
@@ -0,0 +1,83 @@
+/*
+Implements some additional prometheus metrics that we need for privacy preserving
+counts of users and proxies
+*/
+
+package main
+
+import (
+	"sync/atomic"
+
+	"github.com/prometheus/client_golang/prometheus"
+	dto "github.com/prometheus/client_model/go"
+	"google.golang.org/protobuf/proto"
+)
+
+// New Prometheus counter type that produces rounded counts of metrics
+// for privacy preserving reasons
+type RoundedCounter interface {
+	prometheus.Metric
+
+	Inc()
+}
+
+type roundedCounter struct {
+	total uint64 //reflects the true count
+	value uint64 //reflects the rounded count
+
+	desc       *prometheus.Desc
+	labelPairs []*dto.LabelPair
+}
+
+// Implements the RoundedCounter interface
+func (c *roundedCounter) Inc() {
+	atomic.AddUint64(&c.total, 1)
+	if c.total > c.value {
+		atomic.AddUint64(&c.value, 8)
+	}
+}
+
+// Implements the prometheus.Metric interface
+func (c *roundedCounter) Desc() *prometheus.Desc {
+	return c.desc
+}
+
+// Implements the prometheus.Metric interface
+func (c *roundedCounter) Write(m *dto.Metric) error {
+	m.Label = c.labelPairs
+
+	m.Counter = &dto.Counter{Value: proto.Float64(float64(c.value))}
+	return nil
+}
+
+// New prometheus vector type that will track RoundedCounter metrics
+// accross multiple labels
+type RoundedCounterVec struct {
+	*prometheus.MetricVec
+}
+
+func NewRoundedCounterVec(opts prometheus.CounterOpts, labelNames []string) *RoundedCounterVec {
+	desc := prometheus.NewDesc(
+		prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+		opts.Help,
+		labelNames,
+		opts.ConstLabels,
+	)
+	return &RoundedCounterVec{
+		MetricVec: prometheus.NewMetricVec(desc, func(lvs ...string) prometheus.Metric {
+			if len(lvs) != len(labelNames) {
+				panic("inconsistent cardinality")
+			}
+			return &roundedCounter{desc: desc, labelPairs: prometheus.MakeLabelPairs(desc, lvs)}
+		}),
+	}
+}
+
+// Helper function to return the underlying RoundedCounter metric from MetricVec
+func (v *RoundedCounterVec) With(labels prometheus.Labels) RoundedCounter {
+	metric, err := v.GetMetricWith(labels)
+	if err != nil {
+		panic(err)
+	}
+	return metric.(RoundedCounter)
+}
diff --git a/broker/snowflake-broker_test.go b/broker/snowflake-broker_test.go
index 987aae8..b676b04 100644
--- a/broker/snowflake-broker_test.go
+++ b/broker/snowflake-broker_test.go
@@ -26,8 +26,6 @@ var promOnce sync.Once
 
 func TestBroker(t *testing.T) {
 
-	promOnce.Do(InitPrometheus)
-
 	Convey("Context", t, func() {
 		ctx := NewBrokerContext(NullLogger())
 
@@ -303,8 +301,6 @@ func TestBroker(t *testing.T) {
 }
 
 func TestSnowflakeHeap(t *testing.T) {
-	promOnce.Do(InitPrometheus)
-
 	Convey("SnowflakeHeap", t, func() {
 		h := new(SnowflakeHeap)
 		heap.Init(h)
@@ -348,8 +344,6 @@ func TestSnowflakeHeap(t *testing.T) {
 }
 
 func TestGeoip(t *testing.T) {
-	promOnce.Do(InitPrometheus)
-
 	Convey("Geoip", t, func() {
 		tv4 := new(GeoIPv4Table)
 		err := GeoIPLoadFile(tv4, "test_geoip")
@@ -454,8 +448,6 @@ func TestGeoip(t *testing.T) {
 }
 
 func TestMetrics(t *testing.T) {
-	promOnce.Do(InitPrometheus)
-
 	Convey("Test metrics...", t, func() {
 		done := make(chan bool)
 		buf := new(bytes.Buffer)
diff --git a/go.mod b/go.mod
index ab3dc96..ed07394 100644
--- a/go.mod
+++ b/go.mod
@@ -12,10 +12,12 @@ require (
 	github.com/pion/transport v0.12.3 // indirect
 	github.com/pion/webrtc/v3 v3.0.15
 	github.com/prometheus/client_golang v1.10.0
+	github.com/prometheus/client_model v0.2.0
 	github.com/smartystreets/goconvey v1.6.4
 	github.com/xtaci/kcp-go/v5 v5.5.12
 	github.com/xtaci/smux v1.5.12
 	golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670
 	golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4
 	golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e // indirect
+	google.golang.org/protobuf v1.23.0
 )





More information about the tor-commits mailing list