-
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
- Loading branch information
1 parent
6550ff3
commit 37affd2
Showing
5 changed files
with
107 additions
and
12 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
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 refillAfter comes from RLN contract configuration. | ||
type RlnRateLimiter struct { | ||
mu sync.Mutex | ||
capacity int | ||
tokens int | ||
refillAfter time.Duration | ||
lastRefill time.Time | ||
} | ||
|
||
// NewRlnPublishRateLimiter creates a new rate limiter, starts with a full capacity bucket. | ||
func NewRlnRateLimiter(capacity int, refillAfter time.Duration) *RlnRateLimiter { | ||
return &RlnRateLimiter{ | ||
capacity: capacity, | ||
tokens: capacity, // Start with a full bucket | ||
refillAfter: refillAfter, | ||
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.refillAfter { | ||
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 | ||
} | ||
logger.Error("could not send message rate limited", zap.Error(ErrRateLimited)) | ||
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) | ||
} |