]> git.proxmox.com Git - pve-ha-manager.git/commitdiff
avoid out of sync command execution in LRM
authorThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 24 Feb 2016 09:06:52 +0000 (10:06 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Wed, 24 Feb 2016 09:26:21 +0000 (10:26 +0100)
We are only allowed to execute any command once as else we
may disturb the synchrony between CRM and LRM.

The following scenario could happen:
schedule CRM: deploy task 'migrate' for service vm:100 with UID 1234
schedule LRM: fork task wit UID 123
schedule CRM: idle as no result available yet
schedule LRM: collect finished task UID and write result for CRM
      Result was an error
schedule LRM: fork task UID _AGAIN_
schedule CRM: processes _previous_ erroneous result from LRM
      and place vm:100 in started state on source node
schedule LRM: collect finished task UID and write result for CRM
      This time the task was successful and service is on
      target node, but the CRM know _nothing_ of it!
schedule LRM: try to schedule task but service is not on this node!
=> error

To fix that we _never_ execute two exactly same commands after each
other, exactly means here that the UID of the actual command to
queue is already a valid result.

This enforces the originally wanted SYN - ACK behaviour between CRM
and LRMs.

We generate now a new UID for services who does not change state
the one of the following evaluates to true:
* enabled AND in started state

This ensures that the state from the CRM holds in the LRM and thus,
for example, a killed VM gets restarted.

Note that the 'stopped' command is an exception, as we do not check
its result in the CRM (thus no race here) and we always want to
execute it (even when no CRM is active).

src/PVE/HA/LRM.pm
src/PVE/HA/Manager.pm
src/test/test-resource-failure5/log.expect

index 7bbfe4668b808d04c4872fee6169ebe3ec66e25b..b2d7d371ac840c1e7605afd22cd2611747cc08ba 100644 (file)
@@ -455,6 +455,14 @@ sub manage_resources {
 sub queue_resource_command {
     my ($self, $sid, $uid, $state, $target) = @_;
 
+    # do not queue the excatly same command twice as this may lead to
+    # an inconsistent HA state when the first command fails but the CRM
+    # does not process its failure right away and the LRM starts a second
+    # try, without the CRM knowing of it (race condition)
+    # The 'stopped' command is an exception as we do not process its result
+    # in the CRM and we want to execute it always (even with no active CRM)
+    return if $state ne 'stopped' && $uid && defined($self->{results}->{$uid});
+
     if (my $w = $self->{workers}->{$sid}) {
        return if $w->{pid}; # already started
        # else, delete and overwrite queue entry with new command
index 32b0ad72eb6b70b4918928495eab31a6ddab2449..1d110840bea62e5fd6808f916a4e553af8e9fc52 100644 (file)
@@ -517,12 +517,14 @@ sub next_state_stopped {
        } else {
            $haenv->log('err', "unknown command '$cmd' for service '$sid'"); 
        }
-    } 
+    }
 
     if ($cd->{state} eq 'disabled') {
-       # do nothing
+       # NOTE: do nothing here, the stop state is an exception as we do not
+       # process the LRM result here, thus the LRM always tries to stop the
+       # service (protection for the case no CRM is active)
        return;
-    } 
+    }
 
     if ($cd->{state} eq 'enabled') {
        # simply mark it started, if it's on the wrong node
@@ -608,6 +610,7 @@ sub next_state_started {
                                " (exit code $ec))");
                    # we have no save way out (yet) for other errors
                    &$change_service_state($self, $sid, 'error');
+                   return;
                }
            }
 
@@ -623,12 +626,13 @@ sub next_state_started {
                    &$change_service_state($self, $sid, 'relocate', node => $sd->{node}, target => $node);
                }
            } else {
-               # do nothing
+               # ensure service get started again if it went unexpected down
+               $sd->{uid} = compute_new_uuid($sd->{state});
            }
        }
 
        return;
-    } 
+    }
 
     $haenv->log('err', "service '$sid' - unknown state '$cd->{state}' in service configuration");
 }
index b6e7807d7f148aeae81fa33fd480647d4232d5c8..283ca8c3e726cc1ecbe15cd607a580e20474c3df 100644 (file)
@@ -31,8 +31,6 @@ err     143    node2/lrm: unable to start service fa:130 on local node after 1 r
 err     160    node1/crm: recovery policy for service fa:130 failed, entering error state!
 info    160    node1/crm: service 'fa:130': state changed from 'started' to 'error'
 warn    163    node2/lrm: service fa:130 is not running and in an error state
-warn    183    node2/lrm: service fa:130 is not running and in an error state
-warn    203    node2/lrm: service fa:130 is not running and in an error state
 info    220      cmdlist: execute service fa:130 disabled
 info    220    node1/crm: service 'fa:130': state changed from 'error' to 'stopped'
 info    820     hardware: exit simulation - done