MembershipRefusal

Which converging-membership condition refused a local command — the reason of MembershipChangeInProgressException.

The command-side counterpart of RefusalGate, which does the same job for an inbound frame (#1989 / #1998). A frame refusal's only observable is the absence of a state change; a command refusal's is an exception, which looks like attribution but is not one as soon as two guards throw the same type. RaftEngine.onChangeMembership refuses for six distinct reasons and three of them are spelled MembershipChangeInProgressException, so before this enum a caller — and a test — could see only that the change was refused (#2032).

The measurement this exists to restore

On the leader, UncommittedConfigEntry subsumes PendingLocalChange: pendingConfigChange is assigned at onChangeMembership's tail immediately before a config entry is appended above commitIndex, and every write that could break the relation restores it in the same call. The three-step argument is spelled out in OneChangeAtATimeGuardTest's KDoc. The consequence is that deleting the PendingLocalChange guard alone was behaviour-preserving, so no state-effect test could ever be red under that mutation — #2030 could pin the superset guard and had to report the subsumed one as unmeasurable-by-construction.

Naming the reason is what makes it measurable: the two guards refuse the same call at the same instant, so they are distinguishable only by what they say. OneChangeAtATimeGuardTest .inFlightLocalChange_refusesASecondChange asserts PendingLocalChange there, and reddens the moment the subsumed guard is deleted and the refusal slides down to UncommittedConfigEntry.

Only the ambiguous refusals are named here

onChangeMembership's other three refusals are already discriminated by exception type — not-leader and transfer-in-flight throw NotLeaderException, an empty target voter set throws IllegalArgumentException — so giving them values here would add a vocabulary without adding a distinction. (The first two share a type and differ only in their message text, which is the same class of gap one type-level down; it is not closed here because neither shadows the other, so both remain individually measurable from their state effects.)

One value is declared but has no known emit trajectory

UnsettledJointConfig is reachable in the source and, as far as can be shown, not at run time: see its own KDoc. It is declared because the guard that would throw it must name something, and left unclaimed rather than given a test that would only prove the fixture. Do not read this enum as a coverage list. Which of delete it / keep it as documented depth / find the missed trajectory is right stays open under #2737.

No declared→emitted reachability suite, deliberately

RefusalGate has one — FrameRefusedTest.everyRefusalGateIsReachable drives every emit site in one simulation and compares the observed set against RefusalGate.entries, so a value with no emit site reds. Copying it here was considered and rejected: with an unreachable value declared, such a suite can only go green by excluding it, and an "I cannot reach this state" opt-out moves the vacuity one level up, where it is harder to see — the exclusion list becomes the thing nobody re-reads, and the next value added under it inherits a green suite that has stopped asserting anything about it. So the suite is worth writing only once no unreachable value is declared; that resolution is the trigger, not noticing the test is missing. Tracked under #2737.

Entries

Link copied to clipboard

RaftEngine.onChangeMembership guard 3: this node already holds a local caller's in-flight membership change — pendingConfigChange is non-null.

Link copied to clipboard

RaftEngine.onChangeMembership guard 4: the log's last config entry is still uncommitted (lastConfigIndex > currentCommitIndex).

Link copied to clipboard

RaftEngine.onChangeMembership guard 6: the effective config is still Joint — a §6 transition has not settled onto a Simple.

Properties

Link copied to clipboard

A short phrase naming the condition, used to build the default exception message.

Link copied to clipboard

Returns a representation of an immutable list of all enum entries, in the order they're declared.

Link copied to clipboard
expect val name: String
Link copied to clipboard
expect val ordinal: Int

Functions

Link copied to clipboard

Returns the enum constant of this type with the specified name. The string must match exactly an identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)

Link copied to clipboard

Returns an array containing the constants of this enum type, in the order they're declared.