Files
felhom.eu/hub/internal/api/host_swap.go
T

54 lines
2.6 KiB
Go

package api
import (
"encoding/json"
"fmt"
)
// R-1192 follow-up (night 2026-10-21). The guest swap fix (agent: every guest may swap as much as its RAM) moves a
// memory burst onto the HOST's swap. Our installer gives an 8 GB machine 7.75 GB of it (measured on demo-hp); a host
// with none — a BYO install, or swap turned off — gets nothing from the fix, and the household's first big upload can
// freeze the box again. The agent reports both numbers; the hub tells the OPERATOR once per host when the host's swap
// is below its guests' allowance (host_swap_short), and once when it is covered again (host_swap_ok). Edge-triggered
// from the host's own newest swap event, so a hub restart does not mail again. Operator-only (notify.operatorOnlyEvents):
// the household cannot add swap to its host.
const (
eventHostSwapShort = "host_swap_short"
eventHostSwapOK = "host_swap_ok"
)
func (h *Handler) checkHostSwap(hostID, custID string, hostMB, guestMB *int64) {
if hostMB == nil || guestMB == nil || custID == "" {
return // an older agent: not evaluated
}
short := *guestMB > 0 && *hostMB < *guestMB
prev, err := h.store.LatestHostSwapEvent(custID, hostID)
if err != nil {
h.logger.Printf("[WARN] host %s swap check: reading the last swap event failed: %v", hostID, err)
return
}
wasShort := prev != nil && prev.EventType == eventHostSwapShort
var typ, sev, msg string
switch {
case short && !wasShort:
typ, sev = eventHostSwapShort, "warning"
msg = fmt.Sprintf("Host %s: its swap (%d MB) is below its guests' swap allowance (%d MB). The guest swap fix needs swap on the host behind it; "+
"without it a memory burst — a big first upload on an 8 GB box — can freeze the box for minutes (R-1192). Add swap on the host "+
"(our installer gives an 8 GB machine 7.75 GB) or lower the guest's RAM. The household cannot fix this.", hostID, *hostMB, *guestMB)
case !short && wasShort:
typ, sev = eventHostSwapOK, "info"
msg = fmt.Sprintf("Host %s: its swap (%d MB) covers its guests' swap allowance (%d MB) again.", hostID, *hostMB, *guestMB)
default:
return
}
h.logger.Printf("[WARN] host %s: %s (host swap %d MB, guests' swap %d MB)", hostID, typ, *hostMB, *guestMB)
details, _ := json.Marshal(map[string]any{"host_id": hostID, "host_swap_mb": *hostMB, "guest_swap_mb": *guestMB})
if _, eerr := h.store.SaveEvent(custID, typ, sev, msg, string(details), "hub"); eerr != nil {
h.logger.Printf("[WARN] %s event save FAILED for %s: %v", typ, hostID, eerr)
return // the audit row first: a mail without its event row lies
}
if h.dispatcher != nil {
go h.dispatcher.ProcessEvent(custID, typ, sev, msg, string(details), "hub")
}
}