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.
168192type 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.
225249func (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
0 commit comments