state: switch consumers to NodeStore primary routes

Replace routes.PrimaryRoutes reads with NodeStore. Connect bumps
SessionEpoch; Disconnect re-checks it inside UpdateNode so the
check and mutation are atomic against a concurrent Connect on
the same node.

The connect_race regression test is carried in its final
SessionEpoch form.

Updates #3203
This commit is contained in:
Kristoffer Dalby 2026-04-28 12:17:04 +00:00
parent da927eb018
commit 437754aeea
8 changed files with 375 additions and 191 deletions

View file

@ -80,7 +80,7 @@ func TestHAHealthProbe_HealthyNodes(t *testing.T) {
prober.ProbeOnce(ctx, srv.App.Change)
// Both nodes should be healthy, primary unchanged (node 1).
assert.True(t, srv.State().PrimaryRoutes().IsNodeHealthy(nodeID1))
assert.True(t, srv.State().IsNodeHealthy(nodeID1))
primaries := srv.State().GetNodePrimaryRoutes(nodeID1)
assert.Contains(t, primaries, route)
@ -111,7 +111,7 @@ func TestHAHealthProbe_UnhealthyFailover(t *testing.T) {
require.Contains(t, primaries, route, "node 1 should be primary initially")
// Mark node 1 unhealthy — should failover to node 2.
changed := srv.State().PrimaryRoutes().SetNodeHealthy(nodeID1, false)
changed := srv.State().SetNodeUnhealthy(nodeID1, true)
assert.True(t, changed, "marking primary unhealthy should change primaries")
primaries2 := srv.State().GetNodePrimaryRoutes(nodeID2)
@ -141,12 +141,12 @@ func TestHAHealthProbe_RecoveryNoFlap(t *testing.T) {
nodeID2 := advertiseAndApproveRoute(t, srv, c2, route)
// Failover: node 1 → node 2.
srv.State().PrimaryRoutes().SetNodeHealthy(nodeID1, false)
srv.State().SetNodeUnhealthy(nodeID1, true)
primaries := srv.State().GetNodePrimaryRoutes(nodeID2)
require.Contains(t, primaries, route, "node 2 should be primary")
// Recovery: node 1 healthy again. Node 2 should STAY primary.
changed := srv.State().PrimaryRoutes().SetNodeHealthy(nodeID1, true)
changed := srv.State().SetNodeUnhealthy(nodeID1, false)
assert.False(t, changed, "recovery should not change primaries (no flap)")
primaries = srv.State().GetNodePrimaryRoutes(nodeID2)
@ -173,8 +173,8 @@ func TestHAHealthProbe_ConnectClearsUnhealthy(t *testing.T) {
advertiseAndApproveRoute(t, srv, c2, route)
// Mark unhealthy.
srv.State().PrimaryRoutes().SetNodeHealthy(nodeID1, false)
assert.False(t, srv.State().PrimaryRoutes().IsNodeHealthy(nodeID1))
srv.State().SetNodeUnhealthy(nodeID1, true)
assert.False(t, srv.State().IsNodeHealthy(nodeID1))
// Reconnect clears unhealthy via State.Connect → ClearUnhealthy.
c1.Disconnect(t)
@ -182,7 +182,7 @@ func TestHAHealthProbe_ConnectClearsUnhealthy(t *testing.T) {
c1.WaitForPeers(t, 1, 10*time.Second)
assert.True(t, srv.State().PrimaryRoutes().IsNodeHealthy(nodeID1),
assert.True(t, srv.State().IsNodeHealthy(nodeID1),
"reconnect should clear unhealthy state")
}