Skip to content

fix(backend): cancel demo-run polling when client disconnects (#531) - #532

Open
RIOS-JORGE wants to merge 5 commits into
Stellar-Ecosystem:mainfrom
RIOS-JORGE:fix/issue-531-original-suggestion
Open

fix(backend): cancel demo-run polling when client disconnects (#531)#532
RIOS-JORGE wants to merge 5 commits into
Stellar-Ecosystem:mainfrom
RIOS-JORGE:fix/issue-531-original-suggestion

Conversation

@RIOS-JORGE

@RIOS-JORGE RIOS-JORGE commented Aug 3, 2026

Copy link
Copy Markdown

Closes #531

Summary

  • Detects real client disconnects with res.on('close') and !res.writableEnded without aborting normal requests on Node.js >=22.
  • Propagates an optional AbortSignal through waitForActivityTxHash() so polling stops when the client disconnects.
  • Adds focused unit and real-socket integration coverage for normal completion and mid-poll disconnects.

Changes

File Change
backend/src/routes/demo.js Abort the request-scoped polling lifecycle on a genuine response connection close and clean up the listener.
backend/src/lib/waitForActivityTxHash.js Accept and check an optional abort signal during polling.
backend/test/demo-disconnect.test.js Add unit and real HTTP socket tests for cancellation behavior.

Verification

  • Related tests were prepared for the real polling path.
  • The focused Vitest run is currently blocked by the pre-existing trailing comma in backend/package.json on main; that unrelated file is intentionally not included in this PR.

Notes

The implementation deliberately does not use a long-lived req.on('close') listener: on Node.js >=22, that event can fire when the request body is consumed during a normal request. Using res.on('close') with !res.writableEnded distinguishes an incomplete client connection from normal response completion.

Summary by CodeRabbit

  • Bug Fixes

    • Cancelled in-progress demo runs when the connection closes, preventing unnecessary background processing.
    • Stopped activity polling promptly when a request is aborted.
    • Preserved normal completion, response validation, activity recording, and error handling.
  • Tests

    • Added coverage for immediate and mid-process connection cancellations, successful requests, and interrupted polling cleanup.

Move the disconnect listener to res.on('close') guarded by
!res.writableEnded so it stays active for the whole handler, and
pass the AbortSignal into waitForActivityTxHash() so the poll loop
breaks early when the client disconnects mid-request.

req.on('close') fires when the request body is fully consumed, not
on disconnect (verified on Node 22/24), so keeping it registered
would abort every normal request.
…ection

Adds code comments documenting the empirical finding: req.on('close')
fires when the request body is fully consumed (not on client disconnect)
in Node >=22, so keeping it registered would abort every normal request.
res.on('close') + !writableEnded distinguishes real disconnects from
normal completion. Verified on Node 22.23.1 (CI) and 24.15.0.
…lling

New standalone file (backend/test/demo-disconnect.test.js) that covers
issue Stellar-Ecosystem#531's real scenario: client disconnect DURING the polling phase.
The existing suite cannot see this bug because waitForActivityTxHash is
mocked to resolve instantly, so the handler completes before the 'close'
event fires. This suite runs the real poll with a real HTTP socket:

- unit: abort signal in waitForActivityTxHash (already-aborted, mid-wait)
- integration: normal request completes the full poll; disconnect
  mid-request cancels the poll early (no full 8s budget waste)

Lives separately so the maintainer can adopt or discard it freely.
@RIOS-JORGE
RIOS-JORGE requested a review from ritik4ever as a code owner August 3, 2026 11:56
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@RIOS-JORGE, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 274d8573-c79d-4907-8338-148a900a9a4e

📥 Commits

Reviewing files that changed from the base of the PR and between b7e9fbf and feae59c.

📒 Files selected for processing (1)
  • backend/src/lib/waitForActivityTxHash.test.js
📝 Walkthrough

Walkthrough

The change propagates response-close cancellation from /demo-run into activity transaction-hash polling. The polling helper accepts an optional AbortSignal, and tests cover immediate aborts, mid-wait disconnects, and normal request completion.

Changes

Demo request cancellation

Layer / File(s) Summary
Abort-aware activity polling
backend/src/lib/waitForActivityTxHash.js, backend/src/lib/waitForActivityTxHash.test.js
waitForActivityTxHash accepts an optional signal, cancels default sleeps, and returns an empty string after abort. Tests verify that no additional feed check occurs.
Route cancellation and validation
backend/src/routes/demo.js, backend/test/demo-disconnect.test.js
/demo-run aborts unfinished work after response close, passes the signal into polling, removes the listener during cleanup, and tests disconnected and normal requests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DemoRoute
  participant waitForActivityTxHash
  Client->>DemoRoute: submit demo request
  DemoRoute->>waitForActivityTxHash: poll with AbortSignal
  Client-->>DemoRoute: close response
  DemoRoute->>DemoRoute: abort unfinished request
  DemoRoute->>waitForActivityTxHash: observe aborted signal
  waitForActivityTxHash-->>DemoRoute: return empty string
Loading

Possibly related PRs

Suggested reviewers: ritik4ever, aryankn29

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes cancellation of demo-run polling after client disconnection.
Linked Issues check ✅ Passed The changes propagate cancellation through demo-run polling, maintain listener lifecycle, and add tests for disconnect behavior required by issue #531.
Out of Scope Changes check ✅ Passed The implementation and tests are directly related to client-disconnect cancellation during demo-run polling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/lib/waitForActivityTxHash.js`:
- Line 10: Update the polling loop in waitForActivityTxHash to make each
in-flight sleep abortable, racing the delay used by sleep against signal before
continuing. Ensure an already-aborted signal and an abort occurring during the
delay both wake the loop immediately, preserve the existing empty-string return
behavior, and avoid leaving abort listeners or timers behind after either race
completes.

In `@backend/src/routes/demo.js`:
- Around line 118-146: Update the completion flow around waitForActivityTxHash
and the final res.json call to handle an aborted request before writing the
response. Keep recordActivity unconditional, but if abortController.signal is
aborted after polling, avoid the normal response write and return the existing
cancellation response behavior.
🪄 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 Plus

Run ID: 6fac0491-6245-4f6b-9766-6a263d499e63

📥 Commits

Reviewing files that changed from the base of the PR and between 0546692 and e9a33cf.

📒 Files selected for processing (3)
  • backend/src/lib/waitForActivityTxHash.js
  • backend/src/routes/demo.js
  • backend/test/demo-disconnect.test.js

Comment thread backend/src/lib/waitForActivityTxHash.js
Comment thread backend/src/routes/demo.js
The loop's sleep now honors the AbortSignal (timers cleaned up), breaks on
abort, and demo-run no longer responds once the client has gone.
@RIOS-JORGE

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/lib/waitForActivityTxHash.test.js`:
- Around line 100-117: Update the test around waitForActivityTxHash to use fake
timers, assert exactly one pending timer before controller.abort(), and assert
no pending timers after resultPromise resolves. Keep the existing result and
getFeed assertions while making the test verify prompt abort handling without
relying on real-time delays.
🪄 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 Plus

Run ID: a919b10a-8d90-4cf0-824f-09052004f449

📥 Commits

Reviewing files that changed from the base of the PR and between e9a33cf and b7e9fbf.

📒 Files selected for processing (3)
  • backend/src/lib/waitForActivityTxHash.js
  • backend/src/lib/waitForActivityTxHash.test.js
  • backend/src/routes/demo.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/src/routes/demo.js

Comment thread backend/src/lib/waitForActivityTxHash.test.js
Use fake timers to assert one timer is active before the abort and zero
timers after it resolves, so a regression to a non-abortable sleep fails
instead of passing by waiting longer.
@RIOS-JORGE

Copy link
Copy Markdown
Author

"Hi, I’ve submitted the PR and it was approved by Coderabbit. Whenever you have a chance, please review it and, if everything looks good, proceed with the merge. Thanks!"

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.

[Bug]: [Bug]: Client disconnect during /api/demo-run doesn't cancel in-flight polling

1 participant