[ 
https://issues.apache.org/jira/browse/IGNITE-23834?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Roman Puchkovskiy updated IGNITE-23834:
---------------------------------------
    Description: 
All non-test implementations of RaftGroupListener behave identically with 
respect to error handling. They have code like

} catch (IgniteInternalException e) {
clo.result(e);
} catch (CompletionException e) {
clo.result(e.getCause());

} ...

Calling clo.result() with an exception is not enough to let JRaft know that the 
state machine might be broken; this just causes the error being logged and the 
exception being sent to the client (if this is the leader).

As JRaft doesn't know that an error happened, it keeps applying subsequent 
commands. If it so happens that command with index 10 is applied on node A, but 
 on B it throws an IgniteInternalException (and most of our exceptions are 
subclasses of this class) or a CompletionException (which might wrap absolutely 
anything), then a command with index 11 might still be applied on both nodes. 
This would result in state machines' states diverging.

We should rethrow any throwable we catch by default to allow JRaft know about 
it (if it sees such a throwable, it will mark the state machine as broken and 
stop applying subsequent commands, which seems to be the only safe default 
behavior). If, in the future, we realize that some exceptions might be normally 
(also predictably based on the state machine state and command, and 
idempotently) thrown, we should whitelist them individually. Probably, the best 
way would be to create a specific exception class like 
StateMachineLogicException. But currently, we don't seem to have such 
exceptions at all, and by default we should just rethrow them to JRaft.

Another problem is that 

> RaftGroupListener implementations may silently break state machine state
> ------------------------------------------------------------------------
>
>                 Key: IGNITE-23834
>                 URL: https://issues.apache.org/jira/browse/IGNITE-23834
>             Project: Ignite
>          Issue Type: Bug
>            Reporter: Roman Puchkovskiy
>            Assignee: Roman Puchkovskiy
>            Priority: Major
>              Labels: ignite-3
>
> All non-test implementations of RaftGroupListener behave identically with 
> respect to error handling. They have code like
> } catch (IgniteInternalException e) {
> clo.result(e);
> } catch (CompletionException e) {
> clo.result(e.getCause());
> } ...
> Calling clo.result() with an exception is not enough to let JRaft know that 
> the state machine might be broken; this just causes the error being logged 
> and the exception being sent to the client (if this is the leader).
> As JRaft doesn't know that an error happened, it keeps applying subsequent 
> commands. If it so happens that command with index 10 is applied on node A, 
> but  on B it throws an IgniteInternalException (and most of our exceptions 
> are subclasses of this class) or a CompletionException (which might wrap 
> absolutely anything), then a command with index 11 might still be applied on 
> both nodes. This would result in state machines' states diverging.
> We should rethrow any throwable we catch by default to allow JRaft know about 
> it (if it sees such a throwable, it will mark the state machine as broken and 
> stop applying subsequent commands, which seems to be the only safe default 
> behavior). If, in the future, we realize that some exceptions might be 
> normally (also predictably based on the state machine state and command, and 
> idempotently) thrown, we should whitelist them individually. Probably, the 
> best way would be to create a specific exception class like 
> StateMachineLogicException. But currently, we don't seem to have such 
> exceptions at all, and by default we should just rethrow them to JRaft.
> Another problem is that 



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to