@@ -5,6 +5,7 @@ package integration
55
66import (
77 "fmt"
8+ "net/http"
89 "strconv"
910 "strings"
1011 "testing"
@@ -818,6 +819,90 @@ func TestQuerierWithBlocksStorageOnMissingBlocksFromStorage(t *testing.T) {
818819 assert .Contains (t , err .Error (), "500" )
819820}
820821
822+ func TestQuerierWithBlocksStorageLimits (t * testing.T ) {
823+ const blockRangePeriod = 5 * time .Second
824+
825+ s , err := e2e .NewScenario (networkName )
826+ require .NoError (t , err )
827+ defer s .Close ()
828+
829+ // Configure the blocks storage to frequently compact TSDB head
830+ // and ship blocks to the storage.
831+ flags := mergeFlags (BlocksStorageFlags (), map [string ]string {
832+ "-blocks-storage.tsdb.block-ranges-period" : blockRangePeriod .String (),
833+ "-blocks-storage.tsdb.ship-interval" : "1s" ,
834+ "-blocks-storage.tsdb.retention-period" : ((blockRangePeriod * 2 ) - 1 ).String (),
835+ })
836+
837+ // Start dependencies.
838+ consul := e2edb .NewConsul ()
839+ minio := e2edb .NewMinio (9000 , flags ["-blocks-storage.s3.bucket-name" ])
840+ require .NoError (t , s .StartAndWaitReady (consul , minio ))
841+
842+ // Start Cortex components for the write path.
843+ distributor := e2ecortex .NewDistributor ("distributor" , e2ecortex .RingStoreConsul , consul .NetworkHTTPEndpoint (), flags , "" )
844+ ingester := e2ecortex .NewIngester ("ingester" , e2ecortex .RingStoreConsul , consul .NetworkHTTPEndpoint (), flags , "" )
845+ require .NoError (t , s .StartAndWaitReady (distributor , ingester ))
846+
847+ // Wait until the distributor has updated the ring.
848+ require .NoError (t , distributor .WaitSumMetrics (e2e .Equals (512 ), "cortex_ring_tokens_total" ))
849+
850+ // Push some series to Cortex.
851+ c , err := e2ecortex .NewClient (distributor .HTTPEndpoint (), "" , "" , "" , "user-1" )
852+ require .NoError (t , err )
853+
854+ seriesTimestamp := time .Now ()
855+ series2Timestamp := seriesTimestamp .Add (blockRangePeriod * 2 )
856+ series1 , _ := generateSeries ("series_1" , seriesTimestamp , prompb.Label {Name : "job" , Value : "test" })
857+ series2 , _ := generateSeries ("series_2" , series2Timestamp , prompb.Label {Name : "job" , Value : "test" })
858+
859+ res , err := c .Push (series1 )
860+ require .NoError (t , err )
861+ require .Equal (t , 200 , res .StatusCode )
862+
863+ res , err = c .Push (series2 )
864+ require .NoError (t , err )
865+ require .Equal (t , 200 , res .StatusCode )
866+
867+ // Wait until the TSDB head is compacted and shipped to the storage.
868+ // The shipped block contains the 1st series, while the 2ns series in in the head.
869+ require .NoError (t , ingester .WaitSumMetrics (e2e .Equals (1 ), "cortex_ingester_shipper_uploads_total" ))
870+ require .NoError (t , ingester .WaitSumMetrics (e2e .Equals (2 ), "cortex_ingester_memory_series_created_total" ))
871+ require .NoError (t , ingester .WaitSumMetrics (e2e .Equals (1 ), "cortex_ingester_memory_series_removed_total" ))
872+ require .NoError (t , ingester .WaitSumMetrics (e2e .Equals (1 ), "cortex_ingester_memory_series" ))
873+
874+ // Start the querier and store-gateway, and configure them to frequently sync blocks fast enough to trigger consistency check.
875+ storeGateway := e2ecortex .NewStoreGateway ("store-gateway" , e2ecortex .RingStoreConsul , consul .NetworkHTTPEndpoint (), mergeFlags (flags , map [string ]string {
876+ "-blocks-storage.bucket-store.sync-interval" : "5s" ,
877+ "-querier.max-fetched-series-per-query" : "1" ,
878+ }), "" )
879+ querier := e2ecortex .NewQuerier ("querier" , e2ecortex .RingStoreConsul , consul .NetworkHTTPEndpoint (), mergeFlags (flags , map [string ]string {
880+ "-blocks-storage.bucket-store.sync-interval" : "5s" ,
881+ "-querier.max-fetched-series-per-query" : "1" ,
882+ }), "" )
883+ require .NoError (t , s .StartAndWaitReady (querier , storeGateway ))
884+
885+ // Wait until the querier and store-gateway have updated the ring, and wait until the blocks are old enough for consistency check
886+ require .NoError (t , querier .WaitSumMetrics (e2e .Equals (512 * 2 ), "cortex_ring_tokens_total" ))
887+ require .NoError (t , storeGateway .WaitSumMetrics (e2e .Equals (512 ), "cortex_ring_tokens_total" ))
888+ require .NoError (t , querier .WaitSumMetricsWithOptions (e2e .GreaterOrEqual (4 ), []string {"cortex_querier_blocks_scan_duration_seconds" }, e2e .WithMetricCount ))
889+
890+ // Query back the series.
891+ c , err = e2ecortex .NewClient ("" , querier .HTTPEndpoint (), "" , "" , "user-1" )
892+ require .NoError (t , err )
893+
894+ // We expect all queries hitting 422 exceeded series limit
895+ resp , body , err := c .QueryRaw (`{job="test"}` , series2Timestamp )
896+ require .NoError (t , err )
897+ require .Equal (t , http .StatusUnprocessableEntity , resp .StatusCode )
898+ require .Contains (t , string (body ), "max number of series limit" )
899+
900+ resp , body , err = c .SeriesRaw ([]string {`{job="test"}` }, series2Timestamp .Add (- time .Hour ), series2Timestamp )
901+ require .NoError (t , err )
902+ require .Equal (t , http .StatusUnprocessableEntity , resp .StatusCode )
903+ require .Contains (t , string (body ), "max number of series limit" )
904+ }
905+
821906func TestQueryLimitsWithBlocksStorageRunningInMicroServices (t * testing.T ) {
822907 const blockRangePeriod = 5 * time .Second
823908
0 commit comments