-
Notifications
You must be signed in to change notification settings - Fork 148
Address code review feedback on endpoint health status component #617
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
base: 12-15-invoke_the_endpoints_health_check_in_web_o11y_and_render_result_in_toolbar
Are you sure you want to change the base?
Address code review feedback on endpoint health status component #617
Conversation
🦋 Changeset detectedLatest commit: c6502f9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Fix base URL determination to handle non-local backends - Improve config key to include all backend-specific fields - Fix useEffect dependencies to use stable config reference - Add cleanup for unmounted component with AbortController - Document CORS security implications Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
- Add default timeout fallback for fetch requests - Use AbortController.signal.aborted check instead of isMounted flag - Hash postgres URL to avoid exposing credentials in cache key Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
- Move setIsChecking to finally block to ensure cleanup - Use proper hash function (djb2) for postgres URL instead of string split Co-authored-by: TooTallNate <71256+TooTallNate@users.noreply.github.com>
| const response = await fetch(url.toString(), { | ||
| method: 'POST', | ||
| // Short timeout for health checks | ||
| signal: AbortSignal.timeout(5000), | ||
| // Use provided signal or default to 5 second timeout | ||
| signal: signal || AbortSignal.timeout(5000), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const response = await fetch(url.toString(), { | |
| method: 'POST', | |
| // Short timeout for health checks | |
| signal: AbortSignal.timeout(5000), | |
| // Use provided signal or default to 5 second timeout | |
| signal: signal || AbortSignal.timeout(5000), | |
| // Compose the provided signal with a timeout so we get both behaviors: | |
| // - The component can abort the request via its controller | |
| // - The request also times out after 5 seconds if no response | |
| const timeoutSignal = AbortSignal.timeout(5000); | |
| const combinedSignal = signal | |
| ? AbortSignal.any([signal, timeoutSignal]) | |
| : timeoutSignal; | |
| const response = await fetch(url.toString(), { | |
| method: 'POST', | |
| signal: combinedSignal, |
The health check requests now lack a timeout when called from the component because the abort signal provided is used instead of composing it with the timeout.
View Details
Analysis
Fetch timeout lost when AbortSignal provided to checkEndpointHealth()
What fails: The checkEndpointHealth() function in packages/web/src/components/display-utils/endpoints-health-status.tsx uses the pattern signal: signal || AbortSignal.timeout(5000) which removes the timeout guarantee when a signal is provided. When called from the component's useEffect with abortController.signal, the timeout is discarded, causing fetch requests to hang indefinitely if the server doesn't respond.
How to reproduce:
- Open a page that uses
EndpointsHealthStatuscomponent - Start a local server that accepts health check requests but never responds (e.g.,
nc -l localhost:3000) - Observe that the health check request hangs indefinitely instead of timing out after 5 seconds
- Component can only be freed by unmounting (triggering
abortController.abort())
Result: The fetch hangs until component unmounts. With the buggy code using signal || AbortSignal.timeout(5000):
signalparameter is theabortController.signal(truthy)- Expression evaluates to
signal, notAbortSignal.timeout(5000) - No timeout is set on the fetch request
Expected: The fetch should timeout after 5 seconds OR when the component unmounts, whichever comes first.
Fix applied: Changed line 120-122 to compose the signals using AbortSignal.any():
const timeoutSignal = AbortSignal.timeout(5000);
const combinedSignal = signal
? AbortSignal.any([signal, timeoutSignal])
: timeoutSignal;This ensures:
- When no signal is provided: uses
AbortSignal.timeout(5000)only - When signal is provided: composes it with timeout so both behaviors are enforced
- Request aborts if either: (a) the timeout triggers (5 seconds), or (b) the controller aborts (component unmounts)
Verified with Node.js v22.14.0. AbortSignal.any() is available in Node.js 18.17.0+, matching project's minimum requirement of Node.js ^18.0.0.
Addresses four code review issues in the endpoint health status component: hardcoded localhost usage, incomplete cache key generation, missing cleanup handlers, and undocumented CORS security implications.
Backend-aware health checks
getBaseUrl()to derive URLs from config backend type (local/vercel/postgres)Cache key uniqueness
React cleanup patterns
signal.abortedbefore state updates to prevent React warningssetIsChecking(false)runs in all pathsSecurity documentation
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.