-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rpcclient: handle RPC requests based on their priority #2027
Conversation
bbaf161
to
562c8b3
Compare
Resulted from running `go get "golang.org/x/sync/errgroup"` and `go mod tify`.
562c8b3
to
4a80477
Compare
Pull Request Test Coverage Report for Build 6038665903
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea and optimiziations! Though I think some assumptions around reading from channels have changed and might introduce new blocking behavior.
@@ -892,7 +929,7 @@ out: | |||
// Send any messages ready for send until the shutdown channel | |||
// is closed. | |||
select { | |||
case jReq := <-c.highPriorityPostQueue: | |||
case jReq := <-c.nextPostRequest(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now no longer considers the c.shutdown
channel. Because we'll block on the method call c.nextPostRequest()
and then only actually select
once that returns. So I think we need to consider c.shutdown
within nextPostRequest()
and then we can simply return the next request (instead of a channel) or a boolean that indicates we're shutting down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, fixed!
rpcclient/infrastructure.go
Outdated
@@ -905,7 +942,7 @@ out: | |||
cleanup: | |||
for { | |||
select { | |||
case jReq := <-c.highPriorityPostQueue: | |||
case jReq := <-c.nextPostRequest(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, this breaks assumptions and will block forever on shutdown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here is a bit different as we wanna drain the channel, so changed to read the channels directly.
// TODO(yy): Ideally we should pass the `-rpcthreads` used in | ||
// `bitcoind` here to better increase the performance. | ||
const numThreads = 4 | ||
eg.SetLimit(numThreads) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we ever need to wait for this error group? Or we're basically using it as a simple "thread pool"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah it's more like limiting concurrent calls, but think we should also wait in the end so now I added it.
rpcclient/infrastructure.go
Outdated
eg.SetLimit(numThreads) | ||
|
||
// lowReqCounter counts the number of low priority requests. | ||
lowReqCounter := 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use an explicit uint64
type here to avoid rolling over into negative? Wouldn't really matter too much as we're only using the modulus, but would give us some more explicit behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
rpcclient/infrastructure.go
Outdated
// requests to bitcoind. This is only used by low priority messages. | ||
// For every 4 messages, we would sleep this duration before making | ||
// more post requests. | ||
rpcRequestInterval = time.Millisecond * 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we maybe make this configurable when instantiating the client?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added a new commit to achieve this
rpcclient/infrastructure.go
Outdated
// associated server and returns a response channel on which the reply will be | ||
// delivered at some point in the future. It handles both websocket and HTTP | ||
// POST mode depending on the configuration of the client. | ||
func (c *Client) SendCmdLazy(cmd interface{}) chan *Response { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Low prio: To me, the word "lazy" in programming indicates "don't do it immediately, only when we actually need the result". Which kind of fits, but also not fully. Wasn't able to come up with a better suffix though, so I asked ChatGPT. Not sure if any of those fit better:
SendCmdLight - Using "Light" suggests a less intensive or lower priority operation.
SendCmdLazy - "Lazy" often implies that the action isn't urgently prioritized.
SendCmdIdle - "Idle" might indicate that the task is of low urgency.
SendCmdSlow - Directly indicates that it's a less priority-sensitive function.
SendCmdBack - As in "backburner", where it's not the immediate focus.
SendCmdMinor - Signifies lesser importance without being too long.
SendCmdRelax - Suggests that the function can "relax" and doesn't need to hurry.
SendCmdSoft - A softer version, possibly less critical.
SendCmdCasual - Implying the task is non-urgent.
SendCmdChill - A colloquial way to denote low urgency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe SendCmdWithLowPriority
? kinda long...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's a bit long. My favorite from the list is actually SendCmdSlow
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
switched to slow
This commit adds a new message queue `lowPriorityPostQueue` and a new flag on `jsonRequest` so we can split the requests based on priorities.
This commit changes how the messages are handled based on their priorities. For low priority messages, they are processed sequentially and the processing would sleep for 10ms for every four messages. For high priority messages, they will be handled in goroutines so they will be sent to `bitcoind` faster.
4a80477
to
b8d0a62
Compare
This commit adds a new method `GetRawTransactionLazy` to handle low priority requests. To support it, the underlying `SendCmd` is refactored to make use of the priority flag.
b8d0a62
to
e1d7a6c
Compare
What about taking a more generic approach here? The request type is the paramter, and we can have a generic method that'll handle the priotization. I also think we should step back a bit before proceeding to make sure we're actually fixing something here:
In short, adding another channel to consume requests off of, doesn't help to actually address the issue of head of line blocking. If we're sending 10 The btcd/rpcclient/infrastructure.go Lines 986 to 996 in ec401d0
|
The initial idea was to implement a special case that can be used by our mempool poller as it consumes a lot of RPC resources. The original observation was, a lot of However, after digging into
Thus this PR is no longer the right fix. If we are looking for async RPC calls, we should instead use |
This PR introduces a new flag
lowPriority
on RPC requests to differentiate their priorities so more urgent RPC requests can be processed earlier. This is needed asbitcoind
will block when too many RPCs requests are made at the same time, yet our recent addition of mempool notifier puts a lot of pressure on it because it's callinggetrawtransaction
for every new transaction, causing other RPC requests to halt. To solve it, we introduce a new methodGetRawTransactionLazy
, which will make the RPC calls more friendly and leave room for other more urgent RPC calls to go through.