aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-02-24 14:48:50 +0530
committerBobby <[email protected]>2026-02-24 14:48:50 +0530
commit17b77153a862ad1eb3babe1e34e748363ac9916c (patch)
tree33871abe60da895112ebfffd5802f396005f7c79 /utils
parentefb4f68869a9e712c4016ef286d3f16b0bba3110 (diff)
downloadmetachan-17b77153a862ad1eb3babe1e34e748363ac9916c.tar.xz
metachan-17b77153a862ad1eb3babe1e34e748363ac9916c.zip
Refactor rate limiter: simplify implementation and improve token management
Diffstat (limited to 'utils')
-rw-r--r--utils/api/jikan/jikan.go4
-rw-r--r--utils/ratelimit/limiter.go96
-rw-r--r--utils/ratelimit/types.go10
3 files changed, 34 insertions, 76 deletions
diff --git a/utils/api/jikan/jikan.go b/utils/api/jikan/jikan.go
index 244253d..8812c5a 100644
--- a/utils/api/jikan/jikan.go
+++ b/utils/api/jikan/jikan.go
@@ -63,7 +63,7 @@ func (c *client) handleRetry(retries *int, url string, reason string, retryAfter
backoffDuration = retryAfter
}
- logger.Warnf("JikanClient", "%s for %s (attempt %d/%d)", reason, url, *retries, c.maxRetries)
+ logger.Debugf("JikanClient", "%s for %s (attempt %d/%d)", reason, url, *retries, c.maxRetries)
time.Sleep(backoffDuration)
return true
}
@@ -288,7 +288,7 @@ func GetAnimeProducers() (*types.JikanProducersResponse, error) {
response.Pagination = pageResponse.Pagination
}
- logger.Debugf("JikanClient", "Fetched page (%d/%d) - %d producers", page, response.Pagination.LastVisiblePage, len(pageResponse.Data))
+ logger.Infof("JikanClient", "Fetched page (%d/%d) - %d producers", page, response.Pagination.LastVisiblePage, len(pageResponse.Data))
response.Data = append(response.Data, pageResponse.Data...)
hasNextPage = pageResponse.Pagination.HasNextPage
diff --git a/utils/ratelimit/limiter.go b/utils/ratelimit/limiter.go
index 6450c8b..c99a935 100644
--- a/utils/ratelimit/limiter.go
+++ b/utils/ratelimit/limiter.go
@@ -5,80 +5,40 @@ import (
)
func NewRateLimiter(maxRequests int, window time.Duration) *RateLimiter {
- minDelay := window / time.Duration(maxRequests)
- return &RateLimiter{
- lastRequests: make([]time.Time, 0, maxRequests),
- maxRequests: maxRequests,
- window: window,
- minDelay: minDelay,
- }
+ interval := window / time.Duration(maxRequests)
+ rl := &RateLimiter{
+ tokens: make(chan struct{}, maxRequests),
+ done: make(chan struct{}),
+ }
+ rl.tokens <- struct{}{}
+ go func() {
+ ticker := time.NewTicker(interval)
+ defer ticker.Stop()
+ for {
+ select {
+ case <-ticker.C:
+ select {
+ case rl.tokens <- struct{}{}:
+ default:
+ }
+ case <-rl.done:
+ return
+ }
+ }
+ }()
+ return rl
}
func (r *RateLimiter) Wait() {
- r.mu.Lock()
- defer r.mu.Unlock()
-
- now := time.Now()
-
- if !r.lastRequest.IsZero() {
- elapsed := now.Sub(r.lastRequest)
- if elapsed < r.minDelay {
- waitTime := r.minDelay - elapsed
- r.mu.Unlock()
- time.Sleep(waitTime)
- r.mu.Lock()
- now = time.Now()
- }
- }
-
- cutoff := now.Add(-r.window)
- i := 0
- for i < len(r.lastRequests) && r.lastRequests[i].Before(cutoff) {
- i++
- }
- if i > 0 {
- r.lastRequests = r.lastRequests[i:]
- }
-
- if len(r.lastRequests) >= r.maxRequests {
- oldestInWindow := r.lastRequests[0]
- waitDuration := r.window - now.Sub(oldestInWindow)
-
- r.mu.Unlock()
- time.Sleep(waitDuration + time.Millisecond)
- r.mu.Lock()
-
- now = time.Now()
- cutoff = now.Add(-r.window)
- i = 0
- for i < len(r.lastRequests) && r.lastRequests[i].Before(cutoff) {
- i++
- }
- if i > 0 {
- r.lastRequests = r.lastRequests[i:]
- }
- }
+ <-r.tokens
+}
- r.lastRequest = now
- r.lastRequests = append(r.lastRequests, now)
+func (r *RateLimiter) Stop() {
+ close(r.done)
}
func (r *RateLimiter) RemainingRequests() int {
- r.mu.Lock()
- defer r.mu.Unlock()
-
- now := time.Now()
- cutoff := now.Add(-r.window)
-
- i := 0
- for i < len(r.lastRequests) && r.lastRequests[i].Before(cutoff) {
- i++
- }
- if i > 0 {
- r.lastRequests = r.lastRequests[i:]
- }
-
- return r.maxRequests - len(r.lastRequests)
+ return len(r.tokens)
}
func NewMultiLimiter(limiters ...*RateLimiter) *MultiLimiter {
@@ -88,6 +48,8 @@ func NewMultiLimiter(limiters ...*RateLimiter) *MultiLimiter {
}
func (m *MultiLimiter) Wait() {
+ m.mu.Lock()
+ defer m.mu.Unlock()
for _, limiter := range m.limiters {
limiter.Wait()
}
diff --git a/utils/ratelimit/types.go b/utils/ratelimit/types.go
index f962f22..17b7900 100644
--- a/utils/ratelimit/types.go
+++ b/utils/ratelimit/types.go
@@ -2,18 +2,14 @@ package ratelimit
import (
"sync"
- "time"
)
type RateLimiter struct {
- mu sync.Mutex
- lastRequest time.Time
- lastRequests []time.Time
- maxRequests int
- window time.Duration
- minDelay time.Duration
+ tokens chan struct{}
+ done chan struct{}
}
type MultiLimiter struct {
+ mu sync.Mutex
limiters []*RateLimiter
}