Artifact b1d72e127d15250e40330f15125d30be003500ed514a6e8baacb15ec6b4b96cc:
- File
internal/manifest/parsed_memo.go
— part of check-in
[d367012009]
at
2026-10-25 23:10:47
on branch feat/read-blob-content-cache
— feat: read an artifact by hash and cache content on Repo
Repo.ReadBlob expands one artifact by its full hash after the availability check ReadFile applies, and names an unknown hash with ErrArtifactNotFound.
Repo.SetContentCache attaches a byte-bounded content.Cache that ReadFile, ReadFileAt, ReadBlob, ListFiles, Diff and Annotate share; check-in parses are kept in its derived memo, so reads at one check-in parse the manifest once. Default off. A read that finds the DB's finished-write count moved swaps in an empty cache, so a purged, compacted or elided artifact, and a rid SQLite reuses after a purge, is never served from it. Writes by another process are not seen; the method's doc says so.
Tracked in #492. (user: [email protected], size: 3654) [annotate] [blame] [check-ins using] [more...]
package manifest import ( "errors" "fmt" "github.com/danmestas/go-libfossil/db" "github.com/danmestas/go-libfossil/internal/content" "github.com/danmestas/go-libfossil/internal/deck" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/internal/repo" ) // A sweep reads each check-in as a child, again as its child's parent, and // again as a delta baseline, and parsing a large manifest dominates each read. // The parsed deck and file views of the few most recent check-ins are kept in // the sweep's content.Cache beside their bytes, so each is built once. Both are // shared: no caller may modify them. type ( parsedKey libfossil.FslID // -> *deck.Deck filesKey libfossil.FslID // -> *manifestFiles baselineKey string // baseline UUID -> libfossil.FslID ) // errExpandManifest marks a manifest whose bytes could not be expanded, as // against bytes that expanded but do not parse. var errExpandManifest = errors.New("expand manifest") // rememberCheckin keeps d, the parse of the check-in manifest at rid. func rememberCheckin(cache *content.Cache, rid libfossil.FslID, d *deck.Deck) { if d.Type == deck.Checkin { cache.SetDerived(parsedKey(rid), d) } } // parseManifestAt expands and parses the manifest at rid. A check-in is served // from, and kept in, cache's derived memo. func parseManifestAt(q db.Querier, cache *content.Cache, rid libfossil.FslID) (*deck.Deck, error) { if v, ok := cache.Derived(parsedKey(rid)); ok { return v.(*deck.Deck), nil } data, err := expandManifestBytes(q, cache, rid) if err != nil { return nil, fmt.Errorf("%w rid=%d: %w", errExpandManifest, rid, err) } if len(data) == 0 { return nil, fmt.Errorf("manifest rid=%d is empty", rid) } d, err := deck.Parse(data) if err != nil { return nil, fmt.Errorf("parse manifest rid=%d: %w", rid, err) } rememberCheckin(cache, rid, d) return d, nil } // checkinManifest is GetManifestCached for a reader of a check-in's file set, // which only reads the deck: a check-in's parse is served from, and kept in, // cache's derived memo, so reads of several names at one check-in parse its // manifest once. A nil cache is exactly GetManifestCached. func checkinManifest(r *repo.Repo, rid libfossil.FslID, cache *content.Cache) (*deck.Deck, error) { if v, ok := cache.Derived(parsedKey(rid)); ok { return v.(*deck.Deck), nil } d, err := GetManifestCached(r, rid, cache) if err != nil { return nil, err } rememberCheckin(cache, rid, d) return d, nil } // rememberFiles keeps mf, the file views of the check-in manifest at rid. func rememberFiles(cache *content.Cache, rid libfossil.FslID, mf *manifestFiles) { cache.SetDerived(filesKey(rid), mf) } // manifestFilesAt returns the file views of d, the parse of the check-in at rid. func manifestFilesAt(q db.Querier, cache *content.Cache, rid libfossil.FslID, d *deck.Deck) (*manifestFiles, error) { if v, ok := cache.Derived(filesKey(rid)); ok { return v.(*manifestFiles), nil } mf, err := loadManifestFiles(q, cache, d) if err != nil { return nil, err } rememberFiles(cache, rid, mf) return mf, nil } // baselineRid resolves a delta's baseline hash to its available rid. Content // is only ever added, so an available baseline stays available and the chain // walk behind that verdict is kept with the cache. func baselineRid(q db.Querier, cache *content.Cache, uuid string) (libfossil.FslID, bool) { if v, ok := cache.Derived(baselineKey(uuid)); ok { return v.(libfossil.FslID), true } rid, ok := content.AvailableByUUID(q, uuid) if ok { cache.SetDerived(baselineKey(uuid), rid) } return rid, ok }