diff --git a/internal/handlers/setup.go b/internal/handlers/setup.go index 3cd71da..f71733c 100644 --- a/internal/handlers/setup.go +++ b/internal/handlers/setup.go @@ -253,22 +253,46 @@ func (h *SetupHandler) preRegisterPrimary(primaryFQDN string) { } ip := addrs[0] - // Stable ID so repeated calls (join + startup) don't accumulate rows. - nodeID := fmt.Sprintf("prenode-%s", strings.ReplaceAll(primaryFQDN, ".", "-")) - n := models.HANode{ - ID: nodeID, - Name: primaryFQDN, - FQDN: primaryFQDN, - APIURL: "https://" + primaryFQDN + ":3443", - Role: "primary", - Status: "online", - } - n.PublicIP = &ip - - if _, err := h.ClusterStore.UpsertSelf(ctx, n); err != nil { - slog.Warn("setup: pre-register primary in ha_nodes failed", "fqdn", primaryFQDN, "error", err) + // Sobald der Primary sich selbst gemeldet hat (Heartbeat-Push), steht + // hier bereits eine Zeile mit seiner ECHTEN Node-ID. Dann ist nur die + // IP frisch zu halten — mehr will diese Funktion gar nicht. + // + // Vorher lief in dem Fall unbedingt der Platzhalter-Upsert unten, der + // am fqdn-Unique-Index scheiterte: UpsertSelf nutzt ON CONFLICT (id), + // und die Synthetik-ID "prenode-" trifft die echte Zeile nicht. + // Ergebnis war ein "duplicate key value violates unique constraint + // ha_nodes_fqdn_unique" bei JEDEM API-Start auf dem Standby — folgenlos + // (die echte Zeile stimmt ja), aber es sah nach einem Defekt aus und + // verdeckte echte Warnungen. + tag, err := h.ClusterStore.Pool.Exec(ctx, + `UPDATE ha_nodes SET public_ip = $1, updated_at = NOW() WHERE fqdn = $2`, + ip, primaryFQDN) + if err != nil { + slog.Warn("setup: refreshing primary IP in ha_nodes failed", + "fqdn", primaryFQDN, "error", err) return } + if tag.RowsAffected() == 0 { + // Noch keine Zeile: Platzhalter anlegen, damit @peer_ipv4 den + // Primary schon kennt, bevor er sich das erste Mal meldet. + // Stabile ID, damit wiederholte Aufrufe (Join + Start) keine + // Zeilen anhaeufen. + nodeID := fmt.Sprintf("prenode-%s", strings.ReplaceAll(primaryFQDN, ".", "-")) + n := models.HANode{ + ID: nodeID, + Name: primaryFQDN, + FQDN: primaryFQDN, + APIURL: "https://" + primaryFQDN + ":3443", + Role: "primary", + Status: "online", + } + n.PublicIP = &ip + if _, err := h.ClusterStore.UpsertSelf(ctx, n); err != nil { + slog.Warn("setup: pre-register primary in ha_nodes failed", + "fqdn", primaryFQDN, "error", err) + return + } + } if err := h.PeerReloader(ctx); err != nil { slog.Warn("setup: PeerReloader failed after primary pre-register", "error", err) return