π Problem
IOTokenBuckets (pkg/cache/cache.go:704-758) and CostBuckets (cache.go:859-915) are ~55-line near-clones: identical bucket-align preamble, identical strftime bucket-epoch SQL (differing in one line β the SUM expression), identical map-fill and bucket-fill loops, identical slog tail. Only the value type (int64 vs float64) and built struct differ.
#338 already solved this exact problem for the daily path with the generic dailyBuckets[V, T] (cache.go:775-830) β the sub-day path never got the same treatment.
πͺ¦ Drift is proven: the COALESCE defensiveness is present in IOTokenBuckets (line ~720) but absent in CostBuckets (line ~875). The comment on CostBuckets literally says "Mirrors IOTokenBuckets exactly" β a manual sync contract.
π οΈ Suggested shape
Extract a subDayBuckets[V int64 | float64, T any](ctx, c, aggExpr string, dur, from, to time.Time, build func(time.Time, V) T) mirroring dailyBuckets, leaving both public methods as ~6-line dispatchers.
β οΈ Notes
π Problem
IOTokenBuckets(pkg/cache/cache.go:704-758) andCostBuckets(cache.go:859-915) are ~55-line near-clones: identical bucket-align preamble, identicalstrftimebucket-epoch SQL (differing in one line β the SUM expression), identical map-fill and bucket-fill loops, identical slog tail. Only the value type (int64vsfloat64) and built struct differ.#338 already solved this exact problem for the daily path with the generic
dailyBuckets[V, T](cache.go:775-830) β the sub-day path never got the same treatment.πͺ¦ Drift is proven: the
COALESCEdefensiveness is present inIOTokenBuckets(line ~720) but absent inCostBuckets(line ~875). The comment onCostBucketsliterally says "Mirrors IOTokenBuckets exactly" β a manual sync contract.π οΈ Suggested shape
Extract a
subDayBuckets[V int64 | float64, T any](ctx, c, aggExpr string, dur, from, to time.Time, build func(time.Time, V) T)mirroringdailyBuckets, leaving both public methods as ~6-line dispatchers.TestIOTokenBuckets_*,TestCostBuckets_*,BenchmarkIOTokenBuckets/CostBuckets.COALESCEguard belongs in the shared helper (it should), so the consolidation also resolves the drift.