1// Copyright 2021 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package bigquery 16 17import ( 18 "errors" 19 "io" 20 "net/http" 21 "net/url" 22 "testing" 23 24 "golang.org/x/xerrors" 25 "google.golang.org/api/googleapi" 26) 27 28func TestRetryableErrors(t *testing.T) { 29 for _, tc := range []struct { 30 description string 31 in error 32 want bool 33 }{ 34 { 35 "nil error", 36 nil, 37 false, 38 }, 39 { 40 "http stream closed", 41 errors.New("http2: stream closed"), 42 true, 43 }, 44 { 45 "io ErrUnexpectedEOF", 46 io.ErrUnexpectedEOF, 47 true, 48 }, 49 { 50 "unavailable", 51 &googleapi.Error{ 52 Code: http.StatusServiceUnavailable, 53 Message: "foo", 54 }, 55 true, 56 }, 57 { 58 "url connection error", 59 &url.Error{Op: "blah", URL: "blah", Err: errors.New("connection refused")}, 60 true, 61 }, 62 { 63 "url other error", 64 &url.Error{Op: "blah", URL: "blah", Err: errors.New("blah")}, 65 false, 66 }, 67 { 68 "wrapped retryable", 69 xerrors.Errorf("test of wrapped retryable: %w", &googleapi.Error{ 70 Code: http.StatusServiceUnavailable, 71 Message: "foo", 72 Errors: []googleapi.ErrorItem{ 73 {Reason: "backendError", Message: "foo"}, 74 }, 75 }), 76 true, 77 }, 78 { 79 "wrapped non-retryable", 80 xerrors.Errorf("test of wrapped retryable: %w", errors.New("blah")), 81 false, 82 }, 83 { 84 // not retried per https://google.aip.dev/194 85 "internal error", 86 &googleapi.Error{ 87 Code: http.StatusInternalServerError, 88 }, 89 false, 90 }, 91 { 92 "internal w/backend reason", 93 &googleapi.Error{ 94 Code: http.StatusServiceUnavailable, 95 Message: "foo", 96 Errors: []googleapi.ErrorItem{ 97 {Reason: "backendError", Message: "foo"}, 98 }, 99 }, 100 true, 101 }, 102 { 103 "internal w/rateLimitExceeded reason", 104 &googleapi.Error{ 105 Code: http.StatusServiceUnavailable, 106 Message: "foo", 107 Errors: []googleapi.ErrorItem{ 108 {Reason: "rateLimitExceeded", Message: "foo"}, 109 }, 110 }, 111 true, 112 }, 113 { 114 "bad gateway error", 115 &googleapi.Error{ 116 Code: http.StatusBadGateway, 117 Message: "foo", 118 }, 119 true, 120 }, 121 } { 122 got := retryableError(tc.in) 123 if got != tc.want { 124 t.Errorf("case (%s) mismatch: got %t want %t", tc.description, got, tc.want) 125 } 126 } 127} 128