diff --git a/CHANGELOG.md b/CHANGELOG.md index e43bc49..595375e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,13 @@ - **Fixed: `TestR603_GoNamedValuesDoNotHideBehindHTMLEscaping` was red on main since v0.337.0** (CI runs the gates, not `go test`): two English texts added that day carried an apostrophe (`flash.kept.changed`, `err.stacks.memory_overcommit_slow`); reworded. -- Tests (each seen failing first): `TestR1177_*` (5), `TestR1195_*` (5), `TestR1178_*`, `TestR1180_*`. +- **R-1192 follow-up — the install page says when the host has too little swap.** When the memory warning shows and + the agent (≥ the swap release) reports the host's swap below the guest's RAM, the page adds „A gépen nincs elég + cserehely (swap), ezért nagy feltöltéskor a doboz lelassulhat, és percekig sem válaszolhat. Szólj a Felhomnak, mi + rendbe tesszük." (EN „The machine has too little swap space, so during large uploads the box can slow down and not + answer for minutes. Tell Felhom; we will fix it."). An older agent is not judged (`agentapi.GuestMemoryInfo` + `host_swap_mb`/`guest_swap_mb`, 2 s timeout). The `deploy_new` i18n fixture regenerated on purpose (one line). +- Tests (each seen failing first): `TestR1177_*` (5), `TestR1195_*` (5), `TestR1178_*`, `TestR1180_*`, `TestR1192_SwapShort*`. ## v0.337.0 (2026-10-21) — kept second-drive copies (D25: R-1175, R-1181, R-1179); a `.fab` import keeps the family gate (R-1191); a stale restore temp no longer blocks the retry (R-1188 c); the overcommit sentence made true (R-1192); escrow recorded before the floor (R-1176); a finished off-site abandonment closes on the hub's word (R-1182); storage labels follow the language (W26) diff --git a/controller/internal/agentapi/client.go b/controller/internal/agentapi/client.go index 8ea8299..b01fbee 100644 --- a/controller/internal/agentapi/client.go +++ b/controller/internal/agentapi/client.go @@ -587,6 +587,10 @@ type GuestMemoryInfo struct { MaxMB int64 `json:"max_mb"` FloorMB int64 `json:"floor_mb"` Running bool `json:"running"` + // HostSwapMB / GuestSwapMB (agent ≥ the R-1192 swap release): the host's swap and this guest's swap allowance. + // nil = an older agent (not judged). + HostSwapMB *int64 `json:"host_swap_mb,omitempty"` + GuestSwapMB *int64 `json:"guest_swap_mb,omitempty"` } // MemoryResizeResult mirrors the agent's POST /guest/memory success body. diff --git a/controller/internal/backup/restore_record.go b/controller/internal/backup/restore_record.go index 1444f41..f6de2c8 100644 --- a/controller/internal/backup/restore_record.go +++ b/controller/internal/backup/restore_record.go @@ -102,8 +102,12 @@ func (m *Manager) LoadRestoreRecordAfterRecovery(putBack []string) *RestoreOpRes m.opInterrupted[rec.Stack] = res m.opRunning = false if m.logger != nil { - m.logger.Printf("[WARN] [backup] restore of %s (%s, started %s) was INTERRUPTED by a controller stop — recorded as failed; the household is told to run it again", - rec.Stack, rec.Op, rec.StartedAt.Format(time.RFC3339)) + tail := "the household is told to run it again" + if res.PutBack { + tail = "the start-up put its data back as it was before the restore (R-1195), and the household is told so" + } + m.logger.Printf("[WARN] [backup] restore of %s (%s, started %s) was INTERRUPTED by a controller stop — recorded as failed; %s", + rec.Stack, rec.Op, rec.StartedAt.Format(time.RFC3339), tail) } m.persistRestoreRecordLocked() out := res diff --git a/controller/internal/i18n/locales/en.json b/controller/internal/i18n/locales/en.json index 67d3999..43f0f82 100644 --- a/controller/internal/i18n/locales/en.json +++ b/controller/internal/i18n/locales/en.json @@ -2823,5 +2823,6 @@ "flash.kept.changed": "Another folder is in the place of the copy now, so it was not removed.", "flash.kept.remove_failed": "The kept copy could not be removed: %s", "deploy.memory_overcommit_slow": "The apps' peak load can exceed the available memory. The box can then slow down during large uploads and may not answer for minutes. If this happens often, remove an app.", + "deploy.memory_swap_short": "The machine has too little swap space, so during large uploads the box can slow down and not answer for minutes. Tell Felhom; we will fix it.", "err.stacks.memory_overcommit_slow": "The peak load of the apps can exceed the available memory. The box can then slow down during large uploads and may not answer for minutes. If this happens often, remove an app." } diff --git a/controller/internal/i18n/locales/hu.json b/controller/internal/i18n/locales/hu.json index 491882b..71429e9 100644 --- a/controller/internal/i18n/locales/hu.json +++ b/controller/internal/i18n/locales/hu.json @@ -2811,5 +2811,6 @@ "flash.kept.changed": "A másolat helyén már egy másik mappa van, ezért nem távolítottuk el.", "flash.kept.remove_failed": "A megőrzött másolatot nem sikerült eltávolítani: %s", "deploy.memory_overcommit_slow": "Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát. Ilyenkor a doboz nagy feltöltések alatt lelassulhat, és akár percekig sem válaszol. Ha ez gyakran előfordul, távolíts el egy alkalmazást.", + "deploy.memory_swap_short": "A gépen nincs elég cserehely (swap), ezért nagy feltöltéskor a doboz lelassulhat, és percekig sem válaszolhat. Szólj a Felhomnak, mi rendbe tesszük.", "err.stacks.memory_overcommit_slow": "Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát. Ilyenkor a doboz nagy feltöltések alatt lelassulhat, és akár percekig sem válaszol. Ha ez gyakran előfordul, távolíts el egy alkalmazást." } diff --git a/controller/internal/web/handlers.go b/controller/internal/web/handlers.go index 741d7f7..0b1c352 100644 --- a/controller/internal/web/handlers.go +++ b/controller/internal/web/handlers.go @@ -720,6 +720,11 @@ func (s *Server) deployPageData(w http.ResponseWriter, r *http.Request, name str memInfo["UsedPercent"] = usedPercent memInfo["Blocked"] = newReqMB > 0 && afterMB > usableMB memInfo["OvercommitWarn"] = newLimitMB > 0 && afterLimitMB > totalMB + // R-1192 follow-up: when memory is tight, say so if the HOST has too little swap behind the guest — the + // guest swap fix then has nothing to give, and a big upload can freeze the box. + if memInfo["OvercommitWarn"] == true { + memInfo["SwapShort"] = s.hostSwapShort() + } } data["MemoryInfo"] = memInfo } diff --git a/controller/internal/web/i18n_cases_a_test.go b/controller/internal/web/i18n_cases_a_test.go index 6e6700e..1478482 100644 --- a/controller/internal/web/i18n_cases_a_test.go +++ b/controller/internal/web/i18n_cases_a_test.go @@ -400,7 +400,7 @@ func i18nDeployData(deployed bool, st stacks.ContainerState, variant int) map[st d["GeoAppOverride"] = true d["AppEmailGlobalOn"] = false d["AppEmailAppOn"] = true - d["MemoryInfo"] = map[string]interface{}{"Available": true, "Blocked": false, "OvercommitWarn": true, "AfterMB": 3000, "UsableMB": 4000, + d["MemoryInfo"] = map[string]interface{}{"Available": true, "Blocked": false, "OvercommitWarn": true, "SwapShort": true, "AfterMB": 3000, "UsableMB": 4000, "Percent": 75, "UsedPercent": 70, "UsedMB": 2800, "NewRequestMB": 128} d["StorageInfo"] = map[string]interface{}{"Label": "HDD egy", "Path": "/mnt/hdd1", "DataSizeHuman": "2 GB", "FreeHuman": "500 GB", "FreePercent": 50.0} d["OtherStoragePaths"] = []string{"/mnt/hdd2"} diff --git a/controller/internal/web/r1192_swap_short_test.go b/controller/internal/web/r1192_swap_short_test.go new file mode 100644 index 0000000..0cd8174 --- /dev/null +++ b/controller/internal/web/r1192_swap_short_test.go @@ -0,0 +1,28 @@ +package web + +import ( + "testing" + + "gitea.dooplex.hu/admin/felhom-controller/internal/agentapi" +) + +// R-1192 follow-up (night 2026-10-21): the install page adds „A gépen nincs elég cserehely (swap)…" when memory is +// tight AND the agent reports the host's swap below the guest's RAM. An older agent (no number) is not judged. +// RED-PROOF: swapShort returning false always → the first case fails. +func TestR1192_SwapShortJudgesTheHostSwapAgainstTheGuestRAM(t *testing.T) { + mb := func(v int64) *int64 { return &v } + for _, c := range []struct { + name string + info agentapi.GuestMemoryInfo + want bool + }{ + {"a host with no swap", agentapi.GuestMemoryInfo{AllocatedMB: 4096, HostSwapMB: mb(0)}, true}, + {"a host with 2 GB swap behind a 4 GB guest", agentapi.GuestMemoryInfo{AllocatedMB: 4096, HostSwapMB: mb(2048)}, true}, + {"the installer's 8 GB host (7.75 GB swap)", agentapi.GuestMemoryInfo{AllocatedMB: 4096, HostSwapMB: mb(7935)}, false}, + {"an older agent (no number)", agentapi.GuestMemoryInfo{AllocatedMB: 4096}, false}, + } { + if got := swapShort(c.info); got != c.want { + t.Errorf("%s: swapShort = %v, want %v", c.name, got, c.want) + } + } +} diff --git a/controller/internal/web/system_memory_handlers.go b/controller/internal/web/system_memory_handlers.go index 2d978ec..1cdeec6 100644 --- a/controller/internal/web/system_memory_handlers.go +++ b/controller/internal/web/system_memory_handlers.go @@ -192,3 +192,26 @@ func procMemTotalMB() int64 { } return 0 } + +// hostSwapShort (R-1192 follow-up, night 2026-10-21) asks the agent whether the HOST's swap is below this guest's RAM +// — the guest swap fix (swap = RAM) puts a memory burst there, and a host with none gets nothing from it. False when +// it cannot tell (an older agent, an agent that does not answer within 2 s): the install page then shows only the +// overcommit sentence, as before. +func (s *Server) hostSwapShort() bool { + agent, err := s.memAgentForResize() + if err != nil || agent == nil { + return false + } + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + info, err := agent.GuestMemory(ctx) + if err != nil { + return false + } + return swapShort(info) +} + +// swapShort: the host's swap is known and below the guest's RAM. +func swapShort(info agentapi.GuestMemoryInfo) bool { + return info.HostSwapMB != nil && info.AllocatedMB > 0 && *info.HostSwapMB < info.AllocatedMB +} diff --git a/controller/internal/web/templates/deploy.html b/controller/internal/web/templates/deploy.html index 3db291c..56fcf0d 100644 --- a/controller/internal/web/templates/deploy.html +++ b/controller/internal/web/templates/deploy.html @@ -55,7 +55,7 @@ {{if .OvercommitWarn}}