fix(cluster): Repair-Rollenerkennung über pg_publication statt ha_nodes.role — v1.2.88
Bug: Dispatch an utm-2 schlug fehl ('dieser Node ist der Cluster-Primary'), weil ha_nodes je Node lokal ist und sich JEDE Node selbst als role=primary markiert. Fix: Primary-Erkennung über pg_publication (edgeguard_shared, für jeden DB-User lesbar) statt role/pg_role. Primary gibt dem Subscriber seine eigene Adresse als primary_host mit (PostPeerWithBody); Agent-Handler vertraut dem mTLS-Dispatch mit Safety-Guard 'läuft nie auf dem Publication-Primary'. Funktioniert auch bei Direktzugriff auf den Subscriber. UI-Gating vereinfacht (Drift + Peer + Admin).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -392,17 +392,13 @@ export default function ClusterPage() {
|
||||
|
||||
const primaryFqdn = data?.local_node?.fqdn ?? window.location.hostname
|
||||
|
||||
// Repair-Button: sichtbar bei Drift, für Admins, wenn ein Resync-Ziel
|
||||
// existiert — auf dem Standby (lokal) oder auf dem Primary (delegiert
|
||||
// an den Subscriber-Peer). Primary = role ODER pg_role 'primary'
|
||||
// (pg_role bleibt nach setup-standby 'standalone', role ist verlässlich).
|
||||
const isPrimaryNode = (n?: HANode | null) => !!n && (n.pg_role === 'primary' || n.role === 'primary')
|
||||
const localIsPrimary = isPrimaryNode(data?.local_node)
|
||||
const hasSubscriberPeer = data?.peers?.some(p => !isPrimaryNode(p)) ?? false
|
||||
const hasPrimary = localIsPrimary || (data?.peers?.some(isPrimaryNode) ?? false)
|
||||
// Repair-Button: sichtbar bei Drift, für Admins, sobald ein Peer
|
||||
// existiert. Welche Node Primary (Publication-Quelle) bzw. Subscriber
|
||||
// ist, entscheidet das Backend zur Laufzeit über pg_publication — die
|
||||
// UI muss das nicht raten (ha_nodes.role ist je Node lokal/unzuverlässig).
|
||||
const canRepair = !isViewer
|
||||
&& !!data?.drift_found
|
||||
&& (localIsPrimary ? hasSubscriberPeer : hasPrimary)
|
||||
&& ((data?.peers?.length ?? 0) > 0)
|
||||
|
||||
const peerColumns: ColumnsType<HANode> = [
|
||||
{
|
||||
@@ -540,13 +536,7 @@ export default function ClusterPage() {
|
||||
banner
|
||||
className="mb-16"
|
||||
message={t('cluster.driftBanner')}
|
||||
description={
|
||||
<>
|
||||
<Paragraph style={{ marginBottom: 8 }}>{t('cluster.driftBannerDesc')}</Paragraph>
|
||||
{localIsPrimary && !hasSubscriberPeer
|
||||
&& <Text type="secondary">{t('cluster.repair.noStandbyHint')}</Text>}
|
||||
</>
|
||||
}
|
||||
description={t('cluster.driftBannerDesc')}
|
||||
action={
|
||||
canRepair ? (
|
||||
<Popconfirm
|
||||
|
||||
Reference in New Issue
Block a user