@@ -495,3 +495,183 @@ func TestConcurrentUpdatesAndReinitialiseMetric(t *testing.T) {
495495 pe .ScrapeRegistry (r , metric .WithIncludeChildMetrics (true ), metric .WithIncludeAggregateMetrics (true ))
496496 })
497497}
498+
499+ // TestHighCardinalityMetricsWithOptions tests all high cardinality metric types
500+ // (Counter, Gauge, Histogram) with both custom and default options.
501+ func TestHighCardinalityMetricsWithOptions (t * testing.T ) {
502+ defer leaktest .AfterTest (t )()
503+
504+ // Test with custom options
505+ t .Run ("CustomOptions" , func (t * testing.T ) {
506+ // Save and restore the original values
507+ originalMaxLabelValues := metric .MaxLabelValues
508+ originalRetentionTime := metric .RetentionTimeTillEviction
509+ defer func () {
510+ metric .MaxLabelValues = originalMaxLabelValues
511+ metric .RetentionTimeTillEviction = originalRetentionTime
512+ }()
513+
514+ const customCacheSize = 5
515+ const customRetention = 2 * time .Second
516+
517+ // Set the package-level variables directly since they're cached at package load time
518+ metric .MaxLabelValues = 5
519+ metric .RetentionTimeTillEviction = 2 * time .Second
520+
521+ r := metric .NewRegistry ()
522+ writePrometheusMetrics := WritePrometheusMetricsFunc (r )
523+
524+ // Create all three metric types with custom options
525+ counter := NewHighCardinalityCounter (
526+ metric.HighCardinalityMetricOptions {
527+ Metadata : metric.Metadata {Name : "custom_options_counter" },
528+ MaxLabelValues : customCacheSize ,
529+ RetentionTimeTillEviction : customRetention ,
530+ },
531+ "database" , "application_name" ,
532+ )
533+ r .AddMetric (counter )
534+
535+ gauge := NewHighCardinalityGauge (
536+ metric.HighCardinalityMetricOptions {
537+ Metadata : metric.Metadata {Name : "custom_options_gauge" },
538+ MaxLabelValues : customCacheSize ,
539+ RetentionTimeTillEviction : customRetention ,
540+ },
541+ "database" , "application_name" ,
542+ )
543+ r .AddMetric (gauge )
544+
545+ histogram := NewHighCardinalityHistogram (
546+ metric.HistogramOptions {
547+ Metadata : metric.Metadata {
548+ Name : "custom_options_histogram" ,
549+ },
550+ Duration : base .DefaultHistogramWindowInterval (),
551+ MaxVal : 100 ,
552+ SigFigs : 1 ,
553+ BucketConfig : metric .Percent100Buckets ,
554+ HighCardinalityOpts : metric.HighCardinalityMetricOptions {
555+ MaxLabelValues : customCacheSize ,
556+ RetentionTimeTillEviction : customRetention ,
557+ },
558+ },
559+ "database" , "application_name" ,
560+ )
561+ r .AddMetric (histogram )
562+
563+ // Initialize with label slice caches
564+ labelSliceCache := metric .NewLabelSliceCache ()
565+ counter .InitializeMetrics (labelSliceCache )
566+ gauge .InitializeMetrics (labelSliceCache )
567+ histogram .InitializeMetrics (labelSliceCache )
568+
569+ // Add more entries than the custom cache size
570+ for i := 0 ; i < customCacheSize + 3 ; i ++ {
571+ counter .Inc (1 , "db1" , strconv .Itoa (i ))
572+ gauge .Update (int64 (i + 1 ), "db1" , strconv .Itoa (i ))
573+ histogram .RecordValue (int64 (i + 1 ), "db1" , strconv .Itoa (i ))
574+ }
575+
576+ // Wait for custom retention time to pass
577+ time .Sleep (customRetention + 500 * time .Millisecond )
578+
579+ testFile := "HighCardinalityMetrics_custom_options_pre_eviction.txt"
580+ if metric .HdrEnabled () {
581+ testFile = "HighCardinalityMetrics_custom_options_pre_eviction_hdr.txt"
582+ }
583+ echotest .Require (t , writePrometheusMetrics (t ), datapathutils .TestDataPath (t , testFile ))
584+
585+ // Add new entries to trigger eviction
586+ for i := customCacheSize + 3 ; i < customCacheSize + 6 ; i ++ {
587+ counter .Inc (1 , "db2" , strconv .Itoa (i ))
588+ gauge .Inc (5 , "db2" , strconv .Itoa (i ))
589+ histogram .RecordValue (int64 (i + 10 ), "db2" , strconv .Itoa (i ))
590+ }
591+
592+ testFile = "HighCardinalityMetrics_custom_options_post_eviction.txt"
593+ if metric .HdrEnabled () {
594+ testFile = "HighCardinalityMetrics_custom_options_post_eviction_hdr.txt"
595+ }
596+ echotest .Require (t , writePrometheusMetrics (t ), datapathutils .TestDataPath (t , testFile ))
597+
598+ // Verify that old entries were evicted
599+ for i := 0 ; i < 3 ; i ++ {
600+ metricKey := metric .LabelSliceCacheKey (metricKey ("db1" , strconv .Itoa (i )))
601+ _ , ok := labelSliceCache .Get (metricKey )
602+ require .False (t , ok , "old entry should have been evicted" )
603+ }
604+ })
605+
606+ // Test with default options
607+ t .Run ("DefaultOptions" , func (t * testing.T ) {
608+ r := metric .NewRegistry ()
609+ writePrometheusMetrics := WritePrometheusMetricsFunc (r )
610+
611+ // Create all three metric types with default options
612+ counter := NewHighCardinalityCounter (
613+ metric.HighCardinalityMetricOptions {
614+ Metadata : metric.Metadata {Name : "default_options_counter" },
615+ MaxLabelValues : 0 , // Should default to 5000
616+ RetentionTimeTillEviction : 0 , // Should default to 20 seconds
617+ },
618+ "database" , "application_name" ,
619+ )
620+ r .AddMetric (counter )
621+
622+ gauge := NewHighCardinalityGauge (
623+ metric.HighCardinalityMetricOptions {
624+ Metadata : metric.Metadata {Name : "default_options_gauge" },
625+ MaxLabelValues : 0 , // Should default to 5000
626+ RetentionTimeTillEviction : 0 , // Should default to 20 seconds
627+ },
628+ "database" , "application_name" ,
629+ )
630+ r .AddMetric (gauge )
631+
632+ histogram := NewHighCardinalityHistogram (
633+ metric.HistogramOptions {
634+ Metadata : metric.Metadata {
635+ Name : "default_options_histogram" ,
636+ },
637+ Duration : base .DefaultHistogramWindowInterval (),
638+ MaxVal : 100 ,
639+ SigFigs : 1 ,
640+ BucketConfig : metric .Percent100Buckets ,
641+ HighCardinalityOpts : metric.HighCardinalityMetricOptions {
642+ MaxLabelValues : 0 , // Should default to 5000
643+ RetentionTimeTillEviction : 0 , // Should default to 20 seconds
644+ },
645+ },
646+ "database" , "application_name" ,
647+ )
648+ r .AddMetric (histogram )
649+
650+ // Initialize with label slice caches
651+ labelSliceCache := metric .NewLabelSliceCache ()
652+ counter .InitializeMetrics (labelSliceCache )
653+ gauge .InitializeMetrics (labelSliceCache )
654+ histogram .InitializeMetrics (labelSliceCache )
655+
656+ // Add a few entries
657+ for i := 0 ; i < 10 ; i ++ {
658+ counter .Inc (1 , "db1" , strconv .Itoa (i ))
659+ gauge .Update (int64 (i + 1 ), "db1" , strconv .Itoa (i ))
660+ histogram .RecordValue (int64 (i + 1 ), "db1" , strconv .Itoa (i ))
661+ }
662+
663+ testFile := "HighCardinalityMetrics_default_options.txt"
664+ if metric .HdrEnabled () {
665+ testFile = "HighCardinalityMetrics_default_options_hdr.txt"
666+ }
667+ echotest .Require (t , writePrometheusMetrics (t ), datapathutils .TestDataPath (t , testFile ))
668+
669+ // Verify all entries are still present (shouldn't evict with default settings)
670+ for i := 0 ; i < 10 ; i ++ {
671+ metricKey := metric .LabelSliceCacheKey (metricKey ("db1" , strconv .Itoa (i )))
672+ labelSliceValue , ok := labelSliceCache .Get (metricKey )
673+ require .True (t , ok , "entry should still be present with default settings" )
674+ require .Equal (t , int64 (3 ), labelSliceValue .Counter .Load ())
675+ }
676+ })
677+ }
0 commit comments