Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

.nornir/data-set-grow-drain.md

Draining for a data-set growth — the four steps, today and with hot-plug

Written 2026-09-14 beside private-gunnar-ops/DATA-SET-GROWTH-DESIGN.md (the contract) and the boot-stack grow in edda's korp-installer/src/grow.rs. Design only — no code implements the hot-plug half yet; §9 of the contract keeps no-restart growth out of scope until the tunnr kernel has CONFIG_HOTPLUG_PCI. This note fixes the shape of the drain, the journal and the Draining{kind: growing} announcement so that the same four steps work unchanged when hot-plug exists.

The four steps gunnar takes when it drains for a growth

  1. Stop admitting new git sessions. The SSH front door refuses a new transfer with the growing sentence from the contract's §8 (through the standby's err_packet), and Bootstrap.Status.serving_git reads false with readiness naming the growth. The control plane stays up — it lives on the boot disk, not on /data.
  2. Wait for in-flight transfers to finish: the admission counter (the one the shutdown path already waits on) reaching zero. Bounded by the existing drain timeout; a transfer that will not finish is cut the way a shutdown cuts it, never left holding /data open.
  3. sync, then unmount /data. Everything the stores wrote is on the platter before the filesystem goes, and the unmount is the proof that no handle is open (an EBUSY is a finding, not something to force).
  4. Later, remount — and only after gunnar has verified with its own eyes that the set is what it expects (below).

With today's kernel step 4's "later" is the next boot: korp-start (grow.rs, G1–G5) grows and remounts before gunnar is exec'd, so nothing in gunnar implements a remount. Steps 1–3 are the existing shutdown drain; the growth only gives it a kind.

The announcement

Draining{kind: growing} on the control plane (the Draining state the shop-presence contract already carries, with a kind field — growing is the first kind). It is what gunnar-ui turns into the customer sentence and what monetize waits on before it stops the server. It is raised BEFORE step 1 and cleared by Ready after the remount verified.

The remount trigger, when hot-plug exists

The journal is already shaped for this

/serialdat/grow-journal (grow::Record: epoch, target_sectors, member, step, at) does not care whether a step ran in korp-start or in gunnar: a hot-plug grow appends the same G1…G6 records from the same engine, and the disk — never the journal — says which steps are already true. The G4 door (Grow::xfs_allowed) is what keeps a hot remount from growing the xfs before every superblock agrees, exactly as at boot.

What monetize sees

Admin.Capacity.growth: GROWTH_AT_RESTART on a box whose boot stack carries the engine (today), later a GROWTH_HOT value when hot-plug lands — added, never renumbered. grow_state carries the journal's last epoch and verdict either way, so the operator console reads one story.