forked from QuantumNous/new-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathquota_math.go
More file actions
148 lines (130 loc) · 5.49 KB
/
Copy pathquota_math.go
File metadata and controls
148 lines (130 loc) · 5.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package common
import (
"fmt"
"math"
"github.com/shopspring/decimal"
)
// Quota conversions are centralized here so every billing path shares one
// saturation + logging policy. Quota columns (user/token/log) are 32-bit
// integers in the database, so an oversized product must clamp to the int32
// range instead of wrapping around and turning a charge into a credit.
const (
MaxQuota = math.MaxInt32
MinQuota = math.MinInt32
)
// QuotaClampKind identifies why a quota conversion had to be saturated.
type QuotaClampKind string
// Clamp kinds reported by QuotaClamp.Kind.
const (
QuotaClampOverflow QuotaClampKind = "overflow"
QuotaClampUnderflow QuotaClampKind = "underflow"
QuotaClampNaN QuotaClampKind = "nan"
)
// QuotaClamp describes a single saturation event: a quota conversion whose
// input fell outside the representable int32 range (or was NaN) and was
// therefore clamped. It is surfaced to billing callers so the event can be
// recorded on the related consume/task log for admin auditing.
type QuotaClamp struct {
Op string `json:"op"` // "QuotaFromFloat" | "QuotaRound" | "QuotaFromDecimal"
Kind QuotaClampKind `json:"kind"` // "overflow" | "underflow" | "nan"
Original float64 `json:"original"` // best-effort pre-clamp value (decimal -> float64 approx)
Clamped int `json:"clamped"` // the saturated result actually used
}
// Error lets the same typed value serve both as the settlement audit marker
// and as the fail-fast error returned by strict pre-consume conversions.
func (c *QuotaClamp) Error() string {
if c == nil {
return ""
}
return fmt.Sprintf("quota conversion (%s) %s: original=%g, clamped=%d", c.Op, c.Kind, c.Original, c.Clamped)
}
// AuditMap renders the clamp as the marker stored under a log's
// admin_info.quota_saturation. Centralized here so every billing path (consume
// logs, task billing logs, task compensation logs) records the same shape.
func (c *QuotaClamp) AuditMap() map[string]interface{} {
if c == nil {
return nil
}
return map[string]interface{}{
"op": c.Op,
"kind": c.Kind,
"original": c.Original,
"clamped": c.Clamped,
}
}
// saturateQuota converts an already-rounded quota value to int, clamping to
// the int32 range. Whenever clamping (what would otherwise be an integer
// wraparound) or a NaN fallback is triggered it logs a warning, because in
// normal operation a single request never approaches these bounds — hitting
// them signals a bug or an abusive request. `op` names the caller. When a
// clamp occurs it returns a non-nil *QuotaClamp so callers can additionally
// record the event (e.g. on the consume log); the returned pointer is nil for
// in-range values.
func saturateQuota(value float64, op string) (int, *QuotaClamp) {
var clamp *QuotaClamp
switch {
case math.IsNaN(value):
clamp = &QuotaClamp{Op: op, Kind: QuotaClampNaN, Original: value, Clamped: 0}
case value >= MaxQuota:
clamp = &QuotaClamp{Op: op, Kind: QuotaClampOverflow, Original: value, Clamped: MaxQuota}
case value <= MinQuota:
clamp = &QuotaClamp{Op: op, Kind: QuotaClampUnderflow, Original: value, Clamped: MinQuota}
default:
return int(value), nil
}
SysError(clamp.Error())
return clamp.Clamped, clamp
}
func strictQuota(quota int, clamp *QuotaClamp) (int, error) {
if clamp != nil {
return 0, clamp
}
return quota, nil
}
// QuotaFromFloat converts a computed quota value to int, truncating toward
// zero, with saturation. Use for float products of prices, ratios, and
// user-controlled multipliers (image n, video seconds, resolution ratios).
func QuotaFromFloat(value float64) int {
quota, _ := QuotaFromFloatChecked(value)
return quota
}
// QuotaFromFloatChecked is QuotaFromFloat but also returns a non-nil
// *QuotaClamp when the value was clamped, so billing callers can audit it.
func QuotaFromFloatChecked(value float64) (int, *QuotaClamp) {
return saturateQuota(value, "QuotaFromFloat")
}
// QuotaFromFloatStrict converts an in-range value and returns a typed
// *QuotaClamp error instead of allowing a saturated result to reach billing.
func QuotaFromFloatStrict(value float64) (int, error) {
return strictQuota(QuotaFromFloatChecked(value))
}
// QuotaRound converts a float64 quota value to int using half-away-from-zero
// rounding, with saturation. Every tiered billing path (pre-consume,
// settlement, breakdown validation, log fields) MUST use this to avoid +-1
// discrepancies.
func QuotaRound(value float64) int {
quota, _ := QuotaRoundChecked(value)
return quota
}
// QuotaRoundChecked is QuotaRound but also returns a non-nil *QuotaClamp when
// the value was clamped, so billing callers can audit it.
func QuotaRoundChecked(value float64) (int, *QuotaClamp) {
return saturateQuota(math.Round(value), "QuotaRound")
}
// QuotaRoundStrict rounds an in-range value and returns a typed *QuotaClamp
// error instead of allowing a saturated result to reach billing.
func QuotaRoundStrict(value float64) (int, error) {
return strictQuota(QuotaRoundChecked(value))
}
// QuotaFromDecimal converts a computed quota decimal to int with saturation.
// The decimal is rounded (half away from zero) before conversion.
func QuotaFromDecimal(d decimal.Decimal) int {
quota, _ := QuotaFromDecimalChecked(d)
return quota
}
// QuotaFromDecimalChecked is QuotaFromDecimal but also returns a non-nil
// *QuotaClamp when the value was clamped, so billing callers can audit it.
func QuotaFromDecimalChecked(d decimal.Decimal) (int, *QuotaClamp) {
f, _ := d.Round(0).Float64()
return saturateQuota(f, "QuotaFromDecimal")
}