Class CoordinationClient

java.lang.Object
io.krabka.streams.coordination.CoordinationClient
All Implemented Interfaces:
AutoCloseable

public final class CoordinationClient extends Object implements AutoCloseable
Elects one leader per role over a CoordinationTransport.

The client joins the three pure parts of this module. It reads the coordination topic through the transport, folds a RoleState, asks Succession what this member does now, and carries that answer back to the cluster. It owns no state of its own between calls, so two clients of one process see the same cluster.

tryAcquire(Role, MemberId) runs one pass and never blocks. acquire(Role, MemberId, Duration) repeats that pass until the member wins the role or the deadline passes. A test drives tryAcquire with ManualClock and needs no sleep and no broker.

The epoch is the safety mechanism, and the broker enforces it. This client decides only when a member calls InitProducerId. A wrong decision makes a failover early or late. A wrong decision never makes two members authoritative.

Example

try (CoordinationClient client = new CoordinationClient(transport)) {
    try (Leadership leadership = client.acquire(role, me, Duration.ofMinutes(1))) {
        dispatch(leadership.token());
    }
}
  • Field Details

    • DEFAULT_RETRY_INTERVAL

      public static final Duration DEFAULT_RETRY_INTERVAL
      The gap between two passes of a blocking acquire, when no decision names one.
  • Constructor Details

    • CoordinationClient

      public CoordinationClient(CoordinationTransport transport)
      Creates a client with the default lease timings and the clock of the host.
      Parameters:
      transport - the seam to the cluster
      Throws:
      NullPointerException - if the transport is null
    • CoordinationClient

      public CoordinationClient(CoordinationTransport transport, LeaseConfig config, Clock clock, Duration retryInterval)
      Creates a client.
      Parameters:
      transport - the seam to the cluster
      config - the lease timings of every role this client drives
      clock - the source of the current time
      retryInterval - the gap between two passes of a blocking acquire, when no decision names one
      Throws:
      NullPointerException - if an argument is null
  • Method Details

    • config

      public LeaseConfig config()
      Returns the lease timings this client applies.
      Returns:
      the lease policy of every role this client drives
    • readState

      public RoleState readState(Role role)
      Reads and folds the coordination topic for one role.
      Parameters:
      role - the role to read
      Returns:
      the roster and the last lease record of that role
      Throws:
      CoordinationException - if the read fails or a record does not decode
    • describe

      public LeadershipStatus describe(Role role)
      Reports what one role looks like to a third party.

      The call asks the transaction coordinator for the token of the role and folds the coordination topic. It takes no epoch and it joins no group, so any process calls it.

      Parameters:
      role - the role to describe
      Returns:
      the token, the roster, and the last lease record of that role
      Throws:
      CoordinationException - if either read fails
    • tryAcquire

      public Optional<Leadership> tryAcquire(Role role, MemberId member)
      Runs one election pass for one member.

      The pass registers the member when the roster does not name it, and it reads the topic again after that append. The pass then asks Succession what the member does now. It mints an epoch and writes a lease for Decision.Action.CHALLENGE and for Decision.Action.HOLD, and it returns an empty result for Decision.Action.WAIT.

      A member that a live lease already names still mints a fresh epoch here. The member holds no producer bound to the older epoch in this process, and InitProducerId fences the incarnation that did.

      The pass returns an empty result when another member wins the race between the epoch call and the lease write. The broker reports that race as a fence, and the loser reads the newer state on its next pass.

      Parameters:
      role - the role to compete for
      member - the member that competes
      Returns:
      the leadership, and empty when this member does not take the role now
      Throws:
      CoordinationException - if a cluster call fails
    • acquire

      public Leadership acquire(Role role, MemberId member, Duration timeout)
      Competes for one role until this member wins it or the deadline passes.

      The method repeats tryAcquire(Role, MemberId) and sleeps between two passes. It sleeps until the instant that a Decision.Action.WAIT names, and for DEFAULT_RETRY_INTERVAL at most. A caller that drives its own loop should call tryAcquire instead.

      Parameters:
      role - the role to compete for
      member - the member that competes
      timeout - how long this member waits for the role
      Returns:
      the leadership this member won
      Throws:
      CoordinationException - if the deadline passes before this member wins the role, if a cluster call fails, or if another thread interrupts this one
    • close

      public void close()
      Closes the transport this client drives.
      Specified by:
      close in interface AutoCloseable