diff --git a/config/config.go b/config/config.go index 076c9ed..217b7cf 100644 --- a/config/config.go +++ b/config/config.go @@ -52,11 +52,6 @@ type ServerConfig struct { TokenBaseTimeout time.Duration `env:"PIXIVFE_TOKEN_BASE_TIMEOUT,overwrite"` TokenMaxBackoffTime time.Duration `env:"PIXIVFE_TOKEN_MAX_BACKOFF_TIME,overwrite"` - // API request level backoff settings - APIMaxRetries int `env:"PIXIVFE_API_MAX_RETRIES,overwrite"` - APIBaseTimeout time.Duration `env:"PIXIVFE_API_BASE_TIMEOUT,overwrite"` - APIMaxBackoffTime time.Duration `env:"PIXIVFE_API_MAX_BACKOFF_TIME,overwrite"` - AcceptLanguage string `env:"PIXIVFE_ACCEPTLANGUAGE,overwrite"` RequestLimit uint64 `env:"PIXIVFE_REQUESTLIMIT"` // if 0, request limit is disabled @@ -135,10 +130,6 @@ func (s *ServerConfig) LoadConfig() error { s.TokenBaseTimeout = 1000 * time.Millisecond s.TokenMaxBackoffTime = 32000 * time.Millisecond - s.APIMaxRetries = 3 - s.APIBaseTimeout = 500 * time.Millisecond - s.APIMaxBackoffTime = 8000 * time.Millisecond - s.CacheEnabled = false s.CacheSize = 100 s.CacheTTL = 60 * time.Minute @@ -216,8 +207,6 @@ func (s *ServerConfig) LoadConfig() error { log.Printf("Token manager settings: Max retries: %d, Base timeout: %v, Max backoff time: %v\n", s.TokenMaxRetries, s.TokenBaseTimeout, s.TokenMaxBackoffTime) log.Printf("Token load balancing method: %s\n", s.TokenLoadBalancing) - log.Printf("API request backoff settings: Max retries: %d, Base timeout: %v, Max backoff time: %v\n", s.APIMaxRetries, s.APIBaseTimeout, s.APIMaxBackoffTime) - // Print cache configuration if s.CacheEnabled { log.Println("API response cache is enabled") diff --git a/core/requests.go b/core/requests.go index 16233a5..2473834 100644 --- a/core/requests.go +++ b/core/requests.go @@ -1,5 +1,5 @@ // This file implements the core functionality for handling API requests, -// including response caching, retry mechanisms, and request proxying. +// including response caching and and request proxying. package core import ( @@ -24,7 +24,6 @@ import ( "codeberg.org/vnpower/pixivfe/v2/server/token_manager" "codeberg.org/vnpower/pixivfe/v2/server/utils" - "github.com/hashicorp/go-retryablehttp" lru "github.com/hashicorp/golang-lru" "github.com/tidwall/gjson" "github.com/zeebo/xxh3" @@ -56,9 +55,8 @@ type CachingResult struct { } var ( - retryClient *retryablehttp.Client - cacheSeed uint64 - cache *lru.Cache + cacheSeed uint64 + cache *lru.Cache // shortTTLPaths lists API endpoints that require a shorter TTL for their cached responses. shortTTLPaths = []string{ @@ -108,16 +106,6 @@ func InitCache() { cacheSeed = binary.LittleEndian.Uint64(seedBytes[:]) } -// init sets up a configured retryablehttp client. -func init() { - retryClient = retryablehttp.NewClient() - retryClient.RetryMax = config.GlobalConfig.APIMaxRetries - retryClient.RetryWaitMin = config.GlobalConfig.APIBaseTimeout - retryClient.RetryWaitMax = config.GlobalConfig.APIMaxBackoffTime - retryClient.HTTPClient = utils.HttpClient - retryClient.Logger = nil // Disables the default logger in go-retryablehttp -} - // generateCacheKey creates a unique identifier for caching purposes by hashing // the combined request URL and the user's token using seeded xxhash. // @@ -131,6 +119,7 @@ func generateCacheKey(url, userToken string) string { } // determineCachePolicy determines the caching policy for a given request. +// // It returns a CachePolicy struct indicating whether to fetch from cache, // whether to store the response in cache, and the cached response if available. func determineCachePolicy(rawURL, userToken string, headers http.Header) CachePolicy { @@ -152,8 +141,7 @@ func determineCachePolicy(rawURL, userToken string, headers http.Header) CachePo } } - // Retrieve the Cache-Control header from the downstream request - // and check for "no-cache" + // Retrieve the Cache-Control header from the downstream request and check for "no-cache" cacheControl := headers.Get("Cache-Control") lowerCacheControl := strings.ToLower(cacheControl) if strings.Contains(lowerCacheControl, "no-cache") { @@ -191,8 +179,9 @@ func determineCachePolicy(rawURL, userToken string, headers http.Header) CachePo } } -// manageCaching manages the caching logic during and after a request. -// It either returns a cached response or allows the request to proceed and optionally caches the new response. +// manageCaching manages the caching logic after receiving a response. +// +// It either returns a cached response or allows the response to be stored in cache. func manageCaching(rawURL, userToken string, headers http.Header, response *SimpleHTTPResponse) CachingResult { policy := determineCachePolicy(rawURL, userToken, headers) if policy.ShouldUseCached && policy.CachedResponse != nil { @@ -232,14 +221,80 @@ func manageCaching(rawURL, userToken string, headers http.Header, response *Simp } } -// makeRequest is a helper function that encapsulates the common logic for making HTTP requests. +// handleRequest handles HTTP requests. +func handleRequest( + ctx context.Context, + auditor *audit.Auditor, + reqFunc func(context.Context, string) (*http.Request, error), + userToken string, + isPost bool, + url string, + incomingHeaders http.Header, +) (*SimpleHTTPResponse, error) { + // Retrieve the token + tokenManager := config.GlobalConfig.TokenManager + token, err := retrieveToken(tokenManager, userToken) + if err != nil { + return nil, err + } + + // For GET requests, determine if caching should be used + var cachingResult CachingResult + if !isPost { + policy := determineCachePolicy(url, userToken, incomingHeaders) + if policy.ShouldUseCached && policy.CachedResponse != nil { + return policy.CachedResponse, nil + } + } + + // Make the HTTP request using the provided request function + resp, err := makeRequest(ctx, auditor, reqFunc, token, url) + if err != nil { + // NOTE: don't mark the token provided by tokenManager as timed out if the making the request itself failed + // tokenManager.MarkTokenStatus(token, token_manager.TimedOut) + return nil, err + } + + // Handle the response based on the status code + if resp.StatusCode == http.StatusOK { + // Mark the token as good if the response is OK + tokenManager.MarkTokenStatus(token, token_manager.Good) + + if !isPost { + // Handle caching logic after receiving an HTTP 200 response + cachingResult = manageCaching(url, userToken, incomingHeaders, resp) + if cachingResult.FromCache { + return cachingResult.Response, nil + } + } + + return resp, nil + } + + // Handle non-OK status codes + err = i18n.Errorf("HTTP status code: %d", resp.StatusCode) + + // Mark the token provided by tokenManager as timed out if the + // request succeeded, but returned a non-OK response + tokenManager.MarkTokenStatus(token, token_manager.TimedOut) + + // Check if the context has been canceled or timed out + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + return nil, err + } +} + +// makeRequest is a helper function that encapsulates the common logic for making HTTP requests using utils.HttpClient.Do. // -// It constructs the request using the provided request function, executes it using the retryable client, -// reads the response, logs the API round trip details for auditing, and returns a simplified response. +// It constructs the request using the provided request function, executes it using HttpClient, +// reads the response, logs the API round trip details, and returns a SimpleHTTPResponse. func makeRequest( ctx context.Context, auditor *audit.Auditor, - reqFunc func(context.Context, string) (*retryablehttp.Request, error), + reqFunc func(context.Context, string) (*http.Request, error), token *token_manager.Token, url string, ) (*SimpleHTTPResponse, error) { @@ -250,7 +305,7 @@ func makeRequest( } start := time.Now() - resp, err := retryClient.Do(req) + resp, err := utils.HttpClient.Do(req) end := time.Now() if err != nil { @@ -282,18 +337,6 @@ func makeRequest( }, nil } -// executePost handles POST requests without retries. -func executePost( - ctx context.Context, - auditor *audit.Auditor, - reqFunc func(context.Context, string) (*retryablehttp.Request, error), - userToken string, - url string, -) (*SimpleHTTPResponse, error) { - token := &token_manager.Token{Value: userToken} - return makeRequest(ctx, auditor, reqFunc, token, url) -} - // retrieveToken obtains a valid token, preferring the userToken if provided by the caller. func retrieveToken(tokenManager *token_manager.TokenManager, userToken string) (*token_manager.Token, error) { if userToken != "" { @@ -306,7 +349,7 @@ func retrieveToken(tokenManager *token_manager.TokenManager, userToken string) ( return nil, i18n.Errorf( `All tokens (%d) are timed out, resetting all tokens to their initial good state. -Consider providing additional tokens in PIXIVFE_TOKEN or reviewing API request level backoff configuration. +Consider providing additional tokens in PIXIVFE_TOKEN or reviewing token management configuration. Please refer the following documentation for additional information: - https://pixivfe-docs.pages.dev/hosting/obtaining-pixivfe-token/ - https://pixivfe-docs.pages.dev/hosting/environment-variables/#exponential-backoff-configuration`, @@ -316,82 +359,6 @@ Please refer the following documentation for additional information: return token, nil } -// executeGet performs GET requests with retry logic and caching. -func executeGet( - ctx context.Context, - auditor *audit.Auditor, - reqFunc func(context.Context, string) (*retryablehttp.Request, error), - userToken, url string, - incomingHeaders http.Header, -) (*SimpleHTTPResponse, error) { - tokenManager := config.GlobalConfig.TokenManager - - // Retrieve the token - token, err := retrieveToken(tokenManager, userToken) - if err != nil { - return nil, err - } - - // Make the HTTP request - resp, err := makeRequest(ctx, auditor, reqFunc, token, url) - if err != nil { - // Mark the token as timed out if the request failed - tokenManager.MarkTokenStatus(token, token_manager.TimedOut) - return nil, err - } - - // Handle the response based on the status code - if resp.StatusCode == http.StatusOK { - // Mark the token as good if the response is OK - tokenManager.MarkTokenStatus(token, token_manager.Good) - - // Handle caching logic after receiving an HTTP 200 response - cachingResult := manageCaching(url, userToken, incomingHeaders, resp) - if cachingResult.FromCache { - return cachingResult.Response, nil - } - - return cachingResult.Response, nil - } - - // Handle non-OK status codes - err = i18n.Errorf("HTTP status code: %d", resp.StatusCode) - // Mark the token as timed out for non-OK responses - tokenManager.MarkTokenStatus(token, token_manager.TimedOut) - - // Check if the context has been canceled or timed out - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - // Return the error without retrying - return nil, err - } -} - -// retryRequest handles the logic for executing HTTP requests with automatic retries and token management. -func retryRequest( - ctx context.Context, - auditor *audit.Auditor, - reqFunc func(context.Context, string) (*retryablehttp.Request, error), - userToken string, - isPost bool, - url string, - incomingHeaders http.Header, -) (*SimpleHTTPResponse, error) { - if isPost { - return executePost(ctx, auditor, reqFunc, userToken, url) - } - - // Determine caching policy before we send a request - policy := determineCachePolicy(url, userToken, incomingHeaders) - if policy.ShouldUseCached && policy.CachedResponse != nil { - return policy.CachedResponse, nil - } - - return executeGet(ctx, auditor, reqFunc, userToken, url, incomingHeaders) -} - // API_GET performs a GET request to the Pixiv API with automatic retries and caching. func API_GET( ctx context.Context, @@ -400,13 +367,12 @@ func API_GET( userToken string, incomingHeaders http.Header, ) (*SimpleHTTPResponse, error) { - return retryRequest(ctx, auditor, func(ctx context.Context, token string) (*retryablehttp.Request, error) { + return handleRequest(ctx, auditor, func(ctx context.Context, token string) (*http.Request, error) { // Create a new GET request for the specified URL. - req, err := retryablehttp.NewRequest("GET", url, nil) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } - req = req.WithContext(ctx) req.Header.Add("User-Agent", config.GetRandomUserAgent()) req.Header.Add("Accept-Language", config.GlobalConfig.AcceptLanguage) @@ -488,22 +454,22 @@ func API_POST( return nil, i18n.Error("userToken is required for POST requests") } - resp, err := retryRequest(ctx, auditor, func(ctx context.Context, token string) (*retryablehttp.Request, error) { - var req *retryablehttp.Request + return handleRequest(ctx, auditor, func(ctx context.Context, token string) (*http.Request, error) { + var req *http.Request var err error // Determine the type of payload and construct the request accordingly. switch v := payload.(type) { case string: // If the payload is a string, send it as a raw byte buffer. - req, err = retryablehttp.NewRequest("POST", url, bytes.NewBuffer([]byte(v))) + req, err = http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer([]byte(v))) case map[string]string: // If the payload is a map, construct multipart form data. body, formContentType, err := createMultipartFormData(v) if err != nil { return nil, err } - req, err = retryablehttp.NewRequest("POST", url, body) + req, err = http.NewRequestWithContext(ctx, "POST", url, body) if err == nil { // Update the content type to reflect multipart form data. contentType = formContentType @@ -517,8 +483,6 @@ func API_POST( return nil, err } - req = req.WithContext(ctx) - req.Header.Add("User-Agent", config.GetRandomUserAgent()) req.Header.Add("Accept", "application/json") req.Header.Add("x-csrf-token", csrf) @@ -531,15 +495,6 @@ func API_POST( return req, nil }, userToken, true, url, incomingHeaders) - if err != nil { - return nil, err - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("API request failed with status code: %d, body: %s", resp.StatusCode, resp.Body) - } - - return resp, nil } // ProxyRequest forwards an incoming HTTP request to a target server and writes the response back to the original client. diff --git a/doc/dev/features/exponential_backoff.md b/doc/dev/features/exponential_backoff.md deleted file mode 100644 index 7b5b2ac..0000000 --- a/doc/dev/features/exponential_backoff.md +++ /dev/null @@ -1,61 +0,0 @@ -# Exponential backoff - -PixivFE implements exponential backoff as a strategy for handling API request failures and managing token usage. This document outlines the implementation details of this feature across two key components of the application. - -## Overview - -Exponential backoff is a technique used to gradually increase the wait time between retries of a failed operation. In PixivFE, this technique is applied at two levels: - -1. API request level -2. Token management level - -### Configuration - -The `ServerConfig` struct in [`config/config.go`](https://codeberg.org/VnPower/PixivFE/src/branch/v2/config/config.go) includes fields for both API request level and token management level backoff settings. - -The `LoadConfig` method sets default values for these settings if they are not provided through environment variables. - -## API request level backoff - -**Location: `core/requests.go`** - -PixivFE uses the `retryablehttp` package to implement exponential backoff for API requests. The implementation is as follows: - -1. A `retryablehttp.Client` is initialized in the `init` function: - -```go -func init() { - retryClient = retryablehttp.NewClient() - retryClient.RetryMax = config.GlobalConfig.APIMaxRetries - retryClient.RetryWaitMin = config.GlobalConfig.APIBaseTimeout - retryClient.RetryWaitMax = config.GlobalConfig.APIMaxBackoffTime - retryClient.HTTPClient = utils.HttpClient -} -``` - -2. The `retryRequest` function uses this client to perform requests with automatic retries: - -```go -func retryRequest(ctx context.Context, reqFunc func(context.Context, string) (*retryablehttp.Request, error)) (SimpleHTTPResponse, error) { - // ... (function implementation) -} -``` - -## Token management level backoff - -**Location: `server/token_manager/token_manager.go`** - -The `TokenManager` implements exponential backoff for individual tokens: - -- In the `MarkTokenStatus` method, when a token is marked as `TimedOut`, it calculates a timeout duration: - ```go - timeoutDuration := time.Duration(math.Min( - float64(tm.baseTimeout)*math.Pow(2, float64(token.FailureCount-1)), - float64(tm.maxBackoffTime), - )) - ``` -- This calculation uses the `math.Pow` function to implement exponential growth based on the number of consecutive failures. -- The timeout duration is also capped at a maximum value (`tm.maxBackoffTime`). -- The token's `TimeoutUntil` is set to the current time plus this calculated duration. - -This approach allows tokens that repeatedly fail increasingly longer "cool-down" periods before being used again, helping to manage rate limiting of individual tokens by the Pixiv API. diff --git a/doc/hosting/environment-variables.md b/doc/hosting/environment-variables.md index 29815b9..d6a7d85 100644 --- a/doc/hosting/environment-variables.md +++ b/doc/hosting/environment-variables.md @@ -137,43 +137,13 @@ You can disable periodic checks by setting the value to `0`. Then, proxies will ## Exponential backoff configuration -PixivFE implements exponential backoff for API requests and token management to handle failures gracefully and manage rate limiting. The following environment variables can be used to configure this behavior, fine-tuning the exponential backoff behavior for both API requests and token management. If not set, the default values will be used. +PixivFE implements exponential backoff for token management to manage rate limiting. -For more detailed information about the implementation of exponential backoff in PixivFE, please refer to the [Exponential Backoff documentation](../dev/features/exponential_backoff.md). +The following environment variables control how PixivFE manages token timeouts when a token encounters repeated failures. The backoff time for a token starts at the base timeout and doubles with each failure, up to the maximum backoff time. -### API request level backoff +If not set, the default values will be used. -These settings control how PixivFE handles retries for individual API requests. The backoff time starts at the base timeout and doubles with each retry, up to the maximum backoff time. - -#### `PIXIVFE_API_MAX_RETRIES` - -**Required**: No - -**Default:** `3` - -Maximum number of retries for API requests. - -#### `PIXIVFE_API_BASE_TIMEOUT` - -**Required**: No - -**Default:** `500ms` - -Base timeout duration for API requests. - -#### `PIXIVFE_API_MAX_BACKOFF_TIME` - -**Required**: No - -**Default:** `8000ms` - -Maximum backoff time for API requests. - -### Token management level backoff - -These settings control how PixivFE manages token timeouts when a token encounters repeated failures. The backoff time for a token starts at the base timeout and doubles with each failure, up to the maximum backoff time. - -#### `PIXIVFE_TOKEN_MAX_RETRIES` +### `PIXIVFE_TOKEN_MAX_RETRIES` **Required**: No @@ -181,7 +151,7 @@ These settings control how PixivFE manages token timeouts when a token encounter Maximum number of retries for token management. -#### `PIXIVFE_TOKEN_BASE_TIMEOUT` +### `PIXIVFE_TOKEN_BASE_TIMEOUT` **Required**: No @@ -189,7 +159,7 @@ Maximum number of retries for token management. Base timeout duration for token management. -#### `PIXIVFE_TOKEN_MAX_BACKOFF_TIME` +### `PIXIVFE_TOKEN_MAX_BACKOFF_TIME` **Required**: No diff --git a/go.mod b/go.mod index 00f06cb..bd1f370 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/go-faker/faker/v4 v4.5.0 github.com/goccy/go-json v0.10.3 github.com/gorilla/mux v1.8.1 - github.com/hashicorp/go-retryablehttp v0.7.7 github.com/hashicorp/golang-lru v1.0.2 github.com/oklog/ulid/v2 v2.1.0 github.com/sethvargo/go-envconfig v1.1.0 @@ -30,7 +29,6 @@ require ( require ( github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect diff --git a/go.sum b/go.sum index 7c49e1b..28c9d09 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,6 @@ github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsVi github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/go-faker/faker/v4 v4.5.0 h1:ARzAY2XoOL9tOUK+KSecUQzyXQsUaZHefjyF8x6YFHc= github.com/go-faker/faker/v4 v4.5.0/go.mod h1:p3oq1GRjG2PZ7yqeFFfQI20Xm61DoBDlCA8RiSyZ48M= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= @@ -18,20 +16,10 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= -github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= -github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= diff --git a/i18n/locale/en/code.json b/i18n/locale/en/code.json index f4c2634..92db823 100644 --- a/i18n/locale/en/code.json +++ b/i18n/locale/en/code.json @@ -22,7 +22,6 @@ "core/requests.go:9UmoeR3swA0": "failed to make HTTP request: %w", "core/requests.go:XGjPq4l3s2o": "userToken is required for POST requests", "core/requests.go:XdMN7Q7DY3k": "failed to proxy request: %w", - "core/requests.go:_IJGPWU-8jE": "max retries reached for GET request. Last error: %v", "core/requests.go:f78uqyomFJk": "Unsupported payload type", "core/requests.go:fEaOk30bc9I": "failed to read response body: %w", "core/requests.go:lLy9SHFUtQQ": "failed to copy response body: %w",