Prefer QUIC fabric endpoints
This commit is contained in:
@@ -2781,7 +2781,10 @@ func meshOutboundSessionReportFromState(cfg config.Config, meshState *syntheticM
|
|||||||
}
|
}
|
||||||
|
|
||||||
func meshEndpointReport(cfg config.Config, identity state.Identity, meshState *syntheticMeshState, observedAt time.Time, candidates []mesh.PeerEndpointCandidate) map[string]any {
|
func meshEndpointReport(cfg config.Config, identity state.Identity, meshState *syntheticMeshState, observedAt time.Time, candidates []mesh.PeerEndpointCandidate) map[string]any {
|
||||||
transport := cfg.MeshAdvertiseTransport
|
transport := strings.TrimSpace(candidates[0].Transport)
|
||||||
|
if transport == "" {
|
||||||
|
transport = strings.TrimSpace(cfg.MeshAdvertiseTransport)
|
||||||
|
}
|
||||||
if transport == "" {
|
if transport == "" {
|
||||||
transport = "direct_tcp_tls"
|
transport = "direct_tcp_tls"
|
||||||
}
|
}
|
||||||
@@ -3842,6 +3845,20 @@ func advertisedEndpointCandidates(cfg config.Config, identity state.Identity, me
|
|||||||
Priority: 10,
|
Priority: 10,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if cfg.MeshQUICFabricEnabled && meshState != nil && strings.TrimSpace(meshState.QUICFabricListenAddr) != "" {
|
||||||
|
candidates = append(candidates, mesh.PeerEndpointCandidate{
|
||||||
|
EndpointID: identity.NodeID + "-quic-fabric",
|
||||||
|
NodeID: identity.NodeID,
|
||||||
|
Transport: "direct_quic",
|
||||||
|
Address: "quic://" + meshState.QUICFabricListenAddr,
|
||||||
|
Reachability: reachabilityFromConnectivityMode(cfg.MeshConnectivityMode),
|
||||||
|
NATType: cfg.MeshNATType,
|
||||||
|
ConnectivityMode: cfg.MeshConnectivityMode,
|
||||||
|
Region: cfg.MeshRegion,
|
||||||
|
Priority: 5,
|
||||||
|
PolicyTags: []string{"fast-path"},
|
||||||
|
})
|
||||||
|
}
|
||||||
candidates = append(candidates, interfaceEndpointCandidates(cfg, identity, meshState, observedAt)...)
|
candidates = append(candidates, interfaceEndpointCandidates(cfg, identity, meshState, observedAt)...)
|
||||||
for i := range candidates {
|
for i := range candidates {
|
||||||
if candidates[i].EndpointID == "" {
|
if candidates[i].EndpointID == "" {
|
||||||
|
|||||||
@@ -720,12 +720,17 @@ func TestHeartbeatPayloadIncludesMeshEndpointReport(t *testing.T) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("mesh endpoint report missing: %+v", payload.Metadata)
|
t.Fatalf("mesh endpoint report missing: %+v", payload.Metadata)
|
||||||
}
|
}
|
||||||
if report["peer_endpoint"] != "https://node-a.example.test:443" ||
|
if report["peer_endpoint"] != "quic://127.0.0.1:19443" ||
|
||||||
|
report["transport"] != "direct_quic" ||
|
||||||
report["connectivity_mode"] != "outbound_only" ||
|
report["connectivity_mode"] != "outbound_only" ||
|
||||||
report["nat_type"] != "symmetric" ||
|
report["nat_type"] != "symmetric" ||
|
||||||
report["region"] != "eu" {
|
report["region"] != "eu" {
|
||||||
t.Fatalf("unexpected endpoint report: %+v", report)
|
t.Fatalf("unexpected endpoint report: %+v", report)
|
||||||
}
|
}
|
||||||
|
candidates, ok := report["endpoint_candidates"].([]mesh.PeerEndpointCandidate)
|
||||||
|
if !ok || len(candidates) < 2 || candidates[0].Transport != "direct_quic" || candidates[1].Transport != "wss" {
|
||||||
|
t.Fatalf("unexpected endpoint candidates: %+v", report["endpoint_candidates"])
|
||||||
|
}
|
||||||
if payload.Capabilities["mesh_dynamic_endpoint_reporting"] != true {
|
if payload.Capabilities["mesh_dynamic_endpoint_reporting"] != true {
|
||||||
t.Fatalf("dynamic endpoint capability missing: %+v", payload.Capabilities)
|
t.Fatalf("dynamic endpoint capability missing: %+v", payload.Capabilities)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ func scorePeerEndpointCandidate(candidate PeerEndpointCandidate, opts EndpointCa
|
|||||||
reasons := []string{"base"}
|
reasons := []string{"base"}
|
||||||
|
|
||||||
switch candidate.Transport {
|
switch candidate.Transport {
|
||||||
|
case "quic", "direct_quic", "udp_quic", "quic_udp":
|
||||||
|
score += 45
|
||||||
|
reasons = append(reasons, "transport:quic")
|
||||||
case "direct_tcp_tls", "direct_http", "direct_https":
|
case "direct_tcp_tls", "direct_http", "direct_https":
|
||||||
score += 35
|
score += 35
|
||||||
reasons = append(reasons, "transport:direct")
|
reasons = append(reasons, "transport:direct")
|
||||||
|
|||||||
@@ -99,6 +99,45 @@ func TestRankPeerEndpointCandidatesUsesDeterministicTieBreak(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRankPeerEndpointCandidatesPrefersQUICFastPath(t *testing.T) {
|
||||||
|
now := time.Date(2026, 5, 16, 12, 0, 0, 0, time.UTC)
|
||||||
|
candidates := []PeerEndpointCandidate{
|
||||||
|
{
|
||||||
|
EndpointID: "node-b-wss",
|
||||||
|
NodeID: "node-b",
|
||||||
|
Transport: "wss",
|
||||||
|
Address: "wss://node-b.example.test",
|
||||||
|
Reachability: "public",
|
||||||
|
NATType: "none",
|
||||||
|
ConnectivityMode: "direct",
|
||||||
|
Priority: 10,
|
||||||
|
LastVerifiedAt: &now,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EndpointID: "node-b-quic",
|
||||||
|
NodeID: "node-b",
|
||||||
|
Transport: "direct_quic",
|
||||||
|
Address: "quic://node-b.example.test:19443",
|
||||||
|
Reachability: "public",
|
||||||
|
NATType: "none",
|
||||||
|
ConnectivityMode: "direct",
|
||||||
|
Priority: 10,
|
||||||
|
LastVerifiedAt: &now,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ranked := RankPeerEndpointCandidates(candidates, EndpointCandidateScoreOptions{
|
||||||
|
ChannelClass: SyntheticChannelFabricControl,
|
||||||
|
Now: now,
|
||||||
|
MaxVerificationAge: time.Minute,
|
||||||
|
})
|
||||||
|
if ranked[0].Candidate.EndpointID != "node-b-quic" {
|
||||||
|
t.Fatalf("top endpoint = %q, want quic: %+v", ranked[0].Candidate.EndpointID, ranked)
|
||||||
|
}
|
||||||
|
if !containsReason(ranked[0].Reasons, "transport:quic") {
|
||||||
|
t.Fatalf("quic reason missing: %+v", ranked[0].Reasons)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRankPeerEndpointCandidatesPrefersCorporatePrivateEndpoint(t *testing.T) {
|
func TestRankPeerEndpointCandidatesPrefersCorporatePrivateEndpoint(t *testing.T) {
|
||||||
now := time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC)
|
now := time.Date(2026, 4, 28, 12, 0, 0, 0, time.UTC)
|
||||||
candidates := []PeerEndpointCandidate{
|
candidates := []PeerEndpointCandidate{
|
||||||
|
|||||||
@@ -307,6 +307,9 @@ in heartbeat metadata, and pass the setting through host-agent install/update
|
|||||||
profiles.
|
profiles.
|
||||||
`mesh-live-smoke` verifies the QUIC carrier by starting a temporary QUIC fabric
|
`mesh-live-smoke` verifies the QUIC carrier by starting a temporary QUIC fabric
|
||||||
server and requiring a `PING`/`PONG` round trip over `QUICFabricTransport`.
|
server and requiring a `PING`/`PONG` round trip over `QUICFabricTransport`.
|
||||||
|
Nodes now advertise enabled QUIC fabric listeners as `direct_quic` fast-path
|
||||||
|
endpoint candidates, and endpoint ranking prefers QUIC over WebSocket/HTTPS
|
||||||
|
compatibility candidates for fabric sessions.
|
||||||
|
|
||||||
Deliverables:
|
Deliverables:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user