syntax = "proto3";
package eraftpb;

enum EntryType {
    EntryNormal = 0;
    EntryConfChange = 1;
    EntryConfChangeV2 = 2;
}

// The entry is a type of change that needs to be applied. It contains two data fields.
// While the fields are built into the model; their usage is determined by the entry_type.
//
// For normal entries, the data field should contain the data change that should be applied.
// The context field can be used for any contextual data that might be relevant to the
// application of the data.
//
// For configuration changes, the data will contain the ConfChange message and the
// context will provide anything needed to assist the configuration change. The context
// if for the user to set and use in this case.
message Entry {
    EntryType entry_type = 1;
    uint64 term = 2;
    uint64 index = 3;
    bytes data = 4;
    bytes context = 6;

    // Deprecated! It is kept for backward compatibility.
    // TODO: remove it in the next major release.
    bool sync_log = 5;
}

message SnapshotMetadata {
    // The current `ConfState`.
    ConfState conf_state = 1;
    // The applied index.
    uint64 index = 2;
    // The term of the applied index.
    uint64 term = 3;
}

message Snapshot {
    bytes data = 1;
    SnapshotMetadata metadata = 2;
}

enum MessageType {
    MsgHup = 0;
    MsgBeat = 1;
    MsgPropose = 2;
    MsgAppend = 3;
    MsgAppendResponse = 4;
    MsgRequestVote = 5;
    MsgRequestVoteResponse = 6;
    MsgSnapshot = 7;
    MsgHeartbeat = 8;
    MsgHeartbeatResponse = 9;
    MsgUnreachable = 10;
    MsgSnapStatus = 11;
    MsgCheckQuorum = 12;
    MsgTransferLeader = 13;
    MsgTimeoutNow = 14;
    MsgReadIndex = 15;
    MsgReadIndexResp = 16;
    MsgRequestPreVote = 17;
    MsgRequestPreVoteResponse = 18;
}

message Message {
    MessageType msg_type = 1;
    uint64 to = 2;
    uint64 from = 3;
    uint64 term = 4;
    uint64 log_term = 5;
    uint64 index = 6;
    repeated Entry entries = 7;
    uint64 commit = 8;
    Snapshot snapshot = 9;
    uint64 request_snapshot = 13;
    bool reject = 10;
    uint64 reject_hint = 11;
    bytes context = 12;
    uint64 priority = 14;
}

message HardState {
    uint64 term = 1;
    uint64 vote = 2;
    uint64 commit = 3;
}

enum ConfChangeTransition {
    // Automatically use the simple protocol if possible, otherwise fall back
    // to ConfChangeType::Implicit. Most applications will want to use this.
    Auto = 0;
    // Use joint consensus unconditionally, and transition out of them
    // automatically (by proposing a zero configuration change).
    //
    // This option is suitable for applications that want to minimize the time
    // spent in the joint configuration and do not store the joint configuration
    // in the state machine (outside of InitialState).
    Implicit = 1;
    // Use joint consensus and remain in the joint configuration until the
    // application proposes a no-op configuration change. This is suitable for
    // applications that want to explicitly control the transitions, for example
    // to use a custom payload (via the Context field).
    Explicit = 2;
}

message ConfState {
    repeated uint64 voters = 1;
    repeated uint64 learners = 2;

    // The voters in the outgoing config. If not empty the node is in joint consensus.
    repeated uint64 voters_outgoing = 3;
    // The nodes that will become learners when the outgoing config is removed.
    // These nodes are necessarily currently in nodes_joint (or they would have
    // been added to the incoming config right away).
    repeated uint64 learners_next = 4;
    // If set, the config is joint and Raft will automatically transition into
    // the final config (i.e. remove the outgoing config) when this is safe.
    bool auto_leave = 5;
}

enum ConfChangeType {
    AddNode    = 0;
    RemoveNode = 1;
    AddLearnerNode = 2;
}

message ConfChange {
    ConfChangeType change_type = 2;
    uint64 node_id = 3;
    bytes context = 4;

    uint64 id = 1;
}

// ConfChangeSingle is an individual configuration change operation. Multiple
// such operations can be carried out atomically via a ConfChangeV2.
message ConfChangeSingle {
    ConfChangeType change_type = 1;
    uint64 node_id = 2;
}

// ConfChangeV2 messages initiate configuration changes. They support both the
// simple "one at a time" membership change protocol and full Joint Consensus
// allowing for arbitrary changes in membership.
//
// The supplied context is treated as an opaque payload and can be used to
// attach an action on the state machine to the application of the config change
// proposal. Note that contrary to Joint Consensus as outlined in the Raft
// paper[1], configuration changes become active when they are *applied* to the
// state machine (not when they are appended to the log).
//
// The simple protocol can be used whenever only a single change is made.
//
// Non-simple changes require the use of Joint Consensus, for which two
// configuration changes are run. The first configuration change specifies the
// desired changes and transitions the Raft group into the joint configuration,
// in which quorum requires a majority of both the pre-changes and post-changes
// configuration. Joint Consensus avoids entering fragile intermediate
// configurations that could compromise survivability. For example, without the
// use of Joint Consensus and running across three availability zones with a
// replication factor of three, it is not possible to replace a voter without
// entering an intermediate configuration that does not survive the outage of
// one availability zone.
//
// The provided ConfChangeTransition specifies how (and whether) Joint Consensus
// is used, and assigns the task of leaving the joint configuration either to
// Raft or the application. Leaving the joint configuration is accomplished by
// proposing a ConfChangeV2 with only and optionally the Context field
// populated.
//
// For details on Raft membership changes, see:
//
// [1]: https://github.com/ongardie/dissertation/blob/master/online-trim.pdf
message ConfChangeV2 {
    ConfChangeTransition transition = 1;
    repeated ConfChangeSingle changes = 2;
    bytes context = 3;
}
