posit-dev / posit-dev/positron
Asynchronous comm messages
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 4.3k
- Forks
- 184
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 206
Description
Currently, all comm messages are sent to backends via the Shell socket, in accordance with the Jupyter protocol:
The Kernel listens for these messages on the Shell channel, and the Frontend listens for them on the IOPub channel.
However, this has a significant downside: messages are queued alongside execution requests. If user code is running for an extended period (or indefinitely, as with a Shiny app or other long-lived service) frontend messages are blocked from reaching the backend until execution completes.
Queuing on Shell is often the correct behavior when the backend message handler needs to execute code or inspect interpreter state. Sequential execution of handlers avoids typical concurrency pitfalls. This is especially important for the R backend, where concurrently executing R code is inherently unsafe and best avoided (see https://github.com/posit-dev/ark/issues/691 and https://github.com/posit-dev/ark/issues/689).
However, not all comm messages require interpreter access. For example, plot pane size updates can be handled entirely on the Rust side, since plot rendering settings live outside the R interpreter. In contrast, console width changes require modifying the R global option width, which does require exclusive interpreter access.
Another example: Variable pane requests. Currently, expanding a variable while the interpreter is busy results in a timeout error:
This happens because the request is queued on Shell with a timeout. That makes sense with the current backend, where workspace inspection uses R code. But in principle, if the backend maintained an in-memory model of the workspace, it could respond to such requests concurrently if we could lift the Shell socket’s constraints.
The Control socket would be a good way to solve that problem. It's designed for interrupt-like messages that are handled immediately, without queuing. The protocol currently supports only interrupts and shutdown requests on this socket, but we could extend it to allow other message types.
Concretely, we could allow extension code managing backends to define a set of messages that should be sent on the Control socket instead of Shell. Whether a given message should use Control or Shell should be decided by the backend, since different languages may have different requirements.
Initially I thought routing should happen in Kallichore, to keep Shell vs Control details out of Positron’s client abstraction. But it’s unclear how to set that up: comm messages sent via sendClientMessage() are opaque, and it seems inappropriate to inspect them for type matching.
Another option is to extend the language runtime metadata with a field listing message types that should be sent interrupt-style. Then sendClientMessage() could take a boolean flag requesting interrupt-style delivery, which Kallichore would interpret as sending via the Control socket.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing sendClientMessage() and the existing Shell and Control socket handling in Kallichore, then review the language runtime metadata discussed in the issue. Compare backend-selected routing with the proposed interrupt-style flag. Done requires an agreed design for routing safe comm messages without changing messages that require exclusive interpreter access.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- r, rust, typescript
- Domain
- api, backend, backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100