public int localIdOrThrow() {
        return localId.orElseThrow(() -> new IllegalStateException("Required local id is not present"));
    }
This class is responsible for managing the current state of this node and ensuring
only valid state transitions. Below we define the possible state transitions and
how they are triggered:

Unattached|Resigned transitions to:
   Unattached: After learning of a new election with a higher epoch
   Voted: After granting a vote to a candidate
   Candidate: After expiration of the election timeout
   Follower: After discovering a leader with an equal or larger epoch

Voted transitions to:
   Unattached: After learning of a new election with a higher epoch
   Candidate: After expiration of the election timeout

Candidate transitions to:
   Unattached: After learning of a new election with a higher epoch
   Candidate: After expiration of the election timeout
   Leader: After receiving a majority of votes

Leader transitions to:
   Unattached: After learning of a new election with a higher epoch
   Resigned: When shutting down gracefully

Follower transitions to:
   Unattached: After learning of a new election with a higher epoch
   Candidate: After expiration of the fetch timeout
   Follower: After discovering a leader with a larger epoch

Observers follow a simpler state machine. The Voted/Candidate/Leader/Resigned
states are not possible for observers, so the only transitions that are possible
are between Unattached and Follower.

Unattached transitions to:
   Unattached: After learning of a new election with a higher epoch
   Follower: After discovering a leader with an equal or larger epoch

Follower transitions to:
   Unattached: After learning of a new election with a higher epoch
   Follower: After discovering a leader with a larger epoch
raft/src/main/java/org/apache/kafka/raft/QuorumState.java