Add process information cache to avoid duplicate lookups
PreMatch and full match phases each created a fresh InboundContext, causing process search (expensive OS syscalls) to run twice per connection. Use a freelru ShardedLRU cache with 200ms TTL to serve the second lookup from cache.
This commit is contained in:
34
route/process_cache.go
Normal file
34
route/process_cache.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package route
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/process"
|
||||||
|
)
|
||||||
|
|
||||||
|
type processCacheKey struct {
|
||||||
|
Network string
|
||||||
|
Source netip.AddrPort
|
||||||
|
Destination netip.AddrPort
|
||||||
|
}
|
||||||
|
|
||||||
|
type processCacheEntry struct {
|
||||||
|
result *adapter.ConnectionOwner
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Router) findProcessInfoCached(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
|
||||||
|
key := processCacheKey{
|
||||||
|
Network: network,
|
||||||
|
Source: source,
|
||||||
|
Destination: destination,
|
||||||
|
}
|
||||||
|
if entry, ok := r.processCache.Get(key); ok {
|
||||||
|
return entry.result, entry.err
|
||||||
|
}
|
||||||
|
result, err := process.FindProcessInfo(r.processSearcher, ctx, network, source, destination)
|
||||||
|
r.processCache.Add(key, processCacheEntry{result: result, err: err})
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/process"
|
|
||||||
"github.com/sagernet/sing-box/common/sniff"
|
"github.com/sagernet/sing-box/common/sniff"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
R "github.com/sagernet/sing-box/route/rule"
|
R "github.com/sagernet/sing-box/route/rule"
|
||||||
@@ -415,7 +414,7 @@ func (r *Router) matchRule(
|
|||||||
} else if metadata.Destination.IsIP() {
|
} else if metadata.Destination.IsIP() {
|
||||||
originDestination = metadata.Destination.AddrPort()
|
originDestination = metadata.Destination.AddrPort()
|
||||||
}
|
}
|
||||||
processInfo, fErr := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination)
|
processInfo, fErr := r.findProcessInfoCached(ctx, metadata.Network, metadata.Source.AddrPort(), originDestination)
|
||||||
if fErr != nil {
|
if fErr != nil {
|
||||||
r.logger.InfoContext(ctx, "failed to search process: ", fErr)
|
r.logger.InfoContext(ctx, "failed to search process: ", fErr)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/process"
|
"github.com/sagernet/sing-box/common/process"
|
||||||
@@ -12,8 +13,11 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
R "github.com/sagernet/sing-box/route/rule"
|
R "github.com/sagernet/sing-box/route/rule"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/task"
|
"github.com/sagernet/sing/common/task"
|
||||||
|
"github.com/sagernet/sing/contrab/freelru"
|
||||||
|
"github.com/sagernet/sing/contrab/maphash"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
"github.com/sagernet/sing/service/pause"
|
"github.com/sagernet/sing/service/pause"
|
||||||
)
|
)
|
||||||
@@ -34,6 +38,7 @@ type Router struct {
|
|||||||
ruleSets []adapter.RuleSet
|
ruleSets []adapter.RuleSet
|
||||||
ruleSetMap map[string]adapter.RuleSet
|
ruleSetMap map[string]adapter.RuleSet
|
||||||
processSearcher process.Searcher
|
processSearcher process.Searcher
|
||||||
|
processCache freelru.Cache[processCacheKey, processCacheEntry]
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
trackers []adapter.ConnectionTracker
|
trackers []adapter.ConnectionTracker
|
||||||
platformInterface adapter.PlatformInterface
|
platformInterface adapter.PlatformInterface
|
||||||
@@ -141,6 +146,11 @@ func (r *Router) Start(stage adapter.StartStage) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if r.processSearcher != nil {
|
||||||
|
processCache := common.Must1(freelru.NewSharded[processCacheKey, processCacheEntry](256, maphash.NewHasher[processCacheKey]().Hash32))
|
||||||
|
processCache.SetLifetime(200 * time.Millisecond)
|
||||||
|
r.processCache = processCache
|
||||||
|
}
|
||||||
case adapter.StartStatePostStart:
|
case adapter.StartStatePostStart:
|
||||||
for i, rule := range r.rules {
|
for i, rule := range r.rules {
|
||||||
monitor.Start("initialize rule[", i, "]")
|
monitor.Start("initialize rule[", i, "]")
|
||||||
|
|||||||
Reference in New Issue
Block a user