From 714898022e91d3f2efdc5bed011f3d43dd8c2970 Mon Sep 17 00:00:00 2001 From: joshsiv-mulga Date: Thu, 30 Apr 2026 21:58:15 +1000 Subject: [PATCH] fix(vpcd): also subscribe DHCPManager before bootstrap reconcile pass Previous fix moved DHCPManager.Subscribe ahead of ReconcileFromKV but missed Pass 1 (the bootstrap-config Reconcile that runs even earlier). On a dev box with a source="dhcp" pool and a bootstrap IGW, that earlier pass still hit "no responders" and burned the 60-retry cold-boot budget before the Manager came up. Move both reconcile passes after Subscribe. --- spinifex/services/vpcd/vpcd.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/spinifex/services/vpcd/vpcd.go b/spinifex/services/vpcd/vpcd.go index 225b021d7..eaa33a3ba 100644 --- a/spinifex/services/vpcd/vpcd.go +++ b/spinifex/services/vpcd/vpcd.go @@ -363,11 +363,6 @@ func launchService(cfg *Config) error { holder, _ := os.Hostname() releaseLeader, isLeader := AcquireReconcileLeader(nc, holder) - // Run reconciliation before subscribing (leader only) - if isLeader { - Reconcile(ctx, topo, cfg.Bootstrap) - } - // Macvlan mode only: align macvlan MAC with the OVN gateway router MAC. // The macvlan only delivers inbound unicast matching its own MAC. With // centralized NAT, OVN uses the router MAC for all external ARP replies. @@ -396,11 +391,14 @@ func launchService(cfg *Config) error { // DHCP manager: services vpc.dhcp.acquire/release requests from the // daemon-side ExternalIPAM handlers and (mulga-siv-38) topology's gw-LRP - // allocator. MUST subscribe before ReconcileFromKV / retrofit so those - // passes don't fan out vpc.dhcp.acquire requests to "no responders" and - // burn the 60-attempt cold-boot retry budget per existing IGW. Started - // unconditionally — pools with source="static" never issue acquire - // requests, so the Manager sits idle until something actually wants DHCP. + // allocator. MUST subscribe before any reconcile pass — bootstrap + // reconcile, ReconcileFromKV, and retrofit all call into reconcileIGW / + // expectedGatewayPortNetwork which fan out vpc.dhcp.acquire on + // source="dhcp" pools. Without a live subscription those requests hit + // "no responders" and burn the 60-attempt cold-boot retry budget per + // existing IGW. Started unconditionally — pools with source="static" + // never issue acquire requests, so the Manager sits idle until + // something actually wants DHCP. js, err := nc.JetStream() if err != nil { slog.Error("Failed to get JetStream context for DHCP manager", "err", err) @@ -428,6 +426,12 @@ func launchService(cfg *Config) error { } }() + // Pass 1: Bootstrap-config reconcile. Creates the bootstrap VPC / IGW + // topology if missing. Leader-only. + if isLeader { + Reconcile(ctx, topo, cfg.Bootstrap) + } + // Pass 2: Reconcile from NATS KV (handles reboots, OVN DB loss, missed events). // Runs after subscribing so new events are not missed during reconciliation. // Leader-gated (mulga-siv-29) to avoid concurrent Create races against OVN NB.