Distributed & Decentralized Systems Curriculum
Real World Architecture Β· Gossip Protocols SWIM

Key Question

How does SWIM scale failure detection and membership to thousands of nodes?

Deep Dive

Before SWIM (Scalable Weakly-consistent Infection-style Membership protocol, 2002), failure detection was a centralized affair: a leader pings everyone, or every node pings every other node. Neither scales. SWIM made two breakthroughs: (1) failure detection with constant message load per node regardless of cluster size, and (2) piggybacking membership changes on the same messages. In one protocol, you get both.

The Protocol, Step by Step

Each round, a node runs this sequence:

ROUND START
    β”‚
    β”œβ”€β”€(1)──► Pick a random node T from membership list.
    β”‚         Send T a ping message.
    β”‚
    β”œβ”€β”€(2)──► Wait for ack from T.
    β”‚         β”‚
    β”‚         β”œβ”€β”€ ack received β†’ T is alive. Done.
    β”‚         β”‚
    β”‚         └── timeout (no ack) β†’
    β”‚                    β”‚
    β”‚                    └──(3)──► Pick k random nodes
    β”‚                              (indirect probes).
    β”‚                              Send each: "please probe T for me."
    β”‚                              β”‚
    β”‚                              β”œβ”€β”€ any ack β†’ T is alive. Done.
    β”‚                              β”‚
    β”‚                              └── all k fail β†’
    β”‚                                         β”‚
    β”‚                                         └──(4)──► Mark T as "suspected,"
    β”‚                                                   piggyback suspicion
    β”‚                                                   on ping messages.
    β”‚
    └──(5)──► Piggyback any pending membership changes
              (joins, leaves, suspicions) on the ping
              message before sending.

Indirect Probing β€” Why k=3 Works

The indirect probe is the key invention. When a direct ping times out, it could mean the target is dead β€” or it could mean packet loss. SWIM doesn’t guess. It asks k independent nodes to try pinging the target. If any of them succeeds, the target is clearly alive (network partition or transient loss from the original node’s perspective).

Direct ping fails:                    Indirect probes succeed:
β”Œβ”€β”€β”€β”€β”€β”€β”         ping ────┐  βœ—      β”Œβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”
β”‚  A   β”‚                  β”œβ”€β”€β”€β”€β”€    β”‚  A   │──►│  C   │──pingβ”€β”€β–Ίβ”Œβ”€β”€β”€β”€β”€β”€β”
β”‚      β”‚                  β”‚         β”‚      β”‚   β””β”€β”€β”€β”€β”€β”€β”˜         β”‚  B   β”‚
β”‚      β”‚         timeout  β”‚         β”‚      β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”         β”‚      β”‚
β””β”€β”€β”€β”€β”€β”€β”˜         β—„β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚      │──►│  D   │──ping──►│      β”‚
                                     β””β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”˜
                                     If C or D gets an ack from B,
                                     B is alive. A retracts suspicion.

Why k=3? With three independent probes on a lossy network (say 10% loss per path), the chance all three paths also lose the packet is 0.1Β³ = 0.001. That is, the false positive rate drops from 10% to 0.1% with just three extra messages.

Target Selection: Uniformly Random

Each node picks its ping target uniformly at random from the membership list. This is critical for scalability. With N nodes and each node sending one ping per round, the per-node message load is O(1) β€” roughly one ping, one ack, and maybe three indirect probes. The total network load is O(N), which is the best you can do (every node participates).

Message load per node per round (N=10,000):
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ 1 ping         (to random node)  β”‚
  β”‚ 1 ack          (from that node)  β”‚
  β”‚ 3 probe reqs   (if ping fails)   β”‚
  β”‚ 3 probe acks   (from helpers)    β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ β‰ˆ 8 messages per round           β”‚
  β”‚ (same for N=100 or N=100,000)    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Suspicion and False Positive Protection

When node A suspects node B, it doesn’t declare B dead. It broadcasts a β€œB is suspected” message. Other nodes add this to their state. If any node has heard from B (or can reach B), it responds with a β€œB is alive” ack, and the suspicion is cleared. This prevents a single dropped packet from causing an expulsion.

Only after a configurable timeout (suspicion timeout) does a suspected node get declared dead and removed. This gives plenty of time for false positives to be discovered.

Membership Dissemination via Piggybacking

Membership changes (joins, leaves, suspicions, deaths) are piggybacked on existing ping messages. This is why SWIM is β€œinfection-style” β€” it uses the same rumor mongering mechanism as gossip for membership data. No separate channel, no extra messages.

Check Your Understanding

  1. Why does SWIM use indirect probing (k=3) instead of retrying the direct ping? What problem does this solve?

  2. SWIM maintains O(1) message load per node regardless of cluster size. How is this possible? What assumption about target selection enables this?

  3. What is the purpose of the β€œsuspicion” state? What happens if a node is falsely suspected but no other node can reach it either?

The β€œSo What?”

SWIM is the bridge between theory and practice for gossip protocols. It was the first protocol to prove that failure detection can scale linearly (O(N) total messages) with strong guarantees. Today, SWIM and its derivatives (memberlist, Serf) run in production at HashiCorp, Netflix, and cloud infrastructure providers, handling millions of failure-detection rounds per second across clusters of thousands of machines.


✏️ Exercises

Gossip Protocols & SWIM: Exercises

Exercise 1: Fanout and Coverage

In a cluster of 1000 nodes, a push gossip protocol has fanout f = 3. A single node receives a new piece of data at time t = 0.

Using the approximation I(r) β‰ˆ N * (1 - exp(-f^r / N)), how many rounds are needed for 99.9% of the cluster to be infected? Show your work.

Exercise 2: Indirect Probing in SWIM

In a SWIM deployment, the network has a 5% packet loss rate. Node A tries to ping node B directly and gets no ack within the timeout.

a) What is the probability that the direct ping failure was a false positive (B is alive, but the packet was lost)?

b) If Node A now uses indirect probing with k = 3 independent probe nodes, what is the probability that ALL 3 indirect probes also fail (false positive persists)?

c) How many indirect probes k would you need to reduce the false positive rate below 0.001% (one in 100,000)?

Exercise 3: Gossip Congestion in Cassandra

Cassandra nodes gossip once per second with a single random peer. Due to a network congestion event, the gossip interval doubles to 2 seconds for a 100-node cluster. Assume the infection curve is I(r) β‰ˆ N * (1 - exp(-f^r / N)) with fanout effectively 1 (one peer per round).

a) How many rounds does convergence normally take (to 99%) when gossip runs every 1 second? (Round down the exponent to account for the fanout of exactly 1 β€” treat this as: each round, in expectation, doubles the number of nodes reached, since the receiving peer now knows and can tell others.)

b) How many seconds does convergence normally take?

c) How many seconds does convergence take under congestion?

d) If a coordinator waits for β€œquorum gossip” (51 nodes must know) before serving a read, how much extra latency does the congestion add?

πŸ‘οΈ View Solutions

Gossip Protocols & SWIM: Solutions

Exercise 1: Fanout and Coverage

Setup: N = 1000, f = 3, Target I(r) / N β‰₯ 0.999

Formula: I(r) / N β‰ˆ 1 - exp(-f^r / N)

Set 1 - exp(-3^r / 1000) β‰₯ 0.999: β†’ exp(-3^r / 1000) ≀ 0.001 β†’ -3^r / 1000 ≀ ln(0.001) β†’ 3^r / 1000 β‰₯ -ln(0.001) β†’ 3^r / 1000 β‰₯ 6.908 β†’ 3^r β‰₯ 6908 β†’ r β‰₯ log₃(6908) β†’ r β‰₯ ln(6908) / ln(3) β†’ r β‰₯ 8.84 / 1.099 β†’ r β‰₯ 8.04

Answer: 9 rounds (you need a whole round, so round up).

Verification: 3^8 = 6561, ratio 6561/1000 = 6.56, 1 - exp(-6.56) = 0.9986 (~99.86%). Close but just under 99.9%. At round 9: 3^9 = 19683, ratio 19.68, 1 - exp(-19.68) β‰ˆ 1.0 (effectively 100%).

Key insight: With fanout 3, 1000 nodes reach near-perfect coverage in just 9 rounds β€” about 1-3 seconds at typical gossip intervals.


Exercise 2: Indirect Probing in SWIM

Setup: Packet loss rate = 5% = 0.05

a) Probability of false positive on direct ping:

The ping fails only because the packet was lost. B is alive. The probability is simply the packet loss rate:

P(false positive | direct ping) = 0.05 = 5%

Answer: 5%. This is why SWIM doesn’t declare death on a single missed ping β€” 5 false positives per 100 pings is too high.

b) False positive persists with k=3 indirect probes:

The indirect probes are independent. All 3 probes must independently lose their packets:

P(all 3 fail) = 0.05Β³ = 0.000125 = 0.0125%

Answer: 0.0125% β€” a 400Γ— reduction from the 5% false positive rate of a single ping.

c) How many probes to reach 0.001%?

We need P(all k fail) < 0.00001: β†’ 0.05^k < 0.00001 β†’ k * ln(0.05) < ln(0.00001) β†’ k * (-2.996) < -11.513 β†’ k > 11.513 / 2.996 β†’ k > 3.84

Answer: k = 4. With 4 indirect probes, the false positive rate is 0.05⁴ = 0.00000625 = 0.000625%, well below the 0.001% target.


Exercise 3: Gossip Congestion in Cassandra

Setup: N = 100, fanout = 1 (effectively), infection doubles each round since each newly infected node can now infect one more per round. First round: 1 node knows. Second round: 2. Third round: 4. Fourth: 8. This is I(r) = 2^(r-1) capped at N.

a) Rounds to 99%:

99% of 100 = 99 nodes.

From the doubling pattern: 2^(r-1) β‰₯ 99 β†’ r-1 β‰₯ logβ‚‚(99) β†’ r β‰₯ 6.63 + 1 β†’ r β‰₯ 7.63

Answer: 8 rounds. Verification: Round 7 produces 2^6 = 64 nodes (64%). Round 8 produces 2^7 = 128 (capped at 100, so 100%).

b) Normal convergence time:

8 rounds Γ— 1 sec/round = 8 seconds.

c) Congestion convergence time:

Interval doubles to 2 seconds. Same 8 rounds needed.

8 rounds Γ— 2 sec/round = 16 seconds (2Γ— normal).

d) Extra latency for quorum (51 nodes):

Normal time to reach 51 nodes: 2^(r-1) β‰₯ 51 β†’ r β‰₯ 6.67 β†’ 7 rounds. 7 rounds Γ— 1 sec = 7 seconds.

Under congestion: 7 rounds Γ— 2 sec = 14 seconds.

Extra latency = 14 - 7 = 7 seconds.

Key insight: Doubling the gossip interval doesn’t just double convergence time in a trivial way β€” it delays every downstream operation that depends on gossip freshness (node discovery, schema changes, failure detection). In this case, a read that requires quorum knowledge is delayed by an extra 7 seconds.