Artifact 06f223bae9b166a17a13ba16e64015c60400e5eec16f1e479ea3258261465ca5:
- File
internal/content/export_test.go
— part of check-in
[bfce209a24]
at
2026-10-29 12:46:28
on branch perf/463-memo-trees
— perf(content): keep the memo's trees for the life of the memo
HZ-6.2-10 asks that an origin read each structural artifact's F cards once per store. The memo's tree index already kept fossil-scm.org's whole tree history in about 11MiB, yet it still read trees again in two cases: past a 64MiB bound, and after any full read of the store, which dropped the index with its lineage.
Neither was needed. A tree piece is what its name's bytes say, so it is valid in every store that holds the name; the rid it was read at is checked against the view's own rid-to-name map, and a renumbered store reads it once more and moves it to its rid. The index grows with the store like the reference graph beside it, so the byte bound goes; only a names bound at the most artifacts a classified store can hold remains, which one store cannot reach.
On the fossil-scm.org clone a horizon 2000 back after a full read now takes 1.0-1.8s instead of 4.3-6.2s, and the whole history after a full read reads no tree (1.3-1.5s, from 14-17s). Cold runs and peak heap are unchanged. HZ-6.2-10 is now covered; the trees still enter the graph when a walk first reaches them, as the reverted eager read cost 4-5x cold. (user: [email protected], size: 2008) [annotate] [blame] [check-ins using]
package content import ( "context" "maps" "slices" "github.com/danmestas/go-libfossil/db" ) // LoadAbsencesWithin is LoadAbsencesContext under injected budgets, and // reports what classification read. The tests that use it are an external // package: their fixtures need the manifest writer, which imports this one. func LoadAbsencesWithin( ctx context.Context, q db.Querier, refs ReferenceBudget, treePerCheckin int64, ) (*Absences, ReferenceWork, error) { treeMax := treePerCheckin << 10 if treeMax > defaultReferenceLimits.treeMax { treeMax = defaultReferenceLimits.treeMax } a, err := loadAbsences(ctx, q, referenceLimits{ refs: refs, treePerCheckin: treePerCheckin, treeMax: treeMax, }) if a == nil { return nil, ReferenceWork{}, err } return a, a.work, err } // LoadAbsencesWork is LoadAbsencesContext, reporting what classification // read under the default budgets. func LoadAbsencesWork(ctx context.Context, q db.Querier) (*Absences, ReferenceWork, error) { limits := defaultReferenceLimits return LoadAbsencesWithin(ctx, q, limits.refs, limits.treePerCheckin) } // TreesRead names the trees v's horizon walks read from the store, and // counts the reads. func (v *MemoView) TreesRead() (names []string, reads int) { return slices.Collect(maps.Keys(v.trees)), v.treeReads } // TreeBytes is what m charges for the trees it keeps. func (m *Memo) TreeBytes() int64 { m.mu.Lock() defer m.mu.Unlock() if m.trees == nil { return 0 } return m.trees.bytes } // TreeChainDepth is the most set differences any tree m keeps lies from // one kept whole. func (m *Memo) TreeChainDepth() int { m.mu.Lock() defer m.mu.Unlock() depth := 0 if m.trees != nil { for _, p := range m.trees.pieces { depth = max(depth, p.depth) } } return depth } // ForgetStore makes m's next view read its store in full, as one does after // an arrival a held artifact cited, keeping what else m holds. func (m *Memo) ForgetStore() { m.mu.Lock() defer m.mu.Unlock() m.store = nil }