Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 124 additions & 14 deletions common/quota_math.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,131 @@
package common

import "math"
import (
"fmt"
"math"

// QuotaFromFloat converts a computed quota value to int with saturation.
// Quota products can include user-controlled multipliers such as image count,
// video seconds, or resolution ratios; oversized products must never wrap into
// a negative charge. The bound is int32 because quota columns are int fields
// used as 32-bit database integers in supported deployments.
func QuotaFromFloat(value float64) int {
if math.IsNaN(value) {
return 0
"github.com/shopspring/decimal"
)

// Quota conversions are centralized here so every billing path shares one
// saturation + logging policy. Quota columns are 32-bit integers in supported
// deployments, 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

const (
QuotaClampOverflow QuotaClampKind = "overflow"
QuotaClampUnderflow QuotaClampKind = "underflow"
QuotaClampNaN QuotaClampKind = "nan"
)

// QuotaClamp describes a single saturation event and is stored under
// admin_info.quota_saturation for audit when a billing path has to clamp.
type QuotaClamp struct {
Op string `json:"op"`
Kind QuotaClampKind `json:"kind"`
Original float64 `json:"original"`
Clamped int `json:"clamped"`
}

func (c *QuotaClamp) Error() string {
if c == nil {
return ""
}
if value >= math.MaxInt32 {
return math.MaxInt32
return fmt.Sprintf("quota conversion (%s) %s: original=%g, clamped=%d", c.Op, c.Kind, c.Original, c.Clamped)
}

func (c *QuotaClamp) AuditMap() map[string]interface{} {
if c == nil {
return nil
}
if value <= math.MinInt32 {
return math.MinInt32
original := interface{}(c.Original)
switch {
case math.IsNaN(c.Original):
original = "NaN"
case math.IsInf(c.Original, 1):
original = "+Inf"
case math.IsInf(c.Original, -1):
original = "-Inf"
}
return int(value)
return map[string]interface{}{
"op": c.Op,
"kind": c.Kind,
"original": original,
"clamped": 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: 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
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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 int32 saturation.
func QuotaFromFloat(value float64) int {
quota, _ := QuotaFromFloatChecked(value)
return quota
}

// QuotaFromFloatChecked is QuotaFromFloat plus a non-nil clamp descriptor
// when saturation or NaN fallback happened.
func QuotaFromFloatChecked(value float64) (int, *QuotaClamp) {
return saturateQuota(value, "QuotaFromFloat")
}

// QuotaFromFloatStrict rejects unrepresentable billing estimates instead of
// silently saturating them.
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 the same saturation policy.
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) {
return strictQuota(QuotaRoundChecked(value))
}

// QuotaFromDecimal rounds a computed quota decimal before conversion.
func QuotaFromDecimal(d decimal.Decimal) int {
quota, _ := QuotaFromDecimalChecked(d)
return quota
}

func QuotaFromDecimalChecked(d decimal.Decimal) (int, *QuotaClamp) {
f, _ := d.Round(0).Float64()
return saturateQuota(f, "QuotaFromDecimal")
}
151 changes: 146 additions & 5 deletions common/quota_math_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,156 @@ import (
"math"
"testing"

"github.com/shopspring/decimal"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const overflowingProduct = 2000 * 1.8446744073686647e19

func TestQuotaFromFloat(t *testing.T) {
assert.Equal(t, 42, QuotaFromFloat(42.4))
assert.Equal(t, -42, QuotaFromFloat(-42.4))
assert.Equal(t, math.MaxInt32, QuotaFromFloat(2000*1.8446744073686647e19))
assert.Equal(t, math.MinInt32, QuotaFromFloat(-2000*1.8446744073686647e19))
assert.Equal(t, math.MaxInt32, QuotaFromFloat(math.Inf(1)))
assert.Equal(t, math.MinInt32, QuotaFromFloat(math.Inf(-1)))
assert.Equal(t, 42, QuotaFromFloat(42.9))
assert.Equal(t, -42, QuotaFromFloat(-42.9))
assert.Equal(t, MaxQuota, QuotaFromFloat(overflowingProduct))
assert.Equal(t, MinQuota, QuotaFromFloat(-overflowingProduct))
assert.Equal(t, MaxQuota, QuotaFromFloat(math.Inf(1)))
assert.Equal(t, MinQuota, QuotaFromFloat(math.Inf(-1)))
assert.Equal(t, 0, QuotaFromFloat(math.NaN()))
}

func TestQuotaRound(t *testing.T) {
assert.Equal(t, 42, QuotaRound(41.5))
assert.Equal(t, 43, QuotaRound(42.5))
assert.Equal(t, -43, QuotaRound(-42.5))
assert.Equal(t, MaxQuota, QuotaRound(overflowingProduct))
assert.Equal(t, MinQuota, QuotaRound(-overflowingProduct))
assert.Equal(t, 0, QuotaRound(math.NaN()))
}

func TestQuotaFromDecimal(t *testing.T) {
assert.Equal(t, 43, QuotaFromDecimal(decimal.NewFromFloat(42.5)))
assert.Equal(t, 42, QuotaFromDecimal(decimal.NewFromFloat(41.7)))
assert.Equal(t, MaxQuota, QuotaFromDecimal(decimal.NewFromInt(2000).Mul(decimal.NewFromFloat(1.8446744073686647e19))))
assert.Equal(t, MinQuota, QuotaFromDecimal(decimal.NewFromInt(-2000).Mul(decimal.NewFromFloat(1.8446744073686647e19))))
}

func TestQuotaFromFloatChecked(t *testing.T) {
quota, clamp := QuotaFromFloatChecked(42.9)
assert.Equal(t, 42, quota)
assert.Nil(t, clamp)

quota, clamp = QuotaFromFloatChecked(overflowingProduct)
assert.Equal(t, MaxQuota, quota)
if assert.NotNil(t, clamp) {
assert.Equal(t, "QuotaFromFloat", clamp.Op)
assert.Equal(t, QuotaClampOverflow, clamp.Kind)
assert.Equal(t, MaxQuota, clamp.Clamped)
}

quota, clamp = QuotaFromFloatChecked(-overflowingProduct)
assert.Equal(t, MinQuota, quota)
if assert.NotNil(t, clamp) {
assert.Equal(t, QuotaClampUnderflow, clamp.Kind)
assert.Equal(t, MinQuota, clamp.Clamped)
}

quota, clamp = QuotaFromFloatChecked(math.NaN())
assert.Equal(t, 0, quota)
if assert.NotNil(t, clamp) {
assert.Equal(t, QuotaClampNaN, clamp.Kind)
assert.Equal(t, 0, clamp.Clamped)
}
}

func TestQuotaFromFloatStrictReturnsTypedClampError(t *testing.T) {
quota, err := QuotaFromFloatStrict(42.9)
require.NoError(t, err)
assert.Equal(t, 42, quota)

quota, err = QuotaFromFloatStrict(overflowingProduct)
assert.Zero(t, quota)
var clamp *QuotaClamp
require.ErrorAs(t, err, &clamp)
assert.Equal(t, QuotaClampOverflow, clamp.Kind)
assert.Equal(t, MaxQuota, clamp.Clamped)
assert.ErrorContains(t, err, "QuotaFromFloat")
assert.ErrorContains(t, err, "overflow")
assert.ErrorContains(t, err, "original=")
assert.ErrorContains(t, err, "clamped=2147483647")
}

func TestQuotaFromFloatStrictAcceptsExactInt32Boundaries(t *testing.T) {
quota, err := QuotaFromFloatStrict(float64(MaxQuota))
require.NoError(t, err)
assert.Equal(t, MaxQuota, quota)

quota, err = QuotaFromFloatStrict(float64(MinQuota))
require.NoError(t, err)
assert.Equal(t, MinQuota, quota)

quota, err = QuotaFromFloatStrict(float64(MaxQuota) + 1)
assert.Zero(t, quota)
var overflow *QuotaClamp
require.ErrorAs(t, err, &overflow)
assert.Equal(t, QuotaClampOverflow, overflow.Kind)

quota, err = QuotaFromFloatStrict(float64(MinQuota) - 1)
assert.Zero(t, quota)
var underflow *QuotaClamp
require.ErrorAs(t, err, &underflow)
assert.Equal(t, QuotaClampUnderflow, underflow.Kind)
}

func TestQuotaClampAuditMapIsJSONSafe(t *testing.T) {
tests := []struct {
name string
value float64
wantOriginal interface{}
}{
{name: "nan", value: math.NaN(), wantOriginal: "NaN"},
{name: "positive infinity", value: math.Inf(1), wantOriginal: "+Inf"},
{name: "negative infinity", value: math.Inf(-1), wantOriginal: "-Inf"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, clamp := QuotaFromFloatChecked(tt.value)
require.NotNil(t, clamp)

auditMap := clamp.AuditMap()
assert.Equal(t, tt.wantOriginal, auditMap["original"])
assert.NotEmpty(t, MapToJsonStr(map[string]interface{}{
"admin_info": map[string]interface{}{
"quota_saturation": auditMap,
},
}))
})
}
}

func TestQuotaRoundChecked(t *testing.T) {
quota, clamp := QuotaRoundChecked(42.5)
assert.Equal(t, 43, quota)
assert.Nil(t, clamp)

quota, clamp = QuotaRoundChecked(overflowingProduct)
assert.Equal(t, MaxQuota, quota)
if assert.NotNil(t, clamp) {
assert.Equal(t, "QuotaRound", clamp.Op)
assert.Equal(t, QuotaClampOverflow, clamp.Kind)
}
}

func TestQuotaFromDecimalChecked(t *testing.T) {
quota, clamp := QuotaFromDecimalChecked(decimal.NewFromFloat(41.7))
assert.Equal(t, 42, quota)
assert.Nil(t, clamp)

quota, clamp = QuotaFromDecimalChecked(decimal.NewFromInt(2000).Mul(decimal.NewFromFloat(1.8446744073686647e19)))
assert.Equal(t, MaxQuota, quota)
if assert.NotNil(t, clamp) {
assert.Equal(t, "QuotaFromDecimal", clamp.Op)
assert.Equal(t, QuotaClampOverflow, clamp.Kind)
}
}
46 changes: 42 additions & 4 deletions common/verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import (
)

type verificationValue struct {
code string
time time.Time
code string
time time.Time
claimID string
}

const (
Expand Down Expand Up @@ -49,19 +50,56 @@ func VerifyCodeWithKey(key string, code string, purpose string) bool {
defer verificationMutex.Unlock()
value, okay := verificationMap[purpose+key]
now := time.Now()
if !okay || int(now.Sub(value.time).Seconds()) >= VerificationValidMinutes*60 {
if !okay || value.claimID != "" || int(now.Sub(value.time).Seconds()) >= VerificationValidMinutes*60 {
return false
}
return code == value.code
}

// VerifyCodeWithKeyAndRun reserves a code while fn runs. A successful fn
// consumes the code; an error or panic releases the reservation for retry.
func VerifyCodeWithKeyAndRun(key string, code string, purpose string, fn func() error) (verified bool, err error) {
mapKey := purpose + key
claimID := uuid.NewString()

verificationMutex.Lock()
value, okay := verificationMap[mapKey]
now := time.Now()
if !okay || value.claimID != "" || int(now.Sub(value.time).Seconds()) >= VerificationValidMinutes*60 || code != value.code {
verificationMutex.Unlock()
return false, nil
}
value.claimID = claimID
verificationMap[mapKey] = value
verificationMutex.Unlock()

committed := false
defer func() {
verificationMutex.Lock()
current, exists := verificationMap[mapKey]
if exists && current.claimID == claimID {
if committed {
delete(verificationMap, mapKey)
} else {
current.claimID = ""
verificationMap[mapKey] = current
}
}
verificationMutex.Unlock()
}()

err = fn()
committed = err == nil
return true, err
}

func VerifyAndDeleteCodeWithKey(key string, code string, purpose string) bool {
verificationMutex.Lock()
defer verificationMutex.Unlock()
mapKey := purpose + key
value, okay := verificationMap[mapKey]
now := time.Now()
if !okay || int(now.Sub(value.time).Seconds()) >= VerificationValidMinutes*60 || code != value.code {
if !okay || value.claimID != "" || int(now.Sub(value.time).Seconds()) >= VerificationValidMinutes*60 || code != value.code {
return false
}
delete(verificationMap, mapKey)
Expand Down
Loading
Loading