Compare commits
4 Commits
v1.3.34
...
25bc9c3673
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25bc9c3673 | ||
|
|
a54d367c24 | ||
|
|
6149670375 | ||
|
|
1b13df4032 |
@@ -25,8 +25,15 @@ type Store struct {
|
|||||||
|
|
||||||
func NewStore(pool *pgxpool.Pool) *Store { return &Store{Pool: pool} }
|
func NewStore(pool *pgxpool.Pool) *Store { return &Store{Pool: pool} }
|
||||||
|
|
||||||
|
// baseSelect MUSS spaltenweise zu scanNode passen. pg_role fehlte hier
|
||||||
|
// urspruenglich (Befund 2026-09-11): HANode.PGRole kam dadurch ueberall als
|
||||||
|
// leerer String an, wo Store.List/Get benutzt wird — /cluster/nodes,
|
||||||
|
// /cluster/status und damit auch die pg_role-Spalte der Cluster-Seite.
|
||||||
|
// UpsertSelf schreibt pg_role bewusst NICHT (ON CONFLICT laesst die Spalte
|
||||||
|
// unangetastet), liest sie aber im RETURNING mit — sonst passt die
|
||||||
|
// Scan-Reihenfolge nicht.
|
||||||
const baseSelect = `
|
const baseSelect = `
|
||||||
SELECT id, name, fqdn, api_url, public_ip, internal_ip, mgmt_ip, role,
|
SELECT id, name, fqdn, api_url, public_ip, internal_ip, mgmt_ip, role, pg_role,
|
||||||
version, config_hash, status,
|
version, config_hash, status,
|
||||||
last_seen, joined_at, created_at, updated_at
|
last_seen, joined_at, created_at, updated_at
|
||||||
FROM ha_nodes
|
FROM ha_nodes
|
||||||
@@ -90,7 +97,7 @@ ON CONFLICT (id) DO UPDATE SET
|
|||||||
last_seen = EXCLUDED.last_seen,
|
last_seen = EXCLUDED.last_seen,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
RETURNING id, name, fqdn, api_url, public_ip, internal_ip, mgmt_ip,
|
RETURNING id, name, fqdn, api_url, public_ip, internal_ip, mgmt_ip,
|
||||||
role, version, config_hash, status,
|
role, pg_role, version, config_hash, status,
|
||||||
last_seen, joined_at, created_at, updated_at`,
|
last_seen, joined_at, created_at, updated_at`,
|
||||||
n.ID, n.Name, n.FQDN, n.APIURL,
|
n.ID, n.Name, n.FQDN, n.APIURL,
|
||||||
n.PublicIP, n.InternalIP, n.MgmtIP,
|
n.PublicIP, n.InternalIP, n.MgmtIP,
|
||||||
@@ -181,7 +188,7 @@ func scanNode(row interface{ Scan(...any) error }) (*models.HANode, error) {
|
|||||||
if err := row.Scan(
|
if err := row.Scan(
|
||||||
&n.ID, &n.Name, &n.FQDN, &n.APIURL,
|
&n.ID, &n.Name, &n.FQDN, &n.APIURL,
|
||||||
&n.PublicIP, &n.InternalIP, &n.MgmtIP,
|
&n.PublicIP, &n.InternalIP, &n.MgmtIP,
|
||||||
&n.Role, &n.Version, &n.ConfigHash, &n.Status,
|
&n.Role, &n.PGRole, &n.Version, &n.ConfigHash, &n.Status,
|
||||||
&n.LastSeen, &n.JoinedAt,
|
&n.LastSeen, &n.JoinedAt,
|
||||||
&n.CreatedAt, &n.UpdatedAt,
|
&n.CreatedAt, &n.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
|||||||
@@ -253,22 +253,46 @@ func (h *SetupHandler) preRegisterPrimary(primaryFQDN string) {
|
|||||||
}
|
}
|
||||||
ip := addrs[0]
|
ip := addrs[0]
|
||||||
|
|
||||||
// Stable ID so repeated calls (join + startup) don't accumulate rows.
|
// Sobald der Primary sich selbst gemeldet hat (Heartbeat-Push), steht
|
||||||
nodeID := fmt.Sprintf("prenode-%s", strings.ReplaceAll(primaryFQDN, ".", "-"))
|
// hier bereits eine Zeile mit seiner ECHTEN Node-ID. Dann ist nur die
|
||||||
n := models.HANode{
|
// IP frisch zu halten — mehr will diese Funktion gar nicht.
|
||||||
ID: nodeID,
|
//
|
||||||
Name: primaryFQDN,
|
// Vorher lief in dem Fall unbedingt der Platzhalter-Upsert unten, der
|
||||||
FQDN: primaryFQDN,
|
// am fqdn-Unique-Index scheiterte: UpsertSelf nutzt ON CONFLICT (id),
|
||||||
APIURL: "https://" + primaryFQDN + ":3443",
|
// und die Synthetik-ID "prenode-<fqdn>" trifft die echte Zeile nicht.
|
||||||
Role: "primary",
|
// Ergebnis war ein "duplicate key value violates unique constraint
|
||||||
Status: "online",
|
// 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
|
||||||
n.PublicIP = &ip
|
// verdeckte echte Warnungen.
|
||||||
|
tag, err := h.ClusterStore.Pool.Exec(ctx,
|
||||||
if _, err := h.ClusterStore.UpsertSelf(ctx, n); err != nil {
|
`UPDATE ha_nodes SET public_ip = $1, updated_at = NOW() WHERE fqdn = $2`,
|
||||||
slog.Warn("setup: pre-register primary in ha_nodes failed", "fqdn", primaryFQDN, "error", err)
|
ip, primaryFQDN)
|
||||||
|
if err != nil {
|
||||||
|
slog.Warn("setup: refreshing primary IP in ha_nodes failed",
|
||||||
|
"fqdn", primaryFQDN, "error", err)
|
||||||
return
|
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 {
|
if err := h.PeerReloader(ctx); err != nil {
|
||||||
slog.Warn("setup: PeerReloader failed after primary pre-register", "error", err)
|
slog.Warn("setup: PeerReloader failed after primary pre-register", "error", err)
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user