re-implement prometheus metrics endpoint to be simpler (#13922)

data-structures were repeatedly initialized
this causes GC pressure, instead re-use the
collectors.

Initialize collectors in `init()`, also make
sure to honor the cache semantics for performance
requirements.

Avoid a global map and a global lock for metrics
lookup instead let them all be lock-free unless
the cache is being invalidated.
This commit is contained in:
Harshavardhana 2021-12-17 10:11:04 -08:00 committed by GitHub
parent 890f43ffa5
commit 818f0201fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 776 additions and 753 deletions

View File

@ -35,6 +35,51 @@ import (
"github.com/prometheus/procfs" "github.com/prometheus/procfs"
) )
var (
nodeCollector *minioNodeCollector
clusterCollector *minioClusterCollector
peerMetricsGroups []*MetricsGroup
)
func init() {
clusterMetricsGroups := []*MetricsGroup{
getBucketUsageMetrics(),
getMinioHealingMetrics(),
getNodeHealthMetrics(),
getClusterStorageMetrics(),
}
peerMetricsGroups = []*MetricsGroup{
getCacheMetrics(),
getGoMetrics(),
getHTTPMetrics(),
getLocalStorageMetrics(),
getMinioProcMetrics(),
getMinioVersionMetrics(),
getNetworkMetrics(),
getS3TTFBMetric(),
getILMNodeMetrics(),
getScannerNodeMetrics(),
}
allMetricsGroups := func() (allMetrics []*MetricsGroup) {
allMetrics = append(allMetrics, clusterMetricsGroups...)
allMetrics = append(allMetrics, peerMetricsGroups...)
return allMetrics
}()
nodeCollector = newMinioCollectorNode([]*MetricsGroup{
getNodeHealthMetrics(),
getCacheMetrics(),
getHTTPMetrics(),
getNetworkMetrics(),
getMinioVersionMetrics(),
getS3TTFBMetric(),
})
clusterCollector = newMinioClusterCollector(allMetricsGroups)
}
// MetricNamespace is top level grouping of metrics to create the metric name. // MetricNamespace is top level grouping of metrics to create the metric name.
type MetricNamespace string type MetricNamespace string
@ -164,117 +209,37 @@ type Metric struct {
Histogram map[string]uint64 `json:"Histogram"` Histogram map[string]uint64 `json:"Histogram"`
} }
func (m *Metric) copyMetric() Metric {
metric := Metric{
Description: m.Description,
Value: m.Value,
HistogramBucketLabel: m.HistogramBucketLabel,
StaticLabels: make(map[string]string),
VariableLabels: make(map[string]string),
Histogram: make(map[string]uint64),
}
for k, v := range m.StaticLabels {
metric.StaticLabels[k] = v
}
for k, v := range m.VariableLabels {
metric.VariableLabels[k] = v
}
for k, v := range m.Histogram {
metric.Histogram[k] = v
}
return metric
}
// MetricsGroup are a group of metrics that are initialized together. // MetricsGroup are a group of metrics that are initialized together.
type MetricsGroup struct { type MetricsGroup struct {
id string metricsCache timedValue
cacheInterval time.Duration
cachedRead func(ctx context.Context, mg *MetricsGroup) []Metric
read func(ctx context.Context) []Metric
} }
var metricsGroupCache = make(map[string]*timedValue) // RegisterRead register the metrics populator function to be used
var cacheLock sync.Mutex // to populate new values upon cache invalidation.
func (g *MetricsGroup) RegisterRead(read func(ctx context.Context) []Metric) {
func cachedRead(ctx context.Context, mg *MetricsGroup) (metrics []Metric) { g.metricsCache.Once.Do(func() {
cacheLock.Lock() g.metricsCache.TTL = 10 * time.Second
defer cacheLock.Unlock() g.metricsCache.Update = func() (interface{}, error) {
v, ok := metricsGroupCache[mg.id] return read(GlobalContext), nil
if !ok {
interval := mg.cacheInterval
if interval == 0 {
interval = 30 * time.Second
} }
v = &timedValue{}
v.Once.Do(func() {
v.Update = func() (interface{}, error) {
c := mg.read(ctx)
return c, nil
}
v.TTL = interval
}) })
metricsGroupCache[mg.id] = v }
}
c, err := v.Get() // Get - returns cached value always upton the configured TTL,
// once the TTL expires "read()" registered function is called
// to return the new values and updated.
func (g *MetricsGroup) Get() []Metric {
c, err := g.metricsCache.Get()
if err != nil { if err != nil {
return []Metric{} return []Metric{}
} }
m := c.([]Metric)
for i := range m { m, ok := c.([]Metric)
metrics = append(metrics, m[i].copyMetric()) if !ok {
return []Metric{}
} }
return metrics
}
// MetricsGenerator are functions that generate metric groups. return m
type MetricsGenerator func() MetricsGroup
// GetGlobalGenerators gets all the generators the report global metrics pre calculated.
func GetGlobalGenerators() []MetricsGenerator {
g := []MetricsGenerator{
getBucketUsageMetrics,
getMinioHealingMetrics,
getNodeHealthMetrics,
getClusterStorageMetrics,
}
return g
}
// GetAllGenerators gets all the metric generators.
func GetAllGenerators() []MetricsGenerator {
g := GetGlobalGenerators()
g = append(g, GetGeneratorsForPeer()...)
return g
}
// GetGeneratorsForPeer - gets the generators to report to peer.
func GetGeneratorsForPeer() []MetricsGenerator {
g := []MetricsGenerator{
getCacheMetrics,
getGoMetrics,
getHTTPMetrics,
getLocalStorageMetrics,
getMinioProcMetrics,
getMinioVersionMetrics,
getNetworkMetrics,
getS3TTFBMetric,
getILMNodeMetrics,
getScannerNodeMetrics,
}
return g
}
// GetSingleNodeGenerators gets the metrics that are local
func GetSingleNodeGenerators() []MetricsGenerator {
g := []MetricsGenerator{
getNodeHealthMetrics,
getCacheMetrics,
getHTTPMetrics,
getNetworkMetrics,
getMinioVersionMetrics,
getS3TTFBMetric,
}
return g
} }
func getClusterCapacityTotalBytesMD() MetricDescription { func getClusterCapacityTotalBytesMD() MetricDescription {
@ -286,6 +251,7 @@ func getClusterCapacityTotalBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getClusterCapacityFreeBytesMD() MetricDescription { func getClusterCapacityFreeBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: clusterMetricNamespace, Namespace: clusterMetricNamespace,
@ -295,6 +261,7 @@ func getClusterCapacityFreeBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getClusterCapacityUsageBytesMD() MetricDescription { func getClusterCapacityUsageBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: clusterMetricNamespace, Namespace: clusterMetricNamespace,
@ -304,6 +271,7 @@ func getClusterCapacityUsageBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getClusterCapacityUsageFreeBytesMD() MetricDescription { func getClusterCapacityUsageFreeBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: clusterMetricNamespace, Namespace: clusterMetricNamespace,
@ -323,6 +291,7 @@ func getNodeDiskUsedBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getNodeDiskFreeBytesMD() MetricDescription { func getNodeDiskFreeBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -332,6 +301,7 @@ func getNodeDiskFreeBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getClusterDisksOfflineTotalMD() MetricDescription { func getClusterDisksOfflineTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: clusterMetricNamespace, Namespace: clusterMetricNamespace,
@ -381,6 +351,7 @@ func getNodeDiskTotalBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getUsageLastScanActivityMD() MetricDescription { func getUsageLastScanActivityMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioMetricNamespace, Namespace: minioMetricNamespace,
@ -429,6 +400,7 @@ func getBucketRepFailedBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getBucketRepSentBytesMD() MetricDescription { func getBucketRepSentBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: bucketMetricNamespace, Namespace: bucketMetricNamespace,
@ -438,6 +410,7 @@ func getBucketRepSentBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getBucketRepReceivedBytesMD() MetricDescription { func getBucketRepReceivedBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: bucketMetricNamespace, Namespace: bucketMetricNamespace,
@ -457,6 +430,7 @@ func getBucketRepFailedOperationsMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getBucketObjectDistributionMD() MetricDescription { func getBucketObjectDistributionMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: bucketMetricNamespace, Namespace: bucketMetricNamespace,
@ -466,6 +440,7 @@ func getBucketObjectDistributionMD() MetricDescription {
Type: histogramMetric, Type: histogramMetric,
} }
} }
func getInternodeFailedRequests() MetricDescription { func getInternodeFailedRequests() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: interNodeMetricNamespace, Namespace: interNodeMetricNamespace,
@ -485,6 +460,7 @@ func getInterNodeSentBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getInterNodeReceivedBytesMD() MetricDescription { func getInterNodeReceivedBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: interNodeMetricNamespace, Namespace: interNodeMetricNamespace,
@ -494,6 +470,7 @@ func getInterNodeReceivedBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3SentBytesMD() MetricDescription { func getS3SentBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -503,6 +480,7 @@ func getS3SentBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3ReceivedBytesMD() MetricDescription { func getS3ReceivedBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -512,6 +490,7 @@ func getS3ReceivedBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3RequestsInFlightMD() MetricDescription { func getS3RequestsInFlightMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -521,6 +500,7 @@ func getS3RequestsInFlightMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getS3RequestsInQueueMD() MetricDescription { func getS3RequestsInQueueMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -530,6 +510,7 @@ func getS3RequestsInQueueMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getS3RequestsTotalMD() MetricDescription { func getS3RequestsTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -539,6 +520,7 @@ func getS3RequestsTotalMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3RequestsErrorsMD() MetricDescription { func getS3RequestsErrorsMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -548,6 +530,7 @@ func getS3RequestsErrorsMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3RequestsCanceledMD() MetricDescription { func getS3RequestsCanceledMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -557,6 +540,7 @@ func getS3RequestsCanceledMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3RejectedAuthRequestsTotalMD() MetricDescription { func getS3RejectedAuthRequestsTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -566,6 +550,7 @@ func getS3RejectedAuthRequestsTotalMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3RejectedHeaderRequestsTotalMD() MetricDescription { func getS3RejectedHeaderRequestsTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -575,6 +560,7 @@ func getS3RejectedHeaderRequestsTotalMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3RejectedTimestampRequestsTotalMD() MetricDescription { func getS3RejectedTimestampRequestsTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -584,6 +570,7 @@ func getS3RejectedTimestampRequestsTotalMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getS3RejectedInvalidRequestsTotalMD() MetricDescription { func getS3RejectedInvalidRequestsTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -593,6 +580,7 @@ func getS3RejectedInvalidRequestsTotalMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getCacheHitsTotalMD() MetricDescription { func getCacheHitsTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioNamespace, Namespace: minioNamespace,
@ -602,6 +590,7 @@ func getCacheHitsTotalMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getCacheHitsMissedTotalMD() MetricDescription { func getCacheHitsMissedTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioNamespace, Namespace: minioNamespace,
@ -611,6 +600,7 @@ func getCacheHitsMissedTotalMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getCacheUsagePercentMD() MetricDescription { func getCacheUsagePercentMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioNamespace, Namespace: minioNamespace,
@ -620,6 +610,7 @@ func getCacheUsagePercentMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getCacheUsageInfoMD() MetricDescription { func getCacheUsageInfoMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioNamespace, Namespace: minioNamespace,
@ -629,6 +620,7 @@ func getCacheUsageInfoMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getCacheUsedBytesMD() MetricDescription { func getCacheUsedBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioNamespace, Namespace: minioNamespace,
@ -638,6 +630,7 @@ func getCacheUsedBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getCacheTotalBytesMD() MetricDescription { func getCacheTotalBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioNamespace, Namespace: minioNamespace,
@ -647,6 +640,7 @@ func getCacheTotalBytesMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getCacheSentBytesMD() MetricDescription { func getCacheSentBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioNamespace, Namespace: minioNamespace,
@ -656,6 +650,7 @@ func getCacheSentBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getHealObjectsTotalMD() MetricDescription { func getHealObjectsTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: healMetricNamespace, Namespace: healMetricNamespace,
@ -665,6 +660,7 @@ func getHealObjectsTotalMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getHealObjectsHealTotalMD() MetricDescription { func getHealObjectsHealTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: healMetricNamespace, Namespace: healMetricNamespace,
@ -684,6 +680,7 @@ func getHealObjectsFailTotalMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getHealLastActivityTimeMD() MetricDescription { func getHealLastActivityTimeMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: healMetricNamespace, Namespace: healMetricNamespace,
@ -693,6 +690,7 @@ func getHealLastActivityTimeMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getNodeOnlineTotalMD() MetricDescription { func getNodeOnlineTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: clusterMetricNamespace, Namespace: clusterMetricNamespace,
@ -702,6 +700,7 @@ func getNodeOnlineTotalMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getNodeOfflineTotalMD() MetricDescription { func getNodeOfflineTotalMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: clusterMetricNamespace, Namespace: clusterMetricNamespace,
@ -711,6 +710,7 @@ func getNodeOfflineTotalMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinIOVersionMD() MetricDescription { func getMinIOVersionMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioMetricNamespace, Namespace: minioMetricNamespace,
@ -720,6 +720,7 @@ func getMinIOVersionMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinIOCommitMD() MetricDescription { func getMinIOCommitMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: minioMetricNamespace, Namespace: minioMetricNamespace,
@ -729,6 +730,7 @@ func getMinIOCommitMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getS3TTFBDistributionMD() MetricDescription { func getS3TTFBDistributionMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: s3MetricNamespace, Namespace: s3MetricNamespace,
@ -738,6 +740,7 @@ func getS3TTFBDistributionMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinioFDOpenMD() MetricDescription { func getMinioFDOpenMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -747,6 +750,7 @@ func getMinioFDOpenMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinioFDLimitMD() MetricDescription { func getMinioFDLimitMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -756,6 +760,7 @@ func getMinioFDLimitMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinioProcessIOWriteBytesMD() MetricDescription { func getMinioProcessIOWriteBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -765,6 +770,7 @@ func getMinioProcessIOWriteBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getMinioProcessIOReadBytesMD() MetricDescription { func getMinioProcessIOReadBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -774,6 +780,7 @@ func getMinioProcessIOReadBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getMinioProcessIOWriteCachedBytesMD() MetricDescription { func getMinioProcessIOWriteCachedBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -783,6 +790,7 @@ func getMinioProcessIOWriteCachedBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getMinioProcessIOReadCachedBytesMD() MetricDescription { func getMinioProcessIOReadCachedBytesMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -792,6 +800,7 @@ func getMinioProcessIOReadCachedBytesMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getMinIOProcessSysCallRMD() MetricDescription { func getMinIOProcessSysCallRMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -801,6 +810,7 @@ func getMinIOProcessSysCallRMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getMinIOProcessSysCallWMD() MetricDescription { func getMinIOProcessSysCallWMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -810,6 +820,7 @@ func getMinIOProcessSysCallWMD() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getMinIOGORoutineCountMD() MetricDescription { func getMinIOGORoutineCountMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -819,6 +830,7 @@ func getMinIOGORoutineCountMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinIOProcessStartTimeMD() MetricDescription { func getMinIOProcessStartTimeMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -828,6 +840,7 @@ func getMinIOProcessStartTimeMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinIOProcessUptimeMD() MetricDescription { func getMinIOProcessUptimeMD() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -837,6 +850,7 @@ func getMinIOProcessUptimeMD() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinIOProcessResidentMemory() MetricDescription { func getMinIOProcessResidentMemory() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -846,6 +860,7 @@ func getMinIOProcessResidentMemory() MetricDescription {
Type: gaugeMetric, Type: gaugeMetric,
} }
} }
func getMinIOProcessCPUTime() MetricDescription { func getMinIOProcessCPUTime() MetricDescription {
return MetricDescription{ return MetricDescription{
Namespace: nodeMetricNamespace, Namespace: nodeMetricNamespace,
@ -855,11 +870,10 @@ func getMinIOProcessCPUTime() MetricDescription {
Type: counterMetric, Type: counterMetric,
} }
} }
func getMinioProcMetrics() MetricsGroup {
return MetricsGroup{ func getMinioProcMetrics() *MetricsGroup {
id: "MinioProcMetrics", mg := &MetricsGroup{}
cachedRead: cachedRead, mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
read: func(ctx context.Context) (metrics []Metric) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
return nil return nil
} }
@ -958,28 +972,25 @@ func getMinioProcMetrics() MetricsGroup {
Value: stat.CPUTime(), Value: stat.CPUTime(),
}) })
return return
}, })
} return mg
} }
func getGoMetrics() MetricsGroup {
return MetricsGroup{ func getGoMetrics() *MetricsGroup {
id: "GoMetrics", mg := &MetricsGroup{}
cachedRead: cachedRead, mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
read: func(ctx context.Context) (metrics []Metric) {
metrics = append(metrics, Metric{ metrics = append(metrics, Metric{
Description: getMinIOGORoutineCountMD(), Description: getMinIOGORoutineCountMD(),
Value: float64(runtime.NumGoroutine()), Value: float64(runtime.NumGoroutine()),
}) })
return return
}, })
} return mg
} }
func getS3TTFBMetric() MetricsGroup {
return MetricsGroup{
id: "s3TTFBMetric",
cachedRead: cachedRead,
read: func(ctx context.Context) (metrics []Metric) {
func getS3TTFBMetric() *MetricsGroup {
mg := &MetricsGroup{}
mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
// Read prometheus metric on this channel // Read prometheus metric on this channel
ch := make(chan prometheus.Metric) ch := make(chan prometheus.Metric)
var wg sync.WaitGroup var wg sync.WaitGroup
@ -1017,8 +1028,8 @@ func getS3TTFBMetric() MetricsGroup {
close(ch) close(ch)
wg.Wait() wg.Wait()
return return
}, })
} return mg
} }
func getTransitionPendingTasksMD() MetricDescription { func getTransitionPendingTasksMD() MetricDescription {
@ -1051,11 +1062,9 @@ func getExpiryPendingTasksMD() MetricDescription {
} }
} }
func getILMNodeMetrics() MetricsGroup { func getILMNodeMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "ILMNodeMetrics", mg.RegisterRead(func(_ context.Context) []Metric {
cachedRead: cachedRead,
read: func(_ context.Context) []Metric {
expPendingTasks := Metric{ expPendingTasks := Metric{
Description: getExpiryPendingTasksMD(), Description: getExpiryPendingTasksMD(),
} }
@ -1077,15 +1086,13 @@ func getILMNodeMetrics() MetricsGroup {
trPendingTasks, trPendingTasks,
trActiveTasks, trActiveTasks,
} }
}, })
} return mg
} }
func getScannerNodeMetrics() MetricsGroup { func getScannerNodeMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "ScannerNodeMetrics", mg.RegisterRead(func(_ context.Context) []Metric {
cachedRead: cachedRead,
read: func(_ context.Context) []Metric {
metrics := []Metric{ metrics := []Metric{
{ {
Description: MetricDescription{ Description: MetricDescription{
@ -1166,15 +1173,13 @@ func getScannerNodeMetrics() MetricsGroup {
}) })
} }
return metrics return metrics
}, })
} return mg
} }
func getMinioVersionMetrics() MetricsGroup { func getMinioVersionMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "MinioVersionMetrics", mg.RegisterRead(func(_ context.Context) (metrics []Metric) {
cachedRead: cachedRead,
read: func(_ context.Context) (metrics []Metric) {
metrics = append(metrics, Metric{ metrics = append(metrics, Metric{
Description: getMinIOCommitMD(), Description: getMinIOCommitMD(),
VariableLabels: map[string]string{"commit": CommitID}, VariableLabels: map[string]string{"commit": CommitID},
@ -1184,15 +1189,13 @@ func getMinioVersionMetrics() MetricsGroup {
VariableLabels: map[string]string{"version": Version}, VariableLabels: map[string]string{"version": Version},
}) })
return return
}, })
} return mg
} }
func getNodeHealthMetrics() MetricsGroup { func getNodeHealthMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "NodeHealthMetrics", mg.RegisterRead(func(_ context.Context) (metrics []Metric) {
cachedRead: cachedRead,
read: func(_ context.Context) (metrics []Metric) {
if globalIsGateway { if globalIsGateway {
return return
} }
@ -1207,15 +1210,13 @@ func getNodeHealthMetrics() MetricsGroup {
Value: float64(nodesDown), Value: float64(nodesDown),
}) })
return return
}, })
} return mg
} }
func getMinioHealingMetrics() MetricsGroup { func getMinioHealingMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "minioHealingMetrics", mg.RegisterRead(func(_ context.Context) (metrics []Metric) {
cachedRead: cachedRead,
read: func(_ context.Context) (metrics []Metric) {
metrics = make([]Metric, 0, 5) metrics = make([]Metric, 0, 5)
if !globalIsErasure { if !globalIsErasure {
return return
@ -1237,8 +1238,8 @@ func getMinioHealingMetrics() MetricsGroup {
metrics = append(metrics, getHealedItems(bgSeq)...) metrics = append(metrics, getHealedItems(bgSeq)...)
metrics = append(metrics, getFailedItems(bgSeq)...) metrics = append(metrics, getFailedItems(bgSeq)...)
return return
}, })
} return mg
} }
func getFailedItems(seq *healSequence) (m []Metric) { func getFailedItems(seq *healSequence) (m []Metric) {
@ -1283,11 +1284,9 @@ func getObjectsScanned(seq *healSequence) (m []Metric) {
return return
} }
func getCacheMetrics() MetricsGroup { func getCacheMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "CacheMetrics", mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
cachedRead: cachedRead,
read: func(ctx context.Context) (metrics []Metric) {
metrics = make([]Metric, 0, 20) metrics = make([]Metric, 0, 20)
cacheObjLayer := newCachedObjectLayerFn() cacheObjLayer := newCachedObjectLayerFn()
// Service not initialized yet // Service not initialized yet
@ -1329,15 +1328,13 @@ func getCacheMetrics() MetricsGroup {
}) })
} }
return return
}, })
} return mg
} }
func getHTTPMetrics() MetricsGroup { func getHTTPMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "httpMetrics", mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
cachedRead: cachedRead,
read: func(ctx context.Context) (metrics []Metric) {
httpStats := globalHTTPStats.toServerHTTPStats() httpStats := globalHTTPStats.toServerHTTPStats()
metrics = make([]Metric, 0, 3+ metrics = make([]Metric, 0, 3+
len(httpStats.CurrentS3Requests.APIStats)+ len(httpStats.CurrentS3Requests.APIStats)+
@ -1392,15 +1389,13 @@ func getHTTPMetrics() MetricsGroup {
}) })
} }
return return
}, })
} return mg
} }
func getNetworkMetrics() MetricsGroup { func getNetworkMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "networkMetrics", mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
cachedRead: cachedRead,
read: func(ctx context.Context) (metrics []Metric) {
metrics = make([]Metric, 0, 10) metrics = make([]Metric, 0, 10)
metrics = append(metrics, Metric{ metrics = append(metrics, Metric{
Description: getInternodeFailedRequests(), Description: getInternodeFailedRequests(),
@ -1424,15 +1419,13 @@ func getNetworkMetrics() MetricsGroup {
Value: float64(connStats.S3InputBytes), Value: float64(connStats.S3InputBytes),
}) })
return return
}, })
} return mg
} }
func getBucketUsageMetrics() MetricsGroup { func getBucketUsageMetrics() *MetricsGroup {
return MetricsGroup{ mg := &MetricsGroup{}
id: "BucketUsageMetrics", mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
cachedRead: cachedRead,
read: func(ctx context.Context) (metrics []Metric) {
objLayer := newObjectLayerFn() objLayer := newObjectLayerFn()
// Service not initialized yet // Service not initialized yet
if objLayer == nil || globalIsGateway { if objLayer == nil || globalIsGateway {
@ -1512,14 +1505,13 @@ func getBucketUsageMetrics() MetricsGroup {
} }
return return
}, })
} return mg
} }
func getLocalStorageMetrics() MetricsGroup {
return MetricsGroup{ func getLocalStorageMetrics() *MetricsGroup {
id: "localStorageMetrics", mg := &MetricsGroup{}
cachedRead: cachedRead, mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
read: func(ctx context.Context) (metrics []Metric) {
objLayer := newObjectLayerFn() objLayer := newObjectLayerFn()
// Service not initialized yet // Service not initialized yet
if objLayer == nil || globalIsGateway { if objLayer == nil || globalIsGateway {
@ -1554,14 +1546,13 @@ func getLocalStorageMetrics() MetricsGroup {
}) })
} }
return return
}, })
} return mg
} }
func getClusterStorageMetrics() MetricsGroup {
return MetricsGroup{ func getClusterStorageMetrics() *MetricsGroup {
id: "ClusterStorageMetrics", mg := &MetricsGroup{}
cachedRead: cachedRead, mg.RegisterRead(func(ctx context.Context) (metrics []Metric) {
read: func(ctx context.Context) (metrics []Metric) {
objLayer := newObjectLayerFn() objLayer := newObjectLayerFn()
// Service not initialized yet // Service not initialized yet
if objLayer == nil || !globalIsErasure { if objLayer == nil || !globalIsErasure {
@ -1609,17 +1600,19 @@ func getClusterStorageMetrics() MetricsGroup {
Value: float64(totalDisks.Sum()), Value: float64(totalDisks.Sum()),
}) })
return return
}, })
} return mg
} }
type minioClusterCollector struct { type minioClusterCollector struct {
metricsGroups []*MetricsGroup
desc *prometheus.Desc desc *prometheus.Desc
} }
func newMinioClusterCollector() *minioClusterCollector { func newMinioClusterCollector(metricsGroups []*MetricsGroup) *minioClusterCollector {
return &minioClusterCollector{ return &minioClusterCollector{
desc: prometheus.NewDesc("minio_stats", "Statistics exposed by MinIO server", nil, nil), metricsGroups: metricsGroups,
desc: prometheus.NewDesc("minio_stats", "Statistics exposed by MinIO server per cluster", nil, nil),
} }
} }
@ -1630,7 +1623,6 @@ func (c *minioClusterCollector) Describe(ch chan<- *prometheus.Desc) {
// Collect is called by the Prometheus registry when collecting metrics. // Collect is called by the Prometheus registry when collecting metrics.
func (c *minioClusterCollector) Collect(out chan<- prometheus.Metric) { func (c *minioClusterCollector) Collect(out chan<- prometheus.Metric) {
var wg sync.WaitGroup var wg sync.WaitGroup
publish := func(in <-chan Metric) { publish := func(in <-chan Metric) {
defer wg.Done() defer wg.Done()
@ -1679,7 +1671,7 @@ func (c *minioClusterCollector) Collect(out chan<- prometheus.Metric) {
// Call peer api to fetch metrics // Call peer api to fetch metrics
peerCh := globalNotificationSys.GetClusterMetrics(GlobalContext) peerCh := globalNotificationSys.GetClusterMetrics(GlobalContext)
selfCh := ReportMetrics(GlobalContext, GetAllGenerators) selfCh := ReportMetrics(GlobalContext, c.metricsGroups)
wg.Add(2) wg.Add(2)
go publish(peerCh) go publish(peerCh)
go publish(selfCh) go publish(selfCh)
@ -1687,11 +1679,11 @@ func (c *minioClusterCollector) Collect(out chan<- prometheus.Metric) {
} }
// ReportMetrics reports serialized metrics to the channel passed for the metrics generated. // ReportMetrics reports serialized metrics to the channel passed for the metrics generated.
func ReportMetrics(ctx context.Context, generators func() []MetricsGenerator) <-chan Metric { func ReportMetrics(ctx context.Context, metricsGroups []*MetricsGroup) <-chan Metric {
ch := make(chan Metric) ch := make(chan Metric)
go func() { go func() {
defer close(ch) defer close(ch)
populateAndPublish(generators, func(m Metric) bool { populateAndPublish(metricsGroups, func(m Metric) bool {
if m.VariableLabels == nil { if m.VariableLabels == nil {
m.VariableLabels = make(map[string]string) m.VariableLabels = make(map[string]string)
} }
@ -1709,24 +1701,24 @@ func ReportMetrics(ctx context.Context, generators func() []MetricsGenerator) <-
return ch return ch
} }
// minioCollectorV2 is the Custom Collector // minioNodeCollector is the Custom Collector
type minioCollectorV2 struct { type minioNodeCollector struct {
generator func() []MetricsGenerator metricsGroups []*MetricsGroup
desc *prometheus.Desc desc *prometheus.Desc
} }
// Describe sends the super-set of all possible descriptors of metrics // Describe sends the super-set of all possible descriptors of metrics
func (c *minioCollectorV2) Describe(ch chan<- *prometheus.Desc) { func (c *minioNodeCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.desc ch <- c.desc
} }
// populateAndPublish populates and then publishes the metrics generated by the generator function. // populateAndPublish populates and then publishes the metrics generated by the generator function.
func populateAndPublish(generatorFn func() []MetricsGenerator, publish func(m Metric) bool) { func populateAndPublish(metricsGroups []*MetricsGroup, publish func(m Metric) bool) {
generators := generatorFn() for _, mg := range metricsGroups {
for _, g := range generators { if mg == nil {
metricsGroup := g() continue
metrics := metricsGroup.cachedRead(GlobalContext, &metricsGroup) }
for _, metric := range metrics { for _, metric := range mg.Get() {
if !publish(metric) { if !publish(metric) {
return return
} }
@ -1735,12 +1727,12 @@ func populateAndPublish(generatorFn func() []MetricsGenerator, publish func(m Me
} }
// Collect is called by the Prometheus registry when collecting metrics. // Collect is called by the Prometheus registry when collecting metrics.
func (c *minioCollectorV2) Collect(ch chan<- prometheus.Metric) { func (c *minioNodeCollector) Collect(ch chan<- prometheus.Metric) {
// Expose MinIO's version information // Expose MinIO's version information
minioVersionInfo.WithLabelValues(Version, CommitID).Set(1.0) minioVersionInfo.WithLabelValues(Version, CommitID).Set(1.0)
populateAndPublish(c.generator, func(metric Metric) bool { populateAndPublish(c.metricsGroups, func(metric Metric) bool {
labels, values := getOrderedLabelValueArrays(metric.VariableLabels) labels, values := getOrderedLabelValueArrays(metric.VariableLabels)
values = append(values, globalLocalNodeName) values = append(values, globalLocalNodeName)
labels = append(labels, serverName) labels = append(labels, serverName)
@ -1799,30 +1791,31 @@ func getOrderedLabelValueArrays(labelsWithValue map[string]string) (labels, valu
return return
} }
// newMinioCollectorV2 describes the collector // newMinioCollectorNode describes the collector
// and returns reference of minioCollector for version 2 // and returns reference of minioCollector for version 2
// It creates the Prometheus Description which is used // It creates the Prometheus Description which is used
// to define Metric and help string // to define Metric and help string
func newMinioCollectorV2(generator func() []MetricsGenerator) *minioCollectorV2 { func newMinioCollectorNode(metricsGroups []*MetricsGroup) *minioNodeCollector {
return &minioCollectorV2{ return &minioNodeCollector{
generator: generator, metricsGroups: metricsGroups,
desc: prometheus.NewDesc("minio_stats", "Statistics exposed by MinIO server", nil, nil), desc: prometheus.NewDesc("minio_stats", "Statistics exposed by MinIO server per node", nil, nil),
} }
} }
func metricsServerHandler() http.Handler { func metricsServerHandler() http.Handler {
registry := prometheus.NewRegistry() registry := prometheus.NewRegistry()
// Report all other metrics // Report all other metrics
err := registry.Register(newMinioClusterCollector()) err := registry.Register(clusterCollector)
if err != nil { if err != nil {
logger.CriticalIf(GlobalContext, err) logger.CriticalIf(GlobalContext, err)
} }
// DefaultGatherers include golang metrics and process metrics. // DefaultGatherers include golang metrics and process metrics.
gatherers := prometheus.Gatherers{ gatherers := prometheus.Gatherers{
registry, registry,
} }
// Delegate http serving to Prometheus client library, which will call collector.Collect. // Delegate http serving to Prometheus client library, which will call collector.Collect.
return promhttp.InstrumentMetricHandler( return promhttp.InstrumentMetricHandler(
registry, registry,
@ -1836,7 +1829,7 @@ func metricsServerHandler() http.Handler {
func metricsNodeHandler() http.Handler { func metricsNodeHandler() http.Handler {
registry := prometheus.NewRegistry() registry := prometheus.NewRegistry()
err := registry.Register(newMinioCollectorV2(GetSingleNodeGenerators)) err := registry.Register(nodeCollector)
if err != nil { if err != nil {
logger.CriticalIf(GlobalContext, err) logger.CriticalIf(GlobalContext, err)
} }

View File

@ -1113,7 +1113,7 @@ func (s *peerRESTServer) GetPeerMetrics(w http.ResponseWriter, r *http.Request)
enc := gob.NewEncoder(w) enc := gob.NewEncoder(w)
ch := ReportMetrics(r.Context(), GetGeneratorsForPeer) ch := ReportMetrics(r.Context(), peerMetricsGroups)
for m := range ch { for m := range ch {
if err := enc.Encode(m); err != nil { if err := enc.Encode(m); err != nil {
s.writeErrorResponse(w, errors.New("Encoding metric failed: "+err.Error())) s.writeErrorResponse(w, errors.New("Encoding metric failed: "+err.Error()))

View File

@ -29,6 +29,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"time"
) )
// Tests maximum object size. // Tests maximum object size.
@ -486,3 +487,32 @@ func TestGetMinioMode(t *testing.T) {
testMinioMode(globalMinioModeGatewayPrefix + globalGatewayName) testMinioMode(globalMinioModeGatewayPrefix + globalGatewayName)
} }
func TestTimedValue(t *testing.T) {
var cache timedValue
t.Parallel()
cache.Once.Do(func() {
cache.TTL = 2 * time.Second
cache.Update = func() (interface{}, error) {
return time.Now(), nil
}
})
i, _ := cache.Get()
t1 := i.(time.Time)
j, _ := cache.Get()
t2 := j.(time.Time)
if !t1.Equal(t2) {
t.Fatalf("expected time to be equal: %s != %s", t1, t2)
}
time.Sleep(3 * time.Second)
k, _ := cache.Get()
t3 := k.(time.Time)
if t1.Equal(t3) {
t.Fatalf("expected time to be un-equal: %s == %s", t1, t3)
}
}