@@ -5,8 +5,11 @@ import (
5
5
"errors"
6
6
"fmt"
7
7
"math"
8
+ "strings"
8
9
"sync"
9
10
11
+ "github.com/gogo/protobuf/proto"
12
+
10
13
"github.com/go-kit/log/level"
11
14
"github.com/prometheus/client_golang/prometheus"
12
15
dto "github.com/prometheus/client_model/go"
@@ -562,13 +565,14 @@ type UserRegistry struct {
562
565
// UserRegistries holds Prometheus registries for multiple users, guaranteeing
563
566
// multi-thread safety and stable ordering.
564
567
type UserRegistries struct {
565
- regsMu sync.Mutex
566
- regs []UserRegistry
568
+ regsMu sync.Mutex
569
+ regs []UserRegistry
570
+ removedMetrics MetricFamilyMap
567
571
}
568
572
569
573
// NewUserRegistries makes new UserRegistries.
570
574
func NewUserRegistries () * UserRegistries {
571
- return & UserRegistries {}
575
+ return & UserRegistries {removedMetrics : make ( MetricFamilyMap ) }
572
576
}
573
577
574
578
// AddUserRegistry adds an user registry. If user already has a registry,
@@ -647,15 +651,21 @@ func (r *UserRegistries) softRemoveUserRegistry(ur *UserRegistry) bool {
647
651
return false
648
652
}
649
653
650
- ur . lastGather , err = NewMetricFamilyMap (last )
654
+ gatheredMetrics , err : = NewMetricFamilyMap (last )
651
655
if err != nil {
652
656
level .Warn (util_log .Logger ).Log ("msg" , "failed to gather metrics from registry" , "user" , ur .user , "err" , err )
653
657
return false
654
658
}
655
659
660
+ aggregatedMetrics , err := MergeMetricFamilies ([]MetricFamilyMap {gatheredMetrics , r .removedMetrics })
661
+ if err != nil {
662
+ level .Warn (util_log .Logger ).Log ("msg" , "failed to merge metrics" , "user" , ur .user , "err" , err )
663
+ return false
664
+ }
665
+ r .removedMetrics = aggregatedMetrics
656
666
ur .user = ""
657
667
ur .reg = nil
658
- return true
668
+ return false
659
669
}
660
670
661
671
// Registries returns a copy of the user registries list.
@@ -704,6 +714,11 @@ func (r *UserRegistries) BuildMetricFamiliesPerUser() MetricFamiliesPerUser {
704
714
continue
705
715
}
706
716
}
717
+ data = append (data , struct {
718
+ user string
719
+ metrics MetricFamilyMap
720
+ }{
721
+ user : "" , metrics : r .removedMetrics })
707
722
return data
708
723
}
709
724
@@ -805,3 +820,173 @@ type CollectorVec interface {
805
820
prometheus.Collector
806
821
Delete (labels prometheus.Labels ) bool
807
822
}
823
+
824
+ type MergedMetricFamily struct {
825
+ metricFamily * dto.MetricFamily
826
+ metricMap MetricMap
827
+ }
828
+
829
+ func (m * MergedMetricFamily ) CreateMetricFamily () * dto.MetricFamily {
830
+ newMetricFamily := proto .Clone (m .metricFamily ).(* dto.MetricFamily )
831
+ var metrics []* dto.Metric
832
+
833
+ for _ , metric := range m .metricMap .metrics {
834
+ for _ , m := range metric {
835
+ metrics = append (metrics , & m .metric )
836
+ }
837
+ }
838
+
839
+ newMetricFamily .Metric = metrics
840
+ return newMetricFamily
841
+ }
842
+
843
+ // MergeMetricFamilies - Capable of merging summaries, histograms, and counters
844
+ func MergeMetricFamilies (metricFamilies []MetricFamilyMap ) (MetricFamilyMap , error ) {
845
+ mergedMap := make (map [string ]* MergedMetricFamily )
846
+
847
+ for _ , mf := range metricFamilies {
848
+ for metricName , metricFamily := range mf {
849
+ mergeFunc , err := getMergeFunc (metricFamily .GetType ())
850
+ if err != nil {
851
+ return nil , err
852
+ }
853
+
854
+ if _ , found := mergedMap [metricName ]; ! found {
855
+ mergedMap [metricName ] = & MergedMetricFamily {metricFamily : proto .Clone (metricFamily ).(* dto.MetricFamily ), metricMap : NewMetricMap ()}
856
+ }
857
+
858
+ for _ , metric := range metricFamily .Metric {
859
+ (mergedMap [metricName ].metricMap ).AddOrSetMetric (* metric , mergeFunc )
860
+ }
861
+ }
862
+ }
863
+
864
+ metricFamilyMap := make (MetricFamilyMap )
865
+ for metricName , mergedMetricFamily := range mergedMap {
866
+ metricFamilyMap [metricName ] = mergedMetricFamily .CreateMetricFamily ()
867
+ }
868
+
869
+ return metricFamilyMap , nil
870
+ }
871
+
872
+ func getMergeFunc (metricType dto.MetricType ) (func (existing * dto.Metric , new * dto.Metric ), error ) {
873
+ switch metricType {
874
+ case dto .MetricType_SUMMARY :
875
+ return mergeSummary , nil
876
+ case dto .MetricType_COUNTER :
877
+ return mergeCounter , nil
878
+ case dto .MetricType_HISTOGRAM :
879
+ return mergeHistogram , nil
880
+ default :
881
+ return nil , fmt .Errorf ("unknown metric type: %v" , metricType )
882
+ }
883
+ }
884
+
885
+ func mergeCounter (mf1 , mf2 * dto.Metric ) {
886
+ newValue := * mf1 .Counter .Value + * mf2 .Counter .Value
887
+ mf1 .Counter .Value = & newValue
888
+ }
889
+
890
+ func mergeHistogram (mf1 , mf2 * dto.Metric ) {
891
+ bucketMap := map [float64 ]uint64 {}
892
+
893
+ for _ , bucket := range append (mf1 .Histogram .GetBucket (), mf2 .Histogram .GetBucket ()... ) {
894
+ bucketMap [bucket .GetUpperBound ()] += bucket .GetCumulativeCount ()
895
+ }
896
+
897
+ var newBucket []* dto.Bucket
898
+ for upperBound , cumulativeCount := range bucketMap {
899
+ ubValue := upperBound
900
+ ccValue := cumulativeCount
901
+ newBucket = append (newBucket , & dto.Bucket {UpperBound : & ubValue , CumulativeCount : & ccValue })
902
+ }
903
+
904
+ newSampleCount := * mf1 .Histogram .SampleCount + * mf2 .Histogram .SampleCount
905
+ newSampleSum := * mf1 .Histogram .SampleSum + * mf2 .Histogram .SampleSum
906
+ mf1 .Histogram .Bucket = newBucket
907
+ mf1 .Histogram .SampleCount = & newSampleCount
908
+ mf1 .Histogram .SampleSum = & newSampleSum
909
+ }
910
+
911
+ func mergeSummary (mf1 * dto.Metric , mf2 * dto.Metric ) {
912
+ newSampleCount := * mf1 .Summary .SampleCount + * mf2 .Summary .SampleCount
913
+ newSampleSum := * mf1 .Summary .SampleSum + * mf2 .Summary .SampleSum
914
+
915
+ // we are not merging the Quantiles themselves because there's no operation that makes sense
916
+ mf1 .Summary .Quantile = []* dto.Quantile {}
917
+ mf1 .Summary .SampleCount = & newSampleCount
918
+ mf1 .Summary .SampleSum = & newSampleSum
919
+ }
920
+
921
+ type MetricMap struct {
922
+ metrics map [string ][]* Metric
923
+ lock sync.Mutex
924
+ }
925
+
926
+ type Metric struct {
927
+ metric dto.Metric
928
+ lock sync.Mutex
929
+ }
930
+
931
+ func NewMetricMap () MetricMap {
932
+ return MetricMap {
933
+ metrics : make (map [string ][]* Metric ),
934
+ }
935
+ }
936
+
937
+ // AddOrSetMetric - given a metric, see if there's another metric with the same labels. If not, add metric to list
938
+ // If yes, call mergeFn to merge the two metrics in-place, and updating existing metric
939
+ func (m * MetricMap ) AddOrSetMetric (metric dto.Metric , mergeFn func (existing * dto.Metric , new * dto.Metric )) {
940
+ var metricLabels []string
941
+ for _ , labelPair := range metric .GetLabel () {
942
+ metricLabels = append (metricLabels , fmt .Sprintf ("%s=%s" , labelPair .GetName (), labelPair .GetValue ()))
943
+ }
944
+
945
+ metricKey := strings .Join (metricLabels , "," )
946
+
947
+ m .lock .Lock ()
948
+ defer m .lock .Unlock ()
949
+
950
+ if metrics , found := m .metrics [metricKey ]; found {
951
+ // we might have hash collision, so let's iterate through the list to make sure the item is actually what we want
952
+ for _ , existingMetric := range metrics {
953
+ same := m .compareLabels (existingMetric .metric .GetLabel (), metric .GetLabel ())
954
+ if same {
955
+ existingMetric .lock .Lock ()
956
+ mergeFn (& existingMetric .metric , & metric )
957
+ existingMetric .lock .Unlock ()
958
+ return
959
+ }
960
+ }
961
+ // only get there if we don't have the same metric, so let's append it
962
+ m .metrics [metricKey ] = append (m .metrics [metricKey ], & Metric {metric : metric })
963
+ return
964
+ }
965
+
966
+ // no such key, so let's add it
967
+ m .metrics [metricKey ] = []* Metric {{metric : metric }}
968
+ }
969
+
970
+ func (m * MetricMap ) compareLabels (labels1 , labels2 []* dto.LabelPair ) bool {
971
+ if len (labels1 ) != len (labels2 ) {
972
+ return false
973
+ }
974
+
975
+ // create a map of labels for lookup
976
+ labelMap := make (map [string ]string )
977
+ for _ , labelPair := range labels1 {
978
+ labelMap [labelPair .GetName ()] = labelPair .GetValue ()
979
+ }
980
+
981
+ for _ , labelPair := range labels2 {
982
+ if value , found := labelMap [labelPair .GetName ()]; found {
983
+ if value != labelPair .GetValue () {
984
+ return false
985
+ }
986
+ } else {
987
+ return false
988
+ }
989
+ }
990
+
991
+ return true
992
+ }
0 commit comments