@@ -14,6 +14,8 @@ import (
14
14
"testing"
15
15
"time"
16
16
17
+ "google.golang.org/grpc/codes"
18
+
17
19
"github.com/go-kit/log"
18
20
"github.com/prometheus/client_golang/prometheus"
19
21
"github.com/prometheus/client_golang/prometheus/testutil"
@@ -24,6 +26,7 @@ import (
24
26
"github.com/stretchr/testify/require"
25
27
"github.com/weaveworks/common/httpgrpc"
26
28
"github.com/weaveworks/common/user"
29
+ "go.uber.org/atomic"
27
30
"google.golang.org/grpc"
28
31
"google.golang.org/grpc/health/grpc_health_v1"
29
32
"google.golang.org/grpc/status"
@@ -490,6 +493,106 @@ func TestDistributor_PushIngestionRateLimiter(t *testing.T) {
490
493
}
491
494
}
492
495
496
+ func TestPush_QuorumError (t * testing.T ) {
497
+ var limits validation.Limits
498
+ flagext .DefaultValues (& limits )
499
+
500
+ limits .IngestionRate = math .MaxFloat64
501
+
502
+ dists , ingesters , r , _ := prepare (t , prepConfig {
503
+ numDistributors : 1 ,
504
+ numIngesters : 3 ,
505
+ happyIngesters : 0 ,
506
+ shuffleShardSize : 3 ,
507
+ shardByAllLabels : true ,
508
+ shuffleShardEnabled : true ,
509
+ limits : & limits ,
510
+ })
511
+
512
+ ctx := user .InjectOrgID (context .Background (), "user" )
513
+
514
+ d := dists [0 ]
515
+
516
+ // Using 489 just to make sure we are not hitting the &limits
517
+ // Simulating 2 4xx and 1 5xx -> Should return 4xx
518
+ ingesters [0 ].failResp .Store (httpgrpc .Errorf (489 , "Throttling" ))
519
+ ingesters [1 ].failResp .Store (httpgrpc .Errorf (500 , "InternalServerError" ))
520
+ ingesters [2 ].failResp .Store (httpgrpc .Errorf (489 , "Throttling" ))
521
+
522
+ for i := 0 ; i < 1000 ; i ++ {
523
+ request := makeWriteRequest (0 , 30 , 20 )
524
+ _ , err := d .Push (ctx , request )
525
+ status , ok := status .FromError (err )
526
+ require .True (t , ok )
527
+ require .Equal (t , codes .Code (489 ), status .Code ())
528
+ }
529
+
530
+ // Simulating 2 5xx and 1 4xx -> Should return 5xx
531
+ ingesters [0 ].failResp .Store (httpgrpc .Errorf (500 , "InternalServerError" ))
532
+ ingesters [1 ].failResp .Store (httpgrpc .Errorf (489 , "Throttling" ))
533
+ ingesters [2 ].failResp .Store (httpgrpc .Errorf (500 , "InternalServerError" ))
534
+
535
+ for i := 0 ; i < 10000 ; i ++ {
536
+ request := makeWriteRequest (0 , 300 , 200 )
537
+ _ , err := d .Push (ctx , request )
538
+ status , ok := status .FromError (err )
539
+ require .True (t , ok )
540
+ require .Equal (t , codes .Code (500 ), status .Code ())
541
+ }
542
+
543
+ // Simulating 2 different errors and 1 success -> This case we may return any of the errors
544
+ ingesters [0 ].failResp .Store (httpgrpc .Errorf (500 , "InternalServerError" ))
545
+ ingesters [1 ].failResp .Store (httpgrpc .Errorf (489 , "Throttling" ))
546
+ ingesters [2 ].happy .Store (true )
547
+
548
+ for i := 0 ; i < 1000 ; i ++ {
549
+ request := makeWriteRequest (0 , 30 , 20 )
550
+ _ , err := d .Push (ctx , request )
551
+ status , ok := status .FromError (err )
552
+ require .True (t , ok )
553
+ require .True (t , status .Code () == 489 || status .Code () == 500 )
554
+ }
555
+
556
+ // Simulating 1 error -> Should return 2xx
557
+ ingesters [0 ].failResp .Store (httpgrpc .Errorf (500 , "InternalServerError" ))
558
+ ingesters [1 ].happy .Store (true )
559
+ ingesters [2 ].happy .Store (true )
560
+
561
+ for i := 0 ; i < 1000 ; i ++ {
562
+ request := makeWriteRequest (0 , 30 , 20 )
563
+ _ , err := d .Push (ctx , request )
564
+ require .NoError (t , err )
565
+ }
566
+
567
+ // Simulating an unhealthy ingester (ingester 2)
568
+ ingesters [0 ].failResp .Store (httpgrpc .Errorf (500 , "InternalServerError" ))
569
+ ingesters [1 ].happy .Store (true )
570
+ ingesters [2 ].happy .Store (true )
571
+
572
+ err := r .KVClient .CAS (context .Background (), ring .IngesterRingKey , func (in interface {}) (interface {}, bool , error ) {
573
+ r := in .(* ring.Desc )
574
+ ingester2 := r .Ingesters ["2" ]
575
+ ingester2 .State = ring .LEFT
576
+ ingester2 .Timestamp = time .Now ().Unix ()
577
+ r .Ingesters ["2" ] = ingester2
578
+ return in , true , nil
579
+ })
580
+
581
+ require .NoError (t , err )
582
+
583
+ // Give time to the ring get updated with the KV value
584
+ time .Sleep (5 * time .Second )
585
+
586
+ for i := 0 ; i < 1000 ; i ++ {
587
+ request := makeWriteRequest (0 , 30 , 20 )
588
+ _ , err := d .Push (ctx , request )
589
+ require .Error (t , err )
590
+ status , ok := status .FromError (err )
591
+ require .True (t , ok )
592
+ require .Equal (t , codes .Code (500 ), status .Code ())
593
+ }
594
+ }
595
+
493
596
func TestDistributor_PushInstanceLimits (t * testing.T ) {
494
597
495
598
type testPush struct {
@@ -1949,7 +2052,7 @@ func prepare(t *testing.T, cfg prepConfig) ([]*Distributor, []mockIngester, []*p
1949
2052
ingesters := []mockIngester {}
1950
2053
for i := 0 ; i < cfg .happyIngesters ; i ++ {
1951
2054
ingesters = append (ingesters , mockIngester {
1952
- happy : true ,
2055
+ happy : * atomic . NewBool ( true ) ,
1953
2056
queryDelay : cfg .queryDelay ,
1954
2057
})
1955
2058
}
@@ -1961,7 +2064,7 @@ func prepare(t *testing.T, cfg prepConfig) ([]*Distributor, []mockIngester, []*p
1961
2064
1962
2065
ingesters = append (ingesters , mockIngester {
1963
2066
queryDelay : cfg .queryDelay ,
1964
- failResp : miError ,
2067
+ failResp : * atomic . NewError ( miError ) ,
1965
2068
})
1966
2069
}
1967
2070
@@ -2208,8 +2311,8 @@ type mockIngester struct {
2208
2311
sync.Mutex
2209
2312
client.IngesterClient
2210
2313
grpc_health_v1.HealthClient
2211
- happy bool
2212
- failResp error
2314
+ happy atomic. Bool
2315
+ failResp atomic. Error
2213
2316
stats client.UsersStatsResponse
2214
2317
timeseries map [uint32 ]* cortexpb.PreallocTimeseries
2215
2318
metadata map [uint32 ]map [cortexpb.MetricMetadata ]struct {}
@@ -2247,8 +2350,8 @@ func (i *mockIngester) Push(ctx context.Context, req *cortexpb.WriteRequest, opt
2247
2350
2248
2351
i .trackCall ("Push" )
2249
2352
2250
- if ! i .happy {
2251
- return nil , i .failResp
2353
+ if ! i .happy . Load () {
2354
+ return nil , i .failResp . Load ()
2252
2355
}
2253
2356
2254
2357
if i .timeseries == nil {
@@ -2305,7 +2408,7 @@ func (i *mockIngester) Query(ctx context.Context, req *client.QueryRequest, opts
2305
2408
2306
2409
i .trackCall ("Query" )
2307
2410
2308
- if ! i .happy {
2411
+ if ! i .happy . Load () {
2309
2412
return nil , errFail
2310
2413
}
2311
2414
@@ -2331,7 +2434,7 @@ func (i *mockIngester) QueryStream(ctx context.Context, req *client.QueryRequest
2331
2434
2332
2435
i .trackCall ("QueryStream" )
2333
2436
2334
- if ! i .happy {
2437
+ if ! i .happy . Load () {
2335
2438
return nil , errFail
2336
2439
}
2337
2440
@@ -2395,7 +2498,7 @@ func (i *mockIngester) MetricsForLabelMatchers(ctx context.Context, req *client.
2395
2498
2396
2499
i .trackCall ("MetricsForLabelMatchers" )
2397
2500
2398
- if ! i .happy {
2501
+ if ! i .happy . Load () {
2399
2502
return nil , errFail
2400
2503
}
2401
2504
@@ -2421,7 +2524,7 @@ func (i *mockIngester) MetricsMetadata(ctx context.Context, req *client.MetricsM
2421
2524
2422
2525
i .trackCall ("MetricsMetadata" )
2423
2526
2424
- if ! i .happy {
2527
+ if ! i .happy . Load () {
2425
2528
return nil , errFail
2426
2529
}
2427
2530
0 commit comments