Add optimistic DNS cache

This commit is contained in:
世界
2026-04-11 12:10:52 +08:00
parent 1735022e91
commit 9eae48e208
30 changed files with 1233 additions and 383 deletions
+76 -20
View File
@@ -12,9 +12,11 @@ import (
"github.com/sagernet/bbolt"
bboltErrors "github.com/sagernet/bbolt/errors"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental/deprecated"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/service/filemanager"
)
@@ -30,6 +32,7 @@ var (
string(bucketMode),
string(bucketRuleSet),
string(bucketRDRC),
string(bucketDNSCache),
}
cacheIDDefault = []byte("default")
@@ -38,30 +41,43 @@ var (
var _ adapter.CacheFile = (*CacheFile)(nil)
type CacheFile struct {
ctx context.Context
path string
cacheID []byte
storeFakeIP bool
storeRDRC bool
rdrcTimeout time.Duration
DB *bbolt.DB
resetAccess sync.Mutex
saveMetadataTimer *time.Timer
saveFakeIPAccess sync.RWMutex
saveDomain map[netip.Addr]string
saveAddress4 map[string]netip.Addr
saveAddress6 map[string]netip.Addr
saveRDRCAccess sync.RWMutex
saveRDRC map[saveRDRCCacheKey]bool
ctx context.Context
logger logger.Logger
path string
cacheID []byte
storeFakeIP bool
storeRDRC bool
storeDNS bool
disableExpire bool
rdrcTimeout time.Duration
optimisticTimeout time.Duration
DB *bbolt.DB
resetAccess sync.Mutex
saveMetadataTimer *time.Timer
saveFakeIPAccess sync.RWMutex
saveDomain map[netip.Addr]string
saveAddress4 map[string]netip.Addr
saveAddress6 map[string]netip.Addr
saveRDRCAccess sync.RWMutex
saveRDRC map[saveCacheKey]bool
saveDNSCacheAccess sync.RWMutex
saveDNSCache map[saveCacheKey]saveDNSCacheEntry
}
type saveRDRCCacheKey struct {
type saveCacheKey struct {
TransportName string
QuestionName string
QType uint16
}
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
type saveDNSCacheEntry struct {
rawMessage []byte
expireAt time.Time
sequence uint64
saving bool
}
func New(ctx context.Context, logger logger.Logger, options option.CacheFileOptions) *CacheFile {
var path string
if options.Path != "" {
path = options.Path
@@ -72,6 +88,9 @@ func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
if options.CacheID != "" {
cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...)
}
if options.StoreRDRC {
deprecated.Report(ctx, deprecated.OptionStoreRDRC)
}
var rdrcTimeout time.Duration
if options.StoreRDRC {
if options.RDRCTimeout > 0 {
@@ -82,15 +101,18 @@ func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
}
return &CacheFile{
ctx: ctx,
logger: logger,
path: filemanager.BasePath(ctx, path),
cacheID: cacheIDBytes,
storeFakeIP: options.StoreFakeIP,
storeRDRC: options.StoreRDRC,
storeDNS: options.StoreDNS,
rdrcTimeout: rdrcTimeout,
saveDomain: make(map[netip.Addr]string),
saveAddress4: make(map[string]netip.Addr),
saveAddress6: make(map[string]netip.Addr),
saveRDRC: make(map[saveRDRCCacheKey]bool),
saveRDRC: make(map[saveCacheKey]bool),
saveDNSCache: make(map[saveCacheKey]saveDNSCacheEntry),
}
}
@@ -102,10 +124,44 @@ func (c *CacheFile) Dependencies() []string {
return nil
}
func (c *CacheFile) SetOptimisticTimeout(timeout time.Duration) {
c.optimisticTimeout = timeout
}
func (c *CacheFile) SetDisableExpire(disableExpire bool) {
c.disableExpire = disableExpire
}
func (c *CacheFile) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateInitialize {
return nil
switch stage {
case adapter.StartStateInitialize:
return c.start()
case adapter.StartStateStart:
c.startCacheCleanup()
}
return nil
}
func (c *CacheFile) startCacheCleanup() {
if c.storeDNS {
c.clearRDRC()
c.cleanupDNSCache()
interval := c.optimisticTimeout / 2
if interval <= 0 {
interval = time.Hour
}
go c.loopCacheCleanup(interval, c.cleanupDNSCache)
} else if c.storeRDRC {
c.cleanupRDRC()
interval := c.rdrcTimeout / 2
if interval <= 0 {
interval = time.Hour
}
go c.loopCacheCleanup(interval, c.cleanupRDRC)
}
}
func (c *CacheFile) start() error {
const fileMode = 0o666
options := bbolt.Options{Timeout: time.Second}
var (