mirror of
https://github.com/AdguardTeam/dnsproxy.git
synced 2026-10-12 02:01:26 +00:00
Pull request #411: AGDNS-3379-imp-optimistic-cache
Merge in GO/dnsproxy from AGDNS-3379-imp-optimistic-cache to master Squashed commit of the following: commit 94c31763b5ced9b5c2624a9b1f642747266f7387 Author: f.setrakov <[email protected]> Date: Wed Nov 19 11:55:17 2025 +0300 cmd: use default const commit 82f50969701de0ab74e122e03bdde3e0685ca8e7 Author: f.setrakov <[email protected]> Date: Tue Nov 18 19:45:05 2025 +0300 all: imp docs commit 0e1cb4935d9ab716a3e654e89e3758b6b07ccf27 Author: f.setrakov <[email protected]> Date: Tue Nov 18 17:42:11 2025 +0300 all: imp style commit d9b29b6fe4a6cda55599eb6174914e09f54439ed Author: f.setrakov <[email protected]> Date: Tue Nov 18 15:59:02 2025 +0300 all: imp style, imp tests commit 8b03cab16175f49bc03b3314fc3cff504996beca Author: f.setrakov <[email protected]> Date: Mon Nov 17 18:19:18 2025 +0300 all: imp docs, style commit bdd2a7e8e90539caf47747e73f7626e5b4420e2f Author: f.setrakov <[email protected]> Date: Mon Nov 17 17:57:04 2025 +0300 all: imp optimistic cache
This commit is contained in:
@@ -99,6 +99,10 @@ Usage of ./dnsproxy:
|
||||
Listening addresses.
|
||||
--max-go-routines=uint
|
||||
Set the maximum number of go routines. A zero value will not not set a maximum.
|
||||
--optimistic-answer-ttl
|
||||
Default TTL value for expired DNS entries in optimistic cache. Default: 30s
|
||||
--optimistic-max-age
|
||||
Period of time after which entries are removed from optimistic cache in human-readable form. Default: 12h.
|
||||
--output=path/-o path
|
||||
Path to the log file.
|
||||
--pending-requests-enabled
|
||||
|
||||
+65
-49
@@ -42,6 +42,8 @@ const (
|
||||
timeoutIdx
|
||||
cacheMinTTLIdx
|
||||
cacheMaxTTLIdx
|
||||
cacheOptimisticAnswerTTLIdx
|
||||
cacheOptimisticMaxAgeIdx
|
||||
cacheSizeBytesIdx
|
||||
ratelimitIdx
|
||||
ratelimitSubnetLenIPv4Idx
|
||||
@@ -247,6 +249,18 @@ var commandLineOptions = []*commandLineOption{
|
||||
short: "",
|
||||
valueType: "uint32",
|
||||
},
|
||||
cacheOptimisticAnswerTTLIdx: {
|
||||
description: "Default TTL value for expired answers from optimistic cache",
|
||||
long: "optimistic-answer-ttl",
|
||||
short: "",
|
||||
valueType: "duration",
|
||||
},
|
||||
cacheOptimisticMaxAgeIdx: {
|
||||
description: "Period of time after which entries are removed from the optimistic cache",
|
||||
long: "optimistic-max-age",
|
||||
short: "",
|
||||
valueType: "duration",
|
||||
},
|
||||
cacheSizeBytesIdx: {
|
||||
description: "Cache size (in bytes). Default: 64k.",
|
||||
long: "cache-size",
|
||||
@@ -399,55 +413,57 @@ func parseCmdLineOptions(conf *configuration) (err error) {
|
||||
|
||||
flags := flag.NewFlagSet(cmdName, flag.ContinueOnError)
|
||||
for i, fieldPtr := range []any{
|
||||
configPathIdx: &conf.ConfigPath,
|
||||
logOutputIdx: &conf.LogOutput,
|
||||
tlsCertPathIdx: &conf.TLSCertPath,
|
||||
tlsKeyPathIdx: &conf.TLSKeyPath,
|
||||
httpsServerNameIdx: &conf.HTTPSServerName,
|
||||
httpsUserinfoIdx: &conf.HTTPSUserinfo,
|
||||
dnsCryptConfigPathIdx: &conf.DNSCryptConfigPath,
|
||||
ednsAddrIdx: &conf.EDNSAddr,
|
||||
upstreamModeIdx: &conf.UpstreamMode,
|
||||
listenAddrsIdx: &conf.ListenAddrs,
|
||||
listenPortsIdx: &conf.ListenPorts,
|
||||
httpsListenPortsIdx: &conf.HTTPSListenPorts,
|
||||
tlsListenPortsIdx: &conf.TLSListenPorts,
|
||||
quicListenPortsIdx: &conf.QUICListenPorts,
|
||||
dnsCryptListenPortsIdx: &conf.DNSCryptListenPorts,
|
||||
upstreamsIdx: &conf.Upstreams,
|
||||
bootstrapDNSIdx: &conf.BootstrapDNS,
|
||||
fallbacksIdx: &conf.Fallbacks,
|
||||
privateRDNSUpstreamsIdx: &conf.PrivateRDNSUpstreams,
|
||||
dns64PrefixIdx: &conf.DNS64Prefix,
|
||||
privateSubnetsIdx: &conf.PrivateSubnets,
|
||||
bogusNXDomainIdx: &conf.BogusNXDomain,
|
||||
hostsFilesIdx: &conf.HostsFiles,
|
||||
timeoutIdx: &conf.Timeout,
|
||||
cacheMinTTLIdx: &conf.CacheMinTTL,
|
||||
cacheMaxTTLIdx: &conf.CacheMaxTTL,
|
||||
cacheSizeBytesIdx: &conf.CacheSizeBytes,
|
||||
ratelimitIdx: &conf.Ratelimit,
|
||||
ratelimitSubnetLenIPv4Idx: &conf.RatelimitSubnetLenIPv4,
|
||||
ratelimitSubnetLenIPv6Idx: &conf.RatelimitSubnetLenIPv6,
|
||||
udpBufferSizeIdx: &conf.UDPBufferSize,
|
||||
maxGoRoutinesIdx: &conf.MaxGoRoutines,
|
||||
tlsMinVersionIdx: &conf.TLSMinVersion,
|
||||
tlsMaxVersionIdx: &conf.TLSMaxVersion,
|
||||
helpIdx: &conf.help,
|
||||
hostsFileEnabledIdx: &conf.HostsFileEnabled,
|
||||
pprofIdx: &conf.Pprof,
|
||||
versionIdx: &conf.Version,
|
||||
verboseIdx: &conf.Verbose,
|
||||
insecureIdx: &conf.Insecure,
|
||||
ipv6DisabledIdx: &conf.IPv6Disabled,
|
||||
http3Idx: &conf.HTTP3,
|
||||
cacheOptimisticIdx: &conf.CacheOptimistic,
|
||||
cacheIdx: &conf.Cache,
|
||||
refuseAnyIdx: &conf.RefuseAny,
|
||||
enableEDNSSubnetIdx: &conf.EnableEDNSSubnet,
|
||||
pendingRequestsEnabledIdx: &conf.PendingRequestsEnabled,
|
||||
dns64Idx: &conf.DNS64,
|
||||
usePrivateRDNSIdx: &conf.UsePrivateRDNS,
|
||||
configPathIdx: &conf.ConfigPath,
|
||||
logOutputIdx: &conf.LogOutput,
|
||||
tlsCertPathIdx: &conf.TLSCertPath,
|
||||
tlsKeyPathIdx: &conf.TLSKeyPath,
|
||||
httpsServerNameIdx: &conf.HTTPSServerName,
|
||||
httpsUserinfoIdx: &conf.HTTPSUserinfo,
|
||||
dnsCryptConfigPathIdx: &conf.DNSCryptConfigPath,
|
||||
ednsAddrIdx: &conf.EDNSAddr,
|
||||
upstreamModeIdx: &conf.UpstreamMode,
|
||||
listenAddrsIdx: &conf.ListenAddrs,
|
||||
listenPortsIdx: &conf.ListenPorts,
|
||||
httpsListenPortsIdx: &conf.HTTPSListenPorts,
|
||||
tlsListenPortsIdx: &conf.TLSListenPorts,
|
||||
quicListenPortsIdx: &conf.QUICListenPorts,
|
||||
dnsCryptListenPortsIdx: &conf.DNSCryptListenPorts,
|
||||
upstreamsIdx: &conf.Upstreams,
|
||||
bootstrapDNSIdx: &conf.BootstrapDNS,
|
||||
fallbacksIdx: &conf.Fallbacks,
|
||||
privateRDNSUpstreamsIdx: &conf.PrivateRDNSUpstreams,
|
||||
dns64PrefixIdx: &conf.DNS64Prefix,
|
||||
privateSubnetsIdx: &conf.PrivateSubnets,
|
||||
bogusNXDomainIdx: &conf.BogusNXDomain,
|
||||
hostsFilesIdx: &conf.HostsFiles,
|
||||
timeoutIdx: &conf.Timeout,
|
||||
cacheMinTTLIdx: &conf.CacheMinTTL,
|
||||
cacheMaxTTLIdx: &conf.CacheMaxTTL,
|
||||
cacheOptimisticAnswerTTLIdx: &conf.OptimisticAnswerTTL,
|
||||
cacheOptimisticMaxAgeIdx: &conf.OptimisticMaxAge,
|
||||
cacheSizeBytesIdx: &conf.CacheSizeBytes,
|
||||
ratelimitIdx: &conf.Ratelimit,
|
||||
ratelimitSubnetLenIPv4Idx: &conf.RatelimitSubnetLenIPv4,
|
||||
ratelimitSubnetLenIPv6Idx: &conf.RatelimitSubnetLenIPv6,
|
||||
udpBufferSizeIdx: &conf.UDPBufferSize,
|
||||
maxGoRoutinesIdx: &conf.MaxGoRoutines,
|
||||
tlsMinVersionIdx: &conf.TLSMinVersion,
|
||||
tlsMaxVersionIdx: &conf.TLSMaxVersion,
|
||||
helpIdx: &conf.help,
|
||||
hostsFileEnabledIdx: &conf.HostsFileEnabled,
|
||||
pprofIdx: &conf.Pprof,
|
||||
versionIdx: &conf.Version,
|
||||
verboseIdx: &conf.Verbose,
|
||||
insecureIdx: &conf.Insecure,
|
||||
ipv6DisabledIdx: &conf.IPv6Disabled,
|
||||
http3Idx: &conf.HTTP3,
|
||||
cacheOptimisticIdx: &conf.CacheOptimistic,
|
||||
cacheIdx: &conf.Cache,
|
||||
refuseAnyIdx: &conf.RefuseAny,
|
||||
enableEDNSSubnetIdx: &conf.EnableEDNSSubnet,
|
||||
pendingRequestsEnabledIdx: &conf.PendingRequestsEnabled,
|
||||
dns64Idx: &conf.DNS64,
|
||||
usePrivateRDNSIdx: &conf.UsePrivateRDNS,
|
||||
} {
|
||||
addOption(flags, fieldPtr, commandLineOptions[i])
|
||||
}
|
||||
|
||||
@@ -107,6 +107,14 @@ type configuration struct {
|
||||
// greater.
|
||||
CacheMaxTTL uint32 `yaml:"cache-max-ttl"`
|
||||
|
||||
// OptimisticAnswerTTL is the default TTL for expired cached responses
|
||||
// in seconds.
|
||||
OptimisticAnswerTTL timeutil.Duration `yaml:"optimistic-answer-ttl"`
|
||||
|
||||
// OptimisticMaxAge is the maximum time entries remain in the cache
|
||||
// when cache is optimistic.
|
||||
OptimisticMaxAge timeutil.Duration `yaml:"optimistic-max-age"`
|
||||
|
||||
// CacheSizeBytes is the cache size in bytes. Default is 64k.
|
||||
CacheSizeBytes int `yaml:"cache-size"`
|
||||
|
||||
@@ -202,6 +210,8 @@ func parseConfig() (conf *configuration, exitCode int, err error) {
|
||||
UpstreamMode: string(proxy.UpstreamModeLoadBalance),
|
||||
CacheSizeBytes: 64 * 1024,
|
||||
Timeout: timeutil.Duration(10 * time.Second),
|
||||
OptimisticAnswerTTL: timeutil.Duration(proxy.DefaultOptimisticAnswerTTL),
|
||||
OptimisticMaxAge: timeutil.Duration(proxy.DefaultOptimisticMaxAge),
|
||||
RatelimitSubnetLenIPv4: 24,
|
||||
RatelimitSubnetLenIPv6: 56,
|
||||
HostsFileEnabled: true,
|
||||
|
||||
+10
-8
@@ -58,14 +58,16 @@ func createProxyConfig(
|
||||
RatelimitSubnetLenIPv4: conf.RatelimitSubnetLenIPv4,
|
||||
RatelimitSubnetLenIPv6: conf.RatelimitSubnetLenIPv6,
|
||||
|
||||
Ratelimit: conf.Ratelimit,
|
||||
CacheEnabled: conf.Cache,
|
||||
CacheSizeBytes: conf.CacheSizeBytes,
|
||||
CacheMinTTL: conf.CacheMinTTL,
|
||||
CacheMaxTTL: conf.CacheMaxTTL,
|
||||
CacheOptimistic: conf.CacheOptimistic,
|
||||
RefuseAny: conf.RefuseAny,
|
||||
HTTP3: conf.HTTP3,
|
||||
Ratelimit: conf.Ratelimit,
|
||||
CacheEnabled: conf.Cache,
|
||||
CacheSizeBytes: conf.CacheSizeBytes,
|
||||
CacheMinTTL: conf.CacheMinTTL,
|
||||
CacheMaxTTL: conf.CacheMaxTTL,
|
||||
CacheOptimisticAnswerTTL: time.Duration(conf.OptimisticAnswerTTL),
|
||||
CacheOptimisticMaxAge: time.Duration(conf.OptimisticMaxAge),
|
||||
CacheOptimistic: conf.CacheOptimistic,
|
||||
RefuseAny: conf.RefuseAny,
|
||||
HTTP3: conf.HTTP3,
|
||||
// TODO(e.burkov): The following CIDRs are aimed to match any address.
|
||||
// This is not quite proper approach to be used by default so think
|
||||
// about configuring it.
|
||||
|
||||
+53
-25
@@ -39,19 +39,20 @@ type cache struct {
|
||||
// optimistic defines if the cache should return expired items and resolve
|
||||
// those again.
|
||||
optimistic bool
|
||||
|
||||
// optimisticTTL is the default TTL for expired cached responses.
|
||||
optimisticTTL time.Duration
|
||||
|
||||
// optimisticMaxAge is the maximum time entries remain in the cache when
|
||||
// cache is optimistic.
|
||||
optimisticMaxAge time.Duration
|
||||
}
|
||||
|
||||
// cacheItem is a single cache entry. It's a helper type to aggregate the
|
||||
// item-specific logic.
|
||||
type cacheItem struct {
|
||||
// m contains the cached response.
|
||||
m *dns.Msg
|
||||
|
||||
// u contains an address of the upstream which resolved m.
|
||||
u string
|
||||
|
||||
// ttl is the time-to-live value for the item. Should be set before calling
|
||||
// [cacheItem.pack].
|
||||
m *dns.Msg
|
||||
u string
|
||||
ttl uint32
|
||||
}
|
||||
|
||||
@@ -108,29 +109,28 @@ func (ci *cacheItem) pack() (packed []byte) {
|
||||
return packed
|
||||
}
|
||||
|
||||
// optimisticTTL is the default TTL for expired cached responses in seconds.
|
||||
const optimisticTTL = 10
|
||||
|
||||
// unpackItem converts the data into cacheItem using req as a request message.
|
||||
// expired is true if the item exists but expired. The expired cached items are
|
||||
// only returned if c is optimistic. req must not be nil.
|
||||
// only returned if c is optimistic and optimistic max age is not exceeded. req
|
||||
// must not be nil.
|
||||
func (c *cache) unpackItem(data []byte, req *dns.Msg) (ci *cacheItem, expired bool) {
|
||||
if len(data) < minPackedLen {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(data)
|
||||
expire := int64(binary.BigEndian.Uint32(b.Next(expTimeSz)))
|
||||
now := time.Now().Unix()
|
||||
expire := time.Unix(int64(binary.BigEndian.Uint32(b.Next(expTimeSz))), 0)
|
||||
now := time.Now()
|
||||
var ttl uint32
|
||||
if expired = expire <= now; expired {
|
||||
if !c.optimistic {
|
||||
if expired = now.After(expire); expired {
|
||||
optimisticExpire := expire.Add(c.optimisticMaxAge)
|
||||
if !c.optimistic || now.After(optimisticExpire) {
|
||||
return nil, expired
|
||||
}
|
||||
|
||||
ttl = optimisticTTL
|
||||
ttl = uint32(c.optimisticTTL.Seconds())
|
||||
} else {
|
||||
ttl = uint32(expire - now)
|
||||
ttl = uint32(expire.Unix() - now.Unix())
|
||||
}
|
||||
|
||||
l := int(binary.BigEndian.Uint16(b.Next(packedMsgLenSz)))
|
||||
@@ -173,22 +173,50 @@ func (p *Proxy) initCache() {
|
||||
|
||||
size := p.CacheSizeBytes
|
||||
p.logger.Info("cache enabled", "size", size)
|
||||
|
||||
p.cache = newCache(size, p.EnableEDNSClientSubnet, p.CacheOptimistic)
|
||||
p.cache = newCache(&cacheConfig{
|
||||
size: size,
|
||||
optimisticTTL: p.CacheOptimisticAnswerTTL,
|
||||
optimisticMaxAge: p.CacheOptimisticMaxAge,
|
||||
withECS: p.EnableEDNSClientSubnet,
|
||||
optimistic: p.CacheOptimistic,
|
||||
})
|
||||
p.shortFlighter = newOptimisticResolver(p)
|
||||
}
|
||||
|
||||
// cacheConfig is the configuration structure for [cache].
|
||||
type cacheConfig struct {
|
||||
// size is the cache size in bytes.
|
||||
size int
|
||||
|
||||
// optimisticTTL is the default TTL for expired cached responses when
|
||||
// optimistic is enabled.
|
||||
optimisticTTL time.Duration
|
||||
|
||||
// optimisticMaxAge is the maximum time entries remain in the cache when
|
||||
// cache is optimistic.
|
||||
optimisticMaxAge time.Duration
|
||||
|
||||
// withECS enables EDNS Client Subnet support for cache.
|
||||
withECS bool
|
||||
|
||||
// optimistic defines if the cache should return expired items and resolve
|
||||
// those again.
|
||||
optimistic bool
|
||||
}
|
||||
|
||||
// newCache returns a properly initialized cache. logger must not be nil.
|
||||
func newCache(size int, withECS, optimistic bool) (c *cache) {
|
||||
func newCache(conf *cacheConfig) (c *cache) {
|
||||
c = &cache{
|
||||
itemsLock: &sync.RWMutex{},
|
||||
itemsWithSubnetLock: &sync.RWMutex{},
|
||||
items: createCache(size),
|
||||
optimistic: optimistic,
|
||||
items: createCache(conf.size),
|
||||
optimistic: conf.optimistic,
|
||||
optimisticTTL: conf.optimisticTTL,
|
||||
optimisticMaxAge: conf.optimisticMaxAge,
|
||||
}
|
||||
|
||||
if withECS {
|
||||
c.itemsWithSubnet = createCache(size)
|
||||
if conf.withECS {
|
||||
c.itemsWithSubnet = createCache(conf.size)
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
@@ -34,6 +35,22 @@ var upstreamWithAddr = &dnsproxytest.Upstream{
|
||||
OnAddress: func() (addr string) { return testUpsAddr },
|
||||
}
|
||||
|
||||
// newTestCache is a helper that returns new cache and fills its config with
|
||||
// given values. If conf is nil, the default configuration will be used.
|
||||
func newTestCache(tb testing.TB, conf *cacheConfig) (c *cache) {
|
||||
tb.Helper()
|
||||
|
||||
conf = cmp.Or(conf, &cacheConfig{})
|
||||
|
||||
return newCache(&cacheConfig{
|
||||
size: cmp.Or(conf.size, testCacheSize),
|
||||
optimisticTTL: cmp.Or(conf.optimisticTTL, testOptimisticTTL),
|
||||
optimisticMaxAge: cmp.Or(conf.optimisticMaxAge, testOptimisticMaxAge),
|
||||
withECS: conf.withECS,
|
||||
optimistic: conf.optimistic,
|
||||
})
|
||||
}
|
||||
|
||||
func TestServeCached(t *testing.T) {
|
||||
dnsProxy := mustNew(t, &Config{
|
||||
Logger: slogutil.NewDiscardLogger(),
|
||||
@@ -96,12 +113,14 @@ func TestCache_expired(t *testing.T) {
|
||||
}).SetQuestion(host, dns.TypeA)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
ttl uint32
|
||||
wantTTL uint32
|
||||
optimistic bool
|
||||
name string
|
||||
ttl uint32
|
||||
wantTTL uint32
|
||||
optimistic bool
|
||||
optimisticMaxAge time.Duration
|
||||
}{{
|
||||
name: "realistic_hit",
|
||||
name: "realistic_hit",
|
||||
|
||||
ttl: defaultTestTTL,
|
||||
wantTTL: defaultTestTTL,
|
||||
optimistic: false,
|
||||
@@ -111,18 +130,26 @@ func TestCache_expired(t *testing.T) {
|
||||
wantTTL: 0,
|
||||
optimistic: false,
|
||||
}, {
|
||||
name: "optimistic_hit",
|
||||
ttl: defaultTestTTL,
|
||||
wantTTL: defaultTestTTL,
|
||||
optimistic: true,
|
||||
name: "optimistic_hit",
|
||||
ttl: defaultTestTTL,
|
||||
wantTTL: defaultTestTTL,
|
||||
optimistic: true,
|
||||
optimisticMaxAge: testOptimisticMaxAge,
|
||||
}, {
|
||||
name: "optimistic_expired",
|
||||
ttl: 0,
|
||||
wantTTL: optimisticTTL,
|
||||
optimistic: true,
|
||||
name: "optimistic_expired",
|
||||
ttl: 0,
|
||||
wantTTL: uint32(testOptimisticTTL.Seconds()),
|
||||
optimistic: true,
|
||||
optimisticMaxAge: testOptimisticMaxAge,
|
||||
}, {
|
||||
name: "optimistic_max_age_exceeded",
|
||||
ttl: 0,
|
||||
wantTTL: 0,
|
||||
optimistic: true,
|
||||
optimisticMaxAge: time.Hour * -1,
|
||||
}}
|
||||
|
||||
testCache := newCache(testCacheSize, false, false)
|
||||
testCache := newTestCache(t, nil)
|
||||
for _, tc := range testCases {
|
||||
ans.Hdr.Ttl = tc.ttl
|
||||
req := (&dns.Msg{}).SetQuestion(host, dns.TypeA)
|
||||
@@ -130,6 +157,7 @@ func TestCache_expired(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.optimistic {
|
||||
testCache.optimistic = true
|
||||
testCache.optimisticMaxAge = tc.optimisticMaxAge
|
||||
t.Cleanup(func() { testCache.optimistic = false })
|
||||
}
|
||||
|
||||
@@ -159,7 +187,7 @@ func TestCache_expired(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCacheDO(t *testing.T) {
|
||||
testCache := newCache(testCacheSize, false, false)
|
||||
testCache := newTestCache(t, nil)
|
||||
|
||||
// Fill the cache.
|
||||
reply := (&dns.Msg{
|
||||
@@ -204,7 +232,7 @@ func TestCacheDO(t *testing.T) {
|
||||
func TestCacheCNAME(t *testing.T) {
|
||||
l := slogutil.NewDiscardLogger()
|
||||
|
||||
testCache := newCache(testCacheSize, false, false)
|
||||
testCache := newTestCache(t, nil)
|
||||
|
||||
// Fill the cache
|
||||
reply := (&dns.Msg{
|
||||
@@ -241,7 +269,7 @@ func TestCacheCNAME(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCache_uncacheable(t *testing.T) {
|
||||
testCache := newCache(testCacheSize, false, false)
|
||||
testCache := newTestCache(t, nil)
|
||||
|
||||
// Create a DNS request.
|
||||
request := (&dns.Msg{}).SetQuestion("google.com.", dns.TypeA)
|
||||
@@ -257,7 +285,7 @@ func TestCache_uncacheable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCache_concurrent(t *testing.T) {
|
||||
testCache := newCache(testCacheSize, false, false)
|
||||
testCache := newTestCache(t, nil)
|
||||
|
||||
hosts := map[string]string{
|
||||
dns.Fqdn("yandex.com"): "213.180.204.62",
|
||||
@@ -530,7 +558,7 @@ func TestCache(t *testing.T) {
|
||||
func (tests testCases) run(t *testing.T) {
|
||||
l := slogutil.NewDiscardLogger()
|
||||
|
||||
testCache := newCache(testCacheSize, false, false)
|
||||
testCache := newTestCache(t, nil)
|
||||
|
||||
for _, res := range tests.cache {
|
||||
reply := (&dns.Msg{
|
||||
@@ -637,7 +665,7 @@ func TestCache_getWithSubnet(t *testing.T) {
|
||||
mask24 := net.CIDRMask(24, netutil.IPv4BitLen)
|
||||
l := slogutil.NewDiscardLogger()
|
||||
|
||||
c := newCache(testCacheSize, true, false)
|
||||
c := newTestCache(t, &cacheConfig{withECS: true})
|
||||
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
ci, expired, _ := c.getWithSubnet(req, &net.IPNet{IP: ip1234, Mask: mask24})
|
||||
@@ -723,7 +751,10 @@ func TestCache_getWithSubnet_mask(t *testing.T) {
|
||||
|
||||
ansIP := net.IP{4, 4, 4, 4}
|
||||
|
||||
c := newCache(testCacheSize, true, true)
|
||||
c := newTestCache(t, &cacheConfig{
|
||||
withECS: true,
|
||||
optimistic: true,
|
||||
})
|
||||
|
||||
req := (&dns.Msg{}).SetQuestion(testFQDN, dns.TypeA)
|
||||
resp := (&dns.Msg{
|
||||
|
||||
@@ -18,6 +18,16 @@ import (
|
||||
// LogPrefix is a prefix for logging.
|
||||
const LogPrefix = "dnsproxy"
|
||||
|
||||
const (
|
||||
// DefaultOptimisticMaxAge is default value for
|
||||
// [Config.CacheOptimisticMaxAge].
|
||||
DefaultOptimisticMaxAge = 12 * time.Hour
|
||||
|
||||
// DefaultOptimisticAnswerTTL is default value for
|
||||
// [Config.CacheOptimisticAnswerTTL].
|
||||
DefaultOptimisticAnswerTTL = 30 * time.Second
|
||||
)
|
||||
|
||||
// RequestHandler is an optional custom handler for DNS requests. It's used
|
||||
// instead of [Proxy.Resolve] if set. The resulting error doesn't affect the
|
||||
// request processing. The custom handler is responsible for calling
|
||||
@@ -188,6 +198,14 @@ type Config struct {
|
||||
// CacheMaxTTL is the maximum TTL for cached DNS responses in seconds.
|
||||
CacheMaxTTL uint32
|
||||
|
||||
// CacheOptimisticAnswerTTL is the default TTL for expired cached responses.
|
||||
// Default value is [DefaultOptimisticAnswerTTL].
|
||||
CacheOptimisticAnswerTTL time.Duration
|
||||
|
||||
// CacheOptimisticMaxAge is the maximum time entries remain in the cache
|
||||
// when cache is optimistic. Default value is [DefaultOptimisticMaxAge].
|
||||
CacheOptimisticMaxAge time.Duration
|
||||
|
||||
// MaxGoroutines is the maximum number of goroutines processing DNS
|
||||
// requests. Important for mobile users.
|
||||
//
|
||||
|
||||
+5
-1
@@ -231,7 +231,11 @@ func NewCustomUpstreamConfig(
|
||||
var customCache *cache
|
||||
if cacheEnabled {
|
||||
// TODO(d.kolyshev): Support optimistic with newOptimisticResolver.
|
||||
customCache = newCache(cacheSize, enableEDNSClientSubnet, false)
|
||||
customCache = newCache(&cacheConfig{
|
||||
size: cacheSize,
|
||||
withECS: enableEDNSClientSubnet,
|
||||
optimistic: false,
|
||||
})
|
||||
}
|
||||
|
||||
return &CustomUpstreamConfig{
|
||||
|
||||
@@ -38,6 +38,12 @@ const (
|
||||
|
||||
// testTimeout is the default timeout for tests.
|
||||
testTimeout = 500 * time.Millisecond
|
||||
|
||||
// testOptimisticTTL is a common optimistic cache ttl value for tests.
|
||||
testOptimisticTTL = 10 * time.Second
|
||||
|
||||
// testOptimisticMaxAge is a common optimistic max age value for tests.
|
||||
testOptimisticMaxAge = 12 * time.Hour
|
||||
)
|
||||
|
||||
// localhostAnyPort is a [netip.AddrPort] having a value of 127.0.0.1:0.
|
||||
@@ -1391,8 +1397,10 @@ func TestProxy_Resolve_withOptimisticResolver(t *testing.T) {
|
||||
|
||||
p := &Proxy{
|
||||
Config: Config{
|
||||
CacheEnabled: true,
|
||||
CacheOptimistic: true,
|
||||
CacheEnabled: true,
|
||||
CacheOptimistic: true,
|
||||
CacheOptimisticAnswerTTL: testOptimisticTTL,
|
||||
CacheOptimisticMaxAge: testOptimisticMaxAge,
|
||||
},
|
||||
logger: slogutil.NewDiscardLogger(),
|
||||
pendingRequests: newDefaultPendingRequests(),
|
||||
@@ -1440,7 +1448,7 @@ func TestProxy_Resolve_withOptimisticResolver(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, firstCtx.Res.Answer, 1)
|
||||
|
||||
assert.EqualValues(t, optimisticTTL, firstCtx.Res.Answer[0].Header().Ttl)
|
||||
assert.Equal(t, uint32(testOptimisticTTL.Seconds()), firstCtx.Res.Answer[0].Header().Ttl)
|
||||
|
||||
// Wait for optimisticResolver to reach the tested function.
|
||||
<-out
|
||||
@@ -1449,7 +1457,7 @@ func TestProxy_Resolve_withOptimisticResolver(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, secondCtx.Res.Answer, 1)
|
||||
|
||||
assert.EqualValues(t, optimisticTTL, secondCtx.Res.Answer[0].Header().Ttl)
|
||||
assert.Equal(t, uint32(testOptimisticTTL.Seconds()), secondCtx.Res.Answer[0].Header().Ttl)
|
||||
|
||||
// Continue and wait for it to finish.
|
||||
in <- unit{}
|
||||
|
||||
Reference in New Issue
Block a user