cockroachdb / cockroachdb/cockroach

Continuously poll all addresses from `--join` after cluster bootstrapping

Open
#103,594 0 comments 0 reactions 0 assignees View on GitHub
A-kv-server A-server-networking C-enhancement
Dominant language
Go
Stars
32.5k
Forks
4.1k
PR merge metrics
PR metrics pending

Description

**Is your feature request related to a problem? Please describe.**

It should be possible to safely and reliably form a CockroachDB cluster without specifying (or perhaps knowing) the addresses of every node in the `--join` flag. It's possible to provide `--join` with a TCP load balance or a hostname that has SRV records when using `--experimental-srv-dns` but this is neither safe nor reliable because there is no continuous discovery of nodes after the initial bootstrap phase [^1].

When creating a new CRDB cluster or adding a new set of nodes to an existing cluster (expanding into a new region), if `--join` is set to leverage some method of service discovery (either a TCP load balancer or the `--experimental-srv-dns` flag) it is possible for partitions to be formed. [^2]

The likelihood of encountering this case/bug can be exacerbated or potentially mitigated depending on the exact state of the cluster when nodes first enter the bootstrap phase and/or the exact configuration of the service discovery mechanism[1]. For example, discovery mechanisms that only return nodes that are currently accessible (Kubernetes services) appear to make this case more likely.

We'll walk through an unlikely case first and then a significantly more likely case.

### The unlikely case

Let's say that we have nodes: A, B, and C and `--join` is set to a TCP load balance that routes to any of A, B, and C.

When node A dials the load balancer, it is routed to itself. It has discovered a node and therefore does not continue to poll the host passed via `--join`.
The same occurs for nodes B and C.

When `cockroach node init` is executed on node A, it will succeed. However, nodes B and C will not have joined any gossip network and they will remain ignorant of the newly initialized cluster.

### The more realistic case

Let's say we have two regions, `us-east-{1,2}`, each with 3 nodes: A, B, C and D, E, F.

`us-east-1` is brought online and a cluster is successfully initialized containing A, B, and C.

Executing `cockroach node list` on any of A, B, or C would return A, B, and C.

Next, we attempt to add `us-east-2` to the cluster. We'll set the `--join` flag to be two TCP load balancers that route to the nodes in `us-east-1` and `us-east-2`, respectively. [^3]

Similar to our first case, when D, E, and F are brought online, they each reach out to a load balancer. In this case, they'll happen to elect the load balancer for `us-east-2`, their own region.

D, E, and F will all successfully connect to either D, E, or F. They will NOT have any connection to the cluster in `us-east-1` as they all happened to resolve the `--join` flag to nodes within their own region.

**Describe the solution you'd like**

CockroachDB should either continuously poll the values provided to the `--join` flag after establishing gossip or continuously poll until each value in the `--join` flag has resolved to at last one successful connection. This will ensure that any partitions that happened to be formed during the bootstrap phase will eventually be resolved.

**Describe alternatives you've considered**

Defining a Kubernetes Service per "region" that requires target Pods to report as "Ready" (The default value that is often disabled for CockroachDB) and using the services as the `--join` may solve this race condition most of the time. Nodes within this service MUST report an `--advertise-host` that is routable regardless of whether or not the Pod reports as "Ready".

In the "Unlikely case", Nodes would NOT establish gossip, initially. When they attempt to dial the service, the connection would fail as there would be no ready Pods. This would cause each node to enter a wait loop in which they continuously dial the service. Once any of the 3 nodes are initialized, that node will become routable via the service and each previously unready node would successfully connect to it.

In the "more realistic case", `us-east-2` would not be able to connect to any of the newly added nodes within it's own region unless they had successfully connected to the already formed cluster in `us-east-1`.

This all relies on CockroachDB nodes that are not part of a cluster reporting themselves as not ready.

It seems likely that changing the `--join` flag and `--advertise-host` flags at the same time and invalidating all previously used entries, could result in an already initialized cluster forming partitions for the same reasons as an uninitialized cluster. Therefore this solution should be used as a temporary bandaid rather than being a long term solution.

[^1]: My naming may be incorrect here. Please correct me as need be.
[^2]: In theory it may be possible for this to occur without the use of service discovery but we've not observed this behavior.
[^3]: The ordering of the load balancers to the --join flag may be important here. This is currently unclear.

Jira issue: CRDB-28091

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.