-
Notifications
You must be signed in to change notification settings - Fork 11.2k
fix: harden stream billing and affinity handling #6185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d66fd83
3b708fc
99b276f
b78e7db
7cb678c
41203cf
71ea9d8
2d7b64e
a268285
10fa109
6a20928
9914230
907b4e1
3017254
b7f0828
351b64f
00b5724
45a1a56
b55d5b0
b393360
3ed3c0e
afe9fdb
245af52
73b7040
faae963
8b9a28c
8744c10
467840e
305a9aa
2d5d8de
948f953
f0d2dc1
420a166
7ec8842
6de0adf
8f50f01
24a6441
29d5a78
534660e
18b51dc
c874413
4f0328d
1c7af3c
05ebed1
8bda8ba
9e87b3d
0717543
a30f2f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| package common | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "math" | ||
| "strconv" | ||
|
|
||
| "github.com/shopspring/decimal" | ||
| ) | ||
|
|
||
| const ( | ||
| MaxQuota = math.MaxInt32 | ||
| MinQuota = math.MinInt32 | ||
| ) | ||
|
|
||
| type QuotaClampKind string | ||
|
|
||
| const ( | ||
| QuotaClampOverflow QuotaClampKind = "overflow" | ||
| QuotaClampUnderflow QuotaClampKind = "underflow" | ||
| QuotaClampNaN QuotaClampKind = "nan" | ||
| ) | ||
|
|
||
| type QuotaClamp struct { | ||
| Op string `json:"op"` | ||
| Kind QuotaClampKind `json:"kind"` | ||
| Original string `json:"original"` | ||
| Clamped int `json:"clamped"` | ||
| } | ||
|
|
||
| func (c *QuotaClamp) Error() string { | ||
| if c == nil { | ||
| return "" | ||
| } | ||
| return fmt.Sprintf("quota conversion (%s) %s: original=%s, clamped=%d", c.Op, c.Kind, c.Original, c.Clamped) | ||
| } | ||
|
|
||
| func saturateQuota(value float64, op string) (int, *QuotaClamp) { | ||
| var clamp *QuotaClamp | ||
| switch { | ||
| case math.IsNaN(value): | ||
| clamp = &QuotaClamp{Op: op, Kind: QuotaClampNaN, Original: "NaN", Clamped: 0} | ||
| case value > MaxQuota: | ||
| clamp = &QuotaClamp{Op: op, Kind: QuotaClampOverflow, Original: strconv.FormatFloat(value, 'g', -1, 64), Clamped: MaxQuota} | ||
| case value < MinQuota: | ||
| clamp = &QuotaClamp{Op: op, Kind: QuotaClampUnderflow, Original: strconv.FormatFloat(value, 'g', -1, 64), Clamped: MinQuota} | ||
| default: | ||
| return int(value), nil | ||
| } | ||
| return clamp.Clamped, clamp | ||
| } | ||
|
|
||
| func QuotaFromFloat(value float64) int { | ||
| quota, _ := QuotaFromFloatChecked(value) | ||
| return quota | ||
| } | ||
|
|
||
| func QuotaFromFloatChecked(value float64) (int, *QuotaClamp) { | ||
| return saturateQuota(value, "QuotaFromFloat") | ||
| } | ||
|
|
||
| func QuotaFromFloatStrict(value float64) (int, error) { | ||
| quota, clamp := QuotaFromFloatChecked(value) | ||
| if clamp != nil { | ||
| return 0, clamp | ||
| } | ||
| return quota, nil | ||
| } | ||
|
|
||
| func QuotaRound(value float64) int { | ||
| quota, _ := QuotaRoundChecked(value) | ||
| return quota | ||
| } | ||
|
|
||
| func QuotaRoundChecked(value float64) (int, *QuotaClamp) { | ||
| return saturateQuota(math.Round(value), "QuotaRound") | ||
| } | ||
|
|
||
| func QuotaRoundStrict(value float64) (int, error) { | ||
| quota, clamp := QuotaRoundChecked(value) | ||
| if clamp != nil { | ||
| return 0, clamp | ||
| } | ||
| return quota, nil | ||
| } | ||
|
|
||
| func QuotaFromDecimal(value decimal.Decimal) int { | ||
| quota, _ := QuotaFromDecimalChecked(value) | ||
| return quota | ||
| } | ||
|
|
||
| func QuotaFromDecimalChecked(value decimal.Decimal) (int, *QuotaClamp) { | ||
| rounded, _ := value.Round(0).Float64() | ||
| return saturateQuota(rounded, "QuotaFromDecimal") | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package common | ||
|
|
||
| import ( | ||
| "math" | ||
| "testing" | ||
|
|
||
| "github.com/shopspring/decimal" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestQuotaFromFloatSaturatesOutOfRangeValues(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| in float64 | ||
| want int | ||
| }{ | ||
| {name: "overflow", in: math.MaxFloat64, want: MaxQuota}, | ||
| {name: "underflow", in: -math.MaxFloat64, want: MinQuota}, | ||
| {name: "nan", in: math.NaN(), want: 0}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| got := QuotaFromFloat(tt.in) | ||
| require.Equal(t, tt.want, got) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestQuotaRoundStrictRejectsSaturation(t *testing.T) { | ||
| quota, err := QuotaRoundStrict(float64(MaxQuota) + 1) | ||
|
|
||
| require.Error(t, err) | ||
| require.Zero(t, quota) | ||
| clamp, ok := err.(*QuotaClamp) | ||
| require.True(t, ok) | ||
| require.Equal(t, QuotaClampOverflow, clamp.Kind) | ||
| } | ||
|
|
||
| func TestQuotaRoundStrictAcceptsIntegerBounds(t *testing.T) { | ||
| maxQuota, maxErr := QuotaRoundStrict(float64(MaxQuota)) | ||
| minQuota, minErr := QuotaRoundStrict(float64(MinQuota)) | ||
|
|
||
| require.NoError(t, maxErr) | ||
| require.NoError(t, minErr) | ||
| require.Equal(t, MaxQuota, maxQuota) | ||
| require.Equal(t, MinQuota, minQuota) | ||
| } | ||
|
|
||
| func TestQuotaClampNaNIsJSONSafe(t *testing.T) { | ||
| _, clamp := QuotaFromFloatChecked(math.NaN()) | ||
| require.NotNil(t, clamp) | ||
|
|
||
| data, err := Marshal(clamp) | ||
|
|
||
| require.NoError(t, err) | ||
| require.Contains(t, string(data), `"original":"NaN"`) | ||
| } | ||
|
|
||
| func TestQuotaFromDecimalRoundsBeforeSaturating(t *testing.T) { | ||
| quota := QuotaFromDecimal(decimal.NewFromFloat(1.5)) | ||
|
|
||
| require.Equal(t, 2, quota) | ||
| } | ||
|
Comment on lines
+24
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Use These test files violate the coding guideline that strictly requires using
📍 Affects 3 files
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
Prevent runtime panic on type assertion.
If
datais not a string (e.g.,nil,[]byte, or numeric), the type assertiondata.(string)will panic and crash the request. Sincefmt.Sprint(data)gracefully converts any type into a string, you can capture its output in a variable to guarantee safety and avoid the redundant formatting step.🐛 Proposed fix
func writeData(w stringWriter, data interface{}) error { - if _, err := dataReplacer.WriteString(w, fmt.Sprint(data)); err != nil { + str := fmt.Sprint(data) + if _, err := dataReplacer.WriteString(w, str); err != nil { return err } - if strings.HasPrefix(data.(string), "data") { + if strings.HasPrefix(str, "data") { if _, err := w.writeString("\n\n"); err != nil { return err }📝 Committable suggestion
🤖 Prompt for AI Agents