Skip to content

reverseproxy: extract DNS SRV+A discovery into a reusable package (RFC)#7790

Open
tannevaled wants to merge 2 commits into
caddyserver:masterfrom
tannevaled:feat/dedup-dynamic-upstreams
Open

reverseproxy: extract DNS SRV+A discovery into a reusable package (RFC)#7790
tannevaled wants to merge 2 commits into
caddyserver:masterfrom
tannevaled:feat/dedup-dynamic-upstreams

Conversation

@tannevaled

@tannevaled tannevaled commented Jun 2, 2026

Copy link
Copy Markdown

RFC — please treat as a proposal for discussion

Materializes the de-duplication idea from caddyserver/caddy-l4#429, where a few caddy-l4 PRs copied the dynamic-upstreams DNS logic from this repo. Rather than keep a second copy in caddy-l4, this extracts the reusable part into core so both can share it.

What

  • New transport-neutral package dynamicupstreams with the SRV and A/AAAA resolution + caching/refresh/grace logic, returning neutral Target{Host, Port, Priority, Weight} values instead of *reverseproxy.Upstream.
  • reverseproxy.SRVUpstreams and AUpstreams now delegate to it and build their *Upstreams from the returned targets; SRVUpstreams.ResetCache delegates too. Behavior is unchanged (same caching, grace period, filtered-records handling, dial_network prefix, IP-version handling, debug logs).

reverseproxy/upstreams.go shrinks substantially; the logic now lives once, in a package third-party proxies can import.

Why

So proxies outside this repo (e.g. layer4 TCP/UDP proxies) can reuse the same DNS discovery + caching instead of copying it.

Notes

  • Package name/location (dynamicupstreams) is a first guess — happy to move/rename to fit core's conventions.
  • Injectable lookup funcs keep the package unit-testable without DNS.
  • Once this lands, caddy-l4 will consume the package and drop its copy (branch prepared).

Tests

dynamicupstreams has its own tests (SRV + A resolve/cache, error without grace, grace serves stale, reset). Existing reverseproxy tests pass; gofmt / go vet / golangci-lint clean; no go.mod change.

Does this direction work for you?

@CLAassistant

CLAassistant commented Jun 2, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@tannevaled tannevaled force-pushed the feat/dedup-dynamic-upstreams branch from 9eb4ac3 to b93be96 Compare June 2, 2026 13:52
Move the SRV and A/AAAA resolution + caching logic out of reverseproxy
into a new, transport-neutral package (dynamicupstreams) that returns
neutral targets instead of *reverseproxy.Upstream. reverseproxy's
SRVUpstreams and AUpstreams now delegate to it and build their own
upstreams from the targets; behavior is unchanged.

This lets other proxies (e.g. third-party layer4 proxies) reuse the same
DNS discovery + caching instead of copying it, reducing duplication and
maintenance burden. reverseproxy/upstreams.go shrinks substantially.

RFC for the de-duplication discussed in caddyserver/caddy-l4#429.
@tannevaled tannevaled changed the title reverseproxy: extract DNS SRV discovery into a reusable package (RFC) reverseproxy: extract DNS SRV+A discovery into a reusable package (RFC) Jun 2, 2026
@tannevaled tannevaled force-pushed the feat/dedup-dynamic-upstreams branch from b93be96 to 5d83b60 Compare June 2, 2026 13:53
@tannevaled tannevaled marked this pull request as ready for review June 2, 2026 15:54
…und, concurrent refresh

Brings the package to 96.8% statement coverage. Adds:
- TestResetAllSRV: full-cache drop
- TestSRVFilteredRecords: LookupSRV partial-error semantics (usable
  records returned, error downgraded to a warning)
- TestSRVCacheBound / TestACacheBound: eviction keeps the cache bounded
  at maxCacheEntries
- TestSRVConcurrentRefreshDeduplicates / TestAConcurrentRefreshDeduplicates:
  two goroutines missing the read-lock fast path trigger only one lookup
  (verified under -race)

The grace-period and filtered-records logs now use a level-enabled logger
so the log-write branches execute. The only uncovered statements are the
two double-checked-locking re-check returns, which are reachable solely in
a narrow race window (the same untested idiom as reverseproxy's SRV/A).
@francislavoie

francislavoie commented Jun 14, 2026

Copy link
Copy Markdown
Member

I don't think it should be a top-level package/directory, it has nothing to do with Caddy as a runtime specifically. We typically put things in modules/ but it's not really a module in of itself, instead moreso being some shared library code.

I would say this would end up becoming a package in internal long-term, with the assumption that layer4 ends up being moved into Caddy's standard distribution, which would make it allowed to use internal.

So I'm thinking we can probably just put it in modules/internal/network for now, it's a useful refactor, but layer4 won't be able to use this until later, so it'll need to use a copy for the time being.

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.

3 participants