Skip to content

Commit 3c8f741

Browse files
committed
feat(2.53.5): opt-in fix for export.match from config
Implements step 1 from go/gmp:matchstuck Signed-off-by: bwplotka <bwplotka@gmail.com>
1 parent cb9dbbd commit 3c8f741

7 files changed

Lines changed: 559 additions & 127 deletions

File tree

google/config/config.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package config
22

33
// GoogleCloudConfig represents a custom Prometheus config entry
44
// required by the hot-reloading requirements in high security k8s setups.
5+
//
6+
// All configuration parameters overrides the corresponding EXTRA_ARGS environment
7+
// or CLI flags.
58
type GoogleCloudConfig struct {
69
Export GoogleCloudExportConfig `yaml:"export,omitempty"`
710
Query GoogleCloudQueryConfig `yaml:"query,omitempty"`
@@ -11,10 +14,24 @@ type GoogleCloudConfig struct {
1114
type GoogleCloudExportConfig struct {
1215
// Compression controls the ExporterOpts.Compression setting.
1316
Compression string `yaml:"compression,omitempty"`
14-
// CredentialsFile controls ExporterOpts.CredentialsFile
17+
// CredentialsFile controls the ExporterOpts.CredentialsFile setting.
1518
CredentialsFile string `yaml:"credentials,omitempty"`
1619

17-
// Deprecated: This option no longer works, see https://github.com/GoogleCloudPlatform/prometheus-engine/pull/1688
20+
// EnableMatch allows additional control over matching filtering
21+
// given the go/gmp:matchstuck context.
22+
//
23+
// Available settings:
24+
// * not set: This config's Match settings are ignored. Custom export.match flags are used.
25+
// * false: Filtering feature is explicitly disabled; export matches all series.
26+
// * true: This config's Match settings are used. Overwrites all custom export.match flags.
27+
EnableMatch *bool `yaml:"enable_match,omitempty"`
28+
// Match, if EnableMatch is true, controls the ExporterOpts.Matchers setting.
29+
// It offers runtime change-able control of the "matchOneOf" filtering.
30+
// This filtering will skip certain series from entering the series cache (for export).
31+
// This data will still be ingested into Prometheus local storage.
32+
//
33+
// IMPORTANT: This option is prone to misconfiguration, thus opt-in and removed from the public docs.
34+
// See https://cloud.google.com/stackdriver/docs/managed-prometheus/setup-managed#filter-metrics.
1835
Match []string `yaml:"match,omitempty"`
1936
}
2037

google/export/export.go

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"errors"
2020
"fmt"
2121
"math"
22+
"slices"
2223
"strings"
2324
"sync"
2425
"time"
@@ -149,6 +150,13 @@ type Exporter struct {
149150
// Used to construct a new metric client when options change, or at initialization. It
150151
// is exposed as a variable so that unit tests may change the constructor.
151152
newMetricClient func(ctx context.Context, opts ExporterOpts) (metricServiceClient, error)
153+
154+
// Currently applied matchers in seriesCache.matchers. Avoids locking for reads.
155+
matchers Matchers
156+
157+
// We allow runtime config path. For startup consistency make exporter noop until export
158+
// will see the first ApplyConfig.
159+
configured bool
152160
}
153161

154162
const (
@@ -206,7 +214,8 @@ type ExporterOpts struct {
206214
// A list of metric matchers. Only Prometheus time series satisfying at
207215
// least one of the matchers are exported.
208216
// This option matches the semantics of the Prometheus federation match[]
209-
// parameter.
217+
// parameter. This is a setting from flags only, see ApplyConfig on
218+
// how it's applied dynamically in conjunction with Export.Match runtime option.
210219
Matchers Matchers
211220

212221
// Prefix under which metrics are written to GCM.
@@ -367,6 +376,7 @@ func defaultNewMetricClient(ctx context.Context, opts ExporterOpts) (metricServi
367376
}
368377

369378
// New returns a new Cloud Monitoring Exporter.
379+
// IMPORTANT: Created exporter is noop until ApplyConfig is called at least once. This is for configuration startup consistency.
370380
func New(ctx context.Context, logger log.Logger, reg prometheus.Registerer, opts ExporterOpts, lease Lease) (*Exporter, error) {
371381
grpc_prometheus.EnableClientHandlingTimeHistogram(
372382
grpc_prometheus.WithHistogramBuckets([]float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 20, 30, 40, 50, 60}),
@@ -409,7 +419,7 @@ func New(ctx context.Context, logger log.Logger, reg prometheus.Registerer, opts
409419
ctx: ctx,
410420
opts: opts,
411421
metricClient: metricClient,
412-
seriesCache: newSeriesCache(logger, reg, opts.MetricTypePrefix, opts.Matchers),
422+
seriesCache: newSeriesCache(logger, reg, opts.MetricTypePrefix),
413423
externalLabels: createLabelSet(&config.Config{}, &opts),
414424
newMetricClient: defaultNewMetricClient,
415425
nextc: make(chan struct{}, 1),
@@ -418,14 +428,17 @@ func New(ctx context.Context, logger log.Logger, reg prometheus.Registerer, opts
418428
lease: lease,
419429
}
420430

431+
// Set initial matchers.
432+
e.matchers = opts.Matchers
433+
e.seriesCache.setMatchers(e.matchers)
434+
421435
// Whenever the lease is lost, clear the series cache so we don't start off of out-of-range
422436
// reset timestamps when we gain the lease again.
423437
lease.OnLeaderChange(e.seriesCache.clear)
424438

425439
for i := range e.shards {
426440
e.shards[i] = newShard(opts.Efficiency.ShardBufferSize)
427441
}
428-
429442
return e, nil
430443
}
431444

@@ -442,10 +455,12 @@ const (
442455
// ApplyConfig updates the exporter state to the given configuration. For the
443456
// Prometheus fork config's google_cloud entry, ExporterOpts might be
444457
// changed and applied to the exporter, potentially recreating the metric client.
458+
// NOTE: Runtime configuration will not override disable/disableAuth options from flags.
445459
func (e *Exporter) ApplyConfig(cfg *config.Config) (err error) {
446460
// Note: We don't expect the NopExporter to call this. Only the config reloader calls it.
447461
e.mtx.Lock()
448462
defer e.mtx.Unlock()
463+
e.configured = true
449464

450465
// Don't recreate the metric client each time. If the metric client is recreated, it has to
451466
// potentially redo the TCP handshake. With HTTP/2, TCP connections are kept alive for a small
@@ -463,7 +478,6 @@ func (e *Exporter) ApplyConfig(cfg *config.Config) (err error) {
463478
e.opts.CredentialsFile = cfg.GoogleCloud.Export.CredentialsFile
464479
recreateClient = true
465480
}
466-
// NOTE(bwplotka): Matchers are deprecated, see https://github.com/GoogleCloudPlatform/prometheus-engine/pull/1688
467481

468482
lset := createLabelSet(cfg, &e.opts)
469483
labelsChanged := !labels.Equal(e.externalLabels, lset)
@@ -477,20 +491,41 @@ func (e *Exporter) ApplyConfig(cfg *config.Config) (err error) {
477491
}
478492
}
479493

480-
// If changed, or we're calling this for the first time, we need to recreate the client.
494+
var toApply Matchers
495+
switch {
496+
case cfg.GoogleCloud.Export.EnableMatch == nil:
497+
// Ignore Export.Match, whatever was in flag or default wins.
498+
toApply = e.opts.Matchers
499+
case !*cfg.GoogleCloud.Export.EnableMatch:
500+
// Explicit apply all matching (disabling the filtering).
501+
toApply = nil
502+
case *cfg.GoogleCloud.Export.EnableMatch:
503+
// Runtime Export.Match opted-in; override:
504+
for _, match := range cfg.GoogleCloud.Export.Match {
505+
if err := toApply.Set(match); err != nil {
506+
return err
507+
}
508+
}
509+
}
510+
481511
if recreateClient {
512+
// If changed, or we're calling this for the first time, we need to recreate the client.
482513
e.metricClient, err = e.newMetricClient(e.ctx, e.opts)
483514
if err != nil {
484515
return fmt.Errorf("create metric client: %w", err)
485516
}
486517
}
487518

519+
// Updates series cache settings; those require extra care on cache entries.
520+
if !e.matchers.Equals(toApply) {
521+
e.matchers = toApply
522+
e.seriesCache.setMatchers(e.matchers)
523+
}
488524
if labelsChanged {
489525
e.externalLabels = lset
490526
// New external labels possibly invalidate the cached series conversions.
491527
e.seriesCache.forceRefresh()
492528
}
493-
494529
return nil
495530
}
496531

@@ -557,13 +592,18 @@ func (e *Exporter) Export(metadata MetadataFunc, batch []record.RefSample, exemp
557592
return
558593
}
559594

560-
metadata = e.wrapMetadata(metadata)
561-
562595
e.mtx.Lock()
596+
if !e.configured {
597+
// ApplyConfig must be called at least once, otherwise we risk a race between flag and runtime config change on start.
598+
e.mtx.Unlock()
599+
return
600+
}
563601
externalLabels := e.externalLabels
564602
start, end, ok := e.lease.Range()
565603
e.mtx.Unlock()
566604

605+
metadata = e.wrapMetadata(metadata)
606+
567607
if !ok {
568608
exemplarsDropped.WithLabelValues("not-in-ha-range").Add(float64(len(exemplarMap)))
569609
samplesDropped.WithLabelValues("not-in-ha-range").Add(float64(batchSize))
@@ -1030,6 +1070,21 @@ func (m *Matchers) IsCumulative() bool {
10301070
return true
10311071
}
10321072

1073+
// Equals returns true if m is the same as other matchers. Order matters.
1074+
//
1075+
// NOTE: In practice order semi-matter, as the match result should be similar, but
1076+
// things are faster or slower depending on the order. For efficiency of this function
1077+
// and deterministic efficiency of matching, we assume order matter.
1078+
func (m *Matchers) Equals(other Matchers) bool {
1079+
return slices.EqualFunc(*m, other, func(msel, osel labels.Selector) bool {
1080+
return slices.EqualFunc(msel, osel, func(matcher, otherMatcher *labels.Matcher) bool {
1081+
return matcher.Type == otherMatcher.Type &&
1082+
matcher.Name == otherMatcher.Name &&
1083+
matcher.Value == otherMatcher.Value
1084+
})
1085+
})
1086+
}
1087+
10331088
func (m *Matchers) Matches(lset labels.Labels) bool {
10341089
if len(*m) == 0 {
10351090
return true

0 commit comments

Comments
 (0)