Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client middleware that adds automatic retries to unary calls with timeouts between retry attempts. #987

Closed
BrRenat opened this issue Jan 24, 2024 · 3 comments
Labels
enhancement New feature or request

Comments

@BrRenat
Copy link

BrRenat commented Jan 24, 2024

Is your feature request related to a problem? Please describe.
I'm frustrated with intermittent network issues causing failures in unary calls.

Describe the solution you'd like
Add automatic retry support with configurable timeouts to connect-es client middleware for unary calls.

Describe alternatives you've considered
Manual retry logic at the application level introduces complexity and duplication. Centralizing retries in the middleware ensures consistency and ease of maintenance.

@BrRenat BrRenat added the enhancement New feature or request label Jan 24, 2024
@srikrsna-buf
Copy link
Member

Hey! Can you elaborate a bit more on unary calls failing, is it happening in the browser?

@BrRenat
Copy link
Author

BrRenat commented Jan 25, 2024

Yes, certain errors can be anticipated and retried, while others may not. The middleware should be configurable based on error codes. f.e network error or rate limit of api.

@srikrsna-buf
Copy link
Member

Including the middleware as part of the core package seems out of scope. The logic to when and how to retry is beyond the scope of an RPC library, it purely depends on the environment and the API being accessed. But implementing a unary retry is straightforward:

import {
  Code,
  ConnectError,
  type Interceptor,
} from "@connectrpc/connect";

const retryInterceptor: Interceptor = (next) => {
  return async (req) => {
    if (req.stream) {
      return await next(req);
    }
    for (let i = 0; ; i++) {
      try {
        return await next(req);
      } catch (err) {
        if (i == 5) {
          throw err;
        }
        const cErr = ConnectError.from(err);
        if (cErr.code == Code.ResourceExhausted) {
          // Wait for a bit and retry
          await new Promise((resolve) => setTimeout(resolve, 1000));
        } else {
          throw err;
        }
      }
    }
  };
};

As you can see most of the code is just to decide and when and how to retry. Closing this issue for now, feel free to reopen if needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants