github.com/qiniu/go-sdk/v7@v7.27.0/internal/clientv2/interceptor_retry_simple_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package clientv2 5 6 import ( 7 "context" 8 "math" 9 "net" 10 "net/http" 11 "testing" 12 "time" 13 14 "github.com/qiniu/go-sdk/v7/storagev2/backoff" 15 "github.com/qiniu/go-sdk/v7/storagev2/chooser" 16 "github.com/qiniu/go-sdk/v7/storagev2/resolver" 17 "github.com/qiniu/go-sdk/v7/storagev2/retrier" 18 ) 19 20 func TestSimpleAlwaysRetryInterceptor(t *testing.T) { 21 retryMax := 1 22 doCount := 0 23 callbackedCount := 0 24 rInterceptor := NewSimpleRetryInterceptor(SimpleRetryConfig{ 25 RetryMax: retryMax, 26 RetryInterval: func() time.Duration { 27 return time.Second 28 }, 29 ShouldRetry: func(req *http.Request, resp *http.Response, err error) bool { 30 return true 31 }, 32 Resolver: resolver.NewDefaultResolver(), 33 BeforeResolve: func(req *http.Request) { 34 callbackedCount += 1 35 }, 36 AfterResolve: func(req *http.Request, ips []net.IP) { 37 callbackedCount += 1 38 if len(ips) == 0 { 39 t.Fatal("unexpected ips", ips) 40 } 41 }, 42 ResolveError: func(req *http.Request, err error) { 43 t.Fatal("unexpected error", err) 44 }, 45 Chooser: chooser.NewDirectChooser(), 46 BeforeBackoff: func(req *http.Request, options *retrier.RetrierOptions, duration time.Duration) { 47 callbackedCount += 1 48 if options.Attempts != 0 { 49 t.Fatalf("unexpected attempts:%d", options.Attempts) 50 } 51 if duration != time.Second { 52 t.Fatalf("unexpected duration:%v", duration) 53 } 54 }, 55 AfterBackoff: func(req *http.Request, options *retrier.RetrierOptions, duration time.Duration) { 56 callbackedCount += 1 57 if options.Attempts != 0 { 58 t.Fatalf("unexpected attempts:%d", options.Attempts) 59 } 60 if duration != time.Second { 61 t.Fatalf("unexpected duration:%v", duration) 62 } 63 }, 64 BeforeRequest: func(req *http.Request, options *retrier.RetrierOptions) { 65 callbackedCount += 1 66 if options.Attempts != doCount { 67 t.Fatal("unexpected attempts", options.Attempts) 68 } 69 }, 70 AfterResponse: func(resp *http.Response, options *retrier.RetrierOptions, err error) { 71 callbackedCount += 1 72 if options.Attempts != (doCount - 1) { 73 t.Fatal("unexpected attempts", options.Attempts) 74 } 75 if err != nil { 76 t.Fatal("unexpected error", err) 77 } 78 }, 79 }) 80 81 interceptor := NewSimpleInterceptor(func(req *http.Request, handler Handler) (*http.Response, error) { 82 doCount += 1 83 84 value := req.Header.Get(headerKey) 85 value += " -> request" 86 req.Header.Set(headerKey, value) 87 88 resp, err := handler(req) 89 90 value = resp.Header.Get(headerKey) 91 value += " -> response" 92 resp.Header.Set(headerKey, value) 93 return resp, err 94 }) 95 96 c := NewClient(&testClient{}, rInterceptor, interceptor) 97 98 start := time.Now() 99 resp, _ := Do(c, RequestParams{ 100 Context: nil, 101 Method: "", 102 Url: "https://aaa.com", 103 Header: nil, 104 GetBody: nil, 105 }) 106 duration := float32(time.Now().UnixNano()-start.UnixNano()) / 1e9 107 108 if duration > float32(doCount-1)+0.3 || duration < float32(doCount-1)-0.3 { 109 t.Fatalf("retry interval may be error:%f", duration) 110 } 111 112 if (retryMax + 1) != doCount { 113 t.Fatalf("retry count is not 2") 114 } 115 116 value := resp.Header.Get(headerKey) 117 if value != " -> request -> Do -> response" { 118 t.Fatalf("retry flow error") 119 } 120 if callbackedCount != 8 { 121 t.Fatalf("unexpected callbackedCount: %d", callbackedCount) 122 } 123 } 124 125 func TestSimpleNotRetryInterceptor(t *testing.T) { 126 retryMax := 1 127 doCount := 0 128 callbackedCount := 0 129 rInterceptor := NewSimpleRetryInterceptor(SimpleRetryConfig{ 130 RetryMax: retryMax, 131 RetryInterval: func() time.Duration { 132 return time.Second 133 }, 134 // 默认状态码是 400,400 不重试 135 //ShouldRetry: func(req *http.Request, resp *http.Response, err error) bool { 136 // return true 137 //}, 138 Resolver: resolver.NewDefaultResolver(), 139 BeforeResolve: func(req *http.Request) { 140 callbackedCount += 1 141 }, 142 AfterResolve: func(req *http.Request, ips []net.IP) { 143 callbackedCount += 1 144 if len(ips) == 0 { 145 t.Fatal("unexpected ips", ips) 146 } 147 }, 148 ResolveError: func(req *http.Request, err error) { 149 t.Fatal("unexpected error", err) 150 }, 151 Chooser: chooser.NewDirectChooser(), 152 BeforeBackoff: func(req *http.Request, options *retrier.RetrierOptions, duration time.Duration) { 153 callbackedCount += 1 154 if options.Attempts != 0 { 155 t.Fatalf("unexpected attempts:%d", options.Attempts) 156 } 157 if duration != time.Second { 158 t.Fatalf("unexpected duration:%v", duration) 159 } 160 }, 161 AfterBackoff: func(req *http.Request, options *retrier.RetrierOptions, duration time.Duration) { 162 callbackedCount += 1 163 if options.Attempts != 0 { 164 t.Fatalf("unexpected attempts:%d", options.Attempts) 165 } 166 if duration != time.Second { 167 t.Fatalf("unexpected duration:%v", duration) 168 } 169 }, 170 BeforeRequest: func(req *http.Request, options *retrier.RetrierOptions) { 171 callbackedCount += 1 172 if options.Attempts != doCount { 173 t.Fatal("unexpected attempts", options.Attempts) 174 } 175 }, 176 AfterResponse: func(resp *http.Response, options *retrier.RetrierOptions, err error) { 177 callbackedCount += 1 178 if options.Attempts != (doCount - 1) { 179 t.Fatal("unexpected attempts", options.Attempts) 180 } 181 if err != nil { 182 t.Fatal("unexpected error", err) 183 } 184 }, 185 }) 186 187 interceptor := NewSimpleInterceptor(func(req *http.Request, handler Handler) (*http.Response, error) { 188 doCount += 1 189 190 value := req.Header.Get(headerKey) 191 value += " -> request" 192 req.Header.Set(headerKey, value) 193 194 resp, err := handler(req) 195 196 value = resp.Header.Get(headerKey) 197 value += " -> response" 198 resp.Header.Set(headerKey, value) 199 return resp, err 200 }) 201 202 c := NewClient(&testClient{statusCode: 400}, rInterceptor, interceptor) 203 204 start := time.Now() 205 resp, _ := Do(c, RequestParams{ 206 Context: nil, 207 Method: "", 208 Url: "https://aaa.com", 209 Header: nil, 210 GetBody: nil, 211 }) 212 duration := time.Since(start) 213 214 // 不重试,只执行一次,不等待 215 if duration > 500*time.Millisecond { 216 t.Fatalf("retry interval may be error") 217 } 218 219 if doCount != 1 { 220 t.Fatalf("retry count is not 1") 221 } 222 223 value := resp.Header.Get(headerKey) 224 if value != " -> request -> Do -> response" { 225 t.Fatalf("retry flow error") 226 } 227 if callbackedCount != 4 { 228 t.Fatalf("unexpected callbackedCount: %d", callbackedCount) 229 } 230 } 231 232 func TestSimpleRetryInterceptorWithNoopResolver(t *testing.T) { 233 doCount := 0 234 resolveCount := 0 235 rInterceptor := NewSimpleRetryInterceptor(SimpleRetryConfig{ 236 RetryMax: 0, 237 Resolver: resolver.NewResolver(func(ctx context.Context, host string) ([]net.IP, error) { 238 return nil, nil 239 }), 240 BeforeResolve: func(req *http.Request) { 241 resolveCount += 1 242 }, 243 AfterResolve: func(req *http.Request, ips []net.IP) { 244 resolveCount += 1 245 if len(ips) != 0 { 246 t.Fatalf("expected empty ips, got %v", ips) 247 } 248 }, 249 ResolveError: func(req *http.Request, err error) { 250 t.Fatal("unexpected resolve error", err) 251 }, 252 Chooser: chooser.NewDirectChooser(), 253 }) 254 255 interceptor := NewSimpleInterceptor(func(req *http.Request, handler Handler) (*http.Response, error) { 256 doCount += 1 257 resp, err := handler(req) 258 return resp, err 259 }) 260 261 c := NewClient(&testClient{statusCode: 200}, rInterceptor, interceptor) 262 263 resp, err := Do(c, RequestParams{ 264 Context: nil, 265 Method: "", 266 Url: "https://aaa.com", 267 Header: nil, 268 GetBody: nil, 269 }) 270 if err != nil { 271 t.Fatal("unexpected error", err) 272 } 273 274 if doCount != 1 { 275 t.Fatalf("expected 1 request, got %d", doCount) 276 } 277 if resp.StatusCode != 200 { 278 t.Fatalf("expected status 200, got %d", resp.StatusCode) 279 } 280 // 首次 resolve 调 BeforeResolve + AfterResolve, 281 // ensureChoose 发现无可用 IP 后再次 bypassCache resolve 调 BeforeResolve + AfterResolve 282 if resolveCount != 4 { 283 t.Fatalf("expected 4 resolve callbacks, got %d", resolveCount) 284 } 285 } 286 287 func TestRetryInterceptorWithBackoff(t *testing.T) { 288 retryMax := 5 289 doCount := 0 290 callbackedCount := 0 291 rInterceptor := NewSimpleRetryInterceptor(SimpleRetryConfig{ 292 RetryMax: retryMax, 293 Backoff: backoff.NewExponentialBackoff(1*time.Second, 2), 294 ShouldRetry: func(req *http.Request, resp *http.Response, err error) bool { 295 return true 296 }, 297 Resolver: resolver.NewDefaultResolver(), 298 BeforeResolve: func(req *http.Request) { 299 callbackedCount += 1 300 }, 301 AfterResolve: func(req *http.Request, ips []net.IP) { 302 callbackedCount += 1 303 if len(ips) == 0 { 304 t.Fatal("unexpected ips", ips) 305 } 306 }, 307 ResolveError: func(req *http.Request, err error) { 308 t.Fatal("unexpected error", err) 309 }, 310 Chooser: chooser.NewDirectChooser(), 311 BeforeBackoff: func(req *http.Request, options *retrier.RetrierOptions, duration time.Duration) { 312 callbackedCount += 1 313 if options.Attempts != (doCount - 1) { 314 t.Fatalf("unexpected attempts:%d", options.Attempts) 315 } 316 if duration != 1*time.Second*time.Duration(math.Pow(2, float64(options.Attempts))) { 317 t.Fatalf("unexpected duration:%v", duration) 318 } 319 }, 320 AfterBackoff: func(req *http.Request, options *retrier.RetrierOptions, duration time.Duration) { 321 callbackedCount += 1 322 if options.Attempts != (doCount - 1) { 323 t.Fatalf("unexpected attempts:%d", options.Attempts) 324 } 325 if duration != 1*time.Second*time.Duration(math.Pow(2, float64(options.Attempts))) { 326 t.Fatalf("unexpected duration:%v", duration) 327 } 328 }, 329 BeforeRequest: func(req *http.Request, options *retrier.RetrierOptions) { 330 callbackedCount += 1 331 if options.Attempts != doCount { 332 t.Fatal("unexpected attempts", options.Attempts) 333 } 334 }, 335 AfterResponse: func(resp *http.Response, options *retrier.RetrierOptions, err error) { 336 callbackedCount += 1 337 if options.Attempts != (doCount - 1) { 338 t.Fatal("unexpected attempts", options.Attempts) 339 } 340 if err != nil { 341 t.Fatal("unexpected error", err) 342 } 343 }, 344 }) 345 346 interceptor := NewSimpleInterceptor(func(req *http.Request, handler Handler) (*http.Response, error) { 347 doCount += 1 348 349 value := req.Header.Get(headerKey) 350 value += " -> request" 351 req.Header.Set(headerKey, value) 352 353 resp, err := handler(req) 354 355 value = resp.Header.Get(headerKey) 356 value += " -> response" 357 resp.Header.Set(headerKey, value) 358 return resp, err 359 }) 360 361 c := NewClient(&testClient{}, rInterceptor, interceptor) 362 363 start := time.Now() 364 resp, _ := Do(c, RequestParams{ 365 Context: nil, 366 Method: "", 367 Url: "https://aaa.com", 368 Header: nil, 369 GetBody: nil, 370 }) 371 duration := time.Since(start) 372 373 if d := duration - 31*time.Second; d >= 900*time.Millisecond || d <= -900*time.Millisecond { 374 t.Fatalf("retry interval may be error:%v", duration) 375 } 376 377 if (retryMax + 1) != doCount { 378 t.Fatalf("retry count is not 2") 379 } 380 381 value := resp.Header.Get(headerKey) 382 if value != " -> request -> Do -> response" { 383 t.Fatalf("retry flow error") 384 } 385 if callbackedCount != 24 { 386 t.Fatalf("unexpected callbackedCount: %d", callbackedCount) 387 } 388 }