diff --git a/app/api/api.go b/app/api/api.go index fc7362d5..1f5aa7c0 100644 --- a/app/api/api.go +++ b/app/api/api.go @@ -513,6 +513,7 @@ func (a *api) start(ctx context.Context) error { Portrange: portrange, Collector: a.sessions.Collector("ffmpeg"), Resource: a.resources, + Throttling: !cfg.FFmpeg.DisableThrottling, }) if err != nil { return fmt.Errorf("unable to create ffmpeg: %w", err) diff --git a/cluster/docs/ClusterAPI_docs.go b/cluster/docs/ClusterAPI_docs.go index 1d0240fc..c0b656ec 100644 --- a/cluster/docs/ClusterAPI_docs.go +++ b/cluster/docs/ClusterAPI_docs.go @@ -1922,6 +1922,9 @@ const docTemplateClusterAPI = `{ "binary": { "type": "string" }, + "disable_throttling": { + "type": "boolean" + }, "log": { "type": "object", "properties": { diff --git a/cluster/docs/ClusterAPI_swagger.json b/cluster/docs/ClusterAPI_swagger.json index 6e5c519f..95fc74bd 100644 --- a/cluster/docs/ClusterAPI_swagger.json +++ b/cluster/docs/ClusterAPI_swagger.json @@ -1915,6 +1915,9 @@ "binary": { "type": "string" }, + "disable_throttling": { + "type": "boolean" + }, "log": { "type": "object", "properties": { diff --git a/cluster/docs/ClusterAPI_swagger.yaml b/cluster/docs/ClusterAPI_swagger.yaml index 011e0c67..9e508c41 100644 --- a/cluster/docs/ClusterAPI_swagger.yaml +++ b/cluster/docs/ClusterAPI_swagger.yaml @@ -429,6 +429,8 @@ definitions: type: object binary: type: string + disable_throttling: + type: boolean log: properties: max_history: diff --git a/config/config.go b/config/config.go index 5e535b4b..d4947865 100644 --- a/config/config.go +++ b/config/config.go @@ -283,6 +283,7 @@ func (d *Config) init() { // FFmpeg d.vars.Register(value.NewExec(&d.FFmpeg.Binary, "ffmpeg", d.fs), "ffmpeg.binary", "CORE_FFMPEG_BINARY", nil, "Path to ffmpeg binary", true, false) d.vars.Register(value.NewInt64(&d.FFmpeg.MaxProcesses, 0), "ffmpeg.max_processes", "CORE_FFMPEG_MAXPROCESSES", nil, "Max. allowed simultaneously running ffmpeg instances, 0 for unlimited", false, false) + d.vars.Register(value.NewBool(&d.FFmpeg.DisableThrottling, false), "ffmpeg.disable_throttling", "CORE_FFMPEG_DISABLE_THROTTLING", nil, "Disable CPU throttling", false, false) d.vars.Register(value.NewStringList(&d.FFmpeg.Access.Input.Allow, []string{}, " "), "ffmpeg.access.input.allow", "CORE_FFMPEG_ACCESS_INPUT_ALLOW", nil, "List of allowed expression to match against the input addresses", false, false) d.vars.Register(value.NewStringList(&d.FFmpeg.Access.Input.Block, []string{}, " "), "ffmpeg.access.input.block", "CORE_FFMPEG_ACCESS_INPUT_BLOCK", nil, "List of blocked expression to match against the input addresses", false, false) d.vars.Register(value.NewStringList(&d.FFmpeg.Access.Output.Allow, []string{}, " "), "ffmpeg.access.output.allow", "CORE_FFMPEG_ACCESS_OUTPUT_ALLOW", nil, "List of allowed expression to match against the output addresses", false, false) diff --git a/config/data.go b/config/data.go index 7596575f..bfd3fa49 100644 --- a/config/data.go +++ b/config/data.go @@ -127,9 +127,10 @@ type Data struct { } `json:"log"` } `json:"srt"` FFmpeg struct { - Binary string `json:"binary"` - MaxProcesses int64 `json:"max_processes" format:"int64"` - Access struct { + Binary string `json:"binary"` + MaxProcesses int64 `json:"max_processes" format:"int64"` + DisableThrottling bool `json:"disable_throttling"` + Access struct { Input struct { Allow []string `json:"allow"` Block []string `json:"block"` diff --git a/docs/docs.go b/docs/docs.go index cddffdea..d739b911 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -6077,6 +6077,9 @@ const docTemplate = `{ "binary": { "type": "string" }, + "disable_throttling": { + "type": "boolean" + }, "log": { "type": "object", "properties": { @@ -8628,6 +8631,9 @@ const docTemplate = `{ "binary": { "type": "string" }, + "disable_throttling": { + "type": "boolean" + }, "log": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 5b2f3150..ed0e77ba 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -6070,6 +6070,9 @@ "binary": { "type": "string" }, + "disable_throttling": { + "type": "boolean" + }, "log": { "type": "object", "properties": { @@ -8621,6 +8624,9 @@ "binary": { "type": "string" }, + "disable_throttling": { + "type": "boolean" + }, "log": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 9b7c2ab6..71677ebe 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -572,6 +572,8 @@ definitions: type: object binary: type: string + disable_throttling: + type: boolean log: properties: max_history: @@ -2363,6 +2365,8 @@ definitions: type: object binary: type: string + disable_throttling: + type: boolean log: properties: max_history: diff --git a/ffmpeg/ffmpeg.go b/ffmpeg/ffmpeg.go index 05135116..7d661ab3 100644 --- a/ffmpeg/ffmpeg.go +++ b/ffmpeg/ffmpeg.go @@ -65,7 +65,8 @@ type Config struct { ValidatorOutput Validator Portrange net.Portranger Collector session.Collector - Resource resources.Resources + Resource resources.Resources // Resource observer + Throttling bool // Whether to allow CPU throttling } type ffmpeg struct { @@ -84,7 +85,8 @@ type ffmpeg struct { states process.States statesLock sync.RWMutex - resources resources.Resources + resources resources.Resources + throttling bool } func New(config Config) (FFmpeg, error) { @@ -95,6 +97,7 @@ func New(config Config) (FFmpeg, error) { } f.resources = config.Resource + f.throttling = config.Throttling binary, err := exec.LookPath(config.Binary) if err != nil { @@ -164,6 +167,7 @@ func (f *ffmpeg) New(config ProcessConfig) (process.Process, error) { StaleTimeout: config.StaleTimeout, Timeout: config.Timeout, LimitCPU: config.LimitCPU, + Throttling: f.throttling, LimitMemory: config.LimitMemory, LimitGPUUsage: config.LimitGPUUsage, LimitGPUEncoder: config.LimitGPUEncoder, diff --git a/process/limiter.go b/process/limiter.go index 29a32877..d9f07f14 100644 --- a/process/limiter.go +++ b/process/limiter.go @@ -86,6 +86,7 @@ type LimiterConfig struct { OnLimit LimitFunc // Function to be triggered if limits are exceeded. Mode LimitMode // How to limit CPU usage. NCPU float64 // Number of available CPU + Throttling bool // Whether to allow CPU throttling Logger log.Logger } @@ -236,8 +237,9 @@ type limiter struct { lastUsage Usage lastUsageLock sync.RWMutex - cpu metric[float64] // CPU limit - cpuThrottling bool // Whether CPU throttling is currently active (soft limiter mode) + cpu metric[float64] // CPU limit + cpuThrottling bool // Whether CPU throttling is currently active (soft limiter mode) + cpuEnableThrottling bool // Whether to enable CPU throttling memory metric[uint64] // Memory limit (bytes) @@ -269,7 +271,10 @@ func NewLimiter(config LimiterConfig) (Limiter, error) { } l.cpu.SetLimit(config.CPU / 100) + l.cpuEnableThrottling = config.Throttling + l.memory.SetLimit(config.Memory) + l.gpu.memory.SetLimit(config.GPUMemory) l.gpu.usage.SetLimit(config.GPUUsage / 100) l.gpu.encoder.SetLimit(config.GPUEncoder / 100) @@ -342,7 +347,7 @@ func (l *limiter) Start(process resources.Process) error { go l.ticker(ctx, time.Second) - if l.mode == LimitModeSoft { + if l.mode == LimitModeSoft && l.cpuEnableThrottling { go l.limitCPU(ctx, l.cpu.Limit(), time.Second) } @@ -407,7 +412,7 @@ func (l *limiter) collect() { isLimitExceeded := false - if l.mode == LimitModeHard { + if l.mode == LimitModeHard || !l.cpuEnableThrottling { if l.cpu.IsExceeded(l.waitFor, l.mode) { l.logger.Warn().Log("CPU limit exceeded") isLimitExceeded = true diff --git a/process/process.go b/process/process.go index e05c6345..77f8e456 100644 --- a/process/process.go +++ b/process/process.go @@ -58,6 +58,7 @@ type Config struct { StaleTimeout time.Duration // Kill the process after this duration if it doesn't produce any output. Timeout time.Duration // Kill the process after this duration. LimitCPU float64 // Kill the process if the CPU usage in percent is above this value, in percent 0-100 in hard mode, 0-100*ncpu in soft mode. + Throttling bool // Whether to allow CPU throttling LimitMemory uint64 // Kill the process if the memory consumption in bytes is above this value. LimitGPUUsage float64 // Kill the process if the GPU usage in percent is above this value, in percent 0-100. LimitGPUEncoder float64 // Kill the process if the GPU encoder usage in percent is above this value, in percent 0-100. @@ -312,6 +313,7 @@ func New(config Config) (Process, error) { limits, err := NewLimiter(LimiterConfig{ CPU: config.LimitCPU, + Throttling: config.Throttling, NCPU: ncpu, Memory: config.LimitMemory, GPUUsage: config.LimitGPUUsage,