mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-09-19 12:16:24 +08:00
97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
![]() |
package aliyundrive_open
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"sync"
|
||
|
|
||
|
"golang.org/x/time/rate"
|
||
|
)
|
||
|
|
||
|
// See document https://www.yuque.com/aliyundrive/zpfszx/mqocg38hlxzc5vcd
|
||
|
// See issue https://github.com/OpenListTeam/OpenList/issues/724
|
||
|
// We got limit per user per app, so the limiter should be global.
|
||
|
|
||
|
type limiterType int
|
||
|
|
||
|
const (
|
||
|
limiterList limiterType = iota
|
||
|
limiterLink
|
||
|
limiterOther
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
listRateLimit = 3.9 // 4 per second in document, but we use 3.9 per second to be safe
|
||
|
linkRateLimit = 0.9 // 1 per second in document, but we use 0.9 per second to be safe
|
||
|
otherRateLimit = 14.9 // 15 per second in document, but we use 14.9 per second to be safe
|
||
|
globalLimiterUserID = "" // Global limiter user ID, used to limit the initial requests.
|
||
|
)
|
||
|
|
||
|
type limiter struct {
|
||
|
usedBy int
|
||
|
list *rate.Limiter
|
||
|
link *rate.Limiter
|
||
|
other *rate.Limiter
|
||
|
}
|
||
|
|
||
|
var limiters = make(map[string]*limiter)
|
||
|
var limitersLock = &sync.Mutex{}
|
||
|
|
||
|
func getLimiterForUser(userid string) *limiter {
|
||
|
limitersLock.Lock()
|
||
|
defer limitersLock.Unlock()
|
||
|
defer func() {
|
||
|
// Clean up limiters that are no longer used.
|
||
|
for id, lim := range limiters {
|
||
|
if lim.usedBy <= 0 && id != globalLimiterUserID { // Do not delete the global limiter.
|
||
|
delete(limiters, id)
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
if lim, ok := limiters[userid]; ok {
|
||
|
lim.usedBy++
|
||
|
return lim
|
||
|
}
|
||
|
lim := &limiter{
|
||
|
usedBy: 1,
|
||
|
list: rate.NewLimiter(rate.Limit(listRateLimit), 1),
|
||
|
link: rate.NewLimiter(rate.Limit(linkRateLimit), 1),
|
||
|
other: rate.NewLimiter(rate.Limit(otherRateLimit), 1),
|
||
|
}
|
||
|
limiters[userid] = lim
|
||
|
return lim
|
||
|
}
|
||
|
|
||
|
func (l *limiter) wait(ctx context.Context, typ limiterType) error {
|
||
|
if l == nil {
|
||
|
return fmt.Errorf("driver not init")
|
||
|
}
|
||
|
switch typ {
|
||
|
case limiterList:
|
||
|
return l.list.Wait(ctx)
|
||
|
case limiterLink:
|
||
|
return l.link.Wait(ctx)
|
||
|
case limiterOther:
|
||
|
return l.other.Wait(ctx)
|
||
|
default:
|
||
|
return fmt.Errorf("unknown limiter type")
|
||
|
}
|
||
|
}
|
||
|
func (l *limiter) free() {
|
||
|
if l == nil {
|
||
|
return
|
||
|
}
|
||
|
limitersLock.Lock()
|
||
|
defer limitersLock.Unlock()
|
||
|
l.usedBy--
|
||
|
}
|
||
|
func (d *AliyundriveOpen) wait(ctx context.Context, typ limiterType) error {
|
||
|
if d == nil {
|
||
|
return fmt.Errorf("driver not init")
|
||
|
}
|
||
|
if d.ref != nil {
|
||
|
return d.ref.wait(ctx, typ) // If this is a reference driver, wait on the reference driver.
|
||
|
}
|
||
|
return d.limiter.wait(ctx, typ)
|
||
|
}
|