@@ -619,3 +619,104 @@ func TestQuerierWithChunksStorage(t *testing.T) {
619
619
assertServiceMetricsPrefixes (t , Querier , querier )
620
620
assertServiceMetricsPrefixes (t , TableManager , tableManager )
621
621
}
622
+
623
+ func TestHashCollisionHandling (t * testing.T ) {
624
+ s , err := e2e .NewScenario (networkName )
625
+ require .NoError (t , err )
626
+ defer s .Close ()
627
+
628
+ require .NoError (t , writeFileToSharedDir (s , cortexSchemaConfigFile , []byte (cortexSchemaConfigYaml )))
629
+ flags := mergeFlags (ChunksStorageFlags , map [string ]string {})
630
+
631
+ // Start dependencies.
632
+ dynamo := e2edb .NewDynamoDB ()
633
+
634
+ consul := e2edb .NewConsul ()
635
+ require .NoError (t , s .StartAndWaitReady (consul , dynamo ))
636
+
637
+ tableManager := e2ecortex .NewTableManager ("table-manager" , ChunksStorageFlags , "" )
638
+ require .NoError (t , s .StartAndWaitReady (tableManager ))
639
+
640
+ // Wait until the first table-manager sync has completed, so that we're
641
+ // sure the tables have been created.
642
+ require .NoError (t , tableManager .WaitSumMetrics (e2e .Greater (0 ), "cortex_table_manager_sync_success_timestamp_seconds" ))
643
+
644
+ // Start Cortex components for the write path.
645
+ distributor := e2ecortex .NewDistributor ("distributor" , consul .NetworkHTTPEndpoint (), flags , "" )
646
+ ingester := e2ecortex .NewIngester ("ingester" , consul .NetworkHTTPEndpoint (), flags , "" )
647
+ require .NoError (t , s .StartAndWaitReady (distributor , ingester ))
648
+
649
+ // Wait until the distributor has updated the ring.
650
+ require .NoError (t , distributor .WaitSumMetrics (e2e .Equals (512 ), "cortex_ring_tokens_total" ))
651
+
652
+ // Push a series for each user to Cortex.
653
+ now := time .Now ()
654
+
655
+ c , err := e2ecortex .NewClient (distributor .HTTPEndpoint (), "" , "" , "" , "user-0" )
656
+ require .NoError (t , err )
657
+
658
+ var series []prompb.TimeSeries
659
+ var expectedVector model.Vector
660
+ // Generate two series which collide on fingerprints and fast fingerprints.
661
+ tsMillis := e2e .TimeToMilliseconds (now )
662
+ metric1 := []prompb.Label {
663
+ {Name : "A" , Value : "K6sjsNNczPl" },
664
+ {Name : labels .MetricName , Value : "fingerprint_collision" },
665
+ }
666
+ metric2 := []prompb.Label {
667
+ {Name : "A" , Value : "cswpLMIZpwt" },
668
+ {Name : labels .MetricName , Value : "fingerprint_collision" },
669
+ }
670
+
671
+ series = append (series , prompb.TimeSeries {
672
+ Labels : metric1 ,
673
+ Samples : []prompb.Sample {
674
+ {Value : float64 (0 ), Timestamp : tsMillis },
675
+ },
676
+ })
677
+ expectedVector = append (expectedVector , & model.Sample {
678
+ Metric : prompbLabelsToModelMetric (metric1 ),
679
+ Value : model .SampleValue (float64 (0 )),
680
+ Timestamp : model .Time (tsMillis ),
681
+ })
682
+ series = append (series , prompb.TimeSeries {
683
+ Labels : metric2 ,
684
+ Samples : []prompb.Sample {
685
+ {Value : float64 (1 ), Timestamp : tsMillis },
686
+ },
687
+ })
688
+ expectedVector = append (expectedVector , & model.Sample {
689
+ Metric : prompbLabelsToModelMetric (metric2 ),
690
+ Value : model .SampleValue (float64 (1 )),
691
+ Timestamp : model .Time (tsMillis ),
692
+ })
693
+
694
+ res , err := c .Push (series )
695
+ require .NoError (t , err )
696
+ require .Equal (t , 200 , res .StatusCode )
697
+
698
+ querier := e2ecortex .NewQuerier ("querier" , consul .NetworkHTTPEndpoint (), flags , "" )
699
+ require .NoError (t , s .StartAndWaitReady (querier ))
700
+
701
+ // Wait until the querier has updated the ring.
702
+ require .NoError (t , querier .WaitSumMetrics (e2e .Equals (512 ), "cortex_ring_tokens_total" ))
703
+
704
+ // Query the series.
705
+ c , err = e2ecortex .NewClient ("" , querier .HTTPEndpoint (), "" , "" , "user-0" )
706
+ require .NoError (t , err )
707
+
708
+ result , err := c .Query ("fingerprint_collision" , now )
709
+ require .NoError (t , err )
710
+ require .Equal (t , model .ValVector , result .Type ())
711
+ require .Equal (t , expectedVector , result .(model.Vector ))
712
+ }
713
+
714
+ func prompbLabelsToModelMetric (pbLabels []prompb.Label ) model.Metric {
715
+ metric := model.Metric {}
716
+
717
+ for _ , l := range pbLabels {
718
+ metric [model .LabelName (l .Name )] = model .LabelValue (l .Value )
719
+ }
720
+
721
+ return metric
722
+ }
0 commit comments