Skip to content

Commit

Permalink
Changed WithRateLimiter costFunc-arg to include the methods of al…
Browse files Browse the repository at this point in the history
…l calls (#122)

* When rate limiting, include cost of batch request itself too.

This allows the cost func to provide a cost for the batch itself in
batch requests. Some providers (Infura) include this as part of their
rate limiting.

Ref: https://docs.infura.io/api/networks/ethereum/how-to/make-batch-requests

* In WithRateLimiter, change costFunc signature to receive all batch methods at once.

* simplified costFunc-example and fixed comment

* small cleanup

---------

Co-authored-by: lmittmann <lmittmann@users.noreply.github.com>
Signed-off-by: Mohammed Sohail <sohailsameja@gmail.com>
  • Loading branch information
2 people authored and kamikazechaser committed May 22, 2024
1 parent 759de14 commit 16bd326
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 6 deletions.
14 changes: 8 additions & 6 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Client struct {

// rate limiter
rl *rate.Limiter
rlCostFunc func(method string) (cost int)
rlCostFunc func(methods []string) (cost int)
}

// NewClient returns a new Client given an rpc.Client client.
Expand Down Expand Up @@ -148,10 +148,11 @@ func (c *Client) rateLimit(ctx context.Context, batchElems []rpc.BatchElem) erro
}

// limit requests based on Compute Units (CUs)
var cost int
for _, batchElem := range batchElems {
cost += c.rlCostFunc(batchElem.Method)
methods := make([]string, len(batchElems))
for i, batchElem := range batchElems {
methods[i] = batchElem.Method
}
cost := c.rlCostFunc(methods)
return c.rl.WaitN(ctx, cost)
}

Expand Down Expand Up @@ -192,8 +193,9 @@ type Option func(*Client)
// WithRateLimiter sets the rate limiter for the client. Set the optional argument
// costFunc to nil to limit the number of requests. Supply a costFunc to limit
// the the number of requests based on individual RPC calls for advanced rate
// limiting by Compute Units (CUs).
func WithRateLimiter(rl *rate.Limiter, costFunc func(method string) (cost int)) Option {
// limiting by e.g. Compute Units (CUs). Note that only if len(methods) > 1, the
// calls are sent in a batch request.
func WithRateLimiter(rl *rate.Limiter, costFunc func(methods []string) (cost int)) Option {
return func(c *Client) {
c.rl = rl
c.rlCostFunc = costFunc
Expand Down
16 changes: 16 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,19 @@ func ExampleWithRateLimiter() {
)
defer client.Close()
}

func ExampleWithRateLimiter_costFunc() {
// Limit the client to 30 calls per second and allow bursts of up to
// 100 calls using a cost function. Batch requests have an additional charge.
client := w3.MustDial("https://rpc.ankr.com/eth",
w3.WithRateLimiter(rate.NewLimiter(rate.Every(time.Second/30), 100),
func(methods []string) (cost int) {
cost = len(methods) // charge 1 CU per call
if len(methods) > 1 {
cost += 1 // charge 1 CU extra for the batch itself
}
return cost
},
))
defer client.Close()
}

0 comments on commit 16bd326

Please sign in to comment.