@@ -868,6 +868,93 @@ func TestIngesterUserLimitExceeded(t *testing.T) {
868
868
869
869
}
870
870
871
+ func TestIngesterUserLimitExceededForNativeHistograms (t * testing.T ) {
872
+ limits := defaultLimitsTestConfig ()
873
+ limits .EnableNativeHistograms = true
874
+ limits .MaxLocalNativeHistogramsSeriesPerUser = 1
875
+ limits .MaxLocalSeriesPerUser = 1
876
+ limits .MaxLocalMetricsWithMetadataPerUser = 1
877
+
878
+ userID := "1"
879
+ // Series
880
+ labels1 := labels.Labels {{Name : labels .MetricName , Value : "testmetric" }, {Name : "foo" , Value : "bar" }}
881
+ labels3 := labels.Labels {{Name : labels .MetricName , Value : "testmetric" }, {Name : "foo" , Value : "biz" }}
882
+ sampleNativeHistogram1 := cortexpb .HistogramToHistogramProto (0 , tsdbutil .GenerateTestHistogram (1 ))
883
+ sampleNativeHistogram2 := cortexpb .HistogramToHistogramProto (1 , tsdbutil .GenerateTestHistogram (2 ))
884
+ sampleNativeHistogram3 := cortexpb .HistogramToHistogramProto (0 , tsdbutil .GenerateTestHistogram (3 ))
885
+
886
+ // Metadata
887
+ metadata1 := & cortexpb.MetricMetadata {MetricFamilyName : "testmetric" , Help : "a help for testmetric" , Type : cortexpb .COUNTER }
888
+ metadata2 := & cortexpb.MetricMetadata {MetricFamilyName : "testmetric2" , Help : "a help for testmetric2" , Type : cortexpb .COUNTER }
889
+
890
+ dir := t .TempDir ()
891
+
892
+ chunksDir := filepath .Join (dir , "chunks" )
893
+ blocksDir := filepath .Join (dir , "blocks" )
894
+ require .NoError (t , os .Mkdir (chunksDir , os .ModePerm ))
895
+ require .NoError (t , os .Mkdir (blocksDir , os .ModePerm ))
896
+
897
+ blocksIngesterGenerator := func (reg prometheus.Registerer ) * Ingester {
898
+ ing , err := prepareIngesterWithBlocksStorageAndLimits (t , defaultIngesterTestConfig (t ), limits , nil , blocksDir , reg )
899
+ require .NoError (t , err )
900
+ require .NoError (t , services .StartAndAwaitRunning (context .Background (), ing ))
901
+ // Wait until it's ACTIVE
902
+ test .Poll (t , time .Second , ring .ACTIVE , func () interface {} {
903
+ return ing .lifecycler .GetState ()
904
+ })
905
+
906
+ return ing
907
+ }
908
+
909
+ tests := []string {"blocks" }
910
+ for i , ingGenerator := range []func (reg prometheus.Registerer ) * Ingester {blocksIngesterGenerator } {
911
+ t .Run (tests [i ], func (t * testing.T ) {
912
+ reg := prometheus .NewRegistry ()
913
+ ing := ingGenerator (reg )
914
+
915
+ // Append only one series and one metadata first, expect no error.
916
+ ctx := user .InjectOrgID (context .Background (), userID )
917
+ _ , err := ing .Push (ctx , cortexpb .ToWriteRequest ([]labels.Labels {labels1 }, nil , []* cortexpb.MetricMetadata {metadata1 }, []cortexpb.Histogram {sampleNativeHistogram1 }, cortexpb .API ))
918
+ require .NoError (t , err )
919
+
920
+ testLimits := func (reg prometheus.Gatherer ) {
921
+ // Append to two series, expect series-exceeded error.
922
+ _ , err = ing .Push (ctx , cortexpb .ToWriteRequest ([]labels.Labels {labels1 , labels3 }, nil , nil , []cortexpb.Histogram {sampleNativeHistogram2 , sampleNativeHistogram3 }, cortexpb .API ))
923
+ httpResp , ok := httpgrpc .HTTPResponseFromError (err )
924
+ require .True (t , ok , "returned error is not an httpgrpc response" )
925
+ assert .Equal (t , http .StatusBadRequest , int (httpResp .Code ))
926
+ assert .Equal (t , wrapWithUser (makeLimitError (perUserNativeHistogramsSeriesLimit , ing .limiter .FormatError (userID , errMaxNativeHistogramsSeriesPerUserLimitExceeded , labels1 )), userID ).Error (), string (httpResp .Body ))
927
+
928
+ // Append two metadata, expect no error since metadata is a best effort approach.
929
+ _ , err = ing .Push (ctx , cortexpb .ToWriteRequest (nil , nil , []* cortexpb.MetricMetadata {metadata1 , metadata2 }, nil , cortexpb .API ))
930
+ require .NoError (t , err )
931
+
932
+ // Read samples back via ingester queries.
933
+ res , _ , err := runTestQuery (ctx , t , ing , labels .MatchEqual , model .MetricNameLabel , "testmetric" )
934
+ require .NoError (t , err )
935
+ require .NotNil (t , res )
936
+
937
+ // Verify metadata
938
+ m , err := ing .MetricsMetadata (ctx , & client.MetricsMetadataRequest {Limit : - 1 , LimitPerMetric : - 1 , Metric : "" })
939
+ require .NoError (t , err )
940
+ assert .Equal (t , []* cortexpb.MetricMetadata {metadata1 }, m .Metadata )
941
+ }
942
+
943
+ testLimits (reg )
944
+
945
+ // Limits should hold after restart.
946
+ services .StopAndAwaitTerminated (context .Background (), ing ) //nolint:errcheck
947
+ // Use new registry to prevent metrics registration panic.
948
+ reg = prometheus .NewRegistry ()
949
+ ing = ingGenerator (reg )
950
+ defer services .StopAndAwaitTerminated (context .Background (), ing ) //nolint:errcheck
951
+
952
+ testLimits (reg )
953
+ })
954
+ }
955
+
956
+ }
957
+
871
958
func benchmarkData (nSeries int ) (allLabels []labels.Labels , allSamples []cortexpb.Sample ) {
872
959
for j := 0 ; j < nSeries ; j ++ {
873
960
labels := chunk .BenchmarkLabels .Copy ()
0 commit comments