From 823976c1b5d40bd4c0732c344b6990457e27018a Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Fri, 4 Sep 2026 14:58:07 +0700 Subject: [PATCH] #326: state primary's real consequence, and write down the denominator The merged javadoc said a changed primary.terminal leaves a lead 'unresolved as primary until a restart'. That over-claims. CB-532 made the pin deprecated: identity comes from leaders:/leadScan:, and Fleetd.java:511 warns about the pin at startup. A changed pin still needs a restart, but for the fallback nudge destination, the deprecated identity path, and pushReminders/pushBackoffMs - not for a lead that uses leaders:. Also record what I measured. FleetConfig has 22 top-level components; four are named nowhere in ConfigRef. memberCredentials and memberLoginShell are hot and correctly absent (both read live off config.get() at spawn). health and coordinator are undecided, not hot. 'Absent' looks the same for both kinds, and twice now the forgotten kind hid among the correct kind. --- .../java/dev/ltms/fleet/config/ConfigRef.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/fleetd/src/main/java/dev/ltms/fleet/config/ConfigRef.java b/fleetd/src/main/java/dev/ltms/fleet/config/ConfigRef.java index 7b490fb..fcd38c6 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/config/ConfigRef.java +++ b/fleetd/src/main/java/dev/ltms/fleet/config/ConfigRef.java @@ -45,8 +45,12 @@ import java.util.function.Supplier; * {@link #changedDeferredKeys}), {@code primary:} (fleetd #326 — {@code Fleetd.java:506, 519, * 520} read {@code cfg.primary()} only off the startup snapshot to build {@code * PrimaryRegistry} and size {@code ReplyPushLoop}'s reminder cap/backoff, and neither is - * rebuilt on reload; a lead whose pinned terminal changed under a running daemon stays - * unresolved as primary until a restart), {@code configReload:} (fleetd #326 — {@code + * rebuilt on reload. Say the consequence exactly: {@code primary.terminal} is DEPRECATED + * (CB-532, and {@code Fleetd.java:511} warns about it at startup) — a lead's identity comes + * from {@code leaders:}/{@code leadScan:}, so changing this pin does not demote or promote a + * lead that uses those. What a changed pin still does not take effect on until a restart is + * the fallback nudge destination the pin remains, the deprecated identity path for an operator + * who still relies on it, and {@code pushReminders}/{@code pushBackoffMs}), {@code configReload:} (fleetd #326 — {@code * Fleetd.java:679-680} read it only at startup to decide whether to build a {@code * ConfigWatcher} at all and with what interval; the watcher that would apply a later change is * itself built once, so a running watcher keeps polling on its original enabled flag and @@ -73,6 +77,24 @@ import java.util.function.Supplier; * listening. * * + *

The denominator, measured on 2026-09-04 (fleetd #326). {@code FleetConfig} has + * 22 top-level record components. Four of them are named nowhere in this file, and the reason + * differs per key, so do not read "absent" as "forgotten": + *

+ * The point of writing the count down: "not mentioned in this file" looks identical for a key that + * is correctly hot and for a key nobody triaged. Twice now — {@code worktreeGroup} (#323) and + * {@code primary}/{@code configReload} (#326) — the second kind hid among the first. + * *

A cold change refuses the whole reload. Not the hot half applied and the cold * half warned about: that would leave the running daemon in a state matching no file on disk, which * is the worst thing a reload can do to an operator debugging one. Refusing keeps the invariant that @@ -246,7 +268,9 @@ public final class ConfigRef implements Supplier { } // fleetd #326: Fleetd.java:506, 519, 520 read cfg.primary() only off the startup snapshot // (PrimaryRegistry's pinned terminal, ReplyPushLoop's reminder cap and backoff) — neither is - // rebuilt on reload, so a changed pin needs a restart before a lead resolves as primary again. + // rebuilt on reload, so a changed value needs a restart. Note what it does NOT mean: + // primary.terminal is deprecated (CB-532), identity comes from leaders:/leadScan:, so a lead + // using those is unaffected by this pin either way. See the class doc for the exact scope. if (!Objects.equals(old.primary(), fresh.primary())) { changed.add("primary"); }