Skip to content

[proxy, client] Bound embed client WireGuard per-Device memory#5962

Open
lixmal wants to merge 1 commit intomainfrom
reduce-embed-wg-pool
Open

[proxy, client] Bound embed client WireGuard per-Device memory#5962
lixmal wants to merge 1 commit intomainfrom
reduce-embed-wg-pool

Conversation

@lixmal
Copy link
Copy Markdown
Collaborator

@lixmal lixmal commented Apr 22, 2026

Describe your changes

Bounds memory used by per-account WireGuard Devices in the reverse proxy by exposing the wireguard-go buffer-pool and batch-size knobs through the embed client and new debug surfaces.

  • Re-export WireGuard tuning from client/embed so the proxy does not reach into wireguard-go internals: SetWGDefaultPreallocatedBuffersPerPool, SetWGDefaultMaxBatchSize, and live-tune via Client.SetWGTuning(WGTuning{...}).
  • New proxy env vars, read at startup:
    • NB_WG_PREALLOCATED_BUFFERS — per-Device pool cap. 0 = unbounded (unchanged default).
    • NB_WG_MAX_BATCH_SIZE — per-Device batch size. Controls how many buffers each receive/TUN goroutine eagerly allocates for its lifetime. 0 = use bind/tun default.
  • Debug endpoint /debug/wgtune (GET shows defaults; GET with ?value= sets the pool cap globally and on all live clients) plus netbird-proxy debug wgtune get|set <n>. Batch size is startup-only so set only touches the pool cap.
  • Debug endpoint /debug/runtime (MemStats, goroutine count, /proc/self/status VmRSS/VmSize/VmData, client counts) plus netbird-proxy debug runtime. Cheap to call, no pprof.

Depends on netbirdio/wireguard-go#14 (bumps fork replace in go.mod).

Tuning matrix

Each bind receive goroutine (IPv4 + IPv6 + ICE relay ≈ 3) plus RoutineReadFromTUN eagerly reserves batch message buffers for its lifetime. Pool caps below this eager floor will block the pipeline at startup.

NB_WG_MAX_BATCH_SIZE eager floor recommended min NB_WG_PREALLOCATED_BUFFERS
1 ~4 16
8 ~32 64
32 ~128 256
128 (linux default) ~512 1024
0 (leave at bind/tun default) ~512 1024 or 0 (unbounded)

Leaving both at 0 keeps upstream behavior unchanged.

Issue ticket number and link

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)

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)

Operator-only tuning knobs for the internal reverse proxy; no user-facing surface change.

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/__

Summary by CodeRabbit

  • New Features
    • WireGuard tuning: runtime-configurable preallocated buffer pool and max batch-size defaults.
    • New debug CLI: wgtune get/set and runtime commands to inspect and adjust WireGuard and runtime metrics.
    • Debug HTTP endpoints: /debug/wgtune and /debug/runtime for remote inspection and applying tuning across clients.
    • Startup env vars to set default WireGuard settings and warn on dangerously low values.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef15b8db-bcab-443c-838a-07370a716c86

📥 Commits

Reviewing files that changed from the base of the PR and between 8b4ec36 and e81ce81.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • client/embed/embed.go
  • client/internal/engine.go
  • go.mod
  • proxy/cmd/proxy/cmd/debug.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/debug/client.go
  • proxy/internal/debug/handler.go
✅ Files skipped from review due to trivial changes (2)
  • proxy/cmd/proxy/cmd/debug.go
  • client/embed/embed.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • client/internal/engine.go

📝 Walkthrough

Walkthrough

Adds runtime WireGuard tuning: new WGTuning APIs in embed/engine, package-level WG defaults, CLI/debug endpoints to get/set tuning and view runtime stats, environment variable overrides on startup, and debug handlers to apply per-client tuning and report runtime/process metrics.

Changes

