diff --git a/public/api.php b/public/api.php index 8dd542b..41386b4 100644 --- a/public/api.php +++ b/public/api.php @@ -1798,7 +1798,8 @@ try { try { $cm = new ClusterManager($db); $cSet = $cm->getSettings(); - if (!empty($cSet['enabled']) && !empty($cSet['auto_sync']) && ($cSet['role'] ?? '') === 'master' && !empty($cSet['peer_ip'])) { + $isVipActive = $cm->isVirtualIpActive($cSet['virtual_ip'] ?? ''); + if (!empty($cSet['enabled']) && !empty($cSet['auto_sync']) && $isVipActive && !empty($cSet['peer_ip'])) { $syncRes = $cm->pushSyncToPeer(); $clusterSyncMsg = $syncRes['message'] ?? 'Réplication pair OK'; } diff --git a/scripts/nexus_cluster_sync.php b/scripts/nexus_cluster_sync.php index 7a64955..1f4d72b 100644 --- a/scripts/nexus_cluster_sync.php +++ b/scripts/nexus_cluster_sync.php @@ -14,6 +14,12 @@ use App\Database; use App\ClusterManager; try { + // 0. Verrou d'exécution exclusif pour éviter les chevauchements + $lockFp = @fopen('/tmp/nexus_cluster_cron.lock', 'c+'); + if (!$lockFp || !@flock($lockFp, LOCK_EX | LOCK_NB)) { + exit(0); + } + $db = Database::getConnection(); $cm = new ClusterManager($db); $st = $cm->getSettings(); @@ -23,13 +29,10 @@ try { exit(0); } - // 2. Seul le nœud MASTER actif émet la synchronisation vers le nœud BACKUP + // 2. Règle d'or HA : SEUL le nœud qui détient l'IP Virtuelle active (VIP) émet la réplication + // Un nœud passif/standby ne doit JAMAIS écraser le nœud actif ni émettre en boucle croisée $vip = $st['virtual_ip'] ?? ''; - $isVipActive = $cm->isVirtualIpActive($vip); - $configuredRole = $st['role'] ?? 'master'; - - // Un esclave (backup) ne doit jamais écraser le maître - if ($configuredRole !== 'master' && !$isVipActive) { + if (empty($vip) || !$cm->isVirtualIpActive($vip)) { exit(0); } diff --git a/src/ClusterManager.php b/src/ClusterManager.php index 52341a6..c1ce44c 100644 --- a/src/ClusterManager.php +++ b/src/ClusterManager.php @@ -203,12 +203,13 @@ class ClusterManager */ public function isVirtualIpActive(string $vip): bool { - if ($vip === '') { + $cleanVip = explode('/', trim($vip))[0] ?? ''; + if ($cleanVip === '') { return false; } $output = shell_exec('ip -o -4 addr show 2>/dev/null') ?: ''; foreach (preg_split('/\r?\n/', $output) ?: [] as $line) { - if (preg_match('/\sinet\s+' . preg_quote($vip, '/') . '\//', $line)) { + if (preg_match('/\sinet\s+' . preg_quote($cleanVip, '/') . '(\/|\s)/', $line)) { return true; } }