Skip to content
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

Added rate limiting by Compute Units (CUs) #54

Merged
merged 1 commit into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
}