rpc: linear time batch response matching #23856 (#962)

This avoids quadratic time complexity in the lookup of the batch element
corresponding to an RPC response. Unfortunately, the new approach
requires additional memory for the mapping from ID to index.

Fixes #22805

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
Daniel Liu 2025-04-24 18:34:12 +08:00 committed by GitHub
parent de88f30b58
commit 3a618f0fd2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -17,7 +17,6 @@
package rpc
import (
"bytes"
"context"
"encoding/json"
"errors"
@ -381,7 +380,10 @@ func (c *Client) BatchCall(b []BatchElem) error {
//
// Note that batch calls may not be executed atomically on the server side.
func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
msgs := make([]*jsonrpcMessage, len(b))
var (
msgs = make([]*jsonrpcMessage, len(b))
byID = make(map[string]int, len(b))
)
op := &requestOp{
ids: make([]json.RawMessage, len(b)),
resp: make(chan *jsonrpcMessage, len(b)),
@ -393,6 +395,7 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
}
msgs[i] = msg
op.ids[i] = msg.ID
byID[string(msg.ID)] = i
}
var err error
@ -412,13 +415,7 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
// Find the element corresponding to this response.
// The element is guaranteed to be present because dispatch
// only sends valid IDs to our channel.
var elem *BatchElem
for i := range msgs {
if bytes.Equal(msgs[i].ID, resp.ID) {
elem = &b[i]
break
}
}
elem := &b[byID[string(resp.ID)]]
if resp.Error != nil {
elem.Error = resp.Error
continue