* perf(proxy): use mutex-based connection pool
The proxy package (used for example by the forward plugin) utilized
an actor model where a single connManager goroutine managed
connection pooling via unbuffered channels (dial, yield, ret). This
design serialized all connection acquisition and release operations
through a single goroutine, creating a bottleneck under high
concurrency. This was observable as a performance degradation when
using a single upstream backend compared to multiple backends
(which sharded the bottleneck).
Changes:
- Removed dial, yield, and ret channels from the Transport struct.
- Removed the connManager goroutine's request processing loop.
- Implemented Dial() and Yield() using a sync.Mutex to protect the
connection slice, allowing for fast concurrent access without
context switching.
- Downgraded connManager to a simple background cleanup loop that
only handles connection expiration on a ticker.
- Updated plugin/pkg/proxy/connect.go to use direct method calls
instead of channel sends.
- Updated tests to reflect the removal of internal channels.
Benchmarks show that this change eliminates the single-backend
bottleneck. Now a single upstream backend performs on par with
multiple backends, and overall throughput is improved.
The implementation aligns with standard Go patterns for connection
pooling (e.g., net/http.Transport).
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* fix: address PR review for persistent.go
- Named mutex field instead of embedding, to not expose
Lock() and Unlock()
- Move stop check outside of lock in Yield()
- Close() without a separate goroutine
- Change stop channel to struct
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* fix: address code review feedback for conn pool
- Switch from LIFO to FIFO connection selection for source port
diversity, reducing DNS cache poisoning risk (RFC 5452).
- Remove "clear entire cache" optimization as it was LIFO-specific.
FIFO naturally iterates and skips expired connections.
- Remove all goroutines for closing connections; collect connections
while holding lock, close synchronously after releasing lock.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* fix: remove unused error consts
No longer utilised after refactoring the channel based approach.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* feat(forward): add max_idle_conns option
Add configurable connection pool limit for the forward plugin via
the max_idle_conns Corefile option.
Changes:
- Add SetMaxIdleConns to proxy
- Add maxIdleConns field to Forward struct
- Add max_idle_conns parsing in forward plugin setup
- Apply setting to each proxy during configuration
- Update forward plugin README with new option
By default the value is 0 (unbounded). When set, excess
connections returned to the pool are closed immediately
rather than cached.
Also add a yield related test.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* chore(proxy): simple Dial by closing conns inline
Remove toClose slice collection to reduce complexity. Instead close
expired connections directly while iterating. Reduces complexity with
negligible lock-time impact.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
* chore: fewer explicit Unlock calls
Cleaner and less chance of forgetting to unlock on new possible
code paths.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
---------
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
Previously the parsing logic in the forward plugin setup failed to
recognise when NOERROR was used as a failover RCODE criteria. The
check was in the wrong code branch. This PR fixes it and adds
validation tests. Also updates the plugin README.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
In CI, the first two upstream attempts can stall on UDP and each
consume the default 2s read timeout. Possibly exhausting most of
the 5s forward deadline before the healthy third upstream is tried.
Lower the read timeout to make retries faster.
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
Enable nilness linter in govet.
Plugin-by-plugin rationale:
- plugin/transfer: reuse error instead of shadowing it inside the for
loop by declaring "ret" outside of the loop
- plugin/view: remove redundant err check
- plugin/dnstap: avoid possible nil dereference in error reporting
path in setup test
- plugin/forward: prevent nil deference or empty-slice dereference on
error paths in setup test
Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
Allows the forward plugin to execute the next plugin based on the return code. Similar to the externally mainted alternate plugin https://github.com/coredns/alternate
Based on the idea of chrisohaver@ in #6549 (comment)
Also incoperated the request to rename `alternate` to `next` as an option
I am having issues adding a proper test for functionality. Primarily, I do not know the code base enough and having multiple `dnstest.NewServer` with ResponseWriter does not work. From my testing these are "Singletons'' and only the last defined response writer is used for all servers
Signed-off-by: Jasper Bernhardt <jasper.bernhardt@live.de>
* plugin/forward: Move Proxy into pkg/plugin/proxy, to allow forward.Proxy to be used outside of forward plugin.
Signed-off-by: Patrick Downey <patrick.downey@dioadconsulting.com>
* plugin/forward: convert the specified domain of health_check to Fqdn
* plugin/forward: update readme for health check
Signed-off-by: vanceli <vanceli@tencent.com>
* trap unsupported FROM cidr notations
Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
* make is a warning
Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
* For caddy v1 in our org
This RP changes all imports for caddyserver/caddy to coredns/caddy. This
is the v1 code of caddy.
For the coredns/caddy repo the following changes have been made:
* anything not needed by us is deleted
* all `telemetry` stuff is deleted
* all its import paths are also changed to point to coredns/caddy
* the v1 branch has been moved to the master branch
* a v1.1.0 tag has been added to signal the latest release
Signed-off-by: Miek Gieben <miek@miek.nl>
* Fix imports
Signed-off-by: Miek Gieben <miek@miek.nl>
* Group coredns/caddy with out plugins
Signed-off-by: Miek Gieben <miek@miek.nl>
* remove this file
Signed-off-by: Miek Gieben <miek@miek.nl>
* Relax import ordering
github.com/coredns is now also a coredns dep, this makes
github.com/coredns/caddy fit more natural in the list.
Signed-off-by: Miek Gieben <miek@miek.nl>
* Fix final import
Signed-off-by: Miek Gieben <miek@miek.nl>
* parse.HostPortorFile: return error when 0 found
Return an error when we haven't found any nameservers. This is the
alternative considered in #3735. It's also slighly less code to be
changing.
Replaces: #3741Closes: #3741#3735
Signed-off-by: Miek Gieben <miek@miek.nl>
* Add extra test case here as well
Signed-off-by: Miek Gieben <miek@miek.nl>
* Make the RD-flag in health-checks in the Forward-plugin configurable
Introduces a new configuration flag; `health_check_non_recursive`. This
flag makes the health-checker do non-recursive requests when checking
the health of upstream servers.
Signed-off-by: Geir Haugom <ghagit@haugom.org>
Signed-off-by: Christian Tryti <ctryti@gmail.com>
* Changes after feedback from reviewer
* Better tests of health-checks with and without recursion
* Removed the health_check_non_recursive configuration in favor of
extending the existing health_check configuration. Now supports an
optional `no_rec` argument.
Signed-off-by: Christian Tryti <ctryti@gmail.com>
* Add new test that checks setup of health_check.
Signed-off-by: Christian Tryti <ctryti@gmail.com>
* Update Caddy to 1.0.1, and update import path
This fix updates caddy to 1.0.1 and also
updates the import path to github.com/caddyserver/caddy
This fix fixes 2959
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
* Also update plugin.cfg
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
* Update and bump zplugin.go
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
* plugin/forward: add HealthChecker interface
Make the HealthChecker interface and morph the current DNS health
checker into that interface.
Remove all whole bunch of method on Forward that didn't make sense.
This is done in preparation of adding a DoH client to forward - which
requires a completely different healthcheck implementation (and more,
but lets start here)
Signed-off-by: Miek Gieben <miek@miek.nl>
* Use protocol
Signed-off-by: Miek Gieben <miek@miek.nl>
* Dial doesnt need to be method an Forward either
Signed-off-by: Miek Gieben <miek@miek.nl>
* Address comments
Address various comments on the PR.
Signed-off-by: Miek Gieben <miek@miek.nl>
* plugins: Return error for multiple use of some
Return plugin.ErrOnce when a plugin that doesn't support it, is called
mutliple times.
This now adds it for: cache, dnssec, errors, forward, hosts, nsid.
And changes it slightly in kubernetes, pprof, reload, root.
* more tests
* plugin/forward: add it
This moves coredns/forward into CoreDNS. Fixes as a few bugs, adds a
policy option and more tests to the plugin.
Update the documentation, test IPv6 address and add persistent tests.
* Always use random policy when spraying
* include scrub fix here as well
* use correct var name
* Code review
* go vet
* Move logging to metrcs
* Small readme updates
* Fix readme