mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-08-23 01:27:13 +02:00
all: add configuration to limit max concurrent requests
Currently, there's no upper bound for how many requests that ctrld will handle at a time. This could be problem on some low capacity routers, where CPU/RAM is very limited. This commit adds a configuration to limit how many requests that will be handled concurrently. The default is 256, which should works well for most routers (the default concurrent requests of dnsmasq is 150).
This commit is contained in:
@@ -47,6 +47,8 @@ func (p *prog) serveDNS(listenerNum string) error {
|
|||||||
failoverRcodes = listenerConfig.Policy.FailoverRcodeNumbers
|
failoverRcodes = listenerConfig.Policy.FailoverRcodeNumbers
|
||||||
}
|
}
|
||||||
handler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
|
handler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
|
||||||
|
p.sema.acquire()
|
||||||
|
defer p.sema.release()
|
||||||
q := m.Question[0]
|
q := m.Question[0]
|
||||||
domain := canonicalName(q.Name)
|
domain := canonicalName(q.Name)
|
||||||
reqId := requestID()
|
reqId := requestID()
|
||||||
@@ -60,7 +62,6 @@ func (p *prog) serveDNS(listenerNum string) error {
|
|||||||
if !matched && listenerConfig.Restricted {
|
if !matched && listenerConfig.Restricted {
|
||||||
answer = new(dns.Msg)
|
answer = new(dns.Msg)
|
||||||
answer.SetRcode(m, dns.RcodeRefused)
|
answer.SetRcode(m, dns.RcodeRefused)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
answer = p.proxy(ctx, upstreams, failoverRcodes, m)
|
answer = p.proxy(ctx, upstreams, failoverRcodes, m)
|
||||||
rtt := time.Since(t)
|
rtt := time.Since(t)
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/Control-D-Inc/ctrld/internal/router"
|
"github.com/Control-D-Inc/ctrld/internal/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultSemaphoreCap = 256
|
||||||
|
|
||||||
var logf = func(format string, args ...any) {
|
var logf = func(format string, args ...any) {
|
||||||
mainLog.Debug().Msgf(format, args...)
|
mainLog.Debug().Msgf(format, args...)
|
||||||
}
|
}
|
||||||
@@ -36,6 +38,7 @@ type prog struct {
|
|||||||
|
|
||||||
cfg *ctrld.Config
|
cfg *ctrld.Config
|
||||||
cache dnscache.Cacher
|
cache dnscache.Cacher
|
||||||
|
sema semaphore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *prog) Start(s service.Service) error {
|
func (p *prog) Start(s service.Service) error {
|
||||||
@@ -56,6 +59,15 @@ func (p *prog) run() {
|
|||||||
p.cache = cacher
|
p.cache = cacher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
p.sema = &chanSemaphore{ready: make(chan struct{}, defaultSemaphoreCap)}
|
||||||
|
if mcr := p.cfg.Service.MaxConcurrentRequests; mcr != nil {
|
||||||
|
n := *mcr
|
||||||
|
if n == 0 {
|
||||||
|
p.sema = &noopSemaphore{}
|
||||||
|
} else {
|
||||||
|
p.sema = &chanSemaphore{ready: make(chan struct{}, n)}
|
||||||
|
}
|
||||||
|
}
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(p.cfg.Listener))
|
wg.Add(len(p.cfg.Listener))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type semaphore interface {
|
||||||
|
acquire()
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
|
||||||
|
type noopSemaphore struct{}
|
||||||
|
|
||||||
|
func (n noopSemaphore) acquire() {}
|
||||||
|
|
||||||
|
func (n noopSemaphore) release() {}
|
||||||
|
|
||||||
|
type chanSemaphore struct {
|
||||||
|
ready chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chanSemaphore) acquire() {
|
||||||
|
c.ready <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *chanSemaphore) release() {
|
||||||
|
<-c.ready
|
||||||
|
}
|
||||||
@@ -122,14 +122,15 @@ func (c *Config) HasUpstreamSendClientInfo() bool {
|
|||||||
|
|
||||||
// ServiceConfig specifies the general ctrld config.
|
// ServiceConfig specifies the general ctrld config.
|
||||||
type ServiceConfig struct {
|
type ServiceConfig struct {
|
||||||
LogLevel string `mapstructure:"log_level" toml:"log_level,omitempty"`
|
LogLevel string `mapstructure:"log_level" toml:"log_level,omitempty"`
|
||||||
LogPath string `mapstructure:"log_path" toml:"log_path,omitempty"`
|
LogPath string `mapstructure:"log_path" toml:"log_path,omitempty"`
|
||||||
CacheEnable bool `mapstructure:"cache_enable" toml:"cache_enable,omitempty"`
|
CacheEnable bool `mapstructure:"cache_enable" toml:"cache_enable,omitempty"`
|
||||||
CacheSize int `mapstructure:"cache_size" toml:"cache_size,omitempty"`
|
CacheSize int `mapstructure:"cache_size" toml:"cache_size,omitempty"`
|
||||||
CacheTTLOverride int `mapstructure:"cache_ttl_override" toml:"cache_ttl_override,omitempty"`
|
CacheTTLOverride int `mapstructure:"cache_ttl_override" toml:"cache_ttl_override,omitempty"`
|
||||||
CacheServeStale bool `mapstructure:"cache_serve_stale" toml:"cache_serve_stale,omitempty"`
|
CacheServeStale bool `mapstructure:"cache_serve_stale" toml:"cache_serve_stale,omitempty"`
|
||||||
Daemon bool `mapstructure:"-" toml:"-"`
|
MaxConcurrentRequests *int `mapstructure:"max_concurrent_requests" toml:"max_concurrent_requests,omitempty" validate:"omitempty,gte=0"`
|
||||||
AllocateIP bool `mapstructure:"-" toml:"-"`
|
Daemon bool `mapstructure:"-" toml:"-"`
|
||||||
|
AllocateIP bool `mapstructure:"-" toml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkConfig specifies configuration for networks where ctrld will handle requests.
|
// NetworkConfig specifies configuration for networks where ctrld will handle requests.
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ func TestConfigValidation(t *testing.T) {
|
|||||||
{"os upstream", configWithOsUpstream(t), false},
|
{"os upstream", configWithOsUpstream(t), false},
|
||||||
{"invalid rules", configWithInvalidRules(t), true},
|
{"invalid rules", configWithInvalidRules(t), true},
|
||||||
{"invalid dns rcodes", configWithInvalidRcodes(t), true},
|
{"invalid dns rcodes", configWithInvalidRcodes(t), true},
|
||||||
|
{"invalid max concurrent requests", configWithInvalidMaxConcurrentRequests(t), true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
@@ -176,3 +177,10 @@ func configWithInvalidRcodes(t *testing.T) *ctrld.Config {
|
|||||||
}
|
}
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configWithInvalidMaxConcurrentRequests(t *testing.T) *ctrld.Config {
|
||||||
|
cfg := defaultConfig(t)
|
||||||
|
n := -1
|
||||||
|
cfg.Service.MaxConcurrentRequests = &n
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|||||||
@@ -157,6 +157,14 @@ stale cached records (regardless of their TTLs) until upstream comes online.
|
|||||||
- Required: no
|
- Required: no
|
||||||
- Default: false
|
- Default: false
|
||||||
|
|
||||||
|
### max_concurrent_requests
|
||||||
|
The number of concurrent requests that will be handled, must be a non-negative integer.
|
||||||
|
Tweaking this value depends on the capacity of your system.
|
||||||
|
|
||||||
|
- Type: number
|
||||||
|
- Required: no
|
||||||
|
- Default: 256
|
||||||
|
|
||||||
## Upstream
|
## Upstream
|
||||||
The `[upstream]` section specifies the DNS upstream servers that `ctrld` will forward DNS requests to.
|
The `[upstream]` section specifies the DNS upstream servers that `ctrld` will forward DNS requests to.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user