fastify / fastify-circuit-breaker

A low overhead circuit breaker for your routes

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Optionally breaking the circuit without having to wait for the route handler to finish

JaneJeon opened this issue · comments

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the feature has not already been requested

🚀 Feature Proposal

So I saw that on the caveats section, the handler will just wait for the response to be generated anyway and just send an error if the timeout has been reached.

I wonder, with something like p-timeout and p-cancelable, would it not be possible to cancel the Promise without having to wait for it?

For example, you could simply check that a route handler promise has a .cancel method (as is the case when you use p-cancelable), and cancel those promises when the timeout is hit; and when it’s a “vanilla” promise, just fall back to the default behaviour as-is.

That way, we can support “not having to wait for route handler to finish” and actually use the timeout as a timeout while not breaking anything (minor semver) and still supporting the existing behaviour.

Motivation

No response

Example

No response

This looks like a good feature to add! However it might be better implemented on top of AbortController. Could you take a look at that API and see how it could be implemented?

Ah, I've actually used ptimeout, pcancelable and abortcontroller all extensively recently lol

So basically, all you need as the "base" is a promise that can be cancelled, whether thru accepting .cancel() or AbortSignal. There are times where either is better.

For example, for working with modules with built-in abortsignal integration (e.g. native node modules, got, etc), it's basically perfect.

But basically, the API for making promises would look something like this:

const handler = async (req, res) => {
  // this can either be from (req, res, ...) or from a request-scoped storage (fastify. ...);
  // and given that we don't want users to be calling .abort() - we'd rather have them throw - we could just pass the abortsignal directly
  const signal = abortController.signal

  // For modules that support abortcontroller:
  await doPromise(input, { signal })

  // For modules that support .cancel() or any other alternate method of canceling
  const promise = doPromise2()
  signal.on('abort', () => promise.cancel())
  await promise

  // For modules that can't be cancelled in any way:
  if (signal.aborted) throw new Error('timed out!')
  await doWork() // we can just be content with being able to not run some work when aborted, rather than waiting for the promise to reach the end.
}

Just a rough idea of what it'd look like. Many ways to skin the cat, but for optimal results you'd use either modules that support abortsignal/cancellation, or implement the if (signal.aborted) check within your handler to "gate" it.

commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Would you like to send a Pull Request to address this issue? Remember to add unit tests.

Currently have a lot a lot on my plate - work and personal - so if I do get around this, it'll be probably months before I can even block some time for this, but I believe it should still be kept open and not closed by stalebot.