add ZFS ARC memory accounting

This commit is contained in:
Henry Dollman
2024-10-05 18:07:42 -04:00
parent af4877ca30
commit 73aae62c2e
5 changed files with 70 additions and 15 deletions

View File

@@ -13,6 +13,7 @@ import (
type Agent struct {
debug bool // true if LOG_LEVEL is set to debug
zfs bool // true if system has arcstats
memCalc string // Memory calculation formula
fsNames []string // List of filesystem device names being monitored
fsStats map[string]*system.FsStats // Keeps track of disk stats for each filesystem

View File

@@ -3,9 +3,12 @@ package agent
import (
"beszel"
"beszel/internal/entities/system"
"bufio"
"fmt"
"log/slog"
"os"
"strconv"
"strings"
"time"
"github.com/shirou/gopsutil/v4/cpu"
@@ -36,6 +39,13 @@ func (a *Agent) initializeSystemInfo() {
a.systemInfo.Threads = threads
}
}
// zfs
if _, err := getARCSize(); err == nil {
a.zfs = true
} else {
slog.Debug("Not monitoring ZFS ARC", "err", err)
}
}
// Returns current info, stats about the host system
@@ -52,6 +62,9 @@ func (a *Agent) getSystemStats() system.Stats {
// memory
if v, err := mem.VirtualMemory(); err == nil {
// swap
systemStats.Swap = bytesToGigabytes(v.SwapTotal)
systemStats.SwapUsed = bytesToGigabytes(v.SwapTotal - v.SwapFree - v.SwapCached)
// cache + buffers value for default mem calculation
cacheBuff := v.Total - v.Free - v.Used
// htop memory calculation overrides
@@ -61,9 +74,15 @@ func (a *Agent) getSystemStats() system.Stats {
v.Used = v.Total - (v.Free + cacheBuff)
v.UsedPercent = float64(v.Used) / float64(v.Total) * 100.0
}
// subtract ZFS ARC size from used memory and add as its own category
if a.zfs {
if arcSize, _ := getARCSize(); arcSize > 0 && arcSize < v.Used {
v.Used = v.Used - arcSize
v.UsedPercent = float64(v.Used) / float64(v.Total) * 100.0
systemStats.MemZfsArc = bytesToGigabytes(arcSize)
}
}
systemStats.Mem = bytesToGigabytes(v.Total)
systemStats.Swap = bytesToGigabytes(v.SwapTotal)
systemStats.SwapUsed = bytesToGigabytes(v.SwapTotal - v.SwapFree - v.SwapCached)
systemStats.MemBuffCache = bytesToGigabytes(cacheBuff)
systemStats.MemUsed = bytesToGigabytes(v.Used)
systemStats.MemPct = twoDecimals(v.UsedPercent)
@@ -192,3 +211,29 @@ func (a *Agent) getSystemStats() system.Stats {
return systemStats
}
// Returns the size of the ZFS ARC memory cache in bytes
func getARCSize() (uint64, error) {
file, err := os.Open("/proc/spl/kstat/zfs/arcstats")
if err != nil {
return 0, err
}
defer file.Close()
// Scan the lines
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "size") {
// Example line: size 4 15032385536
fields := strings.Fields(line)
if len(fields) < 3 {
return 0, err
}
// Return the size as uint64
return strconv.ParseUint(fields[2], 10, 64)
}
}
return 0, fmt.Errorf("failed to parse size field")
}

View File

@@ -11,6 +11,7 @@ type Stats struct {
MemUsed float64 `json:"mu"`
MemPct float64 `json:"mp"`
MemBuffCache float64 `json:"mb"`
MemZfsArc float64 `json:"mz,omitempty"` // ZFS ARC memory
Swap float64 `json:"s,omitempty"`
SwapUsed float64 `json:"su,omitempty"`
DiskTotal float64 `json:"d"`

View File

@@ -1,16 +1,8 @@
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
import {
useYAxisWidth,
chartTimeData,
cn,
formatShortDate,
toFixedFloat,
twoDecimalString,
} from '@/lib/utils'
import { useYAxisWidth, chartTimeData, cn, toFixedFloat, twoDecimalString } from '@/lib/utils'
import { useMemo } from 'react'
// import Spinner from '../spinner'
import { useStore } from '@nanostores/react'
import { $chartTime } from '@/lib/stores'
import { SystemStatsRecord } from '@/types'
@@ -79,16 +71,16 @@ export default function MemChart({
content={
<ChartTooltipContent
// @ts-ignore
itemSorter={(a, b) => a.name.localeCompare(b.name)}
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
itemSorter={(a, b) => a.order - b.order}
contentFormatter={(item) => twoDecimalString(item.value) + ' GB'}
indicator="line"
/>
}
/>
<Area
dataKey="stats.mu"
name="Used"
order={3}
dataKey="stats.mu"
type="monotoneX"
fill="hsl(var(--chart-2))"
fillOpacity={0.4}
@@ -96,9 +88,23 @@ export default function MemChart({
stackId="1"
isAnimationActive={false}
/>
{systemData.at(-1)?.stats.mz && (
<Area
name="ZFS ARC"
order={2}
dataKey="stats.mz"
type="monotoneX"
fill="hsla(175 60% 45% / 0.8)"
fillOpacity={0.5}
stroke="hsla(175 60% 45% / 0.8)"
stackId="1"
isAnimationActive={false}
/>
)}
<Area
dataKey="stats.mb"
name="Cache / Buffers"
order={1}
dataKey="stats.mb"
type="monotoneX"
fill="hsla(160 60% 45% / 0.5)"
fillOpacity={0.4}

View File

@@ -43,6 +43,8 @@ export interface SystemStats {
mp: number
/** memory buffer + cache (gb) */
mb: number
/** zfs arc memory (gb) */
mz?: number
/** swap space (gb) */
s: number
/** swap used (gb) */