⏳ RateLimit
Globalny rate limiter dla akcji w aplikacji
Opis
Moduł RateLimit kontroluje częstotliwość wykonywania akcji w aplikacji. Obsługuje różne strategie limitowania (sliding window, token bucket) i integruje się z Timer.cooldown oraz SecureStorage dla persystencji.
Podstawowe użycie
// Sprawdź i wykonaj
if (ADict.RateLimit.tryAcquire("api_call")) {
makeApiCall()
} else {
showRateLimitedMessage()
}
// Z automatyczną akcją
ADict.RateLimit.tryAcquire("share_button", cooldownMs = 5000L) {
shareContent()
}
// Z fallbackiem
val result = ADict.RateLimit.withLimit("search", fallback = emptyList()) {
api.search(query)
}
Konfiguracja limitów
// Sliding window - 60 req/min
ADict.RateLimit.configure("api_call") {
maxRequests = 60
windowMinutes(1)
strategy = Strategy.SLIDING_WINDOW
}
// Token bucket - burst + sustained rate
ADict.RateLimit.configure("ad_request") {
maxRequests = 10
windowMs = 60_000L
strategy = Strategy.TOKEN_BUCKET
burstSize = 3
refillRatePerSecond = 0.5
}
// Z blokowaniem po przekroczeniu
ADict.RateLimit.configure("login_attempt") {
maxRequests = 5
windowMinutes(15)
blockDurationMs = 30_000L // Zablokuj na 30s
}
Strategie
| Strategia | Opis | Użycie |
|---|---|---|
FIXED_WINDOW |
Stałe okno czasowe | Proste limity, np. 100/godz |
SLIDING_WINDOW |
Przesuwne okno (domyślna) | Płynne limity bez spike'ów |
TOKEN_BUCKET |
Burst + sustained rate | API z burstami, reklamy |
LEAKY_BUCKET |
Stały output rate | Równomierny przepływ |
Presety
// Użyj gotowych presetów
ADict.RateLimit.applyPreset(RateLimit.Presets.API_STANDARD) // 60/min
ADict.RateLimit.applyPreset(RateLimit.Presets.API_STRICT) // 10/min + block
ADict.RateLimit.applyPreset(RateLimit.Presets.BUTTON_ANTISPAM) // 5/10s
ADict.RateLimit.applyPreset(RateLimit.Presets.AD_REQUEST) // Token bucket
ADict.RateLimit.applyPreset(RateLimit.Presets.SHARE) // 10/5min
Statystyki
val stats = ADict.RateLimit.getStats("api_call")
println("Użyto: ${stats?.currentCount}/${stats?.maxRequests}")
println("Pozostało: ${stats?.remainingRequests}")
println("Reset za: ${stats?.resetInMs}ms")
println("Zablokowano: ${stats?.blockedCount} razy")
// Quick checks
val remaining = ADict.RateLimit.getRemaining("api_call")
val isLimited = ADict.RateLimit.isLimited("api_call")
val resetTime = ADict.RateLimit.getResetTime("api_call")
Callbacks
ADict.RateLimit.onLimitReached { id, stats ->
analytics.log("rate_limited", mapOf(
"action" to id,
"blocked_count" to stats.blockedCount
))
if (id == "api_call") {
showApiLimitWarning()
}
}
Remote Config
// Dynamiczne limity z Remote Config
// Format w RC: "maxRequests,windowMs,strategy"
// np. "100,60000,SLIDING_WINDOW"
ADict.RateLimit.configureFromRemote(
id = "api_call",
remoteKey = "rate_limit_api",
defaultConfig = RateLimit.Presets.API_STANDARD
)
Reset
// Reset pojedynczego limitu
ADict.RateLimit.reset("api_call")
// Reset wszystkich
ADict.RateLimit.resetAll()
Debounce
Executes action only after delay with no new calls. Each new call resets the timer.
// In search field - wait for user to stop typing
searchField.addTextChangedListener { text ->
ADict.RateLimit.debounce("search", 300L) {
performSearch(text.toString())
}
}
// Cancel pending debounce
ADict.RateLimit.cancelDebounce("search")
Throttle
Executes action at most once per interval. First call executes immediately.
// Button throttle - max once per second
button.setOnClickListener {
ADict.RateLimit.throttle("save_button", 1000L) {
saveData()
}
}
// Throttle with trailing edge - executes last call after interval
scrollView.setOnScrollChangeListener { _, _, _, _, _ ->
ADict.RateLimit.throttleTrailing("scroll_analytics", 500L) {
logScrollPosition()
}
}
// Reset throttle (allow immediate execution)
ADict.RateLimit.resetThrottle("save_button")
View Extensions
import rip.nerd.adictlibrary.modules.setOnClickDebounced
import rip.nerd.adictlibrary.modules.setOnClickThrottled
import rip.nerd.adictlibrary.modules.setOnClickRateLimited
// Debounced click - prevents double clicks
button.setOnClickDebounced(500L) {
navigateToNextScreen()
}
// Throttled click - max once per interval
likeButton.setOnClickThrottled(1000L) {
toggleLike()
}
// Rate limited click with callback
shareButton.setOnClickRateLimited(
limitId = "share_action",
maxClicks = 3,
windowMs = 10_000L,
onLimitReached = { showTooManySharesMessage() }
) {
shareContent()
}