diff --git a/userspace/marsadm b/userspace/marsadm index 5cc6164e..a0469e36 100755 --- a/userspace/marsadm +++ b/userspace/marsadm @@ -581,46 +581,49 @@ sub _get_prev_pos { } sub _get_common_ancestor { - # TODO: recursive formulation, improve efficiency - my ($basedir, $pos1, $host1, $dep1, $pos2, $host2, $dep2) = @_; - my ($p1, $nr1, $from1, $len1) = _parse_pos($pos1, 0); - my ($p2, $nr2, $from2, $len2) = _parse_pos($pos2, 0); - if ($p1 eq $p2) { - # usually no split brain here (only if both path depths are non-zero) - my $split = ($dep1 && $dep2); - if (!$split) { # additionally check the corresponding version links - my $path1 = sprintf("$basedir/version-%09d-$from1", $nr1); - my $path2 = sprintf("$basedir/version-%09d-$from2", $nr2); - if (my $vers1 = get_link($path1) and my $vers2 = get_link($path2)) { - $split = 1 if $vers1 ne $vers2; + for (;;) { + my ($basedir, $pos1, $host1, $dep1, $pos2, $host2, $dep2) = @_; + my ($p1, $nr1, $from1, $len1) = _parse_pos($pos1, 0); + my ($p2, $nr2, $from2, $len2) = _parse_pos($pos2, 0); + if ($p1 eq $p2) { + # usually no split brain here (only if both path depths are non-zero) + my $split = ($dep1 && $dep2); + if (!$split) { # additionally check the corresponding version links + my $path1 = sprintf("$basedir/version-%09d-$from1", $nr1); + my $path2 = sprintf("$basedir/version-%09d-$from2", $nr2); + if (my $vers1 = get_link($path1) and my $vers2 = get_link($path2)) { + $split = 1 if $vers1 ne $vers2; + } } + return ($p1, $split); + } elsif ($nr1 > $nr2) { + # just flip arguments + @_ = ($basedir, $pos2, $host2, $dep2, $pos1, $host1, $dep1); + next; + } elsif ($nr1 < $nr2) { + # recursively advance path depth + my $vers2 = _get_prev_pos($basedir, $nr2, $host2); + return ("", -1) if !$vers2; + @_ = ($basedir, $pos1, $host1, $dep1, $vers2, $host2, $dep2 + 1); + next; + } elsif ($from1 ne $from2) { + # split brain is sure now, but continue computing the common split point + my $vers1 = _get_prev_pos($basedir, $nr1, $host1); + return ("", 1) if !$vers1; + my $vers2 = _get_prev_pos($basedir, $nr2, $host2); + return ("", 1) if !$vers2; + my ($res, $split) = _get_common_ancestor($basedir, $vers1, $host1, $dep1 + 1, $vers2, $host2, $dep2 + 1); + return ($res, 1); + } elsif ($len1 < $len2) { + # there may be no split brain (just incomplete replay) depending on path depth + return ($p1, $dep1); + } elsif ($len2 < $len1) { + # dto symmetric + return ($p2, $dep2); } - return ($p1, $split); - } elsif ($nr1 > $nr2) { - # just flip arguments - return _get_common_ancestor($basedir, $pos2, $host2, $dep2, $pos1, $host1, $dep1); - } elsif ($nr1 < $nr2) { - # recursively advance path depth - my $vers2 = _get_prev_pos($basedir, $nr2, $host2); - return ("", -1) if !$vers2; - return _get_common_ancestor($basedir, $pos1, $host1, $dep1, $vers2, $host2, $dep2 + 1); - } elsif ($from1 ne $from2) { - # split brain is sure now, but continue computing the common split point - my $vers1 = _get_prev_pos($basedir, $nr1, $host1); - return ("", 1) if !$vers1; - my $vers2 = _get_prev_pos($basedir, $nr2, $host2); - return ("", 1) if !$vers2; - my ($res, $split) = _get_common_ancestor($basedir, $vers1, $host1, $dep1 + 1, $vers2, $host2, $dep2 + 1); - return ($res, 1); - } elsif ($len1 < $len2) { - # there may be no split brain (just incomplete replay) depending on path depth - return ($p1, $dep1); - } elsif ($len2 < $len1) { - # dto symmetric - return ($p2, $dep2); + lwarn "error in algorithm: $p1, $nr1, $from1, $len1 : $p2, $nr2, $from2, $len2\n"; + return ("", -1); } - lwarn "error in algorithm: $p1, $nr1, $from1, $len1 : $p2, $nr2, $from2, $len2\n"; - return ("", -1); } sub get_common_ancestor {