Skip to content

Plug request-id leaks in raw nREPL response handlers#3894

Merged
bbatsov merged 1 commit into
masterfrom
mark-id-completed-leaks
Apr 29, 2026
Merged

Plug request-id leaks in raw nREPL response handlers#3894
bbatsov merged 1 commit into
masterfrom
mark-id-completed-leaks

Conversation

@bbatsov
Copy link
Copy Markdown
Member

@bbatsov bbatsov commented Apr 29, 2026

Found during a wider audit of nrepl-send-request callbacks (the same audit that produced #3893).

Five raw (lambda (response) ...) callbacks passed to cider-nrepl-send-request never called nrepl--mark-id-completed, so their request ids accumulated in the connection's nrepl-pending-requests hash for the lifetime of the session. The dispatcher falls back to the completed table when a pending entry isn't found, so this was silent: not a functional bug, just a slow leak on every response.

File Op
cider-test.el cider/test-stacktrace
cider-test.el cider/test-var-query (the test runner)
cider-eval.el cider/analyze-last-stacktrace
cider-ns.el cider/ns-reload
cider-repl.el cider/get-state (fire-and-forget)

Four of them now call nrepl--mark-id-completed in their done-status branch. The fire-and-forget cider/get-state site switches to cider-nrepl-send-unhandled-request, which marks the id complete immediately and lets the global cider-repl--state-handler hook do the per-response work.

These don't migrate cleanly to nrepl-make-eval-handler because they consume non-eval response slots (results, summary, phase, class, reloading, ...) -- adding a "raw fallback" slot to the eval-handler abstraction for five sites isn't worth it. One-line mark-id-completed calls per site is.

Full suite still 542/544 pass (3893 is in flight on a separate branch).

Five raw `(lambda (response) ...)' callbacks passed to
`cider-nrepl-send-request' never called `nrepl--mark-id-completed', so
their request ids accumulated in the connection's
`nrepl-pending-requests' hash table for the lifetime of the session.
The dispatcher (`nrepl--dispatch-response') falls back to the completed
table when a pending entry isn't found, so this is silent: the leak
does not cause functional problems, just a slow growth of memory and
stale-id lookups on every response.

Sites:

- cider-test.el       cider/test-stacktrace
- cider-test.el       cider/test-var-query (test runner)
- cider-eval.el       cider/analyze-last-stacktrace
- cider-ns.el         cider/ns-reload
- cider-repl.el       cider/get-state (fire-and-forget)

Each `done'-status branch now calls `nrepl--mark-id-completed'.  The
fire-and-forget `cider/get-state' caller switches to
`cider-nrepl-send-unhandled-request', which marks the id complete
immediately and lets the global state-handler hook do the per-response
work.  The other four can't cleanly migrate to `nrepl-make-eval-handler'
because they consume non-eval response slots (`results', `summary',
`phase', `class', `reloading', etc.).

Found during a wider audit of `nrepl-send-request' callbacks.
@bbatsov bbatsov merged commit 0ff2841 into master Apr 29, 2026
13 checks passed
@bbatsov bbatsov deleted the mark-id-completed-leaks branch April 29, 2026 15:48
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.

1 participant