One Raft group per range

Why each TeselaDB range agrees on its writes with its own replicas and what you get from it.

5 min read

In TeselaDB, each key range is a Raft group of its own. Its replicas agree on the order of their writes among themselves, without going through a central coordinator, and a cluster with thousands of ranges has thousands of groups that each move at their own pace. Almost every guarantee your application sees comes from that decision, from what a write waits for to what happens when a node is cut off from the others.

What a range is

A range is an interval [start, end) of the ordered key space. A collection starts in one range and splits when it holds more than 64 MiB of live data or 512 MiB on disk with its versions, or when it takes too much load. Each range has its replicas on several nodes. The cluster replicates data range by range. It also spreads ranges across nodes and splits them with that same unit.

   range 42                 range 43
 ┌─────────────┐         ┌─────────────┐
 │ leader  n1  │         │ leader  n2  │
 │ replica n2  │         │ replica n3  │
 │ replica n3  │         │ replica n1  │
 └─────────────┘         └─────────────┘

How a write commits

One replica of each range is the leader. It proposes writes in its log, and an entry is applied once a majority of the replicas have it on disk. When you get an answer depends on the write concern you ask for. With w: "majority", the answer comes once the majority has stored the entry with fdatasync and the leader has applied it. With w: 1 it is enough for the entry to be in the leader's log. If you add j: true, it also has to be on its disk. Asking for more replicas than the range has fails with UnsatisfiableWriteConcern (100), as in MongoDB. An orderly shutdown of the node flushes to disk everything its log already had, also what was confirmed with w: 1, while a crash can lose it.

Reading without asking

While its lease lasts, the leader knows no other replica can have committed writes, so it answers reads without sending a single message. A newly elected leader has no lease yet and confirms its leadership with ReadIndex before reading. linearizable reads always confirm it, even with a lease, as MongoDB confirms the majority on every read of that kind. A follower can also serve reads, up to its closed timestamp, which the leader publishes 3 seconds behind its clock.

Writes that can be repeated

When a driver loses the answer to a write, for example because the connection dropped right after the node received it, it sends the same write again with the same session and statement numbers. So that it is not applied twice, each range keeps a table with the session id, the transaction number and the statement number of each write, with its result. If the same write arrives again, the range returns the stored result without repeating it. The table is purged after 30 minutes and travels inside the range's snapshots, so a new replica knows it too.

When the leader is cut off

If a network partition separates the leader from the rest, it can no longer commit writes and its lease expires. The majority elects another leader among the remaining replicas. Drivers do not need to know which one it is. The node they reach answers NotLeader with a hint about the new leader, and TeselaDB's internal client follows that hint or tries the next replica. For your application, the change shows as an operation that takes a little longer or is retried.

Many groups on each core

A node carries many ranges. Each core of each node runs many groups at once, and its Raft log is also the write-ahead log of storage, so a write is not stored twice on disk. An idle group sends no heartbeats, which is why thousands of quiet ranges generate no Raft traffic between the nodes. A Raft entry holds up to 64 MiB, but it travels over the network in pieces of 1 MiB at most, just like the snapshot a new replica catches up with. The cluster metadata, with the nodes, the catalogue and the list of ranges, lives in another Raft group, Group 0.

What you gain

Ranges are independent of each other. A problem in one does not slow the others, because each group elects its leader and commits its writes without waiting for any other. If a node goes down, each range it led elects a new leader among its replicas and keeps taking writes while a majority answers. A write confirmed with w: "majority" survives the loss of any minority of the replicas of its range.

What it costs

A range needs a majority of its replicas to accept writes, so each write waits for the disk of that majority. If you spread the replicas across distant data centres, that wait shows in the latency of each write, because every commit includes the round trip to the replicas that make up the majority. The Raft in every range and Ranges and replicas pages tell the same as reference.

Tags

  • Raft
  • ranges
  • replication
All posts