@@ -24,6 +24,12 @@ type throttleBackend struct {
2424 calls int
2525}
2626
27+ type throttleReaderBackend struct {
28+ core.Backend
29+ err error
30+ calls int
31+ }
32+
2733func (bp * throttleBackend ) errOnce () error {
2834 bp .calls ++
2935 if bp .calls == 1 {
@@ -48,6 +54,11 @@ func (bp *throttleBackend) GetObj(context.Context, *core.LOM, cmn.OWT, *http.Req
4854 return 0 , nil
4955}
5056
57+ func (bp * throttleReaderBackend ) GetObjReader (context.Context , * core.LOM , int64 , int64 ) core.GetReaderResult {
58+ bp .calls ++
59+ return core.GetReaderResult {Err : bp .err , ErrCode : http .StatusTooManyRequests }
60+ }
61+
5162func TestRLBackendGetHead (t * testing.T ) {
5263 tests := []struct {
5364 name string
@@ -129,3 +140,42 @@ func TestRLBackendHeadReturnsRetryError(t *testing.T) {
129140 tassert .Fatalf (t , oa == nil , "expected no object attributes, got %v" , oa )
130141 tassert .Fatalf (t , backend .calls == 1 , "expected no retry call, got %d calls" , backend .calls )
131142}
143+
144+ func TestRLBackendGetObjReaderReturnsRetryError (t * testing.T ) {
145+ lom := core .AllocLOM ("rate-limit-reader-retry-error" )
146+ defer core .FreeLOM (lom )
147+ err := lom .InitBck (meta .NewBck (testBucket , apc .AIS , cmn .NsGlobal ))
148+ tassert .CheckFatal (t , err )
149+
150+ conf := & lom .Bck ().Props .RateLimit .Backend
151+ oldEnabled := conf .Enabled
152+ conf .Enabled = true
153+ defer func () { conf .Enabled = oldEnabled }()
154+
155+ arl , err := cos .NewAdaptRateLim (100 , 1 , time .Second )
156+ tassert .CheckFatal (t , err )
157+ key := lom .Bck ().HashUname (http .MethodGet )
158+ mockTarget .ratelim .Store (key , arl )
159+ defer mockTarget .ratelim .Delete (key )
160+
161+ err429 := cmn .NewErrTooManyRequests (errors .New ("throttled" ), http .StatusTooManyRequests )
162+ backend := & throttleReaderBackend {err : err429 }
163+ bp := & rlbackend {Backend : backend , t : mockTarget }
164+ ctx , cancel := context .WithCancel (context .Background ())
165+ cancel ()
166+ res := bp .GetObjReader (ctx , lom , 0 , 0 )
167+ tassert .Fatalf (t , errors .Is (res .Err , context .Canceled ), "expected retry error %v, got %v" , context .Canceled , res .Err )
168+ tassert .Fatalf (t , res .ErrCode == 0 , "expected retry code 0, got %d" , res .ErrCode )
169+ tassert .Fatalf (t , backend .calls == 1 , "expected no retry call, got %d calls" , backend .calls )
170+ }
171+
172+ func TestRLBackendRetryErrorDetails (t * testing.T ) {
173+ err429 := cmn .NewErrTooManyRequests (errors .New ("throttled" ), http .StatusTooManyRequests )
174+ var err error = & errBackendRetry {err : err429 , retries : 3 , total : 5 * time .Second }
175+ var retryErr * errBackendRetry
176+ tassert .Fatalf (t , errors .As (err , & retryErr ), "expected backend retry error, got %T" , err )
177+ tassert .Fatalf (t , retryErr .retries == 3 , "expected 3 retries, got %d" , retryErr .retries )
178+ tassert .Fatalf (t , retryErr .total == 5 * time .Second , "expected 5s backoff, got %v" , retryErr .total )
179+ tassert .Fatalf (t , errors .Is (err , err429 ), "expected wrapped error %v, got %v" , err429 , err )
180+ tassert .Fatalf (t , cmn .IsErrTooManyRequests (err ), "expected throttling error, got %v" , err )
181+ }
0 commit comments