Investigate: Gossip queue pruning is ineffective at scale
- Dominant language
- Go
- Stars
- 30.1k
- Forks
- 4.6k
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 39
Description
Working with some users with large clusters, we've observed that although Consul sets a maximum intent queue depth for Serf-level intent messages (node "join" and "leave"), it is not effectively enforced at some scale.
This is technically a Serf issue but the overall effect is most import to Consul users so we are tracking the higher level issue here. There are also ramifications for memberlist too as that is where the actual queue is implemented.
Consul sets Serf's [Max.min queue depth](https://github.com/hashicorp/serf/blob/7faa1b06262f70780c3c35ac25a4c96d754f06f3/serf/config.go#L132-L141) such that the queue will be limited to the smaller of 4096 or 2*`SizeOfCluster`. So for a 20k node cluster, the Intent queues should never exceed 40k messages to prevent unbounded growth.
Problems:
1. In real-world scenarios with ~20k nodes and high levels of churn, we see intent queues growing effectively unboundedly - up to 160k messages.
* We enforce this in Serf by periodically (30s default, not configurable) calling `Prune` on the queue which pulls off the oldest (lowest priority) messages until it's back to the limit. This prune operation contends for the queue's lock with both enqueue and dequeue operations which are pretty frequent. We assume that it's just not able to clean up fast enough.
* We experimented in one case with tuning the period down to attempting to prune every 1 second. While this did seem to improve things on one node, other nodes (e.g. servers) who tend to have higher gossip requirements since all new nodes typically announce themselves to servers first still could't keep the queue size in check.
* There may be other reasons this appeared not to be effective, including that the depth metric is also being sampled at the same rate as the pruning which could mean we always sample exactly at the worst point before prune is called. We did not observe a "sawtooth" though only a steady climb which seemed to suggest prune just wasn't keeping up with enqueues at all.
2. The Serf-level intent queue is only one of the queues in the system. Memberlist itself also has a broadcast queue which has no limit at all. We should probably find a way to bound the size of that queue too otherwise arbitrarily stale messages in memberlist will still take priority over newer messages in Serf.
Possible next steps:
* Attempt to validate the contention hypothesis in a benchmark where we subject `memberlist.TransmistLimitedQueue` to an extreme rate of enqueue/dequeue with a high restransmit number say 20. Call `Prune` every 30 seconds and see if it is effective.
* Consider alternative enforcement, for example can we check the limit every time we enqueue a new message and if we went over, pop one off while still under lock? This might slow down enqueue but that seems reasonable quantifying how much with a microbenchmark might make sense but I expect it to be a good tradeoff for properly bounding resource usage.
* Consider adding a limit to memberlist's broadcast queue too. This may need some experimentation as it changes gossip behaviour at scale, but at least in theory it should be OK for the same reason it's OK in Serf - dropping messages that have already been transmitted a few times probably doesn't impact convergence significantly and if it does that's a reasonable tradeoff for bounded resource usage. Anti-entropy should ensure eventual convergence.
Contributor guide
Research direction
Start with memberlist.TransmistLimitedQueue, its Prune operation, and the Serf intent-queue limit described in the issue. Build the proposed extreme enqueue/dequeue benchmark with retransmit rate 20, then investigate contention and the unbounded memberlist broadcast queue. Done means the cause is validated and queue growth is bounded under the reported high-churn workload, with supporting benchmark coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- distributed-systems
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100