Cohort / File(s) Summary
Embed API & WG defaults
client/embed/embed.go
Added exported WGTuning type and (*Client).SetWGTuning, plus package-level setters/getters for WG default PreallocatedBuffersPerPool and MaxBatchSize.
Engine runtime API
client/internal/engine.go
Added WGTuning type and (*Engine).SetWGTuning that locks syncMsgMux, validates wg device presence, and applies PreallocatedBuffersPerPool when non-nil.
WireGuard dependency
go.mod
Updated replace mapping for golang.zx2c4.com/wireguard to newer github.com/netbirdio/wireguard-go pseudo-version.
CLI debug commands
proxy/cmd/proxy/cmd/debug.go
Added `debug wgtune get
Startup env config
proxy/cmd/proxy/cmd/root.go
Reads NB_WG_PREALLOCATED_BUFFERS and NB_WG_MAX_BATCH_SIZE, parses/apply defaults via embed setters, logs applied overrides, warns if pool cap below eager-allocation floor.
Debug client methods
proxy/internal/debug/client.go
Added WGTuneGet, WGTuneSet, and Runtime methods and printers to fetch/format tuning and runtime/process stats from debug endpoints.
Debug HTTP handlers & JSON types
proxy/internal/debug/handler.go
Extended clientProvider with ListClientsForStartup(); added /debug/wgtune and /debug/runtime; handleWGTune sets global default and applies per-client tuning with per-account failure reporting; handleRuntime reports runtime.MemStats and optional /proc/self/status fields. Changed JSON helper types from interface{} to any.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant DebugClient
  participant DebugHTTP as Debug HTTP Handler
  participant Provider as Proxy/Embed Manager
  participant Engine
  participant WGDevice as WireGuard Device

  CLI->>DebugClient: WGTuneSet(value)
  DebugClient->>DebugHTTP: GET /debug/wgtune?value=<v>
  DebugHTTP->>Provider: ListClientsForStartup()
  loop per client
    Provider->>Engine: Engine.SetWGTuning({PreallocatedBuffersPerPool:v})
    Engine->>WGDevice: SetPreallocatedBuffersPerPool(v)
    WGDevice-->>Engine: ack / error
    Engine-->>Provider: result
  end
  DebugHTTP-->>DebugClient: JSON {default,batch_size,applied,failed}
  DebugClient-->>CLI: formatted output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • pappz
  • bcmmbaga
  • mlsmaycon

Poem

🐰 I tweak the buffers, soft and keen,
Hop through handlers, tidy the machine.
CLI hums a tune, engines take the call,
WireGuard readies, packets won't stall.
A rabbit cheers: "Prealloc — joy to all!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: exposing WireGuard buffer and batch-size tuning in the embed client to bound per-Device memory in the proxy.
Description check ✅ Passed The description thoroughly covers the changes, includes rationale, tuning guidance, and addresses all template requirements with appropriate selections (feature enhancement, documentation not needed with explanation).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch reduce-embed-wg-pool

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lixmal lixmal changed the title [proxy, client] Bound embed WireGuard pools via NB_WG_PREALLOCATED_BUFFERS and NB_WG_MAX_BATCH_SIZE [proxy, client] Bound embed client WireGuard per-Device memory Apr 22, 2026
@lixmal lixmal force-pushed the reduce-embed-wg-pool branch from f8e7023 to d217e35 Compare April 22, 2026 10:39
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
proxy/cmd/proxy/cmd/root.go (1)

159-174: Consider warning when pool cap is below the eager-allocation floor.

The PR notes that each bind receive goroutine (~3/device) plus RoutineReadFromTUN eagerly reserve max_batch_size buffers, and a pool cap below this floor will block startup. Operators who set both env vars together (e.g. NB_WG_MAX_BATCH_SIZE=128 + NB_WG_PREALLOCATED_BUFFERS=256) will hang with no log clue. A startup warning when envWGPreallocatedBuffers > 0 && envWGPreallocatedBuffers < eagerFloor(envWGMaxBatchSize) would make the knob far less foot-gunny.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@proxy/cmd/proxy/cmd/root.go` around lines 159 - 174, When parsing
envWGPreallocatedBuffers and envWGMaxBatchSize, add a startup warning if
preallocatedBuffers > 0 and is less than the eager-allocation floor derived from
maxBatchSize so operators aren't blocked silently; compute the floor (the same
logic used for eager allocation / RoutineReadFromTUN) from the parsed max batch
value and compare it after calling embed.SetWGDefaultMaxBatchSize and
embed.SetWGDefaultPreallocatedBuffersPerPool, and use logger.Warnf to emit a
clear message referencing envWGPreallocatedBuffers and envWGMaxBatchSize when
preallocated < eagerFloor to indicate startup may block and recommend increasing
the pool cap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@proxy/internal/debug/client.go`:
- Around line 286-304: The printer printWGTuneSet currently ignores server-side
errors returned in the response body; update printWGTuneSet to check
data["error"] (e.g., as a string) first and, if present, write that error to
c.out (preferably as "Error: <message>") and return early so the silent "Default
set to: 0" / "Applied to 0" output is not shown; keep printWGTuneGet unchanged
but ensure printWGTuneSet handles both the error case and the existing success
fields (default, applied, failed).

In `@proxy/internal/debug/handler.go`:
- Around line 642-679: In handleWGTune, change the parse-failure branch to
return HTTP 400 and a JSON body that matches sibling handlers (include
"success": false and an "error" string) instead of writing a 200 with only
"error"; after parsing succeeds keep setting
nbembed.SetWGDefaultPreallocatedBuffersPerPool(uint32(n)) and applying to
clients via h.provider.ListClientsForStartup() as before, but ensure the success
response always includes "success": true, "default": uint32(n), "batch_size":
nbembed.WGDefaultMaxBatchSize(), and "applied": applied, and if any client
errors occur include "failed" map; use http.Error or your existing write helper
with w.WriteHeader(http.StatusBadRequest) to set the 400 status on parse error
so the CLI treats it as an error.

---

Nitpick comments:
In `@proxy/cmd/proxy/cmd/root.go`:
- Around line 159-174: When parsing envWGPreallocatedBuffers and
envWGMaxBatchSize, add a startup warning if preallocatedBuffers > 0 and is less
than the eager-allocation floor derived from maxBatchSize so operators aren't
blocked silently; compute the floor (the same logic used for eager allocation /
RoutineReadFromTUN) from the parsed max batch value and compare it after calling
embed.SetWGDefaultMaxBatchSize and embed.SetWGDefaultPreallocatedBuffersPerPool,
and use logger.Warnf to emit a clear message referencing
envWGPreallocatedBuffers and envWGMaxBatchSize when preallocated < eagerFloor to
indicate startup may block and recommend increasing the pool cap.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a8d22a9-98da-490d-9747-7737a0209673

📥 Commits

Reviewing files that changed from the base of the PR and between a822a33 and d217e35.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • client/embed/embed.go
  • client/internal/engine.go
  • go.mod
  • proxy/cmd/proxy/cmd/debug.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/debug/client.go
  • proxy/internal/debug/handler.go

Comment thread proxy/internal/debug/client.go
Comment thread proxy/internal/debug/handler.go
@lixmal lixmal force-pushed the reduce-embed-wg-pool branch from d217e35 to 8b4ec36 Compare April 22, 2026 10:51
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@proxy/internal/debug/handler.go`:
- Around line 642-655: The handler handleWGTune currently treats missing and
explicitly empty query parameters identically because it only uses
r.URL.Query().Get("value"); change the logic to first check presence with
r.URL.Query()["value"] (or lookup the map) to distinguish missing vs
present-empty: if the "value" key is absent keep the existing default-response
path, but if the key is present and the string is empty return
http.StatusBadRequest with an error about an empty value; otherwise parse the
non-empty value as before using strconv.ParseUint. Ensure you update only
handleWGTune to follow the same explicit-empty validation pattern as ping and
setloglevel.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8e59ad59-8860-4a2f-bdd3-5128d9971130

📥 Commits

Reviewing files that changed from the base of the PR and between d217e35 and 8b4ec36.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • client/embed/embed.go
  • client/internal/engine.go
  • go.mod
  • proxy/cmd/proxy/cmd/debug.go
  • proxy/cmd/proxy/cmd/root.go
  • proxy/internal/debug/client.go
  • proxy/internal/debug/handler.go
✅ Files skipped from review due to trivial changes (1)
  • go.mod
🚧 Files skipped from review as they are similar to previous changes (3)
  • proxy/cmd/proxy/cmd/root.go
  • client/internal/engine.go
  • client/embed/embed.go

Comment thread proxy/internal/debug/handler.go
@lixmal lixmal force-pushed the reduce-embed-wg-pool branch from 8b4ec36 to e81ce81 Compare April 22, 2026 11:04
@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant