Function: receivegossip in gossip-protocol/gossipprotocol.py

Date: 2026-05-29

Time: 13:06

receive_gossip — Gossip Protocol Membership Merge

Purpose

receive_gossip is the convergence engine of the gossip protocol. When a node receives a membership list from a peer, this method merges the remote state into the local membership table. Its job is to ensure that all nodes in the cluster eventually agree on who is alive, suspected, or dead — without a centralized coordinator.

This implements the "receive" half of the epidemic protocol's push-pull exchange. Without it, heartbeat increments and failure detections would stay local to the node that observed them.

Contract

Preconditions:

Postconditions:

Invariant: The merge is idempotent with respect to heartbeat counters — receiving the same gossip twice produces the same result.

Parameters

| Parameter | Type | Description |

|-----------|------|-------------|

| membershiplist | dict[str, dict] | Remote node's full membership table. Each value has keys heartbeatcounter (int), timestamplastupdated (int), status (str: "alive", "suspected", "dead") |

| currenttime | int | Logical clock value for this gossip round. Used to stamp timestamplastupdated on any entry that gets updated, which feeds into detectfailures' timeout calculations |

Edge cases: If membershiplist is empty, this is a no-op. If it contains the receiving node's own entry, that entry is merged like any other — there's no self-exclusion guard here (unlike detectfailures which skips self).

Return Value

None. All effects are via mutation of self.membership.

Algorithm

The method iterates over every entry in the remote membership list and applies one of three cases:

Case 1: Unknown node (not in local membership)


if nid not in self.membership:

Case 2: Known node, remote has a higher heartbeat counter


if remote["heartbeat_counter"] > local["heartbeat_counter"]:

The remote has fresher information. Update the local entry:

1. Adopt the higher heartbeat counter

2. Reset timestamplastupdated to current_time (restarts the failure detection clock)

3. Propagate status with an asymmetry:

This asymmetry is the critical design choice: once a node is declared dead, it cannot be resurrected by stale "alive" gossip that happens to have a higher counter. This prevents flapping.

Case 3: Known node, equal heartbeat counter, remote says dead


elif (remote["status"] == "dead"
      and remote["heartbeat_counter"] == local["heartbeat_counter"]
      and local["status"] != "dead"):

This handles voluntary leave (leave() sets status to dead without incrementing the heartbeat counter). Without this branch, a node that calls leave() would never have its death accepted by peers — they'd see the same counter and ignore the update. This clause accepts the death notification even at equal counters.

Implicit Case 4: Everything else

If the remote has a lower heartbeat counter, or equal counter without a death notification, the remote info is stale — silently ignored.

Side Effects

Error Handling

None. The method assumes well-formed input matching the membership entry schema. Missing keys (heartbeatcounter, status, timestamplast_updated) will raise KeyError at runtime. There's no validation — this is internal protocol machinery, not a public API boundary.

Usage Patterns

Called in two contexts within GossipCluster.gossip_round():

1. Bidirectional peer exchange — during normal gossip rounds, two nodes swap membership lists and each calls receive_gossip with the other's data

2. Leave broadcast — a departing node sends its gossip to all active peers, who merge it via receive_gossip

The caller is responsible for providing a consistent currenttime — the same value should be used for all merges within a single gossip round so that detectfailures timeout calculations are coherent.

Dependencies