rpcclient: make shutdown interrupt in-flight POSTs#2451
rpcclient: make shutdown interrupt in-flight POSTs#2451wydengyre wants to merge 4 commits intobtcsuite:masterfrom
Conversation
Pull Request Test Coverage Report for Build 21215011456Details
💛 - Coveralls |
rpcclient/infrastructure.go
Outdated
| httpReq, err = http.NewRequestWithContext(ctx, "POST", httpURL, bodyReader) | ||
| if err != nil { | ||
| // We must observe the contract that shutdown returns ErrClientShutdown. | ||
| if errors.Is(err, context.Canceled) && errors.Is(context.Cause(ctx), ErrClientShutdown) { |
There was a problem hiding this comment.
This check is unneeded since http.NewRequestWithContext will never return a error that's of type context.Canceled.
|
Checked: 1: The added One nitpicky gripe is maybe the test could be commented better. Looks good overall except for that one unneeded check. |
Drop unreachable context-canceled mapping after request creation. Clarify the HTTP POST shutdown test flow with brief comments.
@kcalvinalvin thanks for your review. I've addressed your comments in f87d914 Can you re-review? |
|
Hey @wydengyre, nice work on this — we hit the same pain point in production. We run a HAProxy health checker that polls BTC nodes via We searched the issue tracker and PRs for existing context support work — found #2450 (dial timeout, merged), this PR (shutdown interrupt), and #1323 (connection reuse), but nothing addressing per-request Your PR fixes shutdown propagation, which is great. The transport plumbing is almost there with func (c *Client) GetBlockCountWithContext(ctx context.Context) (int64, error)Minimal changes: Happy to open an issue + PR for this if there's interest. We worked around it with a singleflight pattern at our layer, but the proper fix belongs here. |
@seeforschauer I had the same problem as you, and actually have a private library that fixes it in a backward-compatible way. But I'm not affiliated with this project in any official way, so sadly the decision to support such a feature is outside my influence. I would certainly vote in favor of it, though, as it's both pragmatic/necessary and more "Go-like". |
|
Thanks for the support, and good to know you've solved it independently — validates the approach. I went ahead and opened #2499 to formalize the request for per-request In the meantime, pinging @kcalvinalvin — any thoughts on merging this PR as-is and tracking context support separately in #2499? |
starius
left a comment
There was a problem hiding this comment.
The code looks good! I found a couple of remaining cases of returning non-remapped errors and also gaps in test coverage.
Can you consolidate errors remapping in one place, so there are no leaks of non-remapped error from handleSendPostMessage (in case we add more return operators in the middle of it later).
Could you also rebase the PR so it is easier to review, please?
| httpResponse, err = c.httpClient.Do(httpReq) | ||
|
|
||
| // Quit the retry loop on success or if we can't retry anymore. | ||
| if err == nil || i == tries-1 { |
There was a problem hiding this comment.
If errors.Is(err, context.Canceled) && errors.Is(context.Cause(ctx), ErrClientShutdown) && i == tries-1, the added error remapping logic is skipped. The code returns context.Canceled in this case, but it is expected to return ErrClientShutdown instead.
| } | ||
|
|
||
| // Read the raw bytes and close the response. | ||
| respBytes, err := io.ReadAll(httpResponse.Body) |
There was a problem hiding this comment.
If ctx is cancelled in the middle of io.ReadAll running, we return fmt.Errorf("error reading json reply: ..., but I think we should also return ErrClientShutdown in this case, right?
| result, err := future.Receive() | ||
| require.Zero(t, result) | ||
| require.ErrorContains(t, err, ErrClientShutdown.Error()) | ||
| } |
There was a problem hiding this comment.
I propose to add more test coverage of the edge cases:
- Cancellation while waiting in retry backoff path (
ctx.Done()select branch). - Cancellation on final retry attempt (
i == tries-1). - Cancellation during response body read after a successful
Do.
|
I'm going to copy the commits from this PR on top of #2500 and resolve the remaining comments. @wydengyre If you already addressed some of them, please push, so I have the latest version of your commits. |
I have not, sorry. Please go ahead. |
|
This PR was integrated into #2500 |
Change Description
Modify the rpcclient http POST call to ensure that a shutdown immediately interrupts in-flight requests, which otherwise would have to wait until timeout.
This PR includes the change in #2450 as it is necessary to ensure interruption during Dial.
Steps to Test
The test suite has an added test to ensure this works.
Pull Request Checklist
Testing
Code Style and Documentation
📝 Please see our Contribution Guidelines for further guidance.