Skip to content

Commit

Permalink
Added rate limiting by Compute Units (#54)
Browse files Browse the repository at this point in the history
Co-authored-by: lmittmann <[email protected]>
  • Loading branch information
lmittmann and lmittmann authored Jun 29, 2023
1 parent 2198e3b commit 55214b0
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 28 deletions.
43 changes: 32 additions & 11 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ type Client struct {
client *rpc.Client

// rate limiter
rl *rate.Limiter
rlPerCall bool
rl *rate.Limiter
rlCostFunc func(method string) (cost int)
}

// NewClient returns a new Client given an rpc.Client client.
Expand Down Expand Up @@ -72,11 +72,6 @@ func (c *Client) CallCtx(ctx context.Context, calls ...w3types.Caller) error {
return nil
}

// invoke rate limiter
if err := c.rateLimit(ctx, len(calls)); err != nil {
return err
}

// create requests
batchElems := make([]rpc.BatchElem, len(calls))
var err error
Expand All @@ -87,6 +82,11 @@ func (c *Client) CallCtx(ctx context.Context, calls ...w3types.Caller) error {
}
}

// invoke rate limiter
if err := c.rateLimit(ctx, batchElems); err != nil {
return err
}

// do requests
if len(batchElems) > 1 {
// batch requests if >1 request
Expand Down Expand Up @@ -130,15 +130,22 @@ func (c *Client) Call(calls ...w3types.Caller) error {
return c.CallCtx(context.Background(), calls...)
}

func (c *Client) rateLimit(ctx context.Context, n int) error {
func (c *Client) rateLimit(ctx context.Context, batchElems []rpc.BatchElem) error {
if c.rl == nil {
return nil
}

if c.rlPerCall {
return c.rl.WaitN(ctx, n)
if c.rlCostFunc == nil {
// limit requests
return c.rl.Wait(ctx)
}

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

// CallErrors is an error type that contains the errors of multiple calls. The
Expand Down Expand Up @@ -171,3 +178,17 @@ func (e CallErrors) Is(target error) bool {
_, ok := target.(CallErrors)
return ok
}

// An Option configures a Client.
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 {
return func(c *Client) {
c.rl = rl
c.rlCostFunc = costFunc
}
}
16 changes: 0 additions & 16 deletions client_options.go

This file was deleted.

2 changes: 1 addition & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func ExampleWithRateLimiter() {
// Limit the client to 30 requests per second and allow bursts of up to
// 100 requests.
client := w3.MustDial("https://rpc.ankr.com/eth",
w3.WithRateLimiter(rate.NewLimiter(rate.Every(time.Second/30), 100), false),
w3.WithRateLimiter(rate.NewLimiter(rate.Every(time.Second/30), 100), nil),
)
defer client.Close()
}

0 comments on commit 55214b0

Please sign in to comment.