Skip to content

Commit 54fc4c7

Browse files
authored
Merge pull request #315 from hjotha/submit/executor-configurable-timeout
feat(executor): allow MXCLI_EXEC_TIMEOUT to override the per-statement timeout
2 parents 9623ccb + e547850 commit 54fc4c7

2 files changed

Lines changed: 60 additions & 3 deletions

File tree

mdl/executor/executor.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"context"
88
"fmt"
99
"io"
10+
"os"
11+
"strconv"
1012
"sync"
1113
"time"
1214

@@ -160,10 +162,32 @@ const (
160162
// maxOutputLines is the per-statement line limit. Statements that produce more
161163
// lines than this are aborted to prevent runaway output from infinite loops.
162164
maxOutputLines = 10_000
163-
// executeTimeout is the maximum wall-clock time allowed for a single statement.
164-
executeTimeout = 5 * time.Minute
165+
// defaultExecuteTimeout is the maximum wall-clock time allowed for a single
166+
// statement when MXCLI_EXEC_TIMEOUT is not set.
167+
defaultExecuteTimeout = 5 * time.Minute
165168
)
166169

170+
// configuredExecuteTimeout returns the per-statement wall-clock timeout. The
171+
// value is read from the MXCLI_EXEC_TIMEOUT environment variable on every call
172+
// so long-running audits can opt into a higher ceiling without recompiling.
173+
//
174+
// Accepts either a Go duration ("12m", "2h30m") or a bare number of seconds
175+
// ("900"). Falls back to defaultExecuteTimeout when the variable is unset,
176+
// empty, or fails to parse.
177+
func configuredExecuteTimeout() time.Duration {
178+
raw := os.Getenv("MXCLI_EXEC_TIMEOUT")
179+
if raw == "" {
180+
return defaultExecuteTimeout
181+
}
182+
if d, err := time.ParseDuration(raw); err == nil && d > 0 {
183+
return d
184+
}
185+
if seconds, err := strconv.Atoi(raw); err == nil && seconds > 0 {
186+
return time.Duration(seconds) * time.Second
187+
}
188+
return defaultExecuteTimeout
189+
}
190+
167191
// BackendFactory creates a new backend instance for connecting to a project.
168192
type BackendFactory func() backend.FullBackend
169193

@@ -221,7 +245,7 @@ func (e *Executor) SetLogger(l *diaglog.Logger) {
221245

222246
// Execute runs a single MDL statement with output-line and wall-clock guards.
223247
// Each statement gets a fresh line budget. If the statement exceeds maxOutputLines
224-
// lines of output or runs longer than executeTimeout, it is aborted with an error.
248+
// lines of output or runs longer than the configured timeout, it is aborted with an error.
225249
func (e *Executor) Execute(stmt ast.Statement) error {
226250
start := time.Now()
227251

@@ -233,6 +257,7 @@ func (e *Executor) Execute(stmt ast.Statement) error {
233257
// Enforce wall-clock timeout via context.WithTimeout.
234258
// The goroutine pattern is retained because handlers are not yet
235259
// context-aware; threading context through handlers is a follow-up.
260+
executeTimeout := configuredExecuteTimeout()
236261
ctx, cancel := context.WithTimeout(context.Background(), executeTimeout)
237262
defer cancel()
238263

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package executor
4+
5+
import (
6+
"testing"
7+
"time"
8+
)
9+
10+
func TestConfiguredExecuteTimeoutUsesDurationEnv(t *testing.T) {
11+
t.Setenv("MXCLI_EXEC_TIMEOUT", "12m")
12+
13+
if got := configuredExecuteTimeout(); got != 12*time.Minute {
14+
t.Fatalf("configured timeout = %v, want 12m", got)
15+
}
16+
}
17+
18+
func TestConfiguredExecuteTimeoutUsesSecondEnv(t *testing.T) {
19+
t.Setenv("MXCLI_EXEC_TIMEOUT", "900")
20+
21+
if got := configuredExecuteTimeout(); got != 15*time.Minute {
22+
t.Fatalf("configured timeout = %v, want 15m", got)
23+
}
24+
}
25+
26+
func TestConfiguredExecuteTimeoutFallsBackForInvalidEnv(t *testing.T) {
27+
t.Setenv("MXCLI_EXEC_TIMEOUT", "invalid")
28+
29+
if got := configuredExecuteTimeout(); got != defaultExecuteTimeout {
30+
t.Fatalf("configured timeout = %v, want default %v", got, defaultExecuteTimeout)
31+
}
32+
}

0 commit comments

Comments
 (0)