Skip to content

fix: include WebSocket subsystem health in /health endpoint - #103

Merged
dcccrypto merged 2 commits into
dcccrypto:mainfrom
0x-SquidSol:fix/add-ws-health-to-health-endpoint
Apr 1, 2026
Merged

fix: include WebSocket subsystem health in /health endpoint#103
dcccrypto merged 2 commits into
dcccrypto:mainfrom
0x-SquidSol:fix/add-ws-health-to-health-endpoint

Conversation

@0x-SquidSol

@0x-SquidSol 0x-SquidSol commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • The /health endpoint checks Solana RPC and Supabase connectivity but ignores the WebSocket subsystem
  • If WebSocket connections are saturated (at or near MAX_WS_CONNECTIONS), new WS clients cannot connect
  • The health endpoint still reports "ok", so load balancers don't redirect traffic and monitoring tools don't alert
  • During a WS connection-flood DoS, the API appears healthy to infrastructure while WS is effectively down

Fix

  • Add a ws check to the health endpoint that reads getWebSocketMetrics()
  • Reports ws: false (degraded) when connection utilization exceeds 95% of maxGlobalConnections
  • This triggers the existing degraded → 503 status code logic, signaling load balancers to redirect

Files changed

  • src/routes/health.ts — add WS subsystem health check

Test plan

  • Normal WS load (<95% capacity) → checks.ws: true, status "ok"
  • WS at 96% capacity → checks.ws: false, status "degraded", HTTP 503
  • WS metrics error → checks.ws: false, status "degraded"
  • Load balancer with health check on /health → removes instance when WS is saturated

Summary by CodeRabbit

  • New Features
    • Health check now reports WebSocket capacity as a separate check, indicating if connection utilization is within safe limits.
  • Tests
    • Health check tests updated to validate WebSocket health behavior, including success, failure, and error scenarios.

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5118645c-5e9c-47c6-b434-261f87d00b00

📥 Commits

Reviewing files that changed from the base of the PR and between dad24a7 and 374bd8a.

📒 Files selected for processing (2)
  • src/routes/health.ts
  • tests/routes/health.test.ts

📝 Walkthrough

Walkthrough

The /health endpoint now includes a ws boolean in its checks. The route calls getWebSocketMetrics(), computes utilization = totalConnections / maxGlobalConnections, and sets checks.ws to true only if utilization < 0.95; exceptions leave ws as false.

Changes

Cohort / File(s) Summary
Health Route
src/routes/health.ts
Added ws boolean to checks; calls getWebSocketMetrics() inside try/catch; computes utilization = totalConnections / maxGlobalConnections; checks.ws = utilization < 0.95; integrated into overall status calculation.
Health Tests
tests/routes/health.test.ts
Mocked getWebSocketMetrics via vi.mock; added assertions for data.checks.ws in success and failure scenarios; renamed/updated test case to simulate websocket metric failure.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HealthRoute
  participant RPC
  participant Database
  participant WebSocketModule

  Client->>HealthRoute: GET /health
  HealthRoute->>RPC: check RPC connectivity
  RPC-->>HealthRoute: rpcStatus (ok / fail)
  HealthRoute->>Database: check DB connectivity
  Database-->>HealthRoute: dbStatus (ok / fail)
  HealthRoute->>WebSocketModule: getWebSocketMetrics()
  WebSocketModule-->>HealthRoute: { totalConnections, maxGlobalConnections } / error
  HealthRoute->>HealthRoute: compute wsHealthy = total/max < 0.95 (or false on error)
  HealthRoute-->>Client: 200/503 { checks: { rpc, db, ws }, status }
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped to the endpoint, keen and spry,

Peered at sockets beneath the sky.
If slots stay below ninety-five,
I nibble code and feel alive.
Hooray — the checks make this rabbit sigh! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding WebSocket subsystem health monitoring to the /health endpoint.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/routes/health.ts (1)

42-43: WS metrics failure is swallowed without diagnostics.

The empty catch sets checks.ws = false but drops the error context. Add a log entry for operational visibility.

Suggested improvement
-    } catch {
+    } catch (err) {
+      logger.error("WS check failed", { error: truncateErrorMessage(err instanceof Error ? err.message : err, 120) });
       checks.ws = false;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/health.ts` around lines 42 - 43, Replace the empty catch with a
named error parameter and log the error before setting checks.ws = false;
specifically change the catch to catch (err) { /* log */; checks.ws = false }
and use the service's logger (e.g. processLogger.error or logger.error) if
available, otherwise fallback to console.error, with a clear message like
"health check ws failed" plus the err to preserve diagnostics for checks.ws.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/routes/health.ts`:
- Line 41: The health check treats exactly 95% as degraded because it uses
checks.ws = utilization < 0.95; change the comparison so 95% is considered
healthy and only >95% is degraded by using utilization <= 0.95 when assigning
checks.ws (locate the assignment to checks.ws and the utilization variable in
src/routes/health.ts).
- Around line 37-44: The health check flips checks.ws to false when WebSocket
saturation is detected but the existing status-to-HTTP-code mapping still
returns 200 for a "degraded" overall status, preventing LBs from draining;
update the status computation or the HTTP status mapping so that a failed
WebSocket check (checks.ws === false) results in a non-200 response: either (A)
treat any false entry in checks (including checks.ws) as an overall "fail"
instead of "degraded" when computing status, or (B) change the
status-to-statusCode mapping to return 503 for "degraded". Modify the code paths
that compute status (the logic that reads checks and produces status) and the
code that maps status to the HTTP response code so
getWebSocketMetrics()/utilization and checks.ws being false will produce a 503
instead of 200.

---

Nitpick comments:
In `@src/routes/health.ts`:
- Around line 42-43: Replace the empty catch with a named error parameter and
log the error before setting checks.ws = false; specifically change the catch to
catch (err) { /* log */; checks.ws = false } and use the service's logger (e.g.
processLogger.error or logger.error) if available, otherwise fallback to
console.error, with a clear message like "health check ws failed" plus the err
to preserve diagnostics for checks.ws.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b2e24ca-85a7-4180-93a6-6073ef1147b2

📥 Commits

Reviewing files that changed from the base of the PR and between e9619ba and dad24a7.

📒 Files selected for processing (1)
  • src/routes/health.ts

Comment thread src/routes/health.ts
Comment on lines +37 to +44
// Check WebSocket subsystem — saturated WS means new clients can't connect
try {
const wsMetrics = getWebSocketMetrics();
const utilization = wsMetrics.totalConnections / wsMetrics.limits.maxGlobalConnections;
checks.ws = utilization < 0.95; // degraded if >95% of connection slots used
} catch {
checks.ws = false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

WS saturation will still return HTTP 200 with current status-code mapping.

This block correctly flips checks.ws to false, but with db/rpc healthy that produces status = "degraded", and Line 57 currently returns 200 for degraded. That prevents LB drain behavior described in the PR objective.

Suggested fix
-    const statusCode = status === "down" ? 503 : 200;
+    const statusCode = status === "ok" ? 200 : 503;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/health.ts` around lines 37 - 44, The health check flips checks.ws
to false when WebSocket saturation is detected but the existing
status-to-HTTP-code mapping still returns 200 for a "degraded" overall status,
preventing LBs from draining; update the status computation or the HTTP status
mapping so that a failed WebSocket check (checks.ws === false) results in a
non-200 response: either (A) treat any false entry in checks (including
checks.ws) as an overall "fail" instead of "degraded" when computing status, or
(B) change the status-to-statusCode mapping to return 503 for "degraded". Modify
the code paths that compute status (the logic that reads checks and produces
status) and the code that maps status to the HTTP response code so
getWebSocketMetrics()/utilization and checks.ws being false will produce a 503
instead of 200.

Comment thread src/routes/health.ts
try {
const wsMetrics = getWebSocketMetrics();
const utilization = wsMetrics.totalConnections / wsMetrics.limits.maxGlobalConnections;
checks.ws = utilization < 0.95; // degraded if >95% of connection slots used

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Boundary condition does not match “exceeds 95%” requirement.

At Line 41, utilization < 0.95 marks exactly 95% as degraded. If degradation should happen only when utilization exceeds 95%, this should be <= 0.95.

Suggested fix
-      checks.ws = utilization < 0.95; // degraded if >95% of connection slots used
+      checks.ws = utilization <= 0.95; // degraded if >95% of connection slots used
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
checks.ws = utilization < 0.95; // degraded if >95% of connection slots used
checks.ws = utilization <= 0.95; // degraded if >95% of connection slots used
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/health.ts` at line 41, The health check treats exactly 95% as
degraded because it uses checks.ws = utilization < 0.95; change the comparison
so 95% is considered healthy and only >95% is degraded by using utilization <=
0.95 when assigning checks.ws (locate the assignment to checks.ws and the
utilization variable in src/routes/health.ts).

@dcccrypto

Copy link
Copy Markdown
Owner

Re-opening to retrigger CI after 503 fix commit.

@dcccrypto dcccrypto closed this Apr 1, 2026
@dcccrypto dcccrypto reopened this Apr 1, 2026

@dcccrypto dcccrypto left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SECURITY SIGN-OFF ✅ (0x-SquidSol batch review)

@dcccrypto

Copy link
Copy Markdown
Owner

fix(test): Added vitest deps.inline for @percolator/shared to resolve ESM mock isolation flake in CI (Node 22 ubuntu-latest). Re-running CI.

0x-SquidSol and others added 2 commits April 1, 2026 07:05
The health endpoint checks Solana RPC and Supabase but not the WebSocket subsystem. If WebSocket connections are at 95%+ capacity (near MAX_WS_CONNECTIONS), new clients cannot connect but the health endpoint still reports 'ok'. Load balancers and monitoring tools have no signal to redirect traffic or trigger alerts. Add a ws health check that reports degraded when connection utilization exceeds 95%.

Made-with: Cursor
PR adds a ws check to the health endpoint (3 checks: rpc, db, ws).
Tests only mocked rpc/db but not ws, so the 'down' status test failed
because ws was healthy (2/3 fail = degraded, not down).

- Mock ../../src/routes/ws.js with default healthy metrics
- Update 'all fail' test to also fail ws check
- Add ws assertion to 'ok' test
@dcccrypto
dcccrypto force-pushed the fix/add-ws-health-to-health-endpoint branch from f9d26dc to 374bd8a Compare April 1, 2026 06:05
@dcccrypto
dcccrypto merged commit 32a7ebd into dcccrypto:main Apr 1, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants