Skip to content

[Feature] Gossip has no convergence concept and the leader promotes members on its local view alone — decide the seen-set story (survey) #1187

Description

@pathosDev

Problem

Membership gossip has no convergence concept, and the leader acts without one. This is a documented design decision, not an accident — ClusterEvents.ts states it plainly: "There is no seenBy. […] gossip here merges member records individually, so there is no such version to have been seen." Records are merged one by one under scalar last-writer-wins on a wall-clock-seeded version; there is no notion of "the cluster has agreed on this view".

The consequence that makes it worth a tracked decision rather than a code comment: leader actions fire on the local view alone. onGossip promotes every joining/weakly-up member to up the moment isLeader() is true locally — and isLeader() itself is "lowest-addressed up member of my local map", so during any view divergence each side has a leader and each leader promotes. Everything built on membership inherits this: singleton placement (#949), shard allocation (#948), downing decisions (each strategy computes from the local view). Several filed bugs are, at root, instances of "two nodes acted on disagreeing views at the same time" — this issue is the umbrella decision about whether that stays a fact of the design or gets a convergence gate.

Options worth pricing (not mutually exclusive):

  1. Status quo, documented: keep per-record LWW, write the "leader may act on a divergent view" caveat into the cluster docs where singleton/sharding guarantees are stated, and lean on leases as the only uniqueness mechanism (the current de-facto answer).
  2. A seen-set gate for leader actions only: leader-side actions (promotion, allocation defaults) wait until the records they depend on have been echoed back by a quorum of reachable members — a lightweight convergence proxy without versioning the whole membership map.
  3. Versioned-view gossip: the full model — a vector-clocked membership document with a real seen-set. Largest change, retires the class.

Evidence

src/cluster/ClusterEvents.ts:44-46 — the design statement quoted above.

src/cluster/Cluster.ts:917-925 — promotion on the local view:

    // Leader promotes joining (and weakly-up) members to up.
    if (this.isLeader()) {
      for (const member of this.members.values()) {
        if (member.status === 'joining' || member.status === 'weakly-up') {
          this.log.debug(`leader-promote: ${member.address} ${member.status}→up`);
          this.updateMember(member.withStatus('up'));
        }
      }
    }

Verification status

CONFIRMED-BY-READ — both sites quoted from the v0.15.0 tree. From the independent production-readiness pass (2026-08-14), second batch (design decisions). Related consequences already filed: #935 (merge tie-break), #930 (partition heal), #948/#949 (dual ownership), #1176 (island merge); #839/#838 cover the downing-side stability window and strategy selection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority: mediumUseful, not urgentproduction-goalBlocks or defines the path to production readiness

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions