package hae_test import ( "fmt" "runtime" "strings" "testing" "time" "git.vakhrushev.me/av/healthlog/internal/hae" ) // Тела доходили до 42 МиБ, и стратегия декодирования — часть контракта, а не // деталь: разбор такого тела в map[string]any удерживает 197 МиБ кучи против // 54 МиБ у формы с json.RawMessage. Вместе с самим телом и удвоением в чтении // пик доходил бы до ~300 МиБ на доставку — это OOM ровно на пике потока, когда // терять доставки дороже всего. // // Тест сторожит именно порядок величины, а не точное число: он обязан падать // при возврате к map[string]any и не падать от смены версии Go. func TestParseУдержаниеКучи(t *testing.T) { if testing.Short() { t.Skip("измерение кучи: не для -short") } body := largeBody(40, 4000) t.Logf("тело %d МиБ", len(body)>>20) var before, after runtime.MemStats runtime.GC() runtime.ReadMemStats(&before) res, err := hae.Parse(body, hae.Meta{Aggregation: "Minutes"}) if err != nil { t.Fatalf("разбор: %v", err) } runtime.GC() runtime.ReadMemStats(&after) retained := int64(after.HeapAlloc) - int64(before.HeapAlloc) limit := int64(len(body)) * 4 t.Logf("точек %d, удержано %d МиБ при теле %d МиБ", len(res.Points), retained>>20, len(body)>>20) if retained > limit { t.Errorf("удержано %d МиБ при теле %d МиБ — больше четырёх тел; "+ "похоже, точки снова разбираются в map[string]any", retained>>20, len(body)>>20) } // Результат обязан дожить до замера, иначе мерить нечего. runtime.KeepAlive(res) runtime.KeepAlive(body) } // largeBody собирает тело, похожее на настоящее: те же поля, тот же формат // метки, минутное выравнивание. func largeBody(metrics, points int) []byte { var b strings.Builder b.WriteString(`{"data":{"metrics":[`) base := time.Date(2025, 6, 5, 0, 0, 0, 0, time.FixedZone("", 3*3600)) for m := range metrics { if m > 0 { b.WriteByte(',') } fmt.Fprintf(&b, `{"name":"metric_%d","units":"count","data":[`, m) for p := range points { if p > 0 { b.WriteByte(',') } ts := base.Add(time.Duration(p) * time.Minute).Format("2006-01-02 15:04:05 -0700") fmt.Fprintf(&b, `{"date":"%s","qty":%d.09523182962471353,"source":"Device A|Device B"}`, ts, p%97) } b.WriteString(`]}`) } b.WriteString(`]}}`) return []byte(b.String()) }