mirror of https://github.com/prometheus/prometheus
Browse Source
* Introduce a metadata watcher Similarly to the WAL watcher, its purpose is to observe the scrape manager and pull metadata. Then, send it to a remote storage. Signed-off-by: gotjosh <josue@grafana.com> * Additional fixes after rebasing. Signed-off-by: Callum Styan <callumstyan@gmail.com> * Rework samples/metadata metrics. Signed-off-by: Callum Styan <callumstyan@gmail.com> * Use more descriptive variable names in MetadataWatcher collect. Signed-off-by: Callum Styan <callumstyan@gmail.com> * Fix issues caused during rebasing. Signed-off-by: Callum Styan <callumstyan@gmail.com> * Fix missing metric add and unneeded config code. Signed-off-by: Callum Styan <callumstyan@gmail.com> * Address some review comments. Signed-off-by: Callum Styan <callumstyan@gmail.com> * Fix metrics and docs Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in> * Replace assert with require Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in> * Bring back max_samples_per_send metric Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in> * Fix tests Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in> Co-authored-by: Callum Styan <callumstyan@gmail.com> Co-authored-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>pull/8209/head
gotjosh
4 years ago
committed by
GitHub
21 changed files with 1276 additions and 188 deletions
@ -0,0 +1,163 @@ |
|||||||
|
// Copyright 2020 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 remote |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/go-kit/kit/log" |
||||||
|
"github.com/go-kit/kit/log/level" |
||||||
|
"github.com/pkg/errors" |
||||||
|
"github.com/prometheus/common/model" |
||||||
|
"github.com/prometheus/prometheus/scrape" |
||||||
|
) |
||||||
|
|
||||||
|
// MetadataAppender is an interface used by the Metadata Watcher to send metadata, It is read from the scrape manager, on to somewhere else.
|
||||||
|
type MetadataAppender interface { |
||||||
|
AppendMetadata(context.Context, []scrape.MetricMetadata) |
||||||
|
} |
||||||
|
|
||||||
|
// Watchable represents from where we fetch active targets for metadata.
|
||||||
|
type Watchable interface { |
||||||
|
TargetsActive() map[string][]*scrape.Target |
||||||
|
} |
||||||
|
|
||||||
|
type noopScrapeManager struct{} |
||||||
|
|
||||||
|
func (noop *noopScrapeManager) Get() (*scrape.Manager, error) { |
||||||
|
return nil, errors.New("Scrape manager not ready") |
||||||
|
} |
||||||
|
|
||||||
|
// MetadataWatcher watches the Scrape Manager for a given WriteMetadataTo.
|
||||||
|
type MetadataWatcher struct { |
||||||
|
name string |
||||||
|
logger log.Logger |
||||||
|
|
||||||
|
managerGetter ReadyScrapeManager |
||||||
|
manager Watchable |
||||||
|
writer MetadataAppender |
||||||
|
|
||||||
|
interval model.Duration |
||||||
|
deadline time.Duration |
||||||
|
|
||||||
|
done chan struct{} |
||||||
|
|
||||||
|
softShutdownCtx context.Context |
||||||
|
softShutdownCancel context.CancelFunc |
||||||
|
hardShutdownCancel context.CancelFunc |
||||||
|
hardShutdownCtx context.Context |
||||||
|
} |
||||||
|
|
||||||
|
// NewMetadataWatcher builds a new MetadataWatcher.
|
||||||
|
func NewMetadataWatcher(l log.Logger, mg ReadyScrapeManager, name string, w MetadataAppender, interval model.Duration, deadline time.Duration) *MetadataWatcher { |
||||||
|
if l == nil { |
||||||
|
l = log.NewNopLogger() |
||||||
|
} |
||||||
|
|
||||||
|
if mg == nil { |
||||||
|
mg = &noopScrapeManager{} |
||||||
|
} |
||||||
|
|
||||||
|
return &MetadataWatcher{ |
||||||
|
name: name, |
||||||
|
logger: l, |
||||||
|
|
||||||
|
managerGetter: mg, |
||||||
|
writer: w, |
||||||
|
|
||||||
|
interval: interval, |
||||||
|
deadline: deadline, |
||||||
|
|
||||||
|
done: make(chan struct{}), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Start the MetadataWatcher.
|
||||||
|
func (mw *MetadataWatcher) Start() { |
||||||
|
level.Info(mw.logger).Log("msg", "Starting scraped metadata watcher") |
||||||
|
mw.hardShutdownCtx, mw.hardShutdownCancel = context.WithCancel(context.Background()) |
||||||
|
mw.softShutdownCtx, mw.softShutdownCancel = context.WithCancel(mw.hardShutdownCtx) |
||||||
|
go mw.loop() |
||||||
|
} |
||||||
|
|
||||||
|
// Stop the MetadataWatcher.
|
||||||
|
func (mw *MetadataWatcher) Stop() { |
||||||
|
level.Info(mw.logger).Log("msg", "Stopping metadata watcher...") |
||||||
|
defer level.Info(mw.logger).Log("msg", "Scraped metadata watcher stopped") |
||||||
|
|
||||||
|
mw.softShutdownCancel() |
||||||
|
select { |
||||||
|
case <-mw.done: |
||||||
|
return |
||||||
|
case <-time.After(mw.deadline): |
||||||
|
level.Error(mw.logger).Log("msg", "Failed to flush metadata") |
||||||
|
} |
||||||
|
|
||||||
|
mw.hardShutdownCancel() |
||||||
|
<-mw.done |
||||||
|
} |
||||||
|
|
||||||
|
func (mw *MetadataWatcher) loop() { |
||||||
|
ticker := time.NewTicker(time.Duration(mw.interval)) |
||||||
|
defer ticker.Stop() |
||||||
|
defer close(mw.done) |
||||||
|
|
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-mw.softShutdownCtx.Done(): |
||||||
|
return |
||||||
|
case <-ticker.C: |
||||||
|
mw.collect() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (mw *MetadataWatcher) collect() { |
||||||
|
if !mw.ready() { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// We create a set of the metadata to help deduplicating based on the attributes of a
|
||||||
|
// scrape.MetricMetadata. In this case, a combination of metric name, help, type, and unit.
|
||||||
|
metadataSet := map[scrape.MetricMetadata]struct{}{} |
||||||
|
metadata := []scrape.MetricMetadata{} |
||||||
|
for _, tset := range mw.manager.TargetsActive() { |
||||||
|
for _, target := range tset { |
||||||
|
for _, entry := range target.MetadataList() { |
||||||
|
if _, ok := metadataSet[entry]; !ok { |
||||||
|
metadata = append(metadata, entry) |
||||||
|
metadataSet[entry] = struct{}{} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Blocks until the metadata is sent to the remote write endpoint or hardShutdownContext is expired.
|
||||||
|
mw.writer.AppendMetadata(mw.hardShutdownCtx, metadata) |
||||||
|
} |
||||||
|
|
||||||
|
func (mw *MetadataWatcher) ready() bool { |
||||||
|
if mw.manager != nil { |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
m, err := mw.managerGetter.Get() |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
mw.manager = m |
||||||
|
return true |
||||||
|
} |
@ -0,0 +1,155 @@ |
|||||||
|
// Copyright 2020 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 remote |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/pkg/errors" |
||||||
|
"github.com/prometheus/common/model" |
||||||
|
"github.com/prometheus/prometheus/pkg/textparse" |
||||||
|
"github.com/prometheus/prometheus/scrape" |
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
interval = model.Duration(1 * time.Millisecond) |
||||||
|
deadline = 1 * time.Millisecond |
||||||
|
) |
||||||
|
|
||||||
|
// TestMetaStore satisfies the MetricMetadataStore interface.
|
||||||
|
// It is used to inject specific metadata as part of a test case.
|
||||||
|
type TestMetaStore struct { |
||||||
|
Metadata []scrape.MetricMetadata |
||||||
|
} |
||||||
|
|
||||||
|
func (s *TestMetaStore) ListMetadata() []scrape.MetricMetadata { |
||||||
|
return s.Metadata |
||||||
|
} |
||||||
|
|
||||||
|
func (s *TestMetaStore) GetMetadata(metric string) (scrape.MetricMetadata, bool) { |
||||||
|
for _, m := range s.Metadata { |
||||||
|
if metric == m.Metric { |
||||||
|
return m, true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return scrape.MetricMetadata{}, false |
||||||
|
} |
||||||
|
|
||||||
|
func (s *TestMetaStore) SizeMetadata() int { return 0 } |
||||||
|
func (s *TestMetaStore) LengthMetadata() int { return 0 } |
||||||
|
|
||||||
|
type writeMetadataToMock struct { |
||||||
|
metadataAppended int |
||||||
|
} |
||||||
|
|
||||||
|
func (mwtm *writeMetadataToMock) AppendMetadata(_ context.Context, m []scrape.MetricMetadata) { |
||||||
|
mwtm.metadataAppended += len(m) |
||||||
|
} |
||||||
|
|
||||||
|
func newMetadataWriteToMock() *writeMetadataToMock { |
||||||
|
return &writeMetadataToMock{} |
||||||
|
} |
||||||
|
|
||||||
|
type scrapeManagerMock struct { |
||||||
|
manager *scrape.Manager |
||||||
|
ready bool |
||||||
|
} |
||||||
|
|
||||||
|
func (smm *scrapeManagerMock) Get() (*scrape.Manager, error) { |
||||||
|
if smm.ready { |
||||||
|
return smm.manager, nil |
||||||
|
} |
||||||
|
|
||||||
|
return nil, errors.New("not ready") |
||||||
|
} |
||||||
|
|
||||||
|
type fakeManager struct { |
||||||
|
activeTargets map[string][]*scrape.Target |
||||||
|
} |
||||||
|
|
||||||
|
func (fm *fakeManager) TargetsActive() map[string][]*scrape.Target { |
||||||
|
return fm.activeTargets |
||||||
|
} |
||||||
|
|
||||||
|
func TestWatchScrapeManager_NotReady(t *testing.T) { |
||||||
|
wt := newMetadataWriteToMock() |
||||||
|
smm := &scrapeManagerMock{ |
||||||
|
ready: false, |
||||||
|
} |
||||||
|
|
||||||
|
mw := NewMetadataWatcher(nil, smm, "", wt, interval, deadline) |
||||||
|
require.Equal(t, false, mw.ready()) |
||||||
|
|
||||||
|
mw.collect() |
||||||
|
|
||||||
|
require.Equal(t, 0, wt.metadataAppended) |
||||||
|
} |
||||||
|
|
||||||
|
func TestWatchScrapeManager_ReadyForCollection(t *testing.T) { |
||||||
|
wt := newMetadataWriteToMock() |
||||||
|
|
||||||
|
metadata := &TestMetaStore{ |
||||||
|
Metadata: []scrape.MetricMetadata{ |
||||||
|
{ |
||||||
|
Metric: "prometheus_tsdb_head_chunks_created_total", |
||||||
|
Type: textparse.MetricTypeCounter, |
||||||
|
Help: "Total number", |
||||||
|
Unit: "", |
||||||
|
}, |
||||||
|
{ |
||||||
|
Metric: "prometheus_remote_storage_retried_samples_total", |
||||||
|
Type: textparse.MetricTypeCounter, |
||||||
|
Help: "Total number", |
||||||
|
Unit: "", |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
metadataDup := &TestMetaStore{ |
||||||
|
Metadata: []scrape.MetricMetadata{ |
||||||
|
{ |
||||||
|
Metric: "prometheus_tsdb_head_chunks_created_total", |
||||||
|
Type: textparse.MetricTypeCounter, |
||||||
|
Help: "Total number", |
||||||
|
Unit: "", |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
target := &scrape.Target{} |
||||||
|
target.SetMetadataStore(metadata) |
||||||
|
targetWithDup := &scrape.Target{} |
||||||
|
targetWithDup.SetMetadataStore(metadataDup) |
||||||
|
|
||||||
|
manager := &fakeManager{ |
||||||
|
activeTargets: map[string][]*scrape.Target{ |
||||||
|
"job": []*scrape.Target{target}, |
||||||
|
"dup": []*scrape.Target{targetWithDup}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
smm := &scrapeManagerMock{ |
||||||
|
ready: true, |
||||||
|
} |
||||||
|
|
||||||
|
mw := NewMetadataWatcher(nil, smm, "", wt, interval, deadline) |
||||||
|
mw.manager = manager |
||||||
|
|
||||||
|
mw.collect() |
||||||
|
|
||||||
|
require.Equal(t, 2, wt.metadataAppended) |
||||||
|
} |
Loading…
Reference in new issue