mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-26 09:24:48 +08:00
Merge origin/main into fix/account-extra-scheduler-pressure-20260311
This commit is contained in:
@@ -626,29 +626,6 @@ func (r *accountRepository) syncSchedulerAccountSnapshot(ctx context.Context, ac
|
||||
}
|
||||
}
|
||||
|
||||
func (r *accountRepository) patchSchedulerAccountExtra(ctx context.Context, accountID int64, updates map[string]any) {
|
||||
if r == nil || r.schedulerCache == nil || accountID <= 0 || len(updates) == 0 {
|
||||
return
|
||||
}
|
||||
account, err := r.schedulerCache.GetAccount(ctx, accountID)
|
||||
if err != nil {
|
||||
logger.LegacyPrintf("repository.account", "[Scheduler] patch account extra read failed: id=%d err=%v", accountID, err)
|
||||
return
|
||||
}
|
||||
if account == nil {
|
||||
return
|
||||
}
|
||||
if account.Extra == nil {
|
||||
account.Extra = make(map[string]any, len(updates))
|
||||
}
|
||||
for key, value := range updates {
|
||||
account.Extra[key] = value
|
||||
}
|
||||
if err := r.schedulerCache.SetAccount(ctx, account); err != nil {
|
||||
logger.LegacyPrintf("repository.account", "[Scheduler] patch account extra write failed: id=%d err=%v", accountID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *accountRepository) syncSchedulerAccountSnapshots(ctx context.Context, accountIDs []int64) {
|
||||
if r == nil || r.schedulerCache == nil || len(accountIDs) == 0 {
|
||||
return
|
||||
@@ -1221,15 +1198,15 @@ func (r *accountRepository) UpdateExtra(ctx context.Context, id int64, updates m
|
||||
if affected == 0 {
|
||||
return service.ErrAccountNotFound
|
||||
}
|
||||
|
||||
if shouldEnqueueSchedulerOutboxForExtraUpdates(updates) {
|
||||
if err := enqueueSchedulerOutbox(ctx, r.sql, service.SchedulerOutboxEventAccountChanged, &id, nil, nil); err != nil {
|
||||
logger.LegacyPrintf("repository.account", "[SchedulerOutbox] enqueue extra update failed: account=%d err=%v", id, err)
|
||||
}
|
||||
} else {
|
||||
// 观测型 extra 字段不需要触发 bucket 重建,但尽量把单账号缓存补到最新,
|
||||
// 让 sticky session / GetAccount 命中缓存时也能读到最新快照。
|
||||
r.patchSchedulerAccountExtra(ctx, id, updates)
|
||||
// 观测型 extra 字段不需要触发 bucket 重建,但仍同步单账号快照,
|
||||
// 让 sticky session / GetAccount 命中缓存时也能读到最新数据,
|
||||
// 同时避免缓存局部 patch 覆盖掉并发写入的其它账号字段。
|
||||
r.syncSchedulerAccountSnapshot(ctx, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1239,9 +1216,10 @@ func shouldEnqueueSchedulerOutboxForExtraUpdates(updates map[string]any) bool {
|
||||
return false
|
||||
}
|
||||
for key := range updates {
|
||||
if !isSchedulerNeutralExtraKey(key) {
|
||||
return true
|
||||
if isSchedulerNeutralExtraKey(key) {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1262,6 +1240,82 @@ func isSchedulerNeutralExtraKey(key string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func shouldSyncSchedulerSnapshotForExtraUpdates(updates map[string]any) bool {
|
||||
return codexExtraIndicatesRateLimit(updates, "7d") || codexExtraIndicatesRateLimit(updates, "5h")
|
||||
}
|
||||
|
||||
func codexExtraIndicatesRateLimit(updates map[string]any, window string) bool {
|
||||
if len(updates) == 0 {
|
||||
return false
|
||||
}
|
||||
usedValue, ok := updates["codex_"+window+"_used_percent"]
|
||||
if !ok || !extraValueIndicatesExhausted(usedValue) {
|
||||
return false
|
||||
}
|
||||
return extraValueHasResetMarker(updates["codex_"+window+"_reset_at"]) ||
|
||||
extraValueHasPositiveNumber(updates["codex_"+window+"_reset_after_seconds"])
|
||||
}
|
||||
|
||||
func extraValueIndicatesExhausted(value any) bool {
|
||||
number, ok := extraValueToFloat64(value)
|
||||
return ok && number >= 100-1e-9
|
||||
}
|
||||
|
||||
func extraValueHasPositiveNumber(value any) bool {
|
||||
number, ok := extraValueToFloat64(value)
|
||||
return ok && number > 0
|
||||
}
|
||||
|
||||
func extraValueHasResetMarker(value any) bool {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(v) != ""
|
||||
case time.Time:
|
||||
return !v.IsZero()
|
||||
case *time.Time:
|
||||
return v != nil && !v.IsZero()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func extraValueToFloat64(value any) (float64, bool) {
|
||||
switch v := value.(type) {
|
||||
case float64:
|
||||
return v, true
|
||||
case float32:
|
||||
return float64(v), true
|
||||
case int:
|
||||
return float64(v), true
|
||||
case int8:
|
||||
return float64(v), true
|
||||
case int16:
|
||||
return float64(v), true
|
||||
case int32:
|
||||
return float64(v), true
|
||||
case int64:
|
||||
return float64(v), true
|
||||
case uint:
|
||||
return float64(v), true
|
||||
case uint8:
|
||||
return float64(v), true
|
||||
case uint16:
|
||||
return float64(v), true
|
||||
case uint32:
|
||||
return float64(v), true
|
||||
case uint64:
|
||||
return float64(v), true
|
||||
case json.Number:
|
||||
parsed, err := v.Float64()
|
||||
return parsed, err == nil
|
||||
case string:
|
||||
parsed, err := strconv.ParseFloat(strings.TrimSpace(v), 64)
|
||||
return parsed, err == nil
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func (r *accountRepository) BulkUpdate(ctx context.Context, ids []int64, updates service.AccountBulkUpdate) (int64, error) {
|
||||
if len(ids) == 0 {
|
||||
return 0, nil
|
||||
|
||||
Reference in New Issue
Block a user