[router]: Dynamic per-request routing to upstream#71
Merged
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR updates the proxy router to select an upstream per request (rather than per process / first-upstream-only), by buffering the first inbound message to extract the Temporal namespace and routing via a config-compiled mux.
Changes:
- Replace the router handler’s single-
ClientConnforwarding with per-request routing viaDirector+Reflector(namespace extraction from the first request frame). - Update the router fx module to pre-build a connection map for all configured upstreams and route requests via a mux-selected upstream name.
- Remove the temporary
Config.PrimaryUpstream()bridge and update dev tooling/docs to demonstrate multi-upstream routing.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/router/handler.go | Introduces per-request routing hooks (Director/Reflector) and first-frame buffering/replay before stream forwarding. |
| internal/router/handler_test.go | Updates tests to use the new handler signature via stubs. |
| internal/router/fx.go | Builds mux + per-upstream pooled connections and wires routing director + namespace extractor into the handler. |
| internal/router/fx_test.go | Ensures router module wiring includes protoutil extractor and routing default where needed. |
| internal/config/config.go | Removes PrimaryUpstream() now that routing no longer needs a single-upstream bridge. |
| internal/config/config_test.go | Removes tests for the deleted PrimaryUpstream() helper. |
| dev/Procfile | Starts two Temporal dev servers for demonstrating routing across upstreams. |
| dev/config.yaml | Adds a second upstream and routing rules that direct the test namespace to it. |
| .github/CONTRIBUTING.md | Updates local-testing docs to cover per-request routing and the second dev upstream. |
cff8d68 to
e0d67cf
Compare
The router previously forwarded every stream to a single upstream resolved from PrimaryUpstream, so a multi-upstream config had no way to send a request to anything but the first upstream. The handler now routes each request on its own. It buffers the first client frame, uses the Reflector (backed by the proto Extractor) to peek the request namespace from that frame, and hands the namespace and metadata to a Director. The module's director matches them against the Mux compiled from the routing config and returns the pooled connection for the selected upstream, dialing one connection per configured upstream up front. Unroutable requests fail with FailedPrecondition, and a matched upstream with no connection fails with Unavailable.
e0d67cf to
6cfb787
Compare
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.
The router previously forwarded every stream to a single upstream resolved from PrimaryUpstream, so a multi-upstream config had no way to send a request to anything but the first upstream.
The handler now routes each request on its own. It buffers the first client frame, uses the Reflector (backed by the proto Extractor) to peek the request namespace from that frame, and hands the namespace and metadata to a Director. The module's director matches them against the Mux compiled from the routing config and returns the pooled connection for the selected upstream, dialing one connection per configured upstream up front. Unroutable requests fail with FailedPrecondition, and a matched upstream with no connection fails with Unavailable.
Testing It Out
Updated in CONTRIBUTING.md as well
mise run server(starts 3 Temporal servers and the proxy)mise run grpc GetSystemInfo(goes to default upstream)mise run grpc DescribeNamespace '{"namespace":"ns1.remote"}'(goes to cluster-1)mise run grpc DescribeNamespace '{"namespace":"test"}'(goes to cluster-2)mise run grpc DescribeNamespace '{"namespace":"test2"}'(goes to cluster-1 - NotFound)mise run grpc DescribeNamespace '{"namespace":"test2"}' -H 'x-cluster: 3'(goes to cluster-3)mise run grpc DescribeNamespace '{"namespace":"test"}' -H 'x-cluster: 3'(goes to cluster-3 - NotFound)