Burndown: Quantity int64 overflow and checked accessors
This tracks ongoing and planned work to fix resource.Quantity's int64 accessor overflow behavior.
Goal
- Resolve all known correctness issues, lock in correct behavior with tests
- Introduce
(int64, ok bool)checked accessors (AsScaledInt64,AsMilliInt64) so that callers can detect overflow.okis false on overflow and the returned value saturates at the sign-correctMinInt64/MaxInt64rail (matching Go'sstrconv) rather than wrapping, so an overflowed result is the least dangerous value possible. Landed in #141305. - Convert all existing accessors that don't return an ok bool (
Value,MilliValue,ScaledValue) to wrap the checked accessors and drop the bool. Landed in #141305.
Known Gaps
| Expression | Returns | Gap |
|---|---|---|
NewScaledQuantity(MaxInt64, 1).Value() |
-10 |
multiply wraps around 64 bits |
MustParse("100E").Value() |
0 |
conversion fails internally and falls back to zero |
MustParse("9223372036854775808").Value() |
MinInt64 |
barely-too-large value becomes negative |
MustParse("-9.5Gi").Value() |
8246196746 |
bug in unsigned math flips the sign (#138510) |
NewQuantity(MinInt64).Neg() |
MinInt64 |
−MinInt64 doesn't fit in int64, so negation silently does nothing |
zero.Sub(MinInt64 quantity) |
MinInt64 |
similar to above, but using .Sub() instead of .Neg() |
MustParse("1000E").String() |
"1" |
serialization drops the suffix for decimal values above 10^18 (#140459) |
ParseQuantity("1e4294967297") |
10, no error |
exponent is truncated to 32 bits (2³²+1 → 1), should be parse error |
NewQuantity(MinInt64).Neg(); q.AsInt64() |
(MinInt64, true) |
AsInt64 reports ok=true for a value that isn't the true result of the operation; same after zero.Sub(MinInt64 quantity) |
MustParse("1e100").Value() vs MustParse("1e100").ToDec().Value() |
0 vs MinInt64 |
same input, different wrong values on the int64 vs Dec backends; fixes must land on both paths |
Task List
#138309: parse boundary tests (18/19/20 digits, MinInt64/MaxInt64 +/- 1)
#140556, #140518, #140702: shared inf.Dec state (aliasing) fixes for #140744
#141170: baseline test matrix; each wrong-today field carries a TODO for its fixed value; suite green on master so it doubles as the burndown tracker
#138510: scaledValue negative rounding + tests
#140459: String() suffix drop for DecimalSI > 10^18
- #141817: regression from #140459. BinarySI values >= 1024 that are not a multiple of 1024 serialize in e notation when divisible by 10 (NewQuantity(5000000000, BinarySI) -> "5e9", was "5000000000"). Must land before 1.38.
#140674: MaxMilliQuantity + overflow docs
#138507: EBNF grammar fix for integer exponents
#141264: fix MinInt64 Neg/Sub via decimal fallback; extreme source scales tracked separately
#141169: switch validateBasicResource to Sign() < 0
#141181: stop using Value() overflow to detect sign or unset limits in csi_client.go and eviction
Checked-accessor contract settled: (int64, ok bool), saturate at the sign-correct rail, round away from zero, Dec path matches; accessors and wrapper flip land together
#141305 (@thc1006): add AsScaledInt64/AsMilliInt64, switch Value/MilliValue/ScaledValue to saturating wrappers, bound int64MultiplyScale*/scaledValue; fixes cmd/ kubelet/app/server.go:1425 in lockstep
#141937: AsApproximateFloat64 NaN for zero at large scale
#141938: MustParse near MaxInt64. Skew note: CEL isInteger()/asInteger() start returning true/values for 19-digit quantities.
#141203 (@thc1006): reject exponents that don't fit in int32 (1e4294967297)
#142013: Cmp bounding
#141201 (@semx): regression test for Scale(MinInt32).infScale() sign edge case
#141980: clear cached serialization when JSON/CBOR null resets a Quantity
#142104: AsInt64 answers the same on both backends (Cmp's ToDec side effect turned "50k" from (50000, true) into (0, false)); value-based follow-up (1000m, 1.5Gi -> whole, true) pending decision
#141306 (@thc1006, draft since 08-11): bound work when parsing sub-nano quantities
#142156: fix canonical exponent overflow (q := MustParse("1000e2147483647"); q.String() returns "1e-2147483646")
TODO: String() fast path caches non-canonical spellings verbatim (
+1,01,1E3,1e+21,1e0); pinned as current behavior by #142052, untouched by #138166; needs an owner and a compatibility decisionTODO: resolve extreme source-scale behavior in conversion, comparison and Neg/Sub; #141201 tests the MinInt32 edge but does not fix it
TODO: audit remaining scale arithmetic and work bounds in Add/Sub, AsScale and AsFloat64Slow
Move callers doing raw int64 math to Cmp or the checked accessors:
- #141348 LimitRanger min/max/ratio
- #141349 core validation integer-resource check
- #141352 hugepage divisibility
- #141327 kubelet CPU quota/shares conversions
- #141328 scheduler NodeInfo requested totals
- #141618 PV controller explicit binding (#141617)
- #142088 volumebinding CSI capacity check and capacity-change queueing hint
- #141999 HPA individual requests and per-pod container sums
- #141978 HPA resource utilization totals and percentages
- #141397 HPA replica-count narrowing
- TODO: HPA GetMetricUsageRatio sums and remaining scaling-policy arithmetic
- #140442, #140666 DRA consumable-capacity request and range bounds
- #141705 (@piyushrajyadav, fixes #141216): DRA negative counter validation and allocator guards
- TODO: kubelet QoS CPU/memory sums and reserve-percentage arithmetic
- #141334: kubelet CPU overhead subtraction after lossy quota/shares conversion (follow-up issue)
- #142112 (@Octopusjust): scheduler scoring additions and request-change event comparisons (the resource_allocation.go hunk stays in #141328)
- #142131 (@6democratickim9): fractional-byte warnings in pod and PVC via AsScale(0) exactness
- TODO: ceil(MilliValue/MilliValue) in pkg/api/v1/resource/helpers.go and its two kubectl copies
- TODO (low priority): endpointslice topologycache zone ratios
- TODO: compatibility decision for LimitRanger re-admission of unchanged PVC values (#141348 merged without it; #141352 ratchets stored pod-level hugepages), plus create/update/skew test coverage for the chosen behavior
#140135: OpenAPI description CommonMark formatting
#142052 (@krishhna24): golden String/JSON/CBOR/proto tests; lifts the hold on #138166
TODO: CEL isInteger()/asInteger() coverage for 19-digit quantities (#141938)
#138166 (@tgao26, fixes #138165): canonicalization of fractional decimal inputs; /hold until #142052 merges; needs rebase; must stay backward compatible
#135995: Mul/Div
Exit criteria
An item is done when it has a merged fix with regression coverage, or an explicit deferral to a linked follow-up issue with an owner. The tracker closes when every task-list line is one of those two.
Out of scope: #140744 and #141404 (shallow-copy detection and the read-mutation data race, separate effort), #137590 (parse-format widening), #131972 (CRD formats).
/sig api-machinery /kind bug /kind cleanup cc @liggitt @thc1006 @qflen @pohly @semx
Source: kubernetes/kubernetes