logos-delivery/waku/common/rate_limit/single_token_limiter.nim
NagyZoltanPeter 284a0816cc
chore: use chronos' TokenBucket (#3670)
* Adapt using chronos' TokenBucket. Removed TokenBucket and test. bump nim-chronos -> nim-libp2p/nim-lsquic/nim-jwt -> adapt to latest libp2p changes
* Fix libp2p/utility reports unlisted exception can occure from close of socket in waitForService - -d:ssl compile flag caused it
* Adapt request_limiter to new chronos' TokenBucket replenish algorithm to keep original intent of use
* Fix filter dos protection test
* Fix peer manager tests due change caused by new libp2p
* Adjust store test rate limit to eliminate CI test flakyness of timing
* Adjust store test rate limit to eliminate CI test flakyness of timing - lightpush/legacy_lightpush/filter
* Rework filter dos protection test to avoid CI crazy timing causing flakyness in test results compared to local runs
* Rework lightpush dos protection test to avoid CI crazy timing causing flakyness in test results compared to local runs
* Rework lightpush and legacy lightpush rate limit tests to eliminate timing effect in CI that cause longer awaits thus result in minting new tokens unlike local runs
2026-01-07 17:48:19 +01:00

70 lines
1.8 KiB
Nim

## This module add usage check helpers for simple rate limiting with the use of TokenBucket.
{.push raises: [].}
import std/[options], chronos/timer, libp2p/stream/connection, libp2p/utility
import std/times except TimeInterval, Duration
import chronos/ratelimit as token_bucket
import ./[setting, service_metrics]
export token_bucket, setting, service_metrics
proc newTokenBucket*(
setting: Option[RateLimitSetting],
replenishMode: static[ReplenishMode] = ReplenishMode.Continuous,
startTime: Moment = Moment.now(),
): Option[TokenBucket] =
if setting.isNone():
return none[TokenBucket]()
if setting.get().isUnlimited():
return none[TokenBucket]()
return some(
TokenBucket.new(
capacity = setting.get().volume,
fillDuration = setting.get().period,
startTime = startTime,
mode = replenishMode,
)
)
proc checkUsage(
t: var TokenBucket, proto: string, now = Moment.now()
): bool {.raises: [].} =
if not t.tryConsume(1, now):
return false
return true
proc checkUsage(
t: var Option[TokenBucket], proto: string, now = Moment.now()
): bool {.raises: [].} =
if t.isNone():
return true
var tokenBucket = t.get()
return checkUsage(tokenBucket, proto, now)
template checkUsageLimit*(
t: var Option[TokenBucket] | var TokenBucket,
proto: string,
conn: Connection,
bodyWithinLimit, bodyRejected: untyped,
) =
if t.checkUsage(proto):
let requestStartTime = Moment.now()
waku_service_requests.inc(labelValues = [proto, "served"])
bodyWithinLimit
let requestDuration = Moment.now() - requestStartTime
waku_service_request_handling_duration_seconds.observe(
requestDuration.milliseconds.float / 1000, labelValues = [proto]
)
else:
waku_service_requests.inc(labelValues = [proto, "rejected"])
bodyRejected