30 lines
4.7 KiB
Markdown
30 lines
4.7 KiB
Markdown
# UsageQuota migration
|
|||
|
|
|
||
|
|
The native Go/Vue workbench now stores and manages per-member daily and monthly quotas at **用量分析 → 成员配额**. Migration `000202_usage_quotas` creates the quota table and the balance-hold lookup index. No legacy quota rows required importing: the legacy quota table was empty.
|
||
|
|
|
||
|
|
## Legacy behavior verified
|
||
|
|
|
||
|
|
- `server/app/models.py:743` defines user/organization scope, daily/monthly periods, token/amount/storage/concurrency limits, warning ratio, enabled state, and warn/soft/hard policies.
|
||
|
|
- `server/app/main.py:7235` implements admin list, upsert, and delete; storage limits are only persisted.
|
||
|
|
- `server/app/billing.py:276` computes quota state and contains a token-check helper, but `check_quota` has no callers in the application.
|
||
|
|
- `server/app/training_reading.py:430` uses the minimum enabled positive concurrent limit, capped at three, for training reading parallelism.
|
||
|
|
|
||
|
|
## Native behavior
|
||
|
|
|
||
|
|
- Admin/owner-only APIs: `GET /api/v1/workbench/operations/quotas`, `PUT /api/v1/workbench/operations/quotas/:userID`, `DELETE /api/v1/workbench/operations/quotas/:id`. Handlers also enforce roles independently of route guards. All operations are tenant scoped; setting a quota requires membership in that tenant.
|
||
|
|
- Daily and monthly periods use explicit `Asia/Shanghai` calendar boundaries, with half-open intervals `[start, end)`. Calls are attributed to the period in which their balance reservation starts. Calls crossing a boundary still count against the concurrent-call cap.
|
||
|
|
- Token and point limits use completed usage plus active reservations. Zero means unlimited; disabled quotas neither block nor warn. Soft/hard policies both reject a proposed reservation that would exceed either limit. A reservation exactly equal to the remaining quota is permitted. Warn policy does not block these limits. The positive concurrent cap applies to all three policies.
|
||
|
|
- Quota validation and balance reservation run in the same transaction under the member's existing balance-row lock. Failed checks roll back the balance reservation. Settlement remains idempotent and releases concurrent capacity; failed/cancelled model calls release their reservations through existing settlement.
|
||
|
|
- Admin changes serialize with reservation checks. Both daily and monthly quotas apply simultaneously; configuration changes do not interrupt already-running calls.
|
||
|
|
- Warnings appear in the admin table, and reaching the configured ratio with a reservation creates one member notification per quota period. The same account's parallel calls cannot create duplicate notices.
|
||
|
|
- `points_limit` is **点数**, in the same units as the existing balance ledger. No currency price, exchange rate, or conversion from the unused legacy monetary field is invented. Actual provider usage above a reservation remains fully recorded and debited by the existing metering code.
|
||
|
|
- Storage values remain explicitly marked as configuration only (`storage_enforced: false`), matching the old behavior. Auxiliary providers that do not expose token/billing information remain unpriced and outside chat balance reservation; the UI specifies that the concurrent cap covers balance-metered generation calls.
|
||
|
|
|
||
|
|
## Verification
|
||
|
|
|
||
|
|
- Targeted Go tests passed: quota policies, disabled/unlimited behavior, failed reservation rollback, settlement release, once-per-period warnings, period boundaries (including leap-year/month rollover), void usage exclusion, expired calls, user/tenant isolation, role restrictions, upsert/zero values, and validation. Existing balance/metering/cancellation tests passed alongside them.
|
||
|
|
- Real PostgreSQL test `TestPostgresQuotaReservationRaces` passed in a fresh isolated `wb_quota_test_*` schema. Twelve simultaneous callers compete for token, point, and concurrent limits; exactly two succeed for each bounded case, and all twelve succeed when unlimited. Parallel duplicate settlements debit each call once and release all holds. The schema is dropped after the test.
|
||
|
|
- Frontend `vue-tsc --build --pretty false` passed.
|
||
|
|
- Live API/browser verifier: `scripts/verify_usage_quotas.cjs`. It creates one disposable member, tests admin CRUD, contributor/viewer denial, daily/monthly coexistence, warning and reservation totals, disabled behavior, UI edit/delete, and desktop/mobile rendering. It removes its quota/ledger fixtures and member and writes `acceptance/usage-quota-live-20260917.json` plus screenshots. It never invokes a paid external provider.
|
||
|
|
- First live attempt confirmed quota write and role enforcement but detected a stale deployed binary lacking the already-tested GORM field exclusion. Its temporary fixtures were removed. Re-run the verifier after deploying the current source; the report records the outcome without hiding failures.
|