Skip to content

Commit 06d3b07

Browse files
committed
chore(nextest): add config to serialize cluster tests and retry flakes
Cluster integration tests spin up 3-node Raft clusters with per-node Tokio runtimes; running them alongside the rest of the suite caused port/fd exhaustion and starved Raft heartbeats on high-core machines. Pin them to a single-threaded test-group that claims all test slots, and allow one retry for startup jitter. CI profile adds more retries and JUnit output.
1 parent 751e804 commit 06d3b07

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

.config/nextest.toml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# nextest configuration. Run with: cargo nextest run --all-features
2+
#
3+
# Why nextest over `cargo test`:
4+
# - Each test runs in its own process → no in-process state contention.
5+
# Integration tests that spawn 3-node clusters used to hang under
6+
# `cargo test`'s default within-binary parallelism because multiple
7+
# clusters in the same process exhausted ports / file descriptors.
8+
# - Per-test timeouts make hangs fail fast instead of stalling CI.
9+
# - Better failure output, retry support, and JUnit XML for CI.
10+
11+
[profile.default]
12+
# Hard ceiling per test. Anything above this is a bug, not a slow test.
13+
slow-timeout = { period = "30s", terminate-after = 4 }
14+
15+
# Use every available core for cheap unit tests. Heavy cluster tests
16+
# are kept from starving by `threads-required` overrides below — they
17+
# claim ALL slots so nothing else runs alongside them, regardless of
18+
# whether you're on a 24-core dev box or a 2-core CI runner.
19+
test-threads = "num-cpus"
20+
21+
# Heavy cluster tests: each one brings up 3 servers + per-node Tokio
22+
# runtimes. Two things keep them stable across machine sizes:
23+
#
24+
# 1. `test-group = "cluster"` with `max-threads = 1` ensures at
25+
# most ONE cluster test runs at a time (no two clusters share
26+
# ports / file descriptors / thread pools).
27+
# 2. `threads-required = "num-test-threads"` makes the running
28+
# cluster test claim every available test slot, which evicts
29+
# every other test from the run-queue while it's executing.
30+
# That's what prevents a 24-core dev box from scheduling 23
31+
# unit tests alongside the cluster and starving its Raft
32+
# heartbeats.
33+
#
34+
# The combined effect: cluster tests run strictly serially AND
35+
# strictly alone, and the rest of the suite gets full parallelism
36+
# the moment the cluster test finishes.
37+
[[profile.default.overrides]]
38+
filter = '''
39+
binary(/cluster/)
40+
| binary(/cross_node/)
41+
| binary(/_lease_/)
42+
| binary(descriptor_lease_drain)
43+
| binary(descriptor_lease_forwarding_and_renewal)
44+
| binary(descriptor_lease_planner_integration)
45+
| binary(descriptor_versioning_cross_node)
46+
| binary(prepared_cache_invalidation)
47+
| binary(sql_cluster_cross_node_dml)
48+
'''
49+
test-group = 'cluster'
50+
threads-required = 'num-test-threads'
51+
# Cluster tests bring up real Raft nodes and racy multi-node
52+
# convergence checks. They're flaky enough that one retry catches
53+
# legitimate startup jitter without hiding real regressions — a
54+
# genuinely broken test fails twice in a row.
55+
retries = { backoff = "fixed", count = 2, delay = "1s" }
56+
57+
[test-groups]
58+
cluster = { max-threads = 1 }
59+
60+
[profile.ci]
61+
# CI inherits the default profile (cluster group, threads-required,
62+
# slow-timeout) and adds:
63+
# - more retries: CI runners are ~2× slower per-core than dev
64+
# workstations, so the cluster tests' in-test `wait_for`
65+
# budgets are proportionally tighter. Three retries (four total
66+
# attempts) buys headroom for jitter without papering over real
67+
# regressions — a genuinely broken test fails four times in a row.
68+
# - JUnit XML: picked up by the workflow's artifact upload.
69+
#
70+
# NOTE: we deliberately do NOT bump `slow-timeout` here. The
71+
# slow-timeout only controls when nextest gives up on a stuck
72+
# *process*; it does NOT extend the test's internal `wait_for`
73+
# budgets. Once a `wait_for` panics, the test has already failed —
74+
# making nextest wait longer just wastes CI minutes on cleanup.
75+
retries = { backoff = "fixed", count = 3, delay = "2s" }
76+
fail-fast = false
77+
78+
[profile.ci.junit]
79+
path = "junit.xml"

0 commit comments

Comments
 (0)