fix(quota): rebuild Codex quota history tracking - #446
Conversation
Replace the invalid Codex-only history schema with generic quota cycle and percentage segment tables. Separate trusted refresh writes from buffered Header samples, stabilize reset boundaries, and query current plus completed cycles by upstream window role. Refs: #445 Co-authored-by: YouShouldBetOnMe <153986019+YouShouldBetOnMe@users.noreply.github.com>
Select history by upstream window role while labeling only the real 5h, Weekly, Monthly, or fallback duration. Include the current cycle in the records list and render effective boundaries, percentage baselines, and observation counts. Refs: #445 Co-authored-by: YouShouldBetOnMe <153986019+YouShouldBetOnMe@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1e887517d8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if !found { | ||
| cycle = newCodexQuotaCycle(observation) | ||
| cycle = newQuotaCycle(observation, quotaKey) | ||
| if err := tx.Create(&cycle).Error; err != nil { | ||
| return fmt.Errorf("create codex quota cycle: %w", err) | ||
| return fmt.Errorf("create quota cycle: %w", err) |
There was a problem hiding this comment.
Reuse a prior cycle when a window role switches back
When a role changes from Weekly to 5h and then returns to the still-existing Weekly cycle, this path only compares against the latest 5h row and unconditionally creates a new Weekly row. The earlier Weekly row already has the same (provider, auth_index, quota_key, window_seconds, reset_at) identity, so the unique index rejects the insert; subsequent observations keep retrying the same failing insert until the reset changes, and the surrounding batch transaction can also roll back unrelated observations. Look up and reuse the exact prior cycle before creating a row.
Useful? React with 👍 / 👎.
| func quotaResetTimesMatch(left time.Time, right time.Time) bool { | ||
| distance := left.Sub(right) | ||
| if distance < 0 { | ||
| distance = -distance | ||
| } | ||
| return time.Duration(toleranceSeconds) * time.Second | ||
| return distance <= codexQuotaResetTolerance |
There was a problem hiding this comment.
Bound reset tolerance by the quota window length
History now accepts arbitrary positive header windows, including 1- or 2-minute windows, but this fixed two-minute tolerance treats consecutive cycles for those windows as the same cycle because their reset times differ by at most 120 seconds. A reset can therefore be rejected as a percentage increase or overwrite/delete the prior cycle's tail instead of creating a new cycle. The tolerance needs to remain strictly smaller than the observed window, such as the previous window-relative bound.
Useful? React with 👍 / 👎.
| // 被丢弃 Header 只损失本轮不足十秒的样本;清空引用让完整快照可尽快回收。 | ||
| for index := range inputs { | ||
| inputs[index].Snapshot = nil | ||
| inputs[index].Observations = nil |
There was a problem hiding this comment.
Preserve header samples for unrelated accounts
When any manual, scheduled, or inspection observation reaches the trusted queue during a header aggregation window, this loop clears every queued header input, regardless of its auth index or window role. For example, a manual refresh of account A can permanently discard account B's only usage-header observation; if B has no subsequent traffic, its baseline or percentage transition is never recorded. Only header observations superseded by a trusted fact for the same account and role should be dropped.
Useful? React with 👍 / 👎.
Summary
Validation
TZ=UTCandTZ=Asia/Shanghaigit diff --checkCloses #445