mirror of
https://github.com/prometheus/prometheus.git
synced 2026-10-28 10:44:59 +08:00
267 lines
11 KiB
Go
267 lines
11 KiB
Go
// Copyright The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/prometheus/prometheus/model/exemplar"
|
|
"github.com/prometheus/prometheus/model/histogram"
|
|
"github.com/prometheus/prometheus/model/labels"
|
|
"github.com/prometheus/prometheus/model/metadata"
|
|
)
|
|
|
|
// AppendableV2 allows creating AppenderV2.
|
|
type AppendableV2 interface {
|
|
// AppenderV2 returns a new appender for the storage.
|
|
//
|
|
// Implementations CAN choose whether to use the context e.g. for deadlines,
|
|
// but it's not mandatory.
|
|
AppenderV2(ctx context.Context) AppenderV2
|
|
}
|
|
|
|
// AOptions is a shorthand for AppendV2Options.
|
|
// NOTE: AppendOption is used already.
|
|
type AOptions = AppendV2Options
|
|
|
|
// AppendV2Options provides optional, auxiliary data and configuration for AppenderV2.Append.
|
|
type AppendV2Options struct {
|
|
// MetricFamilyName (optional) provides metric family name for the appended sample's
|
|
// series. If the client of the AppenderV2 has this information
|
|
// (e.g. from scrape) it's recommended to pass it to the appender.
|
|
//
|
|
// Provided string bytes are unsafe to reuse, it only lives for the duration of the Append call.
|
|
//
|
|
// Some implementations use this to avoid slow and prone to error metric family detection for:
|
|
// * Metadata per metric family storages (e.g. Prometheus metadata WAL/API/RW1)
|
|
// * Strictly complex types storages (e.g. OpenTelemetry Collector).
|
|
//
|
|
// NOTE(krajorama): Example purpose is highlighted in OTLP ingestion: OTLP calculates the
|
|
// metric family name for all metrics and uses it for generating summary,
|
|
// histogram series by adding the magic suffixes. The metric family name is
|
|
// passed down to the appender in case the storage needs it for metadata updates.
|
|
// Known user of this is Mimir that implements /api/v1/metadata and uses
|
|
// Remote-Write 1.0 for this. Might be removed later if no longer
|
|
// needed by any downstream project.
|
|
// NOTE(bwplotka): Long term, once Prometheus uses complex types on storage level
|
|
// the MetricFamilyName can be removed as MetricFamilyName will equal to __name__ always.
|
|
MetricFamilyName string
|
|
|
|
// Metadata (optional) attached to the appended sample.
|
|
// Metadata strings are safe for reuse.
|
|
// IMPORTANT: Appender v1 was only providing update. This field MUST be
|
|
// set (if known) even if it didn't change since the last iteration.
|
|
// This moves the responsibility for metadata storage options to TSDB.
|
|
Metadata metadata.Metadata
|
|
|
|
// Exemplars (optional) attached to the appended sample.
|
|
// Exemplar slice MUST be sorted by Exemplar.TS.
|
|
// Exemplar slice is unsafe for reuse.
|
|
// Duplicate exemplars errors MUST be ignored by implementations.
|
|
Exemplars []exemplar.Exemplar
|
|
|
|
// RejectOutOfOrder tells implementation that this append should not be out
|
|
// of order. An OOO append MUST be rejected with storage.ErrOutOfOrderSample
|
|
// error.
|
|
RejectOutOfOrder bool
|
|
}
|
|
|
|
// AppendPartialError represents an AppenderV2.Append or
|
|
// ExemplarAppenderV2.AppendExemplars error that tells
|
|
// callers sample was written (for Append) and some, or all, of the given
|
|
// auxiliary optional data (e.g. exemplars) was not written.
|
|
//
|
|
// It's up to the caller to decide if it's an ignorable error or not, plus
|
|
// it allows extra reporting (e.g. for Remote Write 2.0 X-Remote-Write-Written headers).
|
|
type AppendPartialError struct {
|
|
// ExemplarErrors reports the exemplars that were not written. Several
|
|
// entries can share an Index when more than one storage rejects the same
|
|
// exemplar, e.g. behind a fanout storage.
|
|
ExemplarErrors []ExemplarError
|
|
}
|
|
|
|
// ExemplarError reports why an exemplar was not written.
|
|
type ExemplarError struct {
|
|
// Index is the position of the exemplar in the slice passed to
|
|
// AppenderV2.Append (AppendV2Options.Exemplars) or
|
|
// ExemplarAppenderV2.AppendExemplars.
|
|
Index int
|
|
// Err is why the exemplar was not written. It must not be nil.
|
|
Err error
|
|
}
|
|
|
|
// Error returns the message of Err.
|
|
func (e ExemplarError) Error() string {
|
|
return e.Err.Error()
|
|
}
|
|
|
|
// Unwrap returns Err.
|
|
func (e ExemplarError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// Error returns combined error string.
|
|
func (e *AppendPartialError) Error() string {
|
|
if e == nil {
|
|
return "<nil>"
|
|
}
|
|
|
|
errs := make([]error, 0, len(e.ExemplarErrors))
|
|
for _, eErr := range e.ExemplarErrors {
|
|
errs = append(errs, eErr.Err)
|
|
}
|
|
joined := errors.Join(errs...)
|
|
if joined == nil {
|
|
return ""
|
|
}
|
|
return joined.Error()
|
|
}
|
|
|
|
// ToError returns AppendPartialError as error, returning nil
|
|
// if there are no errors.
|
|
func (e *AppendPartialError) ToError() error {
|
|
if e == nil || len(e.ExemplarErrors) == 0 {
|
|
return nil
|
|
}
|
|
return e
|
|
}
|
|
|
|
// Is implements method that's expected by errors.Is.
|
|
func (*AppendPartialError) Is(target error) bool {
|
|
// This does not need to handle wrapped errors as AppendPartialError.Is should be used
|
|
// via errors.Is.
|
|
_, ok := target.(*AppendPartialError)
|
|
return ok
|
|
}
|
|
|
|
// Handle handles the given err that may be an AppendPartialError.
|
|
// If the err is nil or not an AppendPartialError it returns err.
|
|
// Otherwise, partial errors are aggregated.
|
|
func (e *AppendPartialError) Handle(err error) (*AppendPartialError, error) {
|
|
if err == nil {
|
|
return e, nil
|
|
}
|
|
|
|
// Fast, alloc-free path first for non-partial error cases.
|
|
if !errors.Is(err, e) {
|
|
return e, err
|
|
}
|
|
var pErr *AppendPartialError
|
|
if !errors.As(err, &pErr) {
|
|
return e, err
|
|
}
|
|
|
|
if e == nil {
|
|
// Lazy allocation.
|
|
e = &AppendPartialError{}
|
|
}
|
|
e.ExemplarErrors = append(e.ExemplarErrors, pErr.ExemplarErrors...)
|
|
return e, nil
|
|
}
|
|
|
|
var _ error = &AppendPartialError{}
|
|
|
|
// AppenderV2 provides appends against a storage for all types of samples.
|
|
// It must be completed with a call to Commit or Rollback and must not be reused afterwards.
|
|
//
|
|
// Operations on the AppenderV2 interface are not goroutine-safe.
|
|
//
|
|
// The order of samples appended via the AppenderV2 is preserved within each series.
|
|
// I.e. timestamp order within batch is not validated, samples are not reordered per timestamp or by float/histogram
|
|
// type.
|
|
type AppenderV2 interface {
|
|
AppenderTransaction
|
|
|
|
// Append appends a sample and related exemplars, metadata, and start timestamp (st) to the storage.
|
|
//
|
|
// ref (optional) represents the stable ID for the given series identified by ls (excluding metadata).
|
|
// Callers MAY provide the ref to help implementation avoid ls -> ref computation, otherwise ref MUST be 0 (unknown).
|
|
//
|
|
// ls represents labels for the sample's series.
|
|
//
|
|
// st (optional) represents sample start timestamp. 0 means unknown. Implementations
|
|
// are responsible for any potential ST storage logic (e.g. ST zero injections).
|
|
//
|
|
// t represents sample timestamp.
|
|
//
|
|
// v, h, fh represents sample value for each sample type.
|
|
// Callers MUST only provide one of the sample types (either v, h or fh).
|
|
// Implementations can detect the type of the sample with the following switch:
|
|
//
|
|
// switch {
|
|
// case fh != nil: It's a float histogram append.
|
|
// case h != nil: It's a histogram append.
|
|
// default: It's a float append.
|
|
// }
|
|
// TODO(bwplotka): We plan to experiment on using generics for complex sampleType, but do it after we unify interface (derisk) and before we add native summaries.
|
|
//
|
|
// Implementations MUST attempt to append sample even if metadata, exemplar or (st) start timestamp appends fail.
|
|
// Implementations MAY return AppendPartialError as an error to report which of the
|
|
// given opts.Exemplars failed. Use errors.As to detect.
|
|
// For the successful Append, Implementations MUST return valid SeriesRef that represents ls.
|
|
// NOTE(bwplotka): Given OTLP and native histograms and the relaxation of the requirement for
|
|
// type and unit suffixes in metric names we start to hit cases of ls being not enough for id
|
|
// of the series (metadata matters). Current solution is to enable 'type-and-unit-label' features for those cases, but we may
|
|
// start to extend the id with metadata one day.
|
|
Append(ref SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts AppendV2Options) (SeriesRef, error)
|
|
}
|
|
|
|
// ExemplarAppenderV2 is an optional extension of AppenderV2 (mirroring the V1
|
|
// ExemplarAppender) that allows appending exemplars in a separate call from the
|
|
// sample Append call.
|
|
//
|
|
// Exemplars appended this way are still part of the AppenderV2 transaction:
|
|
// they are persisted on Commit and discarded on Rollback, exactly like the
|
|
// exemplars passed inline via AppendV2Options.Exemplars.
|
|
//
|
|
// It is intended for callers whose wire protocol decouples exemplars from
|
|
// individual samples (such as the Remote Write 1.0 and 2.0 receivers), while
|
|
// keeping AppenderV2 minimal for downstream implementations that prefer
|
|
// inlined exemplars via AppendV2Options.Exemplars (e.g. OpenTelemetry Collector).
|
|
type ExemplarAppenderV2 interface {
|
|
AppenderV2
|
|
|
|
// AppendExemplars appends one or more exemplars for the given series.
|
|
//
|
|
// AppendExemplars MUST be called after the corresponding sample Append for the same series
|
|
// in the same or an earlier transaction. Implementations MUST NOT create a new series for
|
|
// exemplars alone; if the series does not exist, AppendExemplars MUST return storage.ErrNotFound.
|
|
//
|
|
// The ref (optional) and ls (required) parameters follow the same contract as AppenderV2.Append.
|
|
//
|
|
// The exemplars slice MUST be sorted by Exemplar.Ts, and is unsafe for reuse (it only lives
|
|
// for the duration of the AppendExemplars call; implementations MUST NOT retain references
|
|
// to it or mutate it in place).
|
|
// Duplicate exemplar errors MUST be ignored by implementations (not returned).
|
|
//
|
|
// Implementations MUST attempt to append every exemplar even if one of them fails.
|
|
// Implementations MAY return AppendPartialError as an error to report which of the
|
|
// given exemplars failed. Use errors.As to detect it.
|
|
AppendExemplars(ref SeriesRef, ls labels.Labels, exemplars []exemplar.Exemplar) (SeriesRef, error)
|
|
}
|
|
|
|
// AppenderTransaction allows transactional appends.
|
|
type AppenderTransaction interface {
|
|
// Commit submits the collected samples and purges the batch. If Commit
|
|
// returns a non-nil error, it also rolls back all modifications made in
|
|
// the appender so far, as Rollback would do. In any case, an Appender
|
|
// must not be used anymore after Commit has been called.
|
|
Commit() error
|
|
|
|
// Rollback rolls back all modifications made in the appender so far.
|
|
// Appender has to be discarded after rollback.
|
|
Rollback() error
|
|
}
|