[PR #6767] [CLOSED] [client] warm lazy connections from the DNS resolver #29875

Closed
opened 2026-08-05 08:09:15 -04:00 by saavagebueno · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/6767
Author: @mlsmaycon
Created: 7/14/2026
Status: Closed

Base: mainHead: feature/dns-lazy-conn-warmup


📝 Commits (10+)

  • 588735f [client] warm lazy connections from the DNS resolver
  • f4f3e29 Merge remote-tracking branch 'origin/main' into feature/dns-lazy-conn-warmup
  • b457cde [e2e] resolve endpoint before the proxy-peer gate to trigger DNS warm-up
  • 0325269 [e2e] resolve endpoint before the proxy-peer gate in the allowlist test
  • a0c1548 [e2e] register Bedrock provider with the normalized catalog model id
  • 82d8f0a [e2e] source Bedrock model from AWS_BEDROCK_MODEL and normalize catalog id
  • 38b377f [client] tie DNS warm-up dial to engine context; fix codespell
  • 7825036 Merge branch 'main' into feature/dns-lazy-conn-warmup
  • a4d0f24 [client] Address review comments on DNS lazy-connection warm-up
  • e4f66aa [ci] Temporarily run agent-network e2e on push to this branch

📊 Changes

14 files changed (+656 additions, -40 deletions)

View changed files

📝 client/internal/conn_mgr.go (+25 -3)
📝 client/internal/conn_mgr_test.go (+66 -0)
📝 client/internal/dns/local/local.go (+107 -20)
📝 client/internal/dns/local/local_test.go (+2 -2)
client/internal/dns/local/warmup_test.go (+172 -0)
📝 client/internal/dns/mock_server.go (+6 -0)
📝 client/internal/dns/server.go (+10 -2)
client/internal/dns_peer_activator.go (+76 -0)
client/internal/dns_peer_activator_test.go (+129 -0)
📝 client/internal/engine.go (+10 -0)
📝 e2e/agentnetwork/chat_test.go (+21 -4)
📝 e2e/agentnetwork/guardrail_test.go (+24 -5)
📝 e2e/agentnetwork/skiptls_test.go (+4 -2)
📝 e2e/agentnetwork/vllm_test.go (+4 -2)

📄 Description

Describe your changes

With lazy connections enabled, a peer is not dialed until on-demand traffic arrives, so the first request to a peer resolved by name (e.g. the agent-network reverse-proxy) races — or loses to — the WireGuard handshake. Activation was previously reactive only: a data-path packet or an inbound signal.

This adds a proactive, DNS-time trigger. When the local resolver answers for an overlay name, it now warms the lazy connection to the peer(s) the answer points at and waits briefly for one to connect before returning the response — so by the time the client sends its first packet the tunnel is already up.

  • dns/local: new PeerActivator capability + SetPeerActivator setter (mirrors the existing PeerConnectivity/SetPeerConnectivity injection). ServeDNS warms on the pre-filter answer, so activating a lazily-idle peer also lets it survive the disconnected-peer filter. No-op when no activator is wired (lazy off) or the answer carries no peer IPs. Budget is NB_DNS_LAZY_WARMUP_TIMEOUT (default 2s); on timeout the answer is returned anyway (never SERVFAIL).
  • dns.DefaultServer.SetPeerActivator forwards to the local resolver.
  • client/internal: a small engine-side adapter (dnsPeerActivator) resolves answer IPs to peers via Status.PeerStateByIP, activates them through ConnMgr.ActivatePeer (HA fan-out included), and polls PeerStateByIP until one is connected. ConnMgr is not thread-safe (guarded by syncMsgMux) and DNS queries run on their own goroutines, so activation is done under that mutex and the wait is not. It is injected in the engine after the connection manager is constructed (it does not exist at DNS-server construction time), via a *dns.DefaultServer type assertion to avoid widening the Server interface.

Scope is overlay-only for free: the trigger lives in the local resolver, which only answers for NetBird-managed names; public/upstream DNS is unaffected. Already-connected peers short-circuit, so steady-state DNS latency is unchanged.

N/A — follow-up to the lazy-connection rollout; fixes the agent-network cold-start observed in the e2e (proxy peer stuck disconnected until traffic).

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)
  • This change does not modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — OR I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See CONTRIBUTING.md.

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Internal client behavior; the only knob is the optional NB_DNS_LAZY_WARMUP_TIMEOUT tuning env var with a safe default.

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__

Tests

  • dns/local: warm-up invokes the activator with the answer's peer IP; no-activator path is unchanged; a query with no answer does not invoke the activator.

Summary by CodeRabbit

  • New Features
    • DNS-time lazy-connection warm-up now wakes idle peers based on resolved A/AAAA records, with server-level enable/disable support.
    • Warm-up timeout is configurable via an environment variable.
  • Bug Fixes
    • Address handling was made consistent for resolver filtering and activator invocation.
    • Improved safe concurrent activation during connection manager lifecycle transitions.
  • Tests
    • Added unit tests for warm-up behavior, timeout parsing, and address extraction, plus activator timing/edge-case coverage.
    • Updated e2e tests to ensure warm-up is triggered before proxy/peer connectivity assertions.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/netbirdio/netbird/pull/6767 **Author:** [@mlsmaycon](https://github.com/mlsmaycon) **Created:** 7/14/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `feature/dns-lazy-conn-warmup` --- ### 📝 Commits (10+) - [`588735f`](https://github.com/netbirdio/netbird/commit/588735f2bcfe33324ce2e9b8313a4c9b41721e97) [client] warm lazy connections from the DNS resolver - [`f4f3e29`](https://github.com/netbirdio/netbird/commit/f4f3e29f0837ccc874f959c3d78d817232e5ef60) Merge remote-tracking branch 'origin/main' into feature/dns-lazy-conn-warmup - [`b457cde`](https://github.com/netbirdio/netbird/commit/b457cdedb54c433c3666dc1c69653632aed35d99) [e2e] resolve endpoint before the proxy-peer gate to trigger DNS warm-up - [`0325269`](https://github.com/netbirdio/netbird/commit/03252696b95abb7df8707eac056242153482b3bc) [e2e] resolve endpoint before the proxy-peer gate in the allowlist test - [`a0c1548`](https://github.com/netbirdio/netbird/commit/a0c1548069357d7cbc8ff7e38c193fd754a6cee3) [e2e] register Bedrock provider with the normalized catalog model id - [`82d8f0a`](https://github.com/netbirdio/netbird/commit/82d8f0a4ee8899f6da4491c867773ee10c517e71) [e2e] source Bedrock model from AWS_BEDROCK_MODEL and normalize catalog id - [`38b377f`](https://github.com/netbirdio/netbird/commit/38b377f21e28f3bf38880fc86b82ae02aa826dff) [client] tie DNS warm-up dial to engine context; fix codespell - [`7825036`](https://github.com/netbirdio/netbird/commit/7825036fae2b624cecff3489a596aec274184cf2) Merge branch 'main' into feature/dns-lazy-conn-warmup - [`a4d0f24`](https://github.com/netbirdio/netbird/commit/a4d0f242b490adfa93e9f52153b0cbe245d81564) [client] Address review comments on DNS lazy-connection warm-up - [`e4f66aa`](https://github.com/netbirdio/netbird/commit/e4f66aac720aa1ac78daf87b5bf663fa469737ae) [ci] Temporarily run agent-network e2e on push to this branch ### 📊 Changes **14 files changed** (+656 additions, -40 deletions) <details> <summary>View changed files</summary> 📝 `client/internal/conn_mgr.go` (+25 -3) 📝 `client/internal/conn_mgr_test.go` (+66 -0) 📝 `client/internal/dns/local/local.go` (+107 -20) 📝 `client/internal/dns/local/local_test.go` (+2 -2) ➕ `client/internal/dns/local/warmup_test.go` (+172 -0) 📝 `client/internal/dns/mock_server.go` (+6 -0) 📝 `client/internal/dns/server.go` (+10 -2) ➕ `client/internal/dns_peer_activator.go` (+76 -0) ➕ `client/internal/dns_peer_activator_test.go` (+129 -0) 📝 `client/internal/engine.go` (+10 -0) 📝 `e2e/agentnetwork/chat_test.go` (+21 -4) 📝 `e2e/agentnetwork/guardrail_test.go` (+24 -5) 📝 `e2e/agentnetwork/skiptls_test.go` (+4 -2) 📝 `e2e/agentnetwork/vllm_test.go` (+4 -2) </details> ### 📄 Description ## Describe your changes With lazy connections enabled, a peer is not dialed until on-demand traffic arrives, so the first request to a peer resolved by name (e.g. the agent-network reverse-proxy) races — or loses to — the WireGuard handshake. Activation was previously reactive only: a data-path packet or an inbound signal. This adds a proactive, DNS-time trigger. When the local resolver answers for an overlay name, it now warms the lazy connection to the peer(s) the answer points at and waits briefly for one to connect before returning the response — so by the time the client sends its first packet the tunnel is already up. - `dns/local`: new `PeerActivator` capability + `SetPeerActivator` setter (mirrors the existing `PeerConnectivity`/`SetPeerConnectivity` injection). `ServeDNS` warms on the pre-filter answer, so activating a lazily-idle peer also lets it survive the disconnected-peer filter. No-op when no activator is wired (lazy off) or the answer carries no peer IPs. Budget is `NB_DNS_LAZY_WARMUP_TIMEOUT` (default 2s); on timeout the answer is returned anyway (never SERVFAIL). - `dns.DefaultServer.SetPeerActivator` forwards to the local resolver. - `client/internal`: a small engine-side adapter (`dnsPeerActivator`) resolves answer IPs to peers via `Status.PeerStateByIP`, activates them through `ConnMgr.ActivatePeer` (HA fan-out included), and polls `PeerStateByIP` until one is connected. `ConnMgr` is not thread-safe (guarded by `syncMsgMux`) and DNS queries run on their own goroutines, so activation is done under that mutex and the wait is not. It is injected in the engine after the connection manager is constructed (it does not exist at DNS-server construction time), via a `*dns.DefaultServer` type assertion to avoid widening the `Server` interface. Scope is overlay-only for free: the trigger lives in the local resolver, which only answers for NetBird-managed names; public/upstream DNS is unaffected. Already-connected peers short-circuit, so steady-state DNS latency is unchanged. ## Issue ticket number and link N/A — follow-up to the lazy-connection rollout; fixes the agent-network cold-start observed in the e2e (proxy peer stuck disconnected until traffic). ## Stack <!-- branch-stack --> ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [x] Created tests that fail without the change (if possible) - [x] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) Internal client behavior; the only knob is the optional `NB_DNS_LAZY_WARMUP_TIMEOUT` tuning env var with a safe default. ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ ## Tests - `dns/local`: warm-up invokes the activator with the answer's peer IP; no-activator path is unchanged; a query with no answer does not invoke the activator. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * DNS-time lazy-connection warm-up now wakes idle peers based on resolved A/AAAA records, with server-level enable/disable support. * Warm-up timeout is configurable via an environment variable. * **Bug Fixes** * Address handling was made consistent for resolver filtering and activator invocation. * Improved safe concurrent activation during connection manager lifecycle transitions. * **Tests** * Added unit tests for warm-up behavior, timeout parsing, and address extraction, plus activator timing/edge-case coverage. * Updated e2e tests to ensure warm-up is triggered before proxy/peer connectivity assertions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
saavagebueno added the pull-request label 2026-08-05 08:09:15 -04:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#29875