fix(cluster): ensure only VIP holder pushes sync and prevent deadlocks with flock and strict VIP check

This commit is contained in:
sorcier committed 2026-11-05 23:41:54 +01:00
1 parent 0b740da87e
commit 0b654c885c
3 files changed
+14 -9

No files matched your search

+2 -1
View File
@@ -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';
}
+9 -6
View File
@@ -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);
}
+3 -2
View File
@@ -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;
}
}