Skip to content

Commit 3dcf61c

Browse files
committed
Added blocking registry, with raw collector and transactional handler.
Signed-off-by: Bartlomiej Plotka <[email protected]>
1 parent fdd407a commit 3dcf61c

File tree

3 files changed

+57
-24
lines changed

3 files changed

+57
-24
lines changed

prometheus/cache.go

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"fmt"
1919
"sort"
2020
"sync"
21+
"time"
2122

2223
"github.com/cespare/xxhash/v2"
2324
"github.com/golang/protobuf/proto"
@@ -80,30 +81,42 @@ func (s *CollectSession) Commit() {
8081
s.c.pendingSession = false
8182
}
8283

83-
// NewMetric ...
84+
func (s *CollectSession) MustAddMetric(fqName, help string, labelNames, labelValues []string, valueType ValueType, value float64, ts *time.Time) {
85+
if err := s.AddMetric(fqName, help, labelNames, labelValues, valueType, value, ts); err != nil {
86+
panic(err)
87+
}
88+
}
89+
90+
// AddMetric ...
8491
// TODO(bwplotka): Add validation.
85-
func (s *CollectSession) NewMetric(fqName, help string, labelNames, labelValues []string, valueType ValueType, value float64) error {
92+
func (s *CollectSession) AddMetric(fqName, help string, labelNames, labelValues []string, valueType ValueType, value float64, ts *time.Time) error {
8693
if s.closed {
8794
return errors.New("new metric: collect session is closed, but was attempted to be used")
8895
}
8996

90-
if !sort.StringsAreSorted(labelNames) {
91-
return errors.New("new metric: label names has to be sorted")
92-
}
97+
// Label names can be unsorted, will be sorting them later. The only implication is cachability if
98+
// consumer provide non-deterministic order of those (unlikely since label values has to be matched.
9399

94100
if len(labelNames) != len(labelValues) {
95101
return errors.New("new metric: label name has different len than values")
96102
}
97103

98-
d, ok := s.c.metricFamilyByName[fqName]
104+
d, ok := s.currentByName[fqName]
105+
if !ok {
106+
d, ok = s.c.metricFamilyByName[fqName]
107+
if ok {
108+
d.Metric = d.Metric[:0]
109+
}
110+
}
111+
99112
if !ok {
100113
// TODO(bwplotka): Validate?
101114
d = &dto.MetricFamily{}
102115
d.Name = proto.String(fqName)
103116
d.Type = valueType.ToDTO()
104117
d.Help = proto.String(help)
105118
} else {
106-
// TODO(bwplotka): Validate if same family.
119+
// TODO(bwplotka): Validate if same family?
107120
d.Type = valueType.ToDTO()
108121
d.Help = proto.String(help)
109122
}
@@ -120,6 +133,9 @@ func (s *CollectSession) NewMetric(fqName, help string, labelNames, labelValues
120133
}
121134
hSum := h.Sum64()
122135

136+
if _, ok := s.currentMetrics[hSum]; ok {
137+
return fmt.Errorf("found duplicate metric (same labels and values) to add %v", fqName)
138+
}
123139
m, ok := s.c.metrics[hSum]
124140
if !ok {
125141
m = &dto.Metric{
@@ -166,23 +182,28 @@ func (s *CollectSession) NewMetric(fqName, help string, labelNames, labelValues
166182
return fmt.Errorf("unsupported value type %v", valueType)
167183
}
168184

185+
m.TimestampMs = nil
186+
if ts != nil {
187+
m.TimestampMs = proto.Int64(ts.Unix()*1000 + int64(ts.Nanosecond()/1000000))
188+
}
189+
169190
// Will be sorted later.
170191
d.Metric = append(d.Metric, m)
171192
return nil
172193
}
173194

174195
type BlockingRegistry struct {
175-
Gatherer
196+
*Registry
176197

177198
// rawCollector represents special collectors which requires blocking collect for the whole duration
178199
// of returned dto.MetricFamily usage.
179200
rawCollectors []rawCollector
180201
mu sync.Mutex
181202
}
182203

183-
func NewBlockingRegistry(g Gatherer) *BlockingRegistry {
204+
func NewBlockingRegistry() *BlockingRegistry {
184205
return &BlockingRegistry{
185-
Gatherer: g,
206+
Registry: NewRegistry(),
186207
}
187208
}
188209

@@ -203,9 +224,9 @@ func (b *BlockingRegistry) MustRegisterRaw(r rawCollector) {
203224
}
204225

205226
func (b *BlockingRegistry) Gather() (_ []*dto.MetricFamily, done func(), err error) {
206-
mfs, err := b.Gatherer.Gather()
207-
208227
b.mu.Lock()
228+
mfs, err := b.Registry.Gather()
229+
209230
// TODO(bwplotka): Returned mfs are sorted, so sort raw ones and inject?
210231
// TODO(bwplotka): Implement concurrency for those?
211232
for _, r := range b.rawCollectors {
@@ -225,3 +246,16 @@ type TransactionalGatherer interface {
225246
// Gather ...
226247
Gather() (_ []*dto.MetricFamily, done func(), err error)
227248
}
249+
250+
func ToTransactionalGatherer(g Gatherer) TransactionalGatherer {
251+
return &noTransactionGatherer{g: g}
252+
}
253+
254+
type noTransactionGatherer struct {
255+
g Gatherer
256+
}
257+
258+
func (g *noTransactionGatherer) Gather() (_ []*dto.MetricFamily, done func(), err error) {
259+
mfs, err := g.g.Gather()
260+
return mfs, func() {}, err
261+
}

prometheus/promhttp/http.go

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import (
4040
"sync"
4141
"time"
4242

43-
dto "github.com/prometheus/client_model/go"
4443
"github.com/prometheus/common/expfmt"
4544

4645
"github.com/prometheus/client_golang/prometheus"
@@ -85,16 +84,7 @@ func Handler() http.Handler {
8584
// instrumentation. Use the InstrumentMetricHandler function to apply the same
8685
// kind of instrumentation as it is used by the Handler function.
8786
func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
88-
return HandlerForTransactional(&noTransactionGatherer{reg: reg}, opts)
89-
}
90-
91-
type noTransactionGatherer struct {
92-
reg prometheus.Gatherer
93-
}
94-
95-
func (g *noTransactionGatherer) Gather() (_ []*dto.MetricFamily, done func(), err error) {
96-
mfs, err := g.reg.Gather()
97-
return mfs, func() {}, err
87+
return HandlerForTransactional(prometheus.ToTransactionalGatherer(reg), opts)
9888
}
9989

10090
func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerOpts) http.Handler {

prometheus/testutil/testutil.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,16 @@ func CollectAndCompare(c prometheus.Collector, expected io.Reader, metricNames .
167167
// exposition format. If any metricNames are provided, only metrics with those
168168
// names are compared.
169169
func GatherAndCompare(g prometheus.Gatherer, expected io.Reader, metricNames ...string) error {
170-
got, err := g.Gather()
170+
return TransactionalGatherAndCompare(prometheus.ToTransactionalGatherer(g), expected, metricNames...)
171+
}
172+
173+
// TransactionalGatherAndCompare gathers all metrics from the provided Gatherer and compares
174+
// it to an expected output read from the provided Reader in the Prometheus text
175+
// exposition format. If any metricNames are provided, only metrics with those
176+
// names are compared.
177+
func TransactionalGatherAndCompare(g prometheus.TransactionalGatherer, expected io.Reader, metricNames ...string) error {
178+
got, done, err := g.Gather()
179+
defer done()
171180
if err != nil {
172181
return fmt.Errorf("gathering metrics failed: %s", err)
173182
}

0 commit comments

Comments
 (0)