a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
94 lines
2.6 KiB
Go
94 lines
2.6 KiB
Go
package persistence
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"encoding/gob"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
const (
|
|
frecencyFile = "frecency.gob.gz"
|
|
maxFrecencyAccesses = 16 // entries per symbol; matches FFF's bounded deque
|
|
maxFrecencySymbols = 10000
|
|
)
|
|
|
|
// FrecencyAccesses is the bounded, ordered (oldest→newest) list of unix
|
|
// timestamps (seconds) at which one symbol was consumed. Bounded because
|
|
// the decay formula already weights recent accesses far more heavily than
|
|
// old ones — beyond ~16 entries, additional history contributes noise.
|
|
type FrecencyAccesses struct {
|
|
SymbolID string
|
|
Times []int64
|
|
}
|
|
|
|
// FrecencyStore holds all per-symbol access histories for a single repo.
|
|
type FrecencyStore struct {
|
|
Version string
|
|
RepoPath string
|
|
Symbols []FrecencyAccesses
|
|
}
|
|
|
|
// MaxFrecencyAccesses returns the per-symbol access-history cap.
|
|
func MaxFrecencyAccesses() int { return maxFrecencyAccesses }
|
|
|
|
// FrecencyDir returns the on-disk directory for frecency storage.
|
|
func FrecencyDir(cacheDir, repoPath string) string {
|
|
return filepath.Join(cacheDir, RepoCacheKey(repoPath))
|
|
}
|
|
|
|
// LoadFrecency reads the frecency store from disk.
|
|
func LoadFrecency(dir string) (*FrecencyStore, error) {
|
|
path := filepath.Join(dir, frecencyFile)
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return &FrecencyStore{}, nil
|
|
}
|
|
return nil, fmt.Errorf("persistence: open frecency: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
gz, err := gzip.NewReader(f)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("persistence: gzip reader frecency: %w", err)
|
|
}
|
|
defer gz.Close()
|
|
|
|
var store FrecencyStore
|
|
if err := gob.NewDecoder(gz).Decode(&store); err != nil {
|
|
return nil, fmt.Errorf("persistence: gob decode frecency: %w", err)
|
|
}
|
|
return &store, nil
|
|
}
|
|
|
|
// SaveFrecency writes the frecency store to disk. Trims the oldest-touched
|
|
// symbols if over cap so the file can't grow unboundedly.
|
|
func SaveFrecency(dir string, store *FrecencyStore) error {
|
|
if len(store.Symbols) > maxFrecencySymbols {
|
|
trim := len(store.Symbols) - maxFrecencySymbols
|
|
store.Symbols = store.Symbols[trim:]
|
|
}
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return fmt.Errorf("persistence: mkdir frecency: %w", err)
|
|
}
|
|
path := filepath.Join(dir, frecencyFile)
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return fmt.Errorf("persistence: create frecency: %w", err)
|
|
}
|
|
gz := gzip.NewWriter(f)
|
|
enc := gob.NewEncoder(gz)
|
|
if err := enc.Encode(store); err != nil {
|
|
_ = gz.Close()
|
|
_ = f.Close()
|
|
return fmt.Errorf("persistence: gob encode frecency: %w", err)
|
|
}
|
|
if err := gz.Close(); err != nil {
|
|
_ = f.Close()
|
|
return fmt.Errorf("persistence: gzip close frecency: %w", err)
|
|
}
|
|
return f.Close()
|
|
}
|