Package io.krabka.streams.coordination
A control plane that dispatches commands to external devices needs two properties from its log. A command is durable in a quorum before the dispatch, and a deposed leader cannot dispatch at all. The second property is the hard one. A leader that loses its lease does not know that it lost the lease, so it cannot fence itself. Something outside the leader must refuse its writes.
The epoch is the safety mechanism and the lease is not
The leadership epoch is the producer epoch that Kafka's transaction coordinator
mints for transactional.id = <role>. That one decision supplies every
property this package promises:
- The coordinator writes the epoch to
__transaction_state, which is replicated, so the quorum mints the epoch. - The coordinator advances the epoch on every
InitProducerIdcall and reuses no value, so the epoch grows. - The broker rejects a write that carries a superseded epoch. It answers
INVALID_PRODUCER_EPOCHorPRODUCER_FENCED. The cluster fences a deposed leader, and the leader does not fence itself. DescribeTransactionsreports the current producer id and producer epoch of a transactional id. Any process calls it, so a third party checks the authority of a writer with one request and no membership. SeeLeadershipStatus.
The lease adds no safety of its own. It is a liveness and anti-flap device. It tells a standby when to stop waiting for a quiet holder. A wrong lease makes a failover early or late. A wrong lease never makes two writers authoritative. Read that inversion against a hand-rolled design, where the lease deadline decides who may write. This package never asks the lease that question.
The fencing token is a pair
The producer epoch is a short and it wraps. Kafka handles the exhaustion
with a fresh producer id and an epoch of zero. An implementation that compares the
epoch alone accepts a stale writer after about 32000 leadership changes.
FencingToken holds the pair and compares the
producer id first.
Rank comes from the log
A candidate appends a registration record to __coordination_state. The
offset of that record is the join sequence of the candidate, and log compaction keeps
the offset of every record it retains.
RoleState folds that walk, and the roster it
builds is in offset order. A recovered node registers again, takes a higher offset,
and lands at the tail. That is the no-failback rule, and it needs no counter and no
coordinator. A rank from a configuration file would put the recovered node at the
front, and the node would then preempt the member that replaced it.
The layers
CoordinationCodecis the record codec of the frozen__coordination_statelayouts.Successionholds the succession rules. They read a state, an instant, and a policy, and they perform no input and no output.LeaseConfigandLeaseTimingare the lease clock, andClockis the seam a test replaces.KafkaCoordinationTransportcarries the decisions to a cluster, andCoordinationClientdrives the loop.
Clock assumptions
The lease deadline is a wall-clock instant. The holder writes it, and a challenger compares it against its own clock. Skew between the two clocks moves the failover time by the size of the skew. Skew does not affect safety, for the reason the first section gives. An operator that needs a bound on the failover time needs a bound on the skew.
Example
Role role = Role.of("controller");
MemberId me = MemberId.of("node-1");
try (CoordinationClient client = new CoordinationClient(transport);
Leadership leadership = client.acquire(role, me, Duration.ofMinutes(1))) {
while (running) {
if (leadership.renewDue()) {
leadership.renew();
}
dispatch(leadership.token());
}
} catch (FencedException lost) {
controller.stop();
}
-
ClassDescriptionA source of the current time.Elects one leader per role over a
CoordinationTransport.Encodes and decodes the frozen records of the internal__coordination_statetopic.One decoded record ofCoordinationCodec.TOPIC, with the offset it sits at.Reports a coordination failure.The decoded key of one record ofCoordinationCodec.TOPIC.The seam between the succession rules and the cluster.One decoded value ofCoordinationCodec.TOPIC.What one member does about a role right now.The step that one member takes about a role.Reports that the broker refused a write, because another member holds the role now.The quorum-minted proof that one member holds a role.TheCoordinationTransportthat talks to a Kafka cluster.The live leadership of one role by one member.What one role looks like to a third party right now.The member that holds a role now, and the time its claim ends.The lease timings of one role.The lease clock of one lease record under one policy.A clock that a caller moves by hand.The validated identity of one member that competes for a role.Which record ofCoordinationCodec.TOPICa key names.One member of a role announces that it is available.The validated name of one coordination role, such as the name of an active controller.Computes the partition ofCoordinationCodec.TOPICthat holds every record of one role.The state that a reader folds out of the records of one role.Folds the records of one role into aRoleState.One member of the roster of a role.The succession rules of one role: ordered candidates, and no failback.