4.11 Module classy_vote

This module implements a variation of 2-phase commit.

Important to note:

  1. All callbacks involved in the operations are persistently stored. Certain callbacks may be retried after a node restart. Hence, user must make sure that functions involved in the commit are not removed during upgrade.
  2. Vote is rather heavy operation. Do not use it when frequent coordination is needed.
  3. Commit flows may hang for an unlimited time if the coordinator node fails during the decision stage.

4.11.1 Error Handling

This API uses both synchronous and asynchronous methods of status and error reporting. Both methods must be handled in all cases. Note: when create/1 API returns {ok, _}, it doesn’t mean the commit has been completed.

  1. If this function returns an error tuple, it means the commit followed a "fast abort" path. "Fast abort" path is synchronous, and it implies that no persistent changes have been made to the involved sites (participant and coordinator).
  2. When the function returns {ok, _}, it means commit flow entered "persistent" path. Persistent path continues even after restart of any involved site. Because of that, it uses asynchronous method of status reporting via callbacks passed in the options.

    The coordinator is notified of the outcome via post_vote callback.

    The participants are notified via their respective commit or rollback callbacks.

  3. Additionally, if post_vote, commit or rollback callbacks throw an exception, on_fail callback gets involved.

    Such mechanism is used because classy doesn’t automatically retry any actions that failed on the persistent path: there is a high risk that these actions will just keep failing repeatedly. Instead, they are abandoned until the next node restart. on_fail callback provides a mechanism to signal such failures back to the business logic.

4.11.2 Types

vote_info()
-type vote_info() :: #{tag := tag(), id := id(),
                       role := coordinator | participant, _ => _}.
fail_info()
-type fail_info() :: #{tag := tag(), id := id(),
                       reason := _, _ => _}.
outcome()
-type outcome() :: #c_outcome{}.
vote()
-type vote() :: #c_vote{}.
options()
-type options() :: #{tag := tag(),
                     actions := #{classy:site() => actions()},
                     post_vote => classy_lib:mfargs(),
                     strategy => strategy(), on_fail => classy_lib:mfargs()}.

Common vote options.

tag

An arbitrary tag identifying the commit action. Ongoing commit actions can be efficiently filtered by the tag.

actions

A map from site ID to per-site commit actions. Each site in the map becomes a vote participant. Participants’ actions may be non-uniform.

post_vote

Callback that is executed on the coordinator after the decision is made. Classy prepends two arguments to the user-specified argument list:

  1. A boolean indicating the decision
  2. Vote ID

The return value is ignored.

Note: it’s NOT guaranteed that all commit actions on the participants are finished by the time when post_vote is called. This callback can be retried on node restart.

strategy

See strategy().

on_fail

Executed on both coordinator and participant if commit / rollback / post_commit actions fail. This callback may be used to signal failures to the business logic. Classy prepends an argument of type t:classy_vote:fail_info/0 to the user-specified argument list.

actions()
-type actions() :: #{prepare := classy_lib:mfargs(),
                     commit := [classy_lib:mfargs()],
                     rollback => [classy_lib:mfargs()]}.

Per-participant set of commit actions.

prepare

Callback that lets the participant decide whether to commit.

Classy prepends two additional values to the user-specified argument list:

  1. A boolean indicating whether the prepare action can have side effects. It is set to false during pre-commit fast abort check and to true during the persistent flow.
  2. ID of the vote.

The return value is a boolean indicating the participant’s vote (true means “yes”).

commit

List of actions executed on the sites if the coordinator decides to go ahead with the commit. Classy prepends vote ID to the user-specified argument list. Return value is ignored.

rollback

Action executed on the participant when the coordinator decides to abort the commit. Classy prepends vote ID to the user-specified argument list. Return value is ignored.

strategy()
-type strategy() :: {all, timeout()}.

Strategy used to decide whether to commit.

all: All participant must vote yes within the timeout.

id()
-type id() :: classy_uid:cu_tuple().

Unique ID of the vote.

tag()
-type tag() :: term().

Arbitrary tag associated with the operation. It allows business logic to quickly enumerate ongoing votes of certain kind.

4.11.3 Functions

create(UserOptions)
-spec create(options()) -> {ok, classy_vote:id()} |
                           {error, _}.

Initiate a new vote, see options/0.

fold_ongoing(Fun, Acc0, TagMatch)
-spec fold_ongoing(fun((vote_info(), Acc) -> Acc), Acc,
                   _TagMatchPattern) -> Acc.

Fold over ongoing 2PC flows where the local site is either a coordinator or a participant.

Arguments:

  1. Fold function
  2. Initial accumulator
  3. ETS match expression for filtering the tag
ls_votes(_TagMatch)
-spec ls_votes(_TagMatch) -> [vote_info()].

List all ongoing 2PC flows where the local site is either a coordinator or a participant.

The argument is an ETS match expression that allows to filter on the tag.

ls_votes()
-spec ls_votes() -> [vote_info()].

See classy_vote:ls_votes/1. No filtering.


JavaScript license information