Skip to content

feat: migrate RPC surface to Connect-RPC#118

Merged
rustatian merged 2 commits into
masterfrom
feature/connect-rpc-migration
May 13, 2026
Merged

feat: migrate RPC surface to Connect-RPC#118
rustatian merged 2 commits into
masterfrom
feature/connect-rpc-migration

Conversation

@rustatian
Copy link
Copy Markdown
Member

@rustatian rustatian commented May 13, 2026

Summary

Migrates the informer plugin's RPC surface from net/rpc + goridge codec to Connect-RPC over HTTP/2 (h2c), using informerV1connect.NewInformerServiceHandler from api-go v6.0.0-beta.9.

RPC HTTP method Notes
ListPlugins GET plugins exposing workers; marked NO_SIDE_EFFECTS
GetWorkers GET per-plugin OS-process snapshot; NO_SIDE_EFFECTS
GetJobs GET per-plugin jobs-pipeline state; NO_SIDE_EFFECTS
AddWorker POST dedicated AddWorkerRequest
RemoveWorker POST dedicated RemoveWorkerRequest

The handler converts internal Go types (pool/v2/state/process.State, api-plugins/v6/jobs.State) to the wire types (informerV1.ProcessState, informerV1.JobState) per call. Field widths match the existing service.v1.Status convention: int32 pid, float32 cpu_percent.

All three wire protocols (Connect / plain HTTP+protojson / plain gRPC) reachable on the same rpc.listen port. New tests/informer_api_test.go covers all three plus an HTTP-GET idempotency table.

Breaking changes

Wire protocol change: callers using net/rpc + goridge codec (client.Call("informer.Workers", name, &out), client.Call("informer.List", true, &out)) must switch to a Connect / HTTP / gRPC client targeting /informer.v1.InformerService/{ListPlugins,GetWorkers,GetJobs,AddWorker,RemoveWorker}. Method name changes: WorkersGetWorkers, JobsGetJobs, ListListPlugins.

Dep bumps:

  • github.com/roadrunner-server/api-go/v6v6.0.0-beta.9
  • github.com/roadrunner-server/goridge/v4v4.0.0-beta.2
  • github.com/roadrunner-server/rpc/v6 (tests) → v6.0.0-beta.4

Summary by CodeRabbit

  • New Features

    • Informer plugin now exposes API via ConnectRPC with support for multiple transports: Connect/HTTP, HTTP/1.1 with protobuf, and gRPC.
    • API provides endpoints for listing plugins, querying workers and jobs, and managing worker instances.
  • Tests

    • Added comprehensive integration tests validating plugin listing, job queries, worker management, and HTTP GET idempotency semantics.

Review Change Stack

- plugin.go RPC() returns (string, http.Handler) via
  informerV1connect.NewInformerServiceHandler from api-go v6.0.0-beta.9
- rpc.go reshaped to the Connect handler interface with 5 methods:
  - ListPlugins, GetWorkers, GetJobs (NO_SIDE_EFFECTS, GET-eligible)
  - AddWorker, RemoveWorker
- pool/v2/state/process.State and api-plugins/v6/jobs.State are converted
  per-call to informerV1.ProcessState / informerV1.JobState (int32 pid,
  float32 cpu_percent to match service.v1.Status widths)
- tests/informer_test.go: 4 goridge client.Call sites switched to a single
  newInformerClient helper using h2c Connect client
- new tests/informer_api_test.go covers Connect, plain HTTP+protojson, plain
  gRPC against the same handler, plus HTTP-GET idempotency for the three
  read-only methods
- new tests/configs/.rr-informer-api.yaml minimal config for the API tests
- bump api-go to v6.0.0-beta.9, goridge to v4.0.0-beta.2, rpc/v6 to v6.0.0-beta.4
Copilot AI review requested due to automatic review settings May 13, 2026 09:43
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Warning

Rate limit exceeded

@rustatian has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 51 minutes and 33 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 086410d5-d9a3-4a12-ac20-c8f69ca7452b

📥 Commits

Reviewing files that changed from the base of the PR and between 0d64f10 and dd6be6b.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • tests/go.sum is excluded by !**/*.sum
📒 Files selected for processing (6)
  • go.mod
  • plugin.go
  • rpc.go
  • tests/go.mod
  • tests/informer_api_test.go
  • tests/informer_test.go
📝 Walkthrough

Walkthrough

This PR migrates the informer plugin from legacy net/rpc/goridge-based RPC to ConnectRPC (HTTP/1.1, HTTP/2, and gRPC support). The change adds ConnectRPC dependencies, rewrites the RPC handler to return (string, http.Handler), replaces five legacy methods with ConnectRPC endpoints that convert plugin/worker/job state to informer v1 message types, and updates all integration tests to use the new client pattern.

Changes

ConnectRPC Migration

Layer / File(s) Summary
Module dependencies for ConnectRPC and API v6
go.mod, tests/go.mod
ConnectRPC v1.19.2 and roadrunner-server/api-go/v6 beta.9 added as direct dependencies; transitive grpc, protobuf, and genproto support added to indirect requires in both root and test modules.
Plugin RPC handler signature and wiring
plugin.go
Plugin.RPC() signature updated from any to (string, http.Handler); handler constructed via informerV1connect.NewInformerServiceHandler(&rpc{plugin: p}).
ConnectRPC endpoint implementations
rpc.go
Five ConnectRPC endpoints replace legacy net/rpc methods: ListPlugins (iterate plugins), GetWorkers (read plugin workers with ProcessState conversion), GetJobs (read plugin jobs with JobState conversion), AddWorker (call plugin.AddWorker with error mapping), RemoveWorker (call plugin.RemoveWorker with error mapping).
Test container configuration and startup logic
tests/configs/.rr-informer-api.yaml, tests/informer_api_test.go
New .rr-informer-api.yaml defines server relay/RPC/logging config; startInformerAPIContainer boots endure container and returns shutdown closure.
ConnectRPC integration test suite
tests/informer_api_test.go
Four new tests: TestInformerConnectAPI (Connect HTTP client), TestInformerHTTPApi (plain HTTP/protojson), TestInformerGRPCApi (gRPC client), TestInformerHTTPGetIdempotency (read-only GET allowed, mutating methods reject GET).
Existing test migration to ConnectRPC client
tests/informer_test.go
newInformerClient helper builds HTTP/2 InformerServiceClient; TestInformerEarlyCall and helper tests (informerPluginWOWorkersRPCTest, informerWorkersRPCTest, informerListRPCTest) rewritten to call GetWorkers and ListPlugins via ConnectRPC client.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 ConnectRPC hops in bright,
From legacy code to HTTP light,
Workers and jobs now stream with grace,
Five endpoints in their proper place!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: migrate RPC surface to Connect-RPC' clearly and concisely summarizes the primary change: migrating from net/rpc+goridge to Connect-RPC protocol.
Description check ✅ Passed The PR description provides a comprehensive summary, mapping of RPC endpoints to HTTP methods, type conversions, breaking changes documentation, and dependency version bumps. However, the 'Reason for This PR' and PR Checklist sections from the template are missing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/connect-rpc-migration

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

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 migrates the informer plugin’s RPC surface from the legacy net/rpc + Goridge codec to Connect-RPC handlers, making the service reachable via Connect, plain HTTP+protojson, and gRPC on the same rpc.listen port.

Changes:

  • Replaced the informer RPC implementation with Connect-RPC methods (ListPlugins, GetWorkers, GetJobs, AddWorker, RemoveWorker) and updated Plugin.RPC() to return a Connect handler mount.
  • Updated existing informer tests to use a Connect client instead of net/rpc.
  • Added a new integration-style API test suite that exercises Connect, plain HTTP+protojson, gRPC, and HTTP GET idempotency behavior.

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rpc.go Replaces legacy net/rpc service methods with Connect-RPC method implementations and converts internal state types to wire types.
plugin.go Changes RPC() to return the Connect handler mount ((string, http.Handler)) for informer service.
tests/informer_test.go Updates existing tests to call the new Connect-RPC endpoints via a helper client.
tests/informer_api_test.go Adds new tests covering Connect, HTTP+protojson, gRPC, and GET idempotency behavior.
tests/configs/.rr-informer-api.yaml Adds a dedicated config for the new informer API tests.
go.mod / go.sum Adds Connect-RPC + api-go v6.0.0-beta.9 and related dependency updates for the main module.
tests/go.mod / tests/go.sum Updates test module deps to match Connect-RPC usage and bumped RR deps used by tests.
go.work.sum Updates workspace dependency checksums.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/informer_api_test.go
Comment thread tests/informer_api_test.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/informer_api_test.go (1)

183-189: 💤 Low value

Consider using method-specific request messages.

The test constructs a single GetWorkersRequest message (lines 183-189) and reuses it for all five methods via GET query parameters. While this might work if the server ignores message content for GET requests that don't match, it creates confusion:

  • ListPlugins expects ListPluginsRequest (empty message)
  • GetJobs expects GetJobsRequest{Plugin: ...}
  • AddWorker/RemoveWorker expect AddWorkerRequest/RemoveWorkerRequest

If the intent is to verify HTTP method semantics (405 for mutating methods via GET) regardless of message content, consider either:

  1. Using empty or minimal messages that are valid for all methods, or
  2. Adding a comment explaining why a GetWorkersRequest is being reused
♻️ Optional clarification

If the message content is intentionally ignored for this test, add a comment:

 	body, err := protojson.Marshal(&informerV1.GetWorkersRequest{Plugin: "probe"})
 	require.NoError(t, err)
 
+	// Note: We reuse the same encoded message for all methods since this test
+	// verifies HTTP method (GET) support, not message validation.
 	q := url.Values{}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/informer_api_test.go` around lines 183 - 189, The test currently reuses
a GetWorkersRequest (proto message named GetWorkersRequest) encoded into the GET
query (variable body / message param) for multiple endpoints (ListPlugins,
GetJobs, AddWorker, RemoveWorker), which is confusing; update the test to encode
and send method-specific request messages (e.g., ListPluginsRequest for
ListPlugins, GetJobsRequest{Plugin: "probe"} for GetJobs,
AddWorkerRequest/RemoveWorkerRequest for the mutating endpoints) or, if you
intentionally want to ignore message content to only assert HTTP method
semantics, replace the encoded payload with a minimal/empty message valid for
all endpoints and add a clear comment above the test explaining that choice so
readers understand why GetWorkersRequest is not used across the board.
rpc.go (1)

14-20: ⚡ Quick win

Stabilize ListPlugins response ordering.

plugins is populated from map iteration, so response order is nondeterministic. Sort before returning to avoid flaky comparisons/caching behavior.

Suggested patch
 import (
 	"context"
+	"sort"

 	"connectrpc.com/connect"
 	informerV1 "github.com/roadrunner-server/api-go/v6/informer/v1"
 )
@@
 	for name := range r.plugin.withWorkers {
 		plugins = append(plugins, name)
 	}
+	sort.Strings(plugins)
 	return connect.NewResponse(&informerV1.PluginsList{Plugins: plugins}), nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rpc.go` around lines 14 - 20, ListPlugins builds the plugins slice by
iterating r.plugin.withWorkers which yields nondeterministic map order; to
stabilize responses, sort the plugins slice before returning. Modify the
ListPlugins function to call sort.Strings(plugins) (importing "sort" if missing)
after the loop and before creating the connect.NewResponse so the returned
informerV1.PluginsList.Plugins is consistently ordered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rpc.go`:
- Around line 59-62: The AddWorker and RemoveWorker RPCs currently map all
errors from Plugin.AddWorker/RemoveWorker to connect.CodeInternal; change them
to return a 4xx Connect code for unsupported plugins by distinguishing that
specific error: either (A) have the Plugin implementation return a sentinel
error (e.g., plugin.ErrUnsupportedPlugin) or a typed error and in
rpc.AddWorker/rpc.RemoveWorker use errors.Is / type assertion to detect it and
return connect.NewError(connect.CodeInvalidArgument, err), or (B) add a public
validation method on the Plugin interface (e.g.,
SupportsWorkerManagement(pluginName string) bool) and call it from
rpc.AddWorker/rpc.RemoveWorker before invoking mutation methods and return
connect.CodeInvalidArgument when it returns false; keep all other errors mapped
to connect.CodeInternal. Ensure you reference Plugin.AddWorker,
Plugin.RemoveWorker, rpc.AddWorker and rpc.RemoveWorker when applying the
change.

In `@tests/informer_api_test.go`:
- Around line 63-71: The test incorrectly calls wg.Go(...) on a sync.WaitGroup;
replace that with wg.Add(1) immediately before starting a goroutine and launch
the goroutine with go func() { defer wg.Done(); ... } so the anonymous function
uses defer wg.Done() and preserves the select logic reading from ch and stop;
ensure any later synchronization uses wg.Wait() to block until the goroutine
completes.

---

Nitpick comments:
In `@rpc.go`:
- Around line 14-20: ListPlugins builds the plugins slice by iterating
r.plugin.withWorkers which yields nondeterministic map order; to stabilize
responses, sort the plugins slice before returning. Modify the ListPlugins
function to call sort.Strings(plugins) (importing "sort" if missing) after the
loop and before creating the connect.NewResponse so the returned
informerV1.PluginsList.Plugins is consistently ordered.

In `@tests/informer_api_test.go`:
- Around line 183-189: The test currently reuses a GetWorkersRequest (proto
message named GetWorkersRequest) encoded into the GET query (variable body /
message param) for multiple endpoints (ListPlugins, GetJobs, AddWorker,
RemoveWorker), which is confusing; update the test to encode and send
method-specific request messages (e.g., ListPluginsRequest for ListPlugins,
GetJobsRequest{Plugin: "probe"} for GetJobs,
AddWorkerRequest/RemoveWorkerRequest for the mutating endpoints) or, if you
intentionally want to ignore message content to only assert HTTP method
semantics, replace the encoded payload with a minimal/empty message valid for
all endpoints and add a clear comment above the test explaining that choice so
readers understand why GetWorkersRequest is not used across the board.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bdd77e9e-74bc-42ea-a765-d58ab38ba19c

📥 Commits

Reviewing files that changed from the base of the PR and between 8c0b6c3 and 0d64f10.

⛔ Files ignored due to path filters (3)
  • go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
  • tests/go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • go.mod
  • plugin.go
  • rpc.go
  • tests/configs/.rr-informer-api.yaml
  • tests/go.mod
  • tests/informer_api_test.go
  • tests/informer_test.go

Comment thread rpc.go
Comment thread tests/informer_api_test.go
- handler-side map lookups (mirrors service.loadProcesses / resetter pattern):
  GetWorkers / GetJobs surface unknown plugin as CodeNotFound;
  AddWorker / RemoveWorker surface "plugin does not support workers management"
  as CodeFailedPrecondition (caller-input class, not internal)
- GetJobs maps context.Canceled / DeadlineExceeded to typed Connect codes
- sentinels (errNoSuchPlugin, errNoWorkerManagement) live alongside the handler
- Plugin.{Workers,Jobs,AddWorker,RemoveWorker} thin wrappers removed — no
  external callers, handler owns the logic now
- informerPluginWOWorkersRPCTest now expects CodeNotFound (was the very silent-
  fallback this fixup removes)
- TestInformerConnectAPI extended to cover RemoveWorker + unknown-plugin
  negative paths
- end-of-migration go get -u all sweep: http/v6 beta.6→beta.7 (typecheck-fix
  upstream), server/v6 beta.5→beta.6, status/v6 beta.3→beta.5,
  resetter/v6 beta.2→beta.3, quic-go, sysconf/numcpus
@rustatian rustatian self-assigned this May 13, 2026
@rustatian rustatian added the enhancement New feature or request label May 13, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (8c0b6c3) to head (dd6be6b).

Additional details and impacted files
@@      Coverage Diff      @@
##   master   #118   +/-   ##
=============================
=============================

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

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

@rustatian rustatian merged commit 58a1d76 into master May 13, 2026
9 checks passed
@rustatian rustatian deleted the feature/connect-rpc-migration branch May 13, 2026 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants