Skip to content

Expose all gRPC transport-layer options for worker connections — Closes #41#124

Merged
conradbzura merged 3 commits intowool-labs:mainfrom
conradbzura:41-grpc-keepalive-config
Mar 26, 2026
Merged

Expose all gRPC transport-layer options for worker connections — Closes #41#124
conradbzura merged 3 commits intowool-labs:mainfrom
conradbzura:41-grpc-keepalive-config

Conversation

@conradbzura
Copy link
Copy Markdown
Contributor

@conradbzura conradbzura commented Mar 25, 2026

Summary

Expose all gRPC HTTP/2 transport-layer options and adopt a self-describing worker model where workers advertise their connection configuration via WorkerMetadata so clients connect with compatible settings automatically. Introduce a two-tier options model: ChannelOptions holds advertised settings (message sizes, keepalive, concurrency, compression) serialized in a ChannelOptions protobuf message, while WorkerOptions composes ChannelOptions with server-only settings (ping enforcement, connection lifecycle). Remove the options and limit parameters from WorkerProxy, WorkerPool, and WorkerConnection — clients derive all connection settings from discovered worker metadata.

Closes #41

Proposed changes

New ChannelOptions protobuf message

Add a ChannelOptions message to wire.proto with fields for message sizes, keepalive, max pings without data, max concurrent streams, and compression algorithm. Embed it as an optional connection field on WorkerMetadata so workers advertise their channel configuration over the wire.

Split WorkerOptions into ChannelOptions + WorkerOptions

Replace the flat WorkerOptions dataclass with two frozen dataclasses in worker/base.py:

  • ChannelOptions — advertised settings: message sizes, keepalive (time, timeout, permit-without-calls), max pings without data, max concurrent streams, and compression (grpc.Compression enum). Default to gRPC's own defaults.
  • WorkerOptions — composes a ChannelOptions instance with server-only settings: http2_min_recv_ping_interval_without_data_ms, max_ping_strikes, and optional lifecycle options (max_connection_idle_ms, max_connection_age_ms, max_connection_age_grace_ms). Validate that keepalive_time_ms >= http2_min_recv_ping_interval_without_data_ms in __post_init__.

Move WorkerMetadata to worker/metadata.py

Move WorkerMetadata from discovery/base.py into a dedicated worker/metadata.py module. WorkerMetadata is worker configuration — it belongs in the worker subpackage, not discovery. The discovery module imports it from the new location.

Make WorkerMetadata.options non-optional

Change WorkerMetadata.options from ChannelOptions | None to ChannelOptions with a default of ChannelOptions(). Workers always advertise their transport configuration; to_protobuf() always serializes the connection field; from_protobuf() returns default ChannelOptions() for legacy protobufs without a connection field.

Self-describing workers via WorkerMetadata

Workers serialize their ChannelOptions into the protobuf ChannelOptions sub-message; clients reconstruct it on deserialization. The proxy's _worker_sentinel reads event.metadata.options directly from discovery events when creating WorkerConnection instances, making configuration automatic.

Channel factory passes all advertised options

Update _channel_factory in connection.py to include all ChannelOptions fields in the gRPC channel options list. Use max_concurrent_streams to size the per-channel concurrency semaphore, replacing the removed limit parameter.

Server-side options in WorkerProcess

Update WorkerProcess._serve() to pass all options to grpc.aio.server(): advertised options from ChannelOptions, plus server-only options from WorkerOptions. Lifecycle options (max_connection_idle_ms, max_connection_age_ms, max_connection_age_grace_ms) are conditionally included only when not None.

Remove options from WorkerProxy and WorkerPool

Remove the options parameter from WorkerProxy.__init__, WorkerPool.__init__, WorkerFactory, and __reduce__. Remove the limit parameter from WorkerConnection.__init__. The proxy's _worker_sentinel reads event.metadata.options directly from discovery events, making configuration automatic.

Test cases

# Test Suite Given When Then Coverage Target
1 TestChannelOptions No custom parameters ChannelOptions is instantiated All fields have default values including transport fields Default constructor
2 TestChannelOptions Custom message sizes ChannelOptions is instantiated Both size fields reflect custom values Custom message sizes
3 TestChannelOptions Only max_receive_message_length overridden ChannelOptions is instantiated Overridden field is custom, other is default Partial override
4 TestChannelOptions Custom keepalive_time_ms and keepalive_timeout_ms ChannelOptions is instantiated Both keepalive fields reflect custom values Keepalive time and timeout
5 TestChannelOptions keepalive_permit_without_calls=False ChannelOptions is instantiated Field stores False Permit without calls disabled
6 TestChannelOptions All three keepalive parameters ChannelOptions is instantiated All fields reflect provided values All keepalive options
7 TestChannelOptions Custom max_pings_without_data, max_concurrent_streams, and compression=Gzip ChannelOptions is instantiated All three fields reflect provided values New transport fields
8 TestWorkerOptions No custom parameters WorkerOptions is instantiated Contains default ChannelOptions and default server-only fields Default constructor
9 TestWorkerOptions Custom max_ping_strikes and all three lifecycle options WorkerOptions is instantiated All four server-only fields reflect provided values New server-only fields
10 TestWorkerOptions Custom ChannelOptions with smaller sizes WorkerOptions is instantiated Channel field reflects custom values Custom channel
11 TestWorkerOptions Custom http2_min_recv_ping_interval_without_data_ms WorkerOptions is instantiated Field stores custom value Custom min ping interval
12 TestWorkerOptions keepalive_time_ms=5000, min_ping=10000 WorkerOptions is instantiated Raises ValueError Validation: time < min ping
13 TestWorkerOptions keepalive_time_ms=10000, min_ping=10000 WorkerOptions is instantiated Succeeds without error Validation: time == min ping
14 TestWorkerOptions keepalive_time_ms=20000, min_ping=10000 WorkerOptions is instantiated Succeeds without error Validation: time > min ping
15 TestWorkerOptions Arbitrary positive keepalive_time_ms and min_ping WorkerOptions is instantiated Raises ValueError iff time < min_ping Property-based validation invariant
16 TestWorkerMetadata WorkerMetadata with default options to_protobuf() is called Connection sub-message is populated with default values to_protobuf always serializes
17 TestWorkerMetadata WorkerMetadata with options=ChannelOptions(keepalive_time_ms=60000) to_protobuf() then from_protobuf() Restored options equal original ChannelOptions Options roundtrip
18 TestWorkerMetadata WorkerMetadata with max_pings_without_data=5, max_concurrent_streams=50, compression=Gzip to_protobuf() then from_protobuf() Restored options equal original including all transport fields Transport fields roundtrip
19 TestWorkerMetadata Arbitrary ChannelOptions drawn from the full domain to_protobuf() then from_protobuf() All fields preserved including options Property-based roundtrip
20 TestWorkerConnection WorkerConnection with custom keepalive options A task is dispatched gRPC channel options include custom keepalive values Custom keepalive in channel
21 TestWorkerConnection WorkerConnection with default ChannelOptions A task is dispatched gRPC channel options include default keepalive values Default keepalive in channel
22 TestWorkerConnection WorkerConnection with max_pings_without_data=5, max_concurrent_streams=50, compression=Gzip A task is dispatched gRPC channel options include all three transport entries New transport fields in channel
23 TestWorkerProcess WorkerProcess with all keepalive options set run() is called gRPC server receives all keepalive and transport options All options in server
24 TestWorkerProcess WorkerProcess with default WorkerOptions run() is called gRPC server receives default values for all options Default options in server
25 TestWorkerProcess WorkerProcess with lifecycle options set run() is called gRPC server options include all three lifecycle entries Lifecycle options present
26 TestWorkerProcess WorkerProcess with default WorkerOptions (lifecycle=None) run() is called gRPC server options do not contain lifecycle entries Lifecycle options omitted
27 TestWorkerProcess WorkerProcess with custom ChannelOptions run() executes Metadata sent via pipe includes matching ChannelOptions Metadata includes channel options
28 TestWorkerProxy Discovery yields worker-added with options=ChannelOptions(keepalive_time_ms=60000) Sentinel processes event WorkerConnection created with matching options Sentinel passes metadata options
29 TestWorkerProxy Discovery yields worker-added with default options Sentinel processes event WorkerConnection created with default ChannelOptions Sentinel handles default options
30 TestWorkerProxy Discovery yields worker-added then worker-updated with new options Sentinel processes events Second WorkerConnection uses updated options Sentinel passes updated options
31 TestWorkerPool WorkerPool with default configuration Pool is entered as async context WorkerProxy is not passed an options parameter Options removed from pool
32 TestPoolComposition KEEPALIVE worker options (10s ping, 5s timeout) Pool built and coroutine dispatched Correct result returned Integration: KEEPALIVE options end-to-end
33 test_dispatch_hypothesis Explicit @example with WorkerOptionsKind.KEEPALIVE Hypothesis example runs Passes deterministically Integration: KEEPALIVE smoke test

conradbzura added a commit to conradbzura/wool that referenced this pull request Mar 26, 2026
Remove options from WorkerFactory protocol and default factory to
match the pool's actual calling convention — users bake options
into their factory via partial() or a lambda. Add field comments
to ChannelConfig proto and renumber connection to field 8. Switch
new keepalive tests to mocker.patch.object style per test guide.
@conradbzura conradbzura force-pushed the 41-grpc-keepalive-config branch from a55b736 to a20c003 Compare March 26, 2026 00:34
@conradbzura conradbzura marked this pull request as ready for review March 26, 2026 00:36
Comment thread wool/proto/wire.proto Outdated
Comment thread wool/src/wool/runtime/discovery/base.py Outdated
Comment thread wool/src/wool/runtime/worker/connection.py Outdated
Comment thread wool/src/wool/runtime/worker/process.py Outdated
@conradbzura conradbzura self-assigned this Mar 26, 2026
@conradbzura conradbzura force-pushed the 41-grpc-keepalive-config branch from a20c003 to ea0afc4 Compare March 26, 2026 15:27
Comment thread wool/proto/wire.proto Outdated
@conradbzura conradbzura force-pushed the 41-grpc-keepalive-config branch from ea0afc4 to 00e73e4 Compare March 26, 2026 17:15
@conradbzura conradbzura changed the title Expose gRPC keepalive configuration for worker connections — Closes #41 Expose all gRPC transport-layer options for worker connections — Closes #41 Mar 26, 2026
@conradbzura conradbzura force-pushed the 41-grpc-keepalive-config branch 2 times, most recently from 8746fad to 0e131d4 Compare March 26, 2026 18:56
Comment thread wool/src/wool/runtime/discovery/base.py Outdated
Comment thread wool/src/wool/runtime/discovery/base.py Outdated
Workers now advertise their gRPC channel configuration (keepalive
intervals, message sizes) via a new ChannelConfig protobuf message
embedded in WorkerMetadata.  Clients read the advertised settings
when connecting, removing the need to pass options through
WorkerProxy and WorkerPool — options are baked into the worker
factory instead.

BREAKING CHANGE: WorkerProxy, WorkerPool, and WorkerFactory no
longer accept an options parameter.
Cover keepalive option validation, ChannelConfig round-tripping
through protobuf, keepalive channel options in WorkerConnection,
server-side keepalive options in WorkerProcess, and the updated
WorkerProxy and WorkerPool signatures.  Integration conftest gains
a KEEPALIVE options variant and uses partial() to bake options
into the worker factory.
@conradbzura conradbzura force-pushed the 41-grpc-keepalive-config branch from 0e131d4 to 107fa00 Compare March 26, 2026 19:33
Workers now advertise their gRPC transport configuration via
ChannelOptions in WorkerMetadata.  Clients read these settings
from discovery events and configure their channels to match
automatically — no separate client-side configuration needed.
@conradbzura conradbzura force-pushed the 41-grpc-keepalive-config branch from f9f7ca4 to 92f1394 Compare March 26, 2026 19:44
@conradbzura conradbzura added breaking feature New feature or capability labels Mar 26, 2026
@conradbzura conradbzura merged commit 5e28741 into wool-labs:main Mar 26, 2026
4 checks passed
@conradbzura conradbzura removed the feature New feature or capability label Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose all gRPC transport-layer options for worker connections

1 participant