Skip to content

Bypass jrpc2 marshal to enable raw endpoint response passthrough - #1037

Draft
cjonas9 wants to merge 7 commits into
feature/full-historyfrom
bypass-jrpc2-marshal
Draft

cjonas9 wants to merge 7 commits into
feature/full-historyfrom
bypass-jrpc2-marshal

Conversation

@cjonas9

@cjonas9 cjonas9 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What

Switches stellar-rpc from github.com/creachadair/jrpc2 to the github.com/stellar-experimental/jrpc2 fork, pinned at commit 5b4c6315, the head of its raw-message-passthrough branch (stellar-experimental/jrpc2#1), which is based on upstream's v1.3.5 release. The fork's jhttp.Bridge dispatches HTTP requests straight to the handlers and streams their results to the socket instead of routing every call through an in-memory server.Local client/server pair. It also passes a non-empty json.RawMessage handler result through verbatim, which #1060 relies on for its memoized getLatestLedger body.

Changes in this repo:

  • go.mod replaces creachadair/jrpc2 v1.3.3 with the fork. Between v1.3.3 and v1.3.5, upstream changed behavior only in an omitzero tag on rpc.serverInfo, which stellar-rpc disables. The rest is WaitGroup.Go and reflect.Pointer modernization.
  • go-stellar-sdk moves to 3e6c13bb, the head of stellar/go-stellar-sdk#6020, which moves clients/rpcclient to the fork as well. creachadair/jrpc2 leaves the module graph, so the integration tests' errors.As(err, &*jrpc2.Error) assertions match the type the server returns. The SDK move also brings in ingest: resume a failed bucket download and keep the hash check over every returned record go-stellar-sdk#6017 (bucket download resume) and drops hashicorp/golang-lru.
  • jsonrpc.NewHandler sets ServerOptions.Concurrency: math.MaxInt, so jrpc2's handler semaphore (default runtime.NumCPU()) never blocks. The per-method backlog and duration limiters still wrap every handler.
  • Import path swapped in 38 Go files. No handler, limiter, or transport code changes; jsonrpc.NewHandler still calls jhttp.NewBridge. Handler.Close's doc no longer promises that the handler stops accepting requests.

What the fork changes for this server (full list in the fork PR):

  • Handlers run on the HTTP request's goroutine and context, so they are cancelled when the client disconnects or the global max-request-execution-duration fires. The old bridge ran them on the bridge server's background context, and the rpc.cancel path was unreachable with DisableBuiltin: true.
  • Responses are streamed with Response.WriteTo. Content-Length is still set, from a dry run into a counting writer. Per-request allocation in the bridge no longer scales with result size. stellar-rpc's own MakeHTTPRequestDurationLimiter still buffers the whole body once.
  • Wire parity with the old bridge is kept and pinned by the fork's jhttp tests: statically invalid batch entries are answered first, duplicate ids are each answered, empty batches and notification-only bodies return 204.
  • jhttp.Bridge.Close now only closes the GET Getter. stellar-rpc does not configure one, so it does nothing here. Handler.Close still calls it.
  • The bytes_read and bytes_written expvars no longer count bridge traffic. stellar-rpc does not read them.
  • The json_req log field now carries the caller's JSON-RPC id. The old bridge's client renumbered requests, so it used to log an internal counter.

Why

Every response used to be marshaled in invoke, encoded onto an in-memory pipe, parsed twice by the bridge's client, and compacted again by json.Marshal(rsp) before reaching the socket. For the 2.8 MB pubnet getLatestLedger result that is about 35 ms and 33 MB of allocation per call through jsonrpc.NewHandler, with the handler itself under 6% of it. getLedgers pages scale the same way (limit 20, 56 MB: ~0.7 s). The bridge, not the handler, bounded large-response throughput.

The concurrency cap mattered on the perf-eval box as well. The semaphore is acquired in invoke before the wrapped handler, so while the ingestion commit stall (#1036) parked NumCPU handlers on the cache lock, every other request queued behind them regardless of method.

Measured in the fork, one call returning a 3 MB result over loopback HTTP, at the client:

result before after allocated per call
struct 42.4 ms 3.1 ms 28 MB to 7.5 MB
json.RawMessage 39.7 ms 0.8 ms 20 MB to 17 KB

An earlier draft (#1003) got the same result with an in-tree dispatcher that replaced jhttp.Bridge. Doing it in the library keeps this repo's transport code unchanged.

Known limitations

@socket-security

socket-security Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

@socket-security

socket-security Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Priority Alert  (click "▶" to expand/collapse) Action
Medium priority
License policy violation: golang github.com/creachadair/mds under BSD-3-Clause-HP

License: BSD-3-Clause-HP - The applicable license policy does not permit this license (5) (LICENSE)

From: go.mod → golang/github.com/stellar-experimental/jrpc2@v0.0.0-20261006225839-5b4c6315ee00 → golang/github.com/creachadair/mds@v0.31.0

ℹ Read more on: This package | This alert | What is a license policy violation?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Find a package that does not violate your license policy or adjust your policy to allow this package's license.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore golang/github.com/creachadair/mds@v0.31.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn

View full report

@cjonas9
cjonas9 added this pull request to stack #1038 September 24, 2026 15:24
@cjonas9
cjonas9 force-pushed the bypass-jrpc2-marshal branch from 8715914 to b6d65ee Compare September 25, 2026 17:59
@cjonas9 cjonas9 changed the title bypass jrpc2 marshal Bypass jrpc2 marshal to enable raw endpoint response passthrough Sep 25, 2026
@cjonas9
cjonas9 force-pushed the bypass-jrpc2-marshal branch 2 times, most recently from 670361a to 0bfa01d Compare September 25, 2026 20:03
@cjonas9 cjonas9 added this to the platform sprint 76 milestone Sep 29, 2026
@cjonas9
cjonas9 force-pushed the bypass-jrpc2-marshal branch from 0bfa01d to 6df8786 Compare September 30, 2026 20:24
Base automatically changed from loosen-ingestion-lock-holding to feature/full-history October 2, 2026 21:56
@cjonas9
cjonas9 marked this pull request as ready for review October 2, 2026 23:15
Copilot AI balanced review requested due to automatic review settings October 2, 2026 23:15
@cjonas9
cjonas9 marked this pull request as draft October 2, 2026 23:16

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.

Copilot review overview

🟡 Changes recommended

Commit failures can leave the ledger cache inconsistent, and the dependency switch breaks the documented Handler.Close behavior.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
What changed in this PR

Switches the JSON-RPC server to a fork that bypasses the in-memory client round trip, reducing response serialization overhead.

Changes:

  • Migrates server-side JSON-RPC usage to stellar-experimental/jrpc2.
  • Disables jrpc2’s semaphore in favor of existing request limiters.
  • Updates SQLite oldest-ledger cache publication during trimming.
File Description
go.mod Updates JSON-RPC dependencies.
go.sum Records dependency checksums.
cmd/​stellar-rpc/​internal/​jsonrpc/​jsonrpc.go Uses the forked HTTP bridge and disables its concurrency limit.
cmd/​stellar-rpc/​internal/​jsonrpc/​specs.go Migrates handler types to the fork.
cmd/​stellar-rpc/​internal/​jsonrpc/​specs_test.go Updates specification tests.
cmd/​stellar-rpc/​internal/​network/​backlogQ.go Migrates queue limiter types.
cmd/​stellar-rpc/​internal/​network/​backlogQ_test.go Updates queue limiter tests.
cmd/​stellar-rpc/​internal/​network/​requestdurationlimiter.go Migrates duration limiter types.
cmd/​stellar-rpc/​internal/​network/​requestdurationlimiter_test.go Updates duration limiter tests.
cmd/​stellar-rpc/​internal/​methods/​differential_test.go Updates differential test handlers.
cmd/​stellar-rpc/​internal/​methods/​get_events.go Migrates the events handler.
cmd/​stellar-rpc/​internal/​methods/​get_events_differential_test.go Updates events differential tests.
cmd/​stellar-rpc/​internal/​methods/​get_fee_stats.go Migrates the fee-statistics handler.
cmd/​stellar-rpc/​internal/​methods/​get_health.go Migrates the health handler.
cmd/​stellar-rpc/​internal/​methods/​get_latest_ledger.go Migrates the latest-ledger handler.
cmd/​stellar-rpc/​internal/​methods/​get_latest_ledger_test.go Updates latest-ledger tests.
cmd/​stellar-rpc/​internal/​methods/​get_ledger_entries.go Migrates the ledger-entry handler.
cmd/​stellar-rpc/​internal/​methods/​get_ledgers.go Migrates the ledger-range handler.
cmd/​stellar-rpc/​internal/​methods/​get_network.go Migrates the network handler.
cmd/​stellar-rpc/​internal/​methods/​get_transaction.go Migrates the transaction handler.
cmd/​stellar-rpc/​internal/​methods/​get_transactions.go Migrates the transaction-list handler.
cmd/​stellar-rpc/​internal/​methods/​get_transactions_test.go Updates transaction-list tests.
cmd/​stellar-rpc/​internal/​methods/​get_transactions_differential_test.go Updates transaction differential tests.
cmd/​stellar-rpc/​internal/​methods/​get_version_info.go Migrates the version handler.
cmd/​stellar-rpc/​internal/​methods/​handler.go Uses the forked handler adapter.
cmd/​stellar-rpc/​internal/​methods/​handler_test.go Updates handler-adapter tests.
cmd/​stellar-rpc/​internal/​methods/​send_transaction.go Migrates transaction submission.
cmd/​stellar-rpc/​internal/​methods/​simulate_transaction.go Migrates transaction simulation.
cmd/​stellar-rpc/​internal/​methods/​simulate_transaction_test.go Updates simulation tests.
cmd/​stellar-rpc/​internal/​rpcv1/​sqlitedb/​db.go Publishes oldest-ledger cache values during commits.
cmd/​stellar-rpc/​internal/​rpcv1/​sqlitedb/​ledger.go Extracts oldest-ledger lookup logic.
cmd/​stellar-rpc/​internal/​rpcv1/​sqlitedb/​ledger_test.go Tests trim-time cache publication.
cmd/​stellar-rpc/​internal/​rpcv2/​eventsapi/​get_events_v1.go Migrates the v1 events adapter.
cmd/​stellar-rpc/​internal/​rpcv2/​eventsapi/​query_events.go Migrates event queries.
cmd/​stellar-rpc/​internal/​rpcv2/​eventsapi/​query_events_test.go Updates event-query tests.
cmd/​stellar-rpc/​internal/​rpcv2/​eventsapi/​v1_parity_test.go Updates parity tests.
cmd/​stellar-rpc/​internal/​rpcv2/​jsonrpc.go Migrates RPC v2 handler types.
cmd/​stellar-rpc/​internal/​rpcv2/​jsonrpc_test.go Updates RPC v2 tests.
cmd/​stellar-rpc/​internal/​rpcv2/​ledger_reads_bench_test.go Updates ledger benchmarks.
cmd/​stellar-rpc/​internal/​rpcv2/​rpcv2test/​rpcv2test.go Migrates RPC v2 test infrastructure.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +481 to 483
if err := w.tx.Commit(); err != nil {
return err
}
Comment on lines +21 to +23
"github.com/stellar-experimental/jrpc2"
"github.com/stellar-experimental/jrpc2/handler"
"github.com/stellar-experimental/jrpc2/jhttp"
@cjonas9
cjonas9 force-pushed the bypass-jrpc2-marshal branch from 6df8786 to 4490bc6 Compare October 2, 2026 23:22
@cjonas9
cjonas9 removed this pull request from stack #1038 October 2, 2026 23:23
@cjonas9
cjonas9 changed the base branch from feature/full-history to cache-getLatestLedger October 2, 2026 23:28
@cjonas9
cjonas9 added this pull request to stack #1061 October 2, 2026 23:29
@cjonas9
cjonas9 removed this pull request from stack #1061 October 5, 2026 22:57
@cjonas9 cjonas9 linked an issue Oct 5, 2026 that may be closed by this pull request
2 of 6 tasks
@cjonas9
cjonas9 changed the base branch from cache-getLatestLedger to feature/full-history October 5, 2026 22:58
@cjonas9
cjonas9 force-pushed the bypass-jrpc2-marshal branch from 4490bc6 to 731856f Compare October 5, 2026 23:12
@cjonas9
cjonas9 changed the base branch from feature/full-history to main October 5, 2026 23:47
@cjonas9
cjonas9 added this pull request to stack #1093 October 5, 2026 23:47
@cjonas9
cjonas9 removed this pull request from stack #1093 October 5, 2026 23:48
@cjonas9
cjonas9 changed the base branch from main to feature/full-history October 5, 2026 23:48
@cjonas9
cjonas9 added this pull request to stack #1094 October 5, 2026 23:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Raw response passthrough: stop jrpc2 from re-marshaling and re-parsing handler results

2 participants