Something went wrong. Try again.
Monorepo for Tangled forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127package engine
import ( "context" "slices" "testing" "time"
"tangled.org/core/spindle/db" "tangled.org/core/spindle/nixcache" "tangled.org/core/spindle/quota")
func readyCharged(t *testing.T, d *db.DB, qs *db.QuotaStore, entry db.CacheEntry, size int64) { t.Helper() insertCacheTestEntry(t, d, entry) if _, err := d.MarkCacheEntryReady(context.Background(), entry.ID, size, qs, entry.LastUsedAt); err != nil { t.Fatal(err) }}
func TestCacheEntriesKindSkipsPinnedAndTouchedEntries(t *testing.T) { ctx := context.Background() d := newCacheTestDB(t) qs := db.NewQuotaStore(d, nil) base := time.Now().Add(-time.Hour) for i, id := range []string{"touched", "restoring", "cold"} { readyCharged(t, d, qs, cacheTestEntry(id, "did:plc:repo", "microvm", id, "h", "pending", base.Add(time.Duration(i)*time.Minute)), 10) } readyCharged(t, d, qs, cacheTestEntry("other-repo", "did:plc:other", "microvm", "k", "h", "pending", base), 10) if pinned, err := d.BeginCacheRestore(ctx, "restoring"); err != nil || !pinned { t.Fatal(pinned, err) } store := &fakeStorage{objects: map[string][]byte{"objects/touched": []byte("t"), "objects/cold": []byte("c")}} kind := CacheEntries(d, store)
listed, err := kind.Evictable(ctx, quota.Subject{Scope: quota.ScopeRepo, DID: "did:plc:repo"}, 10) if err != nil || len(listed) != 2 || listed[0].Key != "touched" || listed[1].Key != "cold" { t.Fatalf("listed %v, %v; want the repo's unpinned entries oldest first", listed, err) } owned, err := kind.Evictable(ctx, quota.Subject{Scope: quota.ScopeUser, DID: "did:plc:owner"}, 10) if err != nil || len(owned) != 3 { t.Fatalf("owner listed %v, %v; want both repos' unpinned entries", owned, err) } if err := d.TouchCacheEntry(ctx, "touched", time.Now()); err != nil { t.Fatal(err) }
gone, err := kind.Evict(ctx, listed) if err != nil || len(gone) != 1 || gone[0].Key != "cold" { t.Fatalf("Evict = %v, %v; want only cold", gone, err) } if !cacheTestEntryExists(t, d, "touched") || !store.has("objects/touched") || !genericCharged(t, d, "touched") { t.Fatal("evicted an entry restored after it was listed") } if cacheTestEntryExists(t, d, "cold") || store.has("objects/cold") || genericCharged(t, d, "cold") { t.Fatal("cold entry left its row, object or charge") }}
type fakeAttic struct{ deleted []string }
func (a *fakeAttic) DeletePaths(_ context.Context, _ string, hashes []string) ([]string, error) { a.deleted = append(a.deleted, hashes...) return hashes, nil}
func (a *fakeAttic) MissingPaths(context.Context, string, []string) ([]string, error) { return nil, nil}
func TestEvictorTrimsNixPathsAndCacheEntriesInOneOrder(t *testing.T) { ctx := context.Background() d := newCacheTestDB(t) qs := db.NewQuotaStore(d, quota.Defaults{quota.ScopeRepo: {quota.ResourceCacheStorageBytes: 30}}) id := quota.Identity{OwnerDID: "did:plc:owner", RepoDID: "did:plc:repo"} now := time.Now() nixPath := func(hash string, ago time.Duration) { t.Helper() res, err := qs.Reserve(ctx, quota.ReserveRequest{ Kind: quota.KindNixCache, Key: quota.NixCacheKey("cache", hash), Identity: id, Resources: quota.Resources{quota.ResourceCacheStorageBytes: 10}, NixObject: "a.NixObject{Cache: "cache", StoreHash: hash, StorePath: "/nix/store/" + hash + "-x", NarSize: 10}, }) if err != nil || !res.Allowed { t.Fatal(res, err) } if err := qs.Commit(ctx, res.ID); err != nil { t.Fatal(err) } if _, err := d.Exec(`update nix_objects set last_used_at = ? where store_hash = ?`, now.Add(-ago).Unix(), hash); err != nil { t.Fatal(err) } } nixPath("nix-oldest", 5*time.Hour) readyCharged(t, d, qs, cacheTestEntry("generic-old", id.RepoDID, "microvm", "a", "h", "pending", now.Add(-4*time.Hour)), 10) nixPath("nix-new", 2*time.Hour) readyCharged(t, d, qs, cacheTestEntry("generic-new", id.RepoDID, "microvm", "b", "h", "pending", now.Add(-time.Hour)), 10)
attic := &fakeAttic{} store := &fakeStorage{objects: map[string][]byte{"objects/generic-old": []byte("o"), "objects/generic-new": []byte("n")}} evictor := "a.Evictor{ Usage: qs, Limits: qs, Kinds: []quota.CacheKind{ CacheEntries(d, store), &nixcache.Pruner{Index: qs, Attic: attic}, }, } if n, err := evictor.Evict(ctx); err != nil || n != 1 { t.Fatalf("Evict = %d, %v; want 1 (40 bytes down to 30)", n, err) } if !slices.Equal(attic.deleted, []string{"nix-oldest"}) || !cacheTestEntryExists(t, d, "generic-old") { t.Fatalf("attic deleted %v, want only the oldest path of either kind", attic.deleted) }
if err := qs.SetLimit(ctx, id.RepoDID, quota.ResourceCacheStorageBytes, 15); err != nil { t.Fatal(err) } if n, err := evictor.Evict(ctx); err != nil || n != 2 { t.Fatalf("Evict = %d, %v; want 2 (30 bytes down to 10)", n, err) } if !slices.Equal(attic.deleted, []string{"nix-oldest", "nix-new"}) || cacheTestEntryExists(t, d, "generic-old") || !cacheTestEntryExists(t, d, "generic-new") { t.Fatalf("attic deleted %v; want the next two oldest gone and generic-new kept", attic.deleted) }}