1// Copyright 2014 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package storage
15
16import (
17	"sort"
18	"time"
19
20	//nolint:staticcheck // Ignore SA1019. Dependencies use the deprecated package, so we have to, too.
21	"github.com/golang/protobuf/proto"
22
23	dto "github.com/prometheus/client_model/go"
24)
25
26// MetricStore is the interface to the storage layer for metrics. All its
27// methods must be safe to be called concurrently.
28type MetricStore interface {
29	// SubmitWriteRequest submits a WriteRequest for processing. There is no
30	// guarantee when a request will be processed, but it is guaranteed that
31	// the requests are processed in the order of submission.
32	SubmitWriteRequest(req WriteRequest)
33	// GetMetricFamilies returns all the currently saved MetricFamilies. The
34	// returned MetricFamilies are guaranteed to not be modified by the
35	// MetricStore anymore. However, they may still be read somewhere else,
36	// so the caller is not allowed to modify the returned MetricFamilies.
37	// If different groups have saved MetricFamilies of the same name, they
38	// are all merged into one MetricFamily by concatenating the contained
39	// Metrics. Inconsistent help strings are logged, and one of the
40	// versions will "win". Inconsistent types and inconsistent or duplicate
41	// label sets will go undetected.
42	GetMetricFamilies() []*dto.MetricFamily
43	// GetMetricFamiliesMap returns a map grouping-key -> MetricGroup. The
44	// MetricFamily pointed to by the Metrics map in each MetricGroup is
45	// guaranteed to not be modified by the MetricStore anymore. However,
46	// they may still be read somewhere else, so the caller is not allowed
47	// to modify it. Otherwise, the returned nested map can be seen as a
48	// deep copy of the internal state of the MetricStore and completely
49	// owned by the caller.
50	GetMetricFamiliesMap() GroupingKeyToMetricGroup
51	// Shutdown must only be called after the caller has made sure that
52	// SubmitWriteRequests is not called anymore. (If it is called later,
53	// the request might get submitted, but not processed anymore.) The
54	// Shutdown method waits for the write request queue to empty, then it
55	// persists the content of the MetricStore (if supported by the
56	// implementation). Also, all internal goroutines are stopped. This
57	// method blocks until all of that is complete. If an error is
58	// encountered, it is returned (whereupon the MetricStorage is in an
59	// undefinded state). If nil is returned, the MetricStore cannot be
60	// "restarted" again, but it can still be used for read operations.
61	Shutdown() error
62	// Healthy returns nil if the MetricStore is currently working as
63	// expected. Otherwise, a non-nil error is returned.
64	Healthy() error
65	// Ready returns nil if the MetricStore is ready to be used (all files
66	// are opened and checkpoints have been restored). Otherwise, a non-nil
67	// error is returned.
68	Ready() error
69}
70
71// WriteRequest is a request to change the MetricStore, i.e. to process it, a
72// write lock has to be acquired.
73//
74// If MetricFamilies is nil, this is a request to delete metrics that share the
75// given Labels as a grouping key. Otherwise, this is a request to update the
76// MetricStore with the MetricFamilies.
77//
78// If Replace is true, the MetricFamilies will completely replace the metrics
79// with the same grouping key. Otherwise, only those MetricFamilies with the
80// same name as new MetricFamilies will be replaced.
81//
82// The key in MetricFamilies is the name of the mapped metric family.
83//
84// When the WriteRequest is processed, the metrics in MetricFamilies will be
85// sanitized to have the same job and other labels as those in the Labels
86// fields. Also, if there is no instance label, an instance label with an empty
87// value will be set. This implies that the MetricFamilies in the WriteRequest
88// may be modified be the MetricStore during processing of the WriteRequest!
89//
90// The Timestamp field marks the time the request was received from the
91// network. It is not related to the TimestampMs field in the Metric proto
92// message. In fact, WriteRequests containing any Metrics with a TimestampMs set
93// are invalid and will be rejected.
94//
95// The Done channel may be nil. If it is not nil, it will be closed once the
96// write request is processed. Any errors occurring during processing are sent to
97// the channel before closing it.
98type WriteRequest struct {
99	Labels         map[string]string
100	Timestamp      time.Time
101	MetricFamilies map[string]*dto.MetricFamily
102	Replace        bool
103	Done           chan error
104}
105
106// GroupingKeyToMetricGroup is the first level of the metric store, keyed by
107// grouping key.
108type GroupingKeyToMetricGroup map[string]MetricGroup
109
110// MetricGroup adds the grouping labels to a NameToTimestampedMetricFamilyMap.
111type MetricGroup struct {
112	Labels  map[string]string
113	Metrics NameToTimestampedMetricFamilyMap
114}
115
116// SortedLabels returns the label names of the grouping labels sorted
117// lexicographically but with the "job" label always first. This method exists
118// for presentation purposes, see template.html.
119func (mg MetricGroup) SortedLabels() []string {
120	lns := make([]string, 1, len(mg.Labels))
121	lns[0] = "job"
122	for ln := range mg.Labels {
123		if ln != "job" {
124			lns = append(lns, ln)
125		}
126	}
127	sort.Strings(lns[1:])
128	return lns
129}
130
131// LastPushSuccess returns false if the automatically added metric for the
132// timestamp of the last failed push has a value larger than the value of the
133// automatically added metric for the timestamp of the last successful push. In
134// all other cases, it returns true (including the case that one or both of
135// those metrics are missing for some reason.)
136func (mg MetricGroup) LastPushSuccess() bool {
137	fail := mg.Metrics[pushFailedMetricName].GobbableMetricFamily
138	if fail == nil {
139		return true
140	}
141	success := mg.Metrics[pushMetricName].GobbableMetricFamily
142	if success == nil {
143		return true
144	}
145	return (*dto.MetricFamily)(fail).GetMetric()[0].GetGauge().GetValue() <= (*dto.MetricFamily)(success).GetMetric()[0].GetGauge().GetValue()
146}
147
148// NameToTimestampedMetricFamilyMap is the second level of the metric store,
149// keyed by metric name.
150type NameToTimestampedMetricFamilyMap map[string]TimestampedMetricFamily
151
152// TimestampedMetricFamily adds the push timestamp to a gobbable version of the
153// MetricFamily-DTO.
154type TimestampedMetricFamily struct {
155	Timestamp            time.Time
156	GobbableMetricFamily *GobbableMetricFamily
157}
158
159// GetMetricFamily returns the normal GetMetricFamily DTO (without the gob additions).
160func (tmf TimestampedMetricFamily) GetMetricFamily() *dto.MetricFamily {
161	return (*dto.MetricFamily)(tmf.GobbableMetricFamily)
162}
163
164// GobbableMetricFamily is a dto.MetricFamily that implements GobDecoder and
165// GobEncoder.
166type GobbableMetricFamily dto.MetricFamily
167
168// GobDecode implements gob.GobDecoder.
169func (gmf *GobbableMetricFamily) GobDecode(b []byte) error {
170	return proto.Unmarshal(b, (*dto.MetricFamily)(gmf))
171}
172
173// GobEncode implements gob.GobEncoder.
174func (gmf *GobbableMetricFamily) GobEncode() ([]byte, error) {
175	return proto.Marshal((*dto.MetricFamily)(gmf))
176}
177