Skip to content

rpcclient: add context.Context support to RPC methods #2499

@seeforschauer

Description

@seeforschauer

Problem

rpcclient methods (GetBlockCount, GetBlockHash, SendRawTransaction, etc.) don't accept context.Context. Callers have no way to cancel or timeout individual RPC calls. When a node is slow or unresponsive, GetBlockCount() blocks for up to 600s (10 retries x 60s HTTP timeout) with no way to interrupt it.

The root cause is in handleSendPostMessage — HTTP requests are created via http.NewRequest (without context), and the retry loop has no ctx.Done() check:

// infrastructure.go — retry loop, no cancellation
for i := 0; tries == 0 || i < tries; i++ {
    resp, err = httpClient.Do(req)  // 60s timeout each
    <-time.After(backoff)           // no select on ctx.Done()
}

I searched existing issues and PRs — #2450 and #2451 improve shutdown and dial timeout handling but don't address per-request context cancellation. No other open issues cover this.

Use case

We run a HAProxy health checker that polls BTC nodes via GetBlockCount(). HAProxy sends a TCP probe every N seconds; each probe spawns a goroutine calling GetBlockCount(). When a node hangs:

  • Each probe creates a new goroutine blocked in GetBlockCount for ~600s
  • New probes arrive faster than old ones complete
  • Goroutines accumulate — we observed 117 leaked goroutines on staging

We worked around this with a singleflight pattern + context timeout at our layer, but the proper fix belongs in the library.

Proposed approach

Non-breaking: add *WithContext variants alongside existing methods (same pattern as database/sql).

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
count, err := client.GetBlockCountWithContext(ctx)

Minimal changes to infrastructure.go:

  1. jsonRequest — add ctx context.Context field
  2. handleSendPostMessagehttp.NewRequestWithContext(jReq.ctx, ...) + ctx.Done() in retry loop
  3. Client.SendCmdCtx(ctx, cmd) — new method, passes context through
  4. GetBlockCountWithContext(ctx) — first method using the plumbing

Builds on #2450 and #2451. Remaining methods can follow in incremental PRs. ~80-100 lines total.

Happy to submit a PR if maintainers agree with the approach.

Related

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions