Skip to content

CASSANDRA-20476 & CASSANDRA-20736 Handle CMS member addresses changing concurrently - #4613

Closed
beobal wants to merge 27 commits into
apache:trunkfrom
beobal:samt/CASSANDRA-20476
Closed

CASSANDRA-20476 & CASSANDRA-20736 Handle CMS member addresses changing concurrently #4613
beobal wants to merge 27 commits into
apache:trunkfrom
beobal:samt/CASSANDRA-20476

Conversation

@beobal

@beobal beobal commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Changing broadcast address has always been supported, but it requires the node to inform the CMS of the change at startup. If a majority of the CMS members attempt to do this concurrently, they have no way to establish the quorum required to make those metadata changes, leading to a deadlocked startup.
This is addressed by the combination of 2 patchsets:

  • CASSANDRA-20736 modifies ClusterMetadata to represent the CMS membership as a set of node ids, rather than addresses.
  • CASSANDRA-20476 introduces a protocol for nodes starting up to discover the current address for CMS members if they have changed while that node was down. The node can then construct a temporary address lookup which it uses to establish contact with CMS members and update/get the latest agreed ClusterMetadata. When the starting node is itself a CMS member, this lookup enables it to form a consensus group with the other members so that address changes can be durably committed & disseminated.

@beobal
beobal requested review from krummas and removed request for krummas February 13, 2026 18:10
@beobal beobal changed the title CASSANDRA-20476 & CASSANDRA-20736 Handle all CMS member addresses changing concurrently CASSANDRA-20476 & CASSANDRA-20736 Handle CMS member addresses changing concurrently Feb 13, 2026

public ImmutableSet<NodeId> joiningMembers()
{
return ImmutableSet.copyOf(joiningMembers);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit; BTreeSet is already immutable, we probably don't need to copy it


/**
* Used to derive a CMSMembership when deserializing a ClusterMetadata instance written with a metadata version
* prior to V7. At that time, CMS membership was always inferred from the data placements of the distributed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"prior to V9" I think?

}

private final Map<NodeId, Pair<InetAddressAndPort, InetAddressAndPort>> overrides;
private final BiMap<InetAddressAndPort, InetAddressAndPort> addressMap;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressMap is only used in the toString method

return new InitialBuilder(metadata);
}

private final Map<NodeId, Pair<InetAddressAndPort, InetAddressAndPort>> overrides;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably be an ImmutableMap for clarity?

and if we make InitialBuilder and rebuild below build immutablemaps we can avoid the copying

return state == State.ACTIVE;
}

public InetAddressAndPort getAddressOverride(NodeId id)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused

else
{
// This cluster did not previously upgrade from a gossip based version (i.e. pre-6.0) but did at some point
// run a version prior to MetadataVersion.V7 where we started to encode CMS membership directly. This

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V9

// so we can derive the CMSMembership using the data placement and directory.
DataPlacement placement = placements.get(metadataKs.params.replication);
cmsMembership = CMSMembership.reconstruct(placement, dir);
placements = placements.unbuild().without(metadataKs.params.replication).build();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is unnecessary - we do the same thing directly after the if stmt

int currentRound = 0;
long roundTimeNanos = Math.min(TimeUnit.SECONDS.toNanos(4),
DatabaseDescriptor.getDiscoveryTimeout(TimeUnit.NANOSECONDS) / maxRounds);
// TODO a non-CMS node only needs to be able to contact a single CMS member to commit its STARTUP

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we fix this? It feels like we'll most often discover the full CMS if its up

and if it is not yet up, it might be better to wait here before trying to commit Startup?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think discovering the full CMS in the common case is not really a problem.
To the second point, I don't think it makes an awful lot of difference but once we are sure we know a majority of CMS member addresses isn't it better to start trying to commit the Startup? If we wait for confirmation of addresses for the whole CMS we could end up being blocked unnecessarily by a single DOWN CMS member?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, I think I just wanted to remove the todo


int maxRounds = 5;
int currentRound = 0;
long roundTimeNanos = Math.min(TimeUnit.SECONDS.toNanos(4),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is 4s enough here? Should we add another "discover survey" config setting?

Map<NodeId, InetAddressAndPort> confirmedCMS = new HashMap<>();

Set<InetAddressAndPort> candidates = new HashSet<>(previousCMS.values());
candidates.add(newAddress);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason we don't add the seeds to candidates here? Feels like it could save us a discovery round

@krummas krummas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tiny comment inline, feel free to ignore


public class NodeId implements Comparable<NodeId>, MultiStepOperation.SequenceKey
{
public static final NodeId UNREGISTERED = new NodeId(-1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we throw in toUUID below if we're unregistered? Just to make sure we don't accidentally write the unregistered UUID to a system table or something

@beobal
beobal force-pushed the samt/CASSANDRA-20476 branch from 2236ae1 to b39dfd4 Compare March 19, 2026 14:18
@pmcfadin pmcfadin added the needs-committer Patch ready, waiting for a committer to review and merge label Apr 13, 2026
beobal and others added 21 commits July 29, 2026 14:58
If two nodes restart with new broadcast addresses concurrently
and one is a CMS member, the non-member may send a TCM_COMMIT_REQ
containing its STARTUP transform to the CMS member's old address.
If this is no longer reachable, the sender should time out quickly
so it can resend to another CMS member, or try to discover the new
CMS address(es).
@beobal
beobal force-pushed the samt/CASSANDRA-20476 branch from b39dfd4 to 665839a Compare July 30, 2026 10:03
@beobal

beobal commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #4979 & #4980

@beobal beobal closed this Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-committer Patch ready, waiting for a committer to review and merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants