Skip to content

[router]: Dynamic per-request routing to upstream#71

Merged
pseudomuto merged 1 commit into
mainfrom
upstream_routing
Jul 15, 2026
Merged

[router]: Dynamic per-request routing to upstream#71
pseudomuto merged 1 commit into
mainfrom
upstream_routing

Conversation

@pseudomuto

@pseudomuto pseudomuto commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

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

  • Terminal 1: mise run server (starts 3 Temporal servers and the proxy)
  • Terminal 2:
    • 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)

@pseudomuto pseudomuto requested a review from Copilot July 15, 2026 12:46
@pseudomuto pseudomuto requested a review from a team as a code owner July 15, 2026 12:46
@codecov-commenter

codecov-commenter commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 68.00000% with 16 lines in your changes missing coverage. Please review.

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-ClientConn forwarding with per-request routing via Director + 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.

Comment thread internal/router/handler.go
Comment thread internal/router/handler.go
Comment thread internal/router/handler.go
Comment thread .github/CONTRIBUTING.md Outdated
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.
@pseudomuto pseudomuto merged commit 1f907a9 into main Jul 15, 2026
5 checks passed
@pseudomuto pseudomuto deleted the upstream_routing branch July 15, 2026 16:14
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.

3 participants