Repository navigation
feat(service): supervised Python/Rust services with a non-blocking client - #1330
Merged
Merged
Conversation
…ient
The broker service API was Rust-only, and its frame client blocks. A
caller living on a UI thread whose loop only offers timers (kitty's
main thread, which draws every terminal) can use neither: one blocking
IPC call is a frozen screen.
backend_sdk::service packages the direct-endpoint recipe (INTEGRATE.md)
without the broker:
- ServiceServer binds a per-user endpoint derived from the service name
through bind_singleton, so a second instance is refused and a crashed
one's stale socket reclaimed; writes the identity sidecar; answers
BackendHandle probes via the mux; queues requests for worker threads.
- PollingFrameClient: every method touches only a queue or an atomic.
A supervisor thread probes the identity file on connect and every
health_interval, and when the service is absent spawns the spec's
command through spawn::spawn_daemon under an exclusive lock file,
at most 3 per 30 s (the broker's own default budget). send() fails
fast (Unavailable / QueueFull); replies come back through try_recv()
matched by request id, out of order; a request in flight when the
connection drops comes back as an error reply rather than vanishing.
Python gets it as a separate top-level package, running_process_service,
which loads running_process._native straight from the package directory
so importing it does not execute running_process/__init__.py.
Respawn uses spawn::spawn_daemon rather than launch_detached:
launch_detached is a running-process-daemon client and would make every
service depend on a second daemon being startable.
Payload protocol is the caller's (the binding registers none). The new
client is listed in blessed_api.toml (blocking = false) and the surfaces
in the API snapshots, parity manifest and sync baseline. docs/SERVICE.md
is the user guide.
Local-Gate: v1 tree=25c894fb332adf88e75564e154d2a9abb61cb925 secs=1491 lanes=linux-quick:run
Ci-Attestation: {"at":1792975619,"gate":"rust/all/fmt","host":"linux-x86_64","key":"bc94558ff21386c2012d00067c50572bc9fba0ec34da32a7caa2523f7fcb3e1b","lane":"linux-quick","parents":["dee1c2c6307739b6aed08927264a775bd6a2b727"],"secs":1491,"stamp":"d321ad644b5373401af5043fb21478cc","tree":"25c894fb332adf88e75564e154d2a9abb61cb925","v":1,"via":"run"}
Ci-Attestation: {"at":1792975619,"gate":"python/all/static","host":"linux-x86_64","key":"bc94558ff21386c2012d00067c50572bc9fba0ec34da32a7caa2523f7fcb3e1b","lane":"linux-quick","parents":["dee1c2c6307739b6aed08927264a775bd6a2b727"],"secs":1491,"stamp":"b82bbeaed748ae93b4690897f84c5adf","tree":"25c894fb332adf88e75564e154d2a9abb61cb925","v":1,"via":"run"}
Ci-Attestation: {"at":1792975619,"gate":"rust/x86_64-unknown-linux-gnu/check","host":"linux-x86_64","key":"bc94558ff21386c2012d00067c50572bc9fba0ec34da32a7caa2523f7fcb3e1b","lane":"linux-quick","parents":["dee1c2c6307739b6aed08927264a775bd6a2b727"],"secs":1491,"stamp":"e2b0c7e52aed54a448de89fa8fc211f1","tree":"25c894fb332adf88e75564e154d2a9abb61cb925","v":1,"via":"run"}
Ci-Attestation: {"at":1792975619,"gate":"rust/x86_64-unknown-linux-gnu/dylint","host":"linux-x86_64","key":"bc94558ff21386c2012d00067c50572bc9fba0ec34da32a7caa2523f7fcb3e1b","lane":"linux-quick","parents":["dee1c2c6307739b6aed08927264a775bd6a2b727"],"secs":1491,"stamp":"58dbf9aa3c67bc718dd9da7c1fb43ede","tree":"25c894fb332adf88e75564e154d2a9abb61cb925","v":1,"via":"run"}
Ci-Attestation: {"at":1792975619,"gate":"rust/x86_64-unknown-linux-gnu/test","host":"linux-x86_64","key":"bc94558ff21386c2012d00067c50572bc9fba0ec34da32a7caa2523f7fcb3e1b","lane":"linux-quick","parents":["dee1c2c6307739b6aed08927264a775bd6a2b727"],"secs":1491,"stamp":"b4b5db0afab650af4c26322013eab867","tree":"25c894fb332adf88e75564e154d2a9abb61cb925","v":1,"via":"run"}
Ci-Attestation: {"at":1792975619,"gate":"python/linux-x64/test","host":"linux-x86_64","key":"bc94558ff21386c2012d00067c50572bc9fba0ec34da32a7caa2523f7fcb3e1b","lane":"linux-quick","parents":["dee1c2c6307739b6aed08927264a775bd6a2b727"],"secs":1491,"stamp":"fb661aba610c8f34cc4b43bb5f168595","tree":"25c894fb332adf88e75564e154d2a9abb61cb925","v":1,"via":"run"}
Ci-Attestation: {"at":1792975619,"gate":"general/linux-x64/perf","host":"linux-x86_64","key":"bc94558ff21386c2012d00067c50572bc9fba0ec34da32a7caa2523f7fcb3e1b","lane":"linux-quick","parents":["dee1c2c6307739b6aed08927264a775bd6a2b727"],"secs":1491,"stamp":"de62abe40aea486d7033f86d216cb84c","tree":"25c894fb332adf88e75564e154d2a9abb61cb925","v":1,"via":"run"}
zackees
force-pushed
the
feat/python-broker-service
branch
from
October 26, 2026 00:52
b3729db to
b3a1cf2
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
A Windows named-pipe name stays taken while any connected server
instance is open, so ServiceServer::close released the endpoint only
once clients disconnected -- a restarted service could not rebind
(close_releases_endpoint_and_stops_client failed on windows-x86).
Connection threads now read with a poll timeout, drop their handle when
the server stops, and close waits (bounded, 2 s) for them.
Local-Gate: v1 tree=853931f3500afad19a454602ad61e69b757b4b61 secs=946 lanes=linux-quick:run
Ci-Attestation: {"at":1792978915,"gate":"rust/all/fmt","host":"linux-x86_64","key":"6d961a198cfb7f6b52e73cc8f04c590ffddde6ae459d1d673fce08ffffb067b5","lane":"linux-quick","parents":["b3a1cf2f05e7e39c9cd8c581ec48748c6aee2da2"],"secs":946,"stamp":"65fabf6de6ef8a33443118242f789240","tree":"853931f3500afad19a454602ad61e69b757b4b61","v":1,"via":"run"}
Ci-Attestation: {"at":1792978915,"gate":"python/all/static","host":"linux-x86_64","key":"6d961a198cfb7f6b52e73cc8f04c590ffddde6ae459d1d673fce08ffffb067b5","lane":"linux-quick","parents":["b3a1cf2f05e7e39c9cd8c581ec48748c6aee2da2"],"secs":946,"stamp":"8694bd8faedf64a67dd9365017dc5c01","tree":"853931f3500afad19a454602ad61e69b757b4b61","v":1,"via":"run"}
Ci-Attestation: {"at":1792978915,"gate":"rust/x86_64-unknown-linux-gnu/check","host":"linux-x86_64","key":"6d961a198cfb7f6b52e73cc8f04c590ffddde6ae459d1d673fce08ffffb067b5","lane":"linux-quick","parents":["b3a1cf2f05e7e39c9cd8c581ec48748c6aee2da2"],"secs":946,"stamp":"f31887a964f2b68bf839c0f434f9dcec","tree":"853931f3500afad19a454602ad61e69b757b4b61","v":1,"via":"run"}
Ci-Attestation: {"at":1792978915,"gate":"rust/x86_64-unknown-linux-gnu/dylint","host":"linux-x86_64","key":"6d961a198cfb7f6b52e73cc8f04c590ffddde6ae459d1d673fce08ffffb067b5","lane":"linux-quick","parents":["b3a1cf2f05e7e39c9cd8c581ec48748c6aee2da2"],"secs":946,"stamp":"d9d810daa542b262c2041b7f0b3be610","tree":"853931f3500afad19a454602ad61e69b757b4b61","v":1,"via":"run"}
Ci-Attestation: {"at":1792978915,"gate":"rust/x86_64-unknown-linux-gnu/test","host":"linux-x86_64","key":"6d961a198cfb7f6b52e73cc8f04c590ffddde6ae459d1d673fce08ffffb067b5","lane":"linux-quick","parents":["b3a1cf2f05e7e39c9cd8c581ec48748c6aee2da2"],"secs":946,"stamp":"05b9ce2a455f819a26883a8f6625d969","tree":"853931f3500afad19a454602ad61e69b757b4b61","v":1,"via":"run"}
Ci-Attestation: {"at":1792978915,"gate":"python/linux-x64/test","host":"linux-x86_64","key":"6d961a198cfb7f6b52e73cc8f04c590ffddde6ae459d1d673fce08ffffb067b5","lane":"linux-quick","parents":["b3a1cf2f05e7e39c9cd8c581ec48748c6aee2da2"],"secs":946,"stamp":"86b720be709e94b646f01fe754c62267","tree":"853931f3500afad19a454602ad61e69b757b4b61","v":1,"via":"run"}
Ci-Attestation: {"at":1792978915,"gate":"general/linux-x64/perf","host":"linux-x86_64","key":"6d961a198cfb7f6b52e73cc8f04c590ffddde6ae459d1d673fce08ffffb067b5","lane":"linux-quick","parents":["b3a1cf2f05e7e39c9cd8c581ec48748c6aee2da2"],"secs":946,"stamp":"9a596672524c1b6fd0d0ec60948b94da","tree":"853931f3500afad19a454602ad61e69b757b4b61","v":1,"via":"run"}
A Windows named-pipe name stays taken while any connected instance is
open, and a connection thread blocked in read holds one. Neither
set_recv_timeout (pipes ignore it) nor PIPE_NOWAIT (interprocess reports
ERROR_NO_DATA as end-of-stream) can wake it, which is how
close_releases_endpoint_and_stops_client and then round_trip failed on
windows-x86.
The platform choice lives behind the ipc facade, as the boundary gate
requires: Stream::set_interruptible_reads sets a receive timeout on Unix,
and on Windows says to check Stream::bytes_available (PeekNamedPipe)
before each read. The service polls that way with a 1-20 ms idle
backoff, so every connection thread notices close within a poll and
drops its handle; close waits for them (bounded, 2 s).
Local-Gate: v1 tree=249b6269cd5287c305e24ca2c17ef703fd4516ad secs=2368 lanes=linux-quick:run
Ci-Attestation: {"at":1792990969,"gate":"rust/all/fmt","host":"linux-x86_64","key":"6a939c78e0c85f571d6007eb717aef025b7f4e52acd374a42e7fec4220d5aa56","lane":"linux-quick","parents":["15f441429d074d4ac16dca29b2ceb02fe8eedcee"],"secs":2368,"stamp":"1d0b4f5d70b6e695438f236e9addb5ef","tree":"249b6269cd5287c305e24ca2c17ef703fd4516ad","v":1,"via":"run"}
Ci-Attestation: {"at":1792990969,"gate":"python/all/static","host":"linux-x86_64","key":"6a939c78e0c85f571d6007eb717aef025b7f4e52acd374a42e7fec4220d5aa56","lane":"linux-quick","parents":["15f441429d074d4ac16dca29b2ceb02fe8eedcee"],"secs":2368,"stamp":"47c5eb4ac7b5873ec6f1c025ecca0d39","tree":"249b6269cd5287c305e24ca2c17ef703fd4516ad","v":1,"via":"run"}
Ci-Attestation: {"at":1792990969,"gate":"rust/x86_64-unknown-linux-gnu/check","host":"linux-x86_64","key":"6a939c78e0c85f571d6007eb717aef025b7f4e52acd374a42e7fec4220d5aa56","lane":"linux-quick","parents":["15f441429d074d4ac16dca29b2ceb02fe8eedcee"],"secs":2368,"stamp":"c737f130234ce317198d256133303c8e","tree":"249b6269cd5287c305e24ca2c17ef703fd4516ad","v":1,"via":"run"}
Ci-Attestation: {"at":1792990969,"gate":"rust/x86_64-unknown-linux-gnu/dylint","host":"linux-x86_64","key":"6a939c78e0c85f571d6007eb717aef025b7f4e52acd374a42e7fec4220d5aa56","lane":"linux-quick","parents":["15f441429d074d4ac16dca29b2ceb02fe8eedcee"],"secs":2368,"stamp":"5ded3852f9760a359d413b654f0df027","tree":"249b6269cd5287c305e24ca2c17ef703fd4516ad","v":1,"via":"run"}
Ci-Attestation: {"at":1792990969,"gate":"rust/x86_64-unknown-linux-gnu/test","host":"linux-x86_64","key":"6a939c78e0c85f571d6007eb717aef025b7f4e52acd374a42e7fec4220d5aa56","lane":"linux-quick","parents":["15f441429d074d4ac16dca29b2ceb02fe8eedcee"],"secs":2368,"stamp":"713e85f3933b6e19210be366ae7ae346","tree":"249b6269cd5287c305e24ca2c17ef703fd4516ad","v":1,"via":"run"}
Ci-Attestation: {"at":1792990969,"gate":"python/linux-x64/test","host":"linux-x86_64","key":"6a939c78e0c85f571d6007eb717aef025b7f4e52acd374a42e7fec4220d5aa56","lane":"linux-quick","parents":["15f441429d074d4ac16dca29b2ceb02fe8eedcee"],"secs":2368,"stamp":"4a15e2f72e71cfe2cbdf39bff2726b79","tree":"249b6269cd5287c305e24ca2c17ef703fd4516ad","v":1,"via":"run"}
Ci-Attestation: {"at":1792990969,"gate":"general/linux-x64/perf","host":"linux-x86_64","key":"6a939c78e0c85f571d6007eb717aef025b7f4e52acd374a42e7fec4220d5aa56","lane":"linux-quick","parents":["15f441429d074d4ac16dca29b2ceb02fe8eedcee"],"secs":2368,"stamp":"61c11e1f6345660c131a448025ebe6fc","tree":"249b6269cd5287c305e24ca2c17ef703fd4516ad","v":1,"via":"run"}
zackees
force-pushed
the
feat/python-broker-service
branch
from
October 26, 2026 05:03
3284521 to
7429c9f
Compare
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
backend_sdk::service(ServiceServer,PollingFrameClient) and a light top-level Python packagerunning_process_service(ServiceSpec / ServiceServer / serve / ServiceClient).bind_singletonon a per-user endpoint derived from the name; identity sidecar + BackendHandle probe for health; client-side respawn ofcommandviaspawn::spawn_daemonunder a lock file, 3 per 30 s.sendfails fast (ServiceUnavailable/QueueFull),try_recvpolls, replies matched by request id (out of order OK).PollingFrameClientops listed withblocking = false; API snapshots, parity manifest, sync baseline updated. Docs:docs/SERVICE.md.Deviation from the agreed design, stated: respawn uses
spawn::spawn_daemoninstead oflaunch_detached, becauselaunch_detachedis arunning-process-daemonclient and would make each service depend on that daemon.Tests:
tests/test_service.py(13), Rust unit tests inservice.rs(4). Motivating consumer: kitty-worker in zackees' nixos config (kitty's main thread can only poll from timers).