Leadership election with cassandra
Cassandra has a neat feature that lets you expire data in a column. Using this handy little feature, you can create simple leadership election using cassandra. The whole process is described here which talks about leveraging Cassandras consensus and the column expiration to create leadership electors.
The idea is that a user will try and claim a slot for a period of time in a leadership table. If a slot is full, someone else has leadership. While the leader is still active they needs to heartbeat the table faster than the columns TTL to act as a keepalive. If it fails to heartbeat (i.e. it died) then its leadership claim can be relinquished and someone else can claim it. Unlike most leadership algorithms that claim a single “host” as a leader, I needed a way to create leaders sharded by some “group”. I call this a “LeadershipGroup” and we can leverage the expiring columns in cassandra to do this!
To make this easier, I’ve wrapped this algorithm in a java library available from paradoxical. For the impatient
\<dependency\>
\<groupId\>io.paradoxical\</groupId\>
\<artifactId\>cassandra-leadership\</artifactId\>
\<version\>1.0\</version\>
\</dependency\>
The gist here is that you need to provide a schema similar to
CREATE TABLE leadership\_election (
group text PRIMARY KEY,
leader\_id text
);
Though the actual column names can be custom defined. You can define a leadership election factory using Guice like so
public class LeadershipModule extends AbstractModule {
@Override
protected void configure() {
bind(LeadershipSchema.class).toInstance(LeadershipSchema.Default);
bind(LeadershipStatus.class).to(LeadershipStatusImpl.class);
bind(LeadershipElectionFactory.class).to(CassandraLeadershipElectionFactory.class);
}
}
LeadershipStatus
is a class that lets you query who is leader for what “group”. For example, you can have multiple workers competing for leadership of a certain resource.LeadershipSchema
is a class that defines what the column names in your schema are named. By default if you use the sample table above, the Default schema maps to thatLeadershipElectionFactory
is a class that gives you instances of LeadershipElection classes, and I’ve provided a cassandra leadership factory
Once we have a leader election we can try and claim leadership:
final LeadershipElectionFactory factory = new CassandraLeadershipElectionFactory(session);
// create an election processor for a group id
final LeadershipElection leadership = factory.create(LeadershipGroup.random());
final LeaderIdentity user1 = LeaderIdentity.valueOf("user1");
final LeaderIdentity user2 = LeaderIdentity.valueOf("user2");
assertThat(leadership.tryClaimLeader(user1, Duration.ofSeconds(2))).isPresent();
Thread.sleep(Duration.ofSeconds(3).toMillis());
assertThat(leadership.tryClaimLeader(user2, Duration.ofSeconds(3))).isPresent();
When you claim leadership you claim it for a period of time and if you get it you get a leadership token that you can heartbeat on. And now you have leadership!
As usual, full source available at my github