Files
hamlab/internal/storage/arc_test.go
T
Mitchell-JohnsonandClaude Fable 5.1 b44faf3ac9 The ZFS tab shows the ARC, and names every caching tier a pool has or lacks
A Cache section between the pools and the schedules. One card for the ARC:
size against its ceiling with the target and floor, sparklines of size and
hit rate from the sampler's history, what it holds (MRU/MFU/dirty, data vs
metadata, compression, the ghost lists), the prefetcher and dbuf cache, the
memory-pressure counters, and the since-boot breakdown by demand/prefetch
and data/metadata. Then a card per pool for L2ARC, SLOG, special vdev,
dedup table and spares — the devices and their fill where they exist, and
where they don't, plainly none and what that means (the ZIL writing to the
pool's own vdevs, with how much it has written).

Live numbers come from the sampler, which reads arcstats every tick and
publishes zfs.arc.* sensors; the fuller picture is read on request, with
zpool list -Hpv and zfs get run without sudo and cached for thirty seconds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 18:30:02 -07:00

72 lines
2.4 KiB
Go

package storage
import (
"context"
"encoding/json"
"os"
"testing"
)
// zpool list -Hpv on a pool with every extra class; the data raidz and its disks are skipped
const listV = "tank\t32000000000000\t23000000000000\t9000000000000\tONLINE\n" +
"\traidz2-0\t32000000000000\t23000000000000\t9000000000000\tONLINE\n" +
"\t\twwn-0x1\t4000000000000\t-\t-\tONLINE\n" +
"\t\twwn-0x2\t4000000000000\t-\t-\tONLINE\n" +
"\tspecial\t-\t-\t-\t-\n" +
"\t\tmirror-1\t500000000000\t120000000000\t380000000000\tONLINE\n" +
"\t\t\tnvme0n1p1\t500000000000\t-\t-\tONLINE\n" +
"\t\t\tnvme1n1p1\t500000000000\t-\t-\tONLINE\n" +
"\tlogs\t-\t-\t-\t-\n" +
"\t\tnvme2n1p1\t16000000000\t4000000\t15996000000\tONLINE\n" +
"\tcache\t-\t-\t-\t-\n" +
"\t\tnvme2n1p2\t400000000000\t210000000000\t190000000000\tONLINE\n" +
"\tspares\t-\t-\t-\t-\n" +
"\t\twwn-0x9\t4000000000000\t-\t-\tAVAIL\n" +
"scratch\t1000000000000\t1000000000\t999000000000\tONLINE\n" +
"\tsda\t1000000000000\t1000000000\t999000000000\tONLINE\n"
func TestParsePoolCaching(t *testing.T) {
got := parsePoolCaching(listV)
if len(got) != 2 || got[0].Pool != "tank" || got[1].Pool != "scratch" {
t.Fatalf("pools: %+v", got)
}
tank := got[0]
if len(tank.Cache) != 1 || tank.Cache[0].Name != "nvme2n1p2" || tank.Cache[0].Alloc != 210000000000 {
t.Errorf("cache: %+v", tank.Cache)
}
if len(tank.Log) != 1 || tank.Log[0].Name != "nvme2n1p1" {
t.Errorf("log: %+v", tank.Log)
}
if len(tank.Special) != 3 || tank.Special[0].Name != "mirror-1" || tank.Special[0].Alloc != 120000000000 {
t.Errorf("special: %+v", tank.Special)
}
if len(tank.Spare) != 1 || tank.Spare[0].Health != "AVAIL" {
t.Errorf("spares: %+v", tank.Spare)
}
if len(tank.Dedup) != 0 {
t.Errorf("no dedup class: %+v", tank.Dedup)
}
s := got[1]
if len(s.Cache)+len(s.Log)+len(s.Special)+len(s.Spare) != 0 {
t.Errorf("a plain pool has no extra classes: %+v", s)
}
}
func TestHumanSize(t *testing.T) {
for in, want := range map[uint64]string{131072: "128K", 1048576: "1M", 16384: "16K", 1000: "1000"} {
if got := humanSize(in); got != want {
t.Errorf("%d → %s, want %s", in, got, want)
}
}
}
// TestLiveARC reads this machine's ZFS for real: HAMLAB_LIVE=1 go test -run TestLiveARC -v ./internal/storage
func TestLiveARC(t *testing.T) {
if os.Getenv("HAMLAB_LIVE") == "" {
t.Skip("set HAMLAB_LIVE=1 to read the real machine")
}
d := New(false).ARC(context.Background())
b, _ := json.MarshalIndent(d, "", " ")
t.Logf("%s", b)
}