-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: rate limit with rln configuration (#1262)
- Loading branch information
1 parent
78b522d
commit 6dcf177
Showing
6 changed files
with
117 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package publish | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"sync" | ||
"time" | ||
|
||
"go.uber.org/zap" | ||
) | ||
|
||
var ErrRateLimited = errors.New("rate limit exceeded") | ||
|
||
const RlnLimiterCapacity = 100 | ||
const RlnLimiterRefillInterval = 10 * time.Minute | ||
|
||
// RlnRateLimiter is used to rate limit the outgoing messages, | ||
// The capacity and refillInterval comes from RLN contract configuration. | ||
type RlnRateLimiter struct { | ||
mu sync.Mutex | ||
capacity int | ||
tokens int | ||
refillInterval time.Duration | ||
lastRefill time.Time | ||
} | ||
|
||
// NewRlnPublishRateLimiter creates a new rate limiter, starts with a full capacity bucket. | ||
func NewRlnRateLimiter(capacity int, refillInterval time.Duration) *RlnRateLimiter { | ||
return &RlnRateLimiter{ | ||
capacity: capacity, | ||
tokens: capacity, // Start with a full bucket | ||
refillInterval: refillInterval, | ||
lastRefill: time.Now(), | ||
} | ||
} | ||
|
||
// Allow checks if a token can be consumed, and refills the bucket if necessary | ||
func (rl *RlnRateLimiter) Allow() bool { | ||
rl.mu.Lock() | ||
defer rl.mu.Unlock() | ||
|
||
// Refill tokens if the refill interval has passed | ||
now := time.Now() | ||
if now.Sub(rl.lastRefill) >= rl.refillInterval { | ||
rl.tokens = rl.capacity // Refill the bucket | ||
rl.lastRefill = now | ||
} | ||
|
||
// Check if there are tokens available | ||
if rl.tokens > 0 { | ||
rl.tokens-- | ||
return true | ||
} | ||
|
||
return false | ||
} | ||
|
||
func (rl *RlnRateLimiter) Check(ctx context.Context, logger *zap.Logger) error { | ||
if rl.Allow() { | ||
return nil | ||
} | ||
return ErrRateLimited | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package publish | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/require" | ||
"github.com/waku-org/go-waku/waku/v2/utils" | ||
) | ||
|
||
func TestRlnRateLimit(t *testing.T) { | ||
r := NewRlnRateLimiter(3, 5*time.Second) | ||
l := utils.Logger() | ||
|
||
for i := 0; i < 3; i++ { | ||
require.NoError(t, r.Check(context.Background(), l)) | ||
} | ||
require.ErrorIs(t, r.Check(context.Background(), l), ErrRateLimited) | ||
|
||
time.Sleep(6 * time.Second) | ||
for i := 0; i < 3; i++ { | ||
require.NoError(t, r.Check(context.Background(), l)) | ||
} | ||
require.ErrorIs(t, r.Check(context.Background(), l), ErrRateLimited) | ||
} |