{"record":{"id":"9ab36c7097290177","repo":"JuliusBrussee/caveman","slug":"cacheengine-longer-prefix-cannot-have-higher-expe","errorCode":null,"errorMessage":"cacheengine: longer prefix cannot have higher expected reuse","messagePattern":"cacheengine: longer prefix cannot have higher expected reuse","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/engine.go","lineNumber":377,"sourceCode":"func breakpointCandidates(segments []Segment, defaultCalls int, profile Profile) ([]Breakpoint, bool, bool, error) {\n\tvar candidates []Breakpoint\n\tvar prefix []byte\n\tcumulativeTokens := 0\n\tpreviousCalls := math.MaxInt\n\tbelowMinimum := false\n\tnegative := false\n\tfor index, segment := range segments {\n\t\tprefix = appendFrame(prefix, segment.Name, segment.Content)\n\t\tif segment.Tokens > math.MaxInt-cumulativeTokens {\n\t\t\treturn nil, false, false, errors.New(\"cacheengine: cumulative token count overflow\")\n\t\t}\n\t\tcumulativeTokens += segment.Tokens\n\t\tcalls := segment.ExpectedCalls\n\t\tif calls == 0 {\n\t\t\tcalls = defaultCalls\n\t\t}\n\t\tif calls > previousCalls {\n\t\t\treturn nil, false, false, errors.New(\"cacheengine: longer prefix cannot have higher expected reuse\")\n\t\t}\n\t\tpreviousCalls = calls\n\t\tif calls < 2 {\n\t\t\tcontinue\n\t\t}\n\t\tif cumulativeTokens > 0 && cumulativeTokens < profile.MinPrefixTokens {\n\t\t\tbelowMinimum = true\n\t\t\tcontinue\n\t\t}\n\t\tnet := 0.0\n\t\tif cumulativeTokens > 0 && profile.EconomicsKnown {\n\t\t\trawNet := float64(cumulativeTokens) * (float64(calls) - profile.WriteMultiplier - float64(calls-1)*profile.ReadMultiplier)\n\t\t\tif math.IsNaN(rawNet) || math.IsInf(rawNet, 0) {\n\t\t\t\treturn nil, false, false, errors.New(\"cacheengine: cache economics overflow\")\n\t\t\t}\n\t\t\tnet = roundUnits(rawNet)\n\t\t\tif net <= 0 {\n\t\t\t\tnegative = true","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/engine.go#L359-L395","documentation":"Thrown by breakpointCandidates when expected reuse is not monotonically non-increasing: a longer prefix (more segments) reports strictly more expected calls than the previous shorter prefix. Since every caller of the longer prefix also sees the shorter one, reuse counts physically cannot grow with prefix length — a violation means wrong measurements.","triggerScenarios":"Setting ExpectedCalls per segment such that a later segment in the list has a higher value (after substituting defaultCalls for 0) than any earlier one, e.g. segments [10, 5, 8] or [3, 0(default=100)] where the default exceeds the earlier explicit value.","commonSituations":"Per-segment analytics counting events instead of prefix-sharing callers; mixing a small explicit ExpectedCalls on an early segment with a zero (defaulted) value on a later one where defaultCalls is larger; ordering segments by recency instead of by prefix stability so counts grow down the list.","solutions":["Compute ExpectedCalls as non-increasing over the segment list (it is a property of nested prefixes — cap later values at the earlier ones)","Pass a defaultCalls value no larger than the smallest explicit ExpectedCalls in the list, or set explicit values everywhere","Order segments so the most-reused, most-stable content is first"],"exampleFix":"// before\nsegments := []cacheengine.Segment{\n    {Name: \"sys\", Stable: true, Cacheable: true, Content: sys, Tokens: 900, ExpectedCalls: 3},\n    {Name: \"docs\", Stable: true, Cacheable: true, Content: docs, Tokens: 5000, ExpectedCalls: 8},\n}\n\n// after\nsegments := []cacheengine.Segment{\n    {Name: \"sys\", Stable: true, Cacheable: true, Content: sys, Tokens: 900, ExpectedCalls: 8},\n    {Name: \"docs\", Stable: true, Cacheable: true, Content: docs, Tokens: 5000, ExpectedCalls: 3},\n}","handlingStrategy":"validation","validationCode":"func monotonicCalls(segs []cacheengine.Segment, def int) error {\n    prev := math.MaxInt\n    for _, s := range segs {\n        c := s.ExpectedCalls\n        if c == 0 { c = def }\n        if c > prev { return errors.New(\"expected calls must be non-increasing\") }\n        prev = c\n    }\n    return nil\n}","typeGuard":"// n/a","tryCatchPattern":null,"preventionTips":["Order segments most-reused-first and cap later counts at earlier ones","Keep defaultCalls <= the smallest explicit ExpectedCalls"],"tags":["cacheengine","invariant","segments","reuse-estimation","go"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}