Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
* [ENHANCEMENT] Ingester: Add `cortex_ingest_storage_reader_receive_and_consume_delay_seconds` metric tracking the time between when a write request is received in the distributor and its content is ingested in ingesters, when the ingest storage is enabled. #12751
* [ENHANCEMENT] Ruler: Add `ruler_evaluation_consistency_max_delay` per-tenant configuration option support, to specify the maximum tolerated ingestion delay for eventually consistent rule evaluations. This feature is used only when ingest storage is enabled. By default, no maximum delay is enforced. #12751
* [ENHANCEMENT] Ingester: Export `cortex_attributed_series_overflow_labels` metric on the `/usage-metrics` metrics endpoint with the configured cost-attribution labels set to overflow value. #12846
* [ENHANCEMENT] Usage stats: Report ingest-storage mode as part of usage statistics. #12753
* [BUGFIX] Distributor: Calculate `WriteResponseStats` before validation and `PushWrappers`. This prevents clients using Remote-Write 2.0 from seeing a diff in written samples, histograms and exemplars. #12682
* [BUGFIX] Compactor: Fix cortex_compactor_block_uploads_failed_total metric showing type="unknown". #12477
* [BUGFIX] Querier: Samples with the same timestamp are merged deterministically. Previously, this could lead to flapping query results when an out-of-order sample is ingested that conflicts with a previously ingested in-order sample's value. #8673
Expand Down
3 changes: 3 additions & 0 deletions pkg/mimir/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,9 @@ func (t *Mimir) initIngesterPartitionRing() (services.Service, error) {
// Expose a web page to view the partitions ring state.
t.API.RegisterIngesterPartitionRing(ring.NewPartitionRingPageHandler(t.IngesterPartitionRingWatcher, ring.NewPartitionRingEditor(ingester.PartitionRingKey, kvClient)))

// Track anonymous usage statistics.
usagestats.SetMode(usagestats.ModeIngestStorage)

return t.IngesterPartitionRingWatcher, nil
}

Expand Down
28 changes: 24 additions & 4 deletions pkg/usagestats/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ type Report struct {
// Os is the operating system where Mimir is running.
Os string `json:"os"`

// Arch is the architecture where Mimir is running.
// Arch is the CPU architecture where Mimir is running.
Arch string `json:"arch"`

// Edition is the Mimir edition ("oss" or "enterprise").
Edition string `json:"edition"`

// Mode is the Mimir architecture mode ("classic" or "ingest_storage").
Mode string `json:"mode"`

// Metrics holds custom metrics tracked by Mimir. Can contain nested objects.
Metrics map[string]interface{} `json:"metrics"`
}
Expand All @@ -51,6 +54,7 @@ func buildReport(seed ClusterSeed, reportAt time.Time, reportInterval time.Durat
var (
targetName string
editionName string
modeName string
)
if target := expvar.Get(statsPrefix + targetKey); target != nil {
if target, ok := target.(*expvar.String); ok {
Expand All @@ -62,6 +66,11 @@ func buildReport(seed ClusterSeed, reportAt time.Time, reportInterval time.Durat
editionName = edition.Value()
}
}
if val := expvar.Get(statsPrefix + modeKey); val != nil {
if name, ok := val.(*expvar.String); ok {
modeName = name.Value()
}
}

return &Report{
ClusterID: seed.UID,
Expand All @@ -73,6 +82,7 @@ func buildReport(seed ClusterSeed, reportAt time.Time, reportInterval time.Durat
Arch: runtime.GOARCH,
Target: targetName,
Edition: editionName,
Mode: modeName,
Metrics: buildMetrics(),
}
}
Expand All @@ -86,11 +96,21 @@ func buildMetrics() map[string]interface{} {
defer cpuUsage.Set(0)

expvar.Do(func(kv expvar.KeyValue) {
if !strings.HasPrefix(kv.Key, statsPrefix) || kv.Key == statsPrefix+targetKey || kv.Key == statsPrefix+editionKey {
if !strings.HasPrefix(kv.Key, statsPrefix) {
return
}

key := strings.TrimPrefix(kv.Key, statsPrefix)

// Exclude reserved keys; they are reported on the root level of the Report.
switch key {
case targetKey,
editionKey,
modeKey:
return
}

var value interface{}
var value any
switch v := kv.Value.(type) {
case *expvar.Int:
value = v.Value()
Expand All @@ -107,7 +127,7 @@ func buildMetrics() map[string]interface{} {
return
}

result[strings.TrimPrefix(kv.Key, statsPrefix)] = value
result[key] = value
})

return result
Expand Down
1 change: 1 addition & 0 deletions pkg/usagestats/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func TestBuildReport(t *testing.T) {
assert.Equal(t, runtime.GOOS, report.Os)
assert.Equal(t, runtime.GOARCH, report.Arch)
assert.Equal(t, "oss", report.Edition)
assert.Equal(t, "classic", report.Mode)
assert.NotNil(t, report.Metrics["memstats"])
assert.Equal(t, "dev-version", report.Version.Version)
assert.Equal(t, "dev-branch", report.Version.Branch)
Expand Down
10 changes: 10 additions & 0 deletions pkg/usagestats/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ const (
statsPrefix = "github.com/grafana/mimir/"
targetKey = "target"
editionKey = "edition"
modeKey = "mode"

EditionOSS = "oss"
EditionEnterprise = "enterprise"

ModeClassic = "classic"
ModeIngestStorage = "ingest_storage"
)

func init() {
SetEdition(EditionOSS)
SetMode(ModeClassic)
}

// SetTarget sets the target name.
Expand All @@ -34,6 +39,11 @@ func SetEdition(edition string) {
GetString(editionKey).Set(edition)
}

// SetMode sets mimir architecture.
func SetMode(name string) {
GetString(modeKey).Set(name)
}

// GetString returns the String stats object for the given name.
// It creates the stats object if it doesn't exist yet.
func GetString(name string) *expvar.String {
Expand Down