fix: finalize growth cost controls
This commit is contained in:
@@ -18,19 +18,18 @@ type CoordinatorConfig struct {
|
||||
MaxConcurrent int
|
||||
}
|
||||
type Coordinator struct {
|
||||
redis *redis.Client
|
||||
config CoordinatorConfig
|
||||
mu sync.Mutex
|
||||
counters map[string]int64
|
||||
expiries map[string]time.Time
|
||||
semaphore chan struct{}
|
||||
redis *redis.Client
|
||||
config CoordinatorConfig
|
||||
mu sync.Mutex
|
||||
counters map[string]int64
|
||||
expiries map[string]time.Time
|
||||
}
|
||||
|
||||
func NewCoordinator(client *redis.Client, config CoordinatorConfig) *Coordinator {
|
||||
if config.MaxConcurrent < 1 {
|
||||
config.MaxConcurrent = 4
|
||||
}
|
||||
return &Coordinator{redis: client, config: config, counters: map[string]int64{}, expiries: map[string]time.Time{}, semaphore: make(chan struct{}, config.MaxConcurrent)}
|
||||
return &Coordinator{redis: client, config: config, counters: map[string]int64{}, expiries: map[string]time.Time{}}
|
||||
}
|
||||
func (c *Coordinator) AllowCreate(ctx context.Context, userID int64, now time.Time) error {
|
||||
return c.increment(ctx, fmt.Sprintf("growth:create:%d:%s", userID, now.UTC().Format("200601021504")), c.config.CreatePerMinute, time.Minute, now)
|
||||
@@ -66,19 +65,30 @@ func (c *Coordinator) incrementRedis(ctx context.Context, key string, amount, li
|
||||
return nil
|
||||
}
|
||||
func (c *Coordinator) Enter(ctx context.Context) (func(), error) {
|
||||
select {
|
||||
case c.semaphore <- struct{}{}:
|
||||
return func() { <-c.semaphore }, nil
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
if c.redis == nil {
|
||||
return nil, ErrBudgetExhausted
|
||||
}
|
||||
acquire := redis.NewScript(`local v=redis.call('INCR',KEYS[1]);if v==1 then redis.call('PEXPIRE',KEYS[1],ARGV[2]) end;if v>tonumber(ARGV[1]) then redis.call('DECR',KEYS[1]);return 0 end;return 1`)
|
||||
for {
|
||||
ok, err := acquire.Run(ctx, c.redis, []string{"growth:ai:concurrency"}, c.config.MaxConcurrent, (3 * time.Minute).Milliseconds()).Int()
|
||||
if err != nil {
|
||||
return nil, ErrBudgetExhausted
|
||||
}
|
||||
if ok == 1 {
|
||||
return func() {
|
||||
releaseCtx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
script := redis.NewScript(`local v=tonumber(redis.call('GET',KEYS[1]) or '0');if v>0 then return redis.call('DECR',KEYS[1]) end return 0`)
|
||||
_ = script.Run(releaseCtx, c.redis, []string{"growth:ai:concurrency"}).Err()
|
||||
}, nil
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
func (c *Coordinator) Publish(ctx context.Context, reportID string) {
|
||||
if c.redis != nil {
|
||||
_ = c.redis.LPush(ctx, "growth:jobs", reportID).Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Coordinator) increment(ctx context.Context, key string, limit int64, ttl time.Duration, now time.Time) error {
|
||||
return c.incrementBy(ctx, key, 1, limit, ttl, now)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user