Skip to content

Commit c521117

Browse files
authored
Merge pull request Wei-Shaw#1074 from StarryKira/fix/session-window-reset-from-header
fix(usage): use real reset header for 5h session window countdown fix issue Wei-Shaw#1064 Wei-Shaw#1065
2 parents 07ab051 + 869952d commit c521117

3 files changed

Lines changed: 428 additions & 7 deletions

File tree

backend/internal/service/ratelimit_service.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,16 +1051,44 @@ func (s *RateLimitService) UpdateSessionWindow(ctx context.Context, account *Acc
10511051
var windowStart, windowEnd *time.Time
10521052
needInitWindow := account.SessionWindowEnd == nil || time.Now().After(*account.SessionWindowEnd)
10531053

1054-
if needInitWindow && (status == "allowed" || status == "allowed_warning") {
1055-
// 预测时间窗口:从当前时间的整点开始,+5小时为结束
1056-
// 例如:现在是 14:30,窗口为 14:00 ~ 19:00
1054+
// 优先使用响应头中的真实重置时间(比预测更准确)
1055+
if resetStr := headers.Get("anthropic-ratelimit-unified-5h-reset"); resetStr != "" {
1056+
if ts, err := strconv.ParseInt(resetStr, 10, 64); err == nil {
1057+
// 检测可能的毫秒时间戳(秒级约为 1e9,毫秒约为 1e12)
1058+
if ts > 1e11 {
1059+
slog.Warn("account_session_window_header_millis_detected", "account_id", account.ID, "raw_reset", resetStr)
1060+
ts = ts / 1000
1061+
}
1062+
end := time.Unix(ts, 0)
1063+
// 校验时间戳是否在合理范围内(不早于 5h 前,不晚于 7 天后)
1064+
minAllowed := time.Now().Add(-5 * time.Hour)
1065+
maxAllowed := time.Now().Add(7 * 24 * time.Hour)
1066+
if end.Before(minAllowed) || end.After(maxAllowed) {
1067+
slog.Warn("account_session_window_header_out_of_range", "account_id", account.ID, "raw_reset", resetStr, "parsed_end", end)
1068+
} else if needInitWindow || account.SessionWindowEnd == nil || !end.Equal(*account.SessionWindowEnd) {
1069+
// 窗口需要初始化,或者真实重置时间与已存储的不同,则更新
1070+
start := end.Add(-5 * time.Hour)
1071+
windowStart = &start
1072+
windowEnd = &end
1073+
slog.Info("account_session_window_from_header", "account_id", account.ID, "window_start", start, "window_end", end, "status", status)
1074+
}
1075+
} else {
1076+
slog.Warn("account_session_window_header_parse_failed", "account_id", account.ID, "raw_reset", resetStr, "error", err)
1077+
}
1078+
}
1079+
1080+
// 回退:如果没有真实重置时间且需要初始化窗口,使用预测
1081+
if windowEnd == nil && needInitWindow && (status == "allowed" || status == "allowed_warning") {
10571082
now := time.Now()
10581083
start := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
10591084
end := start.Add(5 * time.Hour)
10601085
windowStart = &start
10611086
windowEnd = &end
10621087
slog.Info("account_session_window_initialized", "account_id", account.ID, "window_start", start, "window_end", end, "status", status)
1063-
// 窗口重置时清除旧的 utilization,避免残留上个窗口的数据
1088+
}
1089+
1090+
// 窗口重置时清除旧的 utilization,避免残留上个窗口的数据
1091+
if windowEnd != nil && needInitWindow {
10641092
_ = s.accountRepo.UpdateExtra(ctx, account.ID, map[string]any{
10651093
"session_window_utilization": nil,
10661094
})

0 commit comments

Comments
 (0)