@@ -2,20 +2,20 @@ package apiserversdk
2
2
3
3
import (
4
4
"bytes"
5
+ "context"
5
6
"fmt"
6
7
"io"
7
8
"net/http"
8
9
"net/http/httputil"
9
10
"net/url"
10
11
"strings"
11
- "time"
12
12
13
13
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14
14
"k8s.io/apimachinery/pkg/util/net"
15
15
"k8s.io/client-go/kubernetes"
16
16
"k8s.io/client-go/rest"
17
17
18
- apiserverutil "github.com/ray-project/kuberay/apiserversdk/util"
18
+ apiserversdkutil "github.com/ray-project/kuberay/apiserversdk/util"
19
19
rayutil "github.com/ray-project/kuberay/ray-operator/controllers/ray/utils"
20
20
)
21
21
@@ -95,30 +95,33 @@ func requireKubeRayService(handler http.Handler, k8sClient *kubernetes.Clientset
95
95
// retryRoundTripper is a custom implementation of http.RoundTripper that retries HTTP requests.
96
96
// It verifies retryable HTTP status codes and retries using exponential backoff.
97
97
type retryRoundTripper struct {
98
- base http.RoundTripper
99
-
100
- // Num of retries after the initial attempt
101
- maxRetries int
102
-
103
- // Retry backoff settings
104
- initBackoff time.Duration
105
- backoffBase float64
106
- maxBackoff time.Duration
98
+ base http.RoundTripper
99
+ retryCfg apiserversdkutil.RetryConfig
107
100
}
108
101
109
102
func newRetryRoundTripper (base http.RoundTripper ) http.RoundTripper {
103
+ retryCfg := apiserversdkutil.RetryConfig {
104
+ MaxRetry : apiserversdkutil .HTTPClientDefaultMaxRetry ,
105
+ BackoffFactor : apiserversdkutil .HTTPClientDefaultBackoffFactor ,
106
+ InitBackoff : apiserversdkutil .HTTPClientDefaultInitBackoff ,
107
+ MaxBackoff : apiserversdkutil .HTTPClientDefaultMaxBackoff ,
108
+ OverallTimeout : apiserversdkutil .HTTPClientDefaultOverallTimeout ,
109
+ }
110
+
110
111
return & retryRoundTripper {
111
- base : base ,
112
- maxRetries : apiserverutil .HTTPClientDefaultMaxRetry ,
113
- initBackoff : apiserverutil .HTTPClientDefaultInitBackoff ,
114
- backoffBase : apiserverutil .HTTPClientDefaultBackoffBase ,
115
- maxBackoff : apiserverutil .HTTPClientDefaultMaxBackoff ,
112
+ base : base ,
113
+ retryCfg : retryCfg ,
116
114
}
117
115
}
118
116
119
117
func (rrt * retryRoundTripper ) RoundTrip (req * http.Request ) (* http.Response , error ) {
120
118
ctx := req .Context ()
121
119
120
+ ctx , cancel := context .WithTimeout (ctx , rrt .retryCfg .OverallTimeout )
121
+ defer cancel ()
122
+
123
+ req = req .WithContext (ctx )
124
+
122
125
var bodyBytes []byte
123
126
var resp * http.Response
124
127
var err error
@@ -135,8 +138,8 @@ func (rrt *retryRoundTripper) RoundTrip(req *http.Request) (*http.Response, erro
135
138
}
136
139
}
137
140
138
- for attempt := 0 ; attempt <= rrt .maxRetries ; attempt ++ {
139
- /* Try up to (rrt.maxRetries + 1) times: initial attempt + retries */
141
+ for attempt := 0 ; attempt <= rrt .retryCfg . MaxRetry ; attempt ++ {
142
+ /* Try up to (rrt.retryCfg.MaxRetry + 1) times: initial attempt + retries */
140
143
141
144
if bodyBytes != nil {
142
145
req .Body = io .NopCloser (bytes .NewReader (bodyBytes ))
@@ -147,15 +150,15 @@ func (rrt *retryRoundTripper) RoundTrip(req *http.Request) (*http.Response, erro
147
150
return resp , fmt .Errorf ("request to %s %s failed with error: %w" , req .Method , req .URL .String (), err )
148
151
}
149
152
150
- if apiserverutil .IsSuccessfulStatusCode (resp .StatusCode ) {
153
+ if apiserversdkutil .IsSuccessfulStatusCode (resp .StatusCode ) {
151
154
return resp , nil
152
155
}
153
156
154
- if ! apiserverutil .IsRetryableHTTPStatusCodes (resp .StatusCode ) {
157
+ if ! apiserversdkutil .IsRetryableHTTPStatusCodes (resp .StatusCode ) {
155
158
return resp , nil
156
159
}
157
160
158
- if attempt == rrt .maxRetries {
161
+ if attempt == rrt .retryCfg . MaxRetry {
159
162
return resp , nil
160
163
}
161
164
@@ -169,20 +172,14 @@ func (rrt *retryRoundTripper) RoundTrip(req *http.Request) (*http.Response, erro
169
172
}
170
173
}
171
174
172
- sleepDuration := apiserverutil .GetRetryBackoff (attempt , rrt .initBackoff , rrt .backoffBase , rrt .maxBackoff )
175
+ sleepDuration := apiserversdkutil .GetRetryBackoff (attempt , rrt .retryCfg . InitBackoff , rrt .retryCfg . BackoffFactor , rrt .retryCfg . MaxBackoff )
173
176
174
- // TODO: merge common utils for apiserver v1 and v2
175
- if deadline , ok := ctx .Deadline (); ok {
176
- remaining := time .Until (deadline )
177
- if sleepDuration > remaining {
178
- return resp , fmt .Errorf ("retry timeout exceeded context deadline" )
179
- }
177
+ if ok := apiserversdkutil .CheckContextDeadline (ctx , sleepDuration ); ! ok {
178
+ return resp , fmt .Errorf ("retry timeout exceeded context deadline" )
180
179
}
181
180
182
- select {
183
- case <- time .After (sleepDuration ):
184
- case <- ctx .Done ():
185
- return resp , fmt .Errorf ("retry canceled during backoff: %w" , ctx .Err ())
181
+ if err = apiserversdkutil .Sleep (ctx , sleepDuration ); err != nil {
182
+ return resp , fmt .Errorf ("retry canceled during backoff: %w" , err )
186
183
}
187
184
}
188
185
return resp , err
0 commit comments