feat: make global default timeout 0 (infinite) instead of required 30s
DefaultTimeoutSeconds now means "no timeout" when 0/empty, and that is the new default, rather than an invalid config forcing a positive value. runner.RunJob avoids context.WithTimeout with a zero duration (which would expire immediately) and instead runs on a plain cancelable context when no timeout is configured. Per-job TimeoutSeconds inherit semantics are unchanged.
This commit is contained in:
+4
-1
@@ -105,11 +105,14 @@ func DisplayOverlapPolicy(job domain.Job, globalPolicy domain.OverlapPolicy) str
|
||||
// DisplayTimeout formats a job's effective run timeout for the details panel.
|
||||
// When the job sets its own TimeoutSeconds it is shown as-is; when 0 (inherit),
|
||||
// the global default is shown with "(global default)" appended, mirroring
|
||||
// DisplayOverlapPolicy.
|
||||
// DisplayOverlapPolicy. A non-positive global default means no timeout at all.
|
||||
func DisplayTimeout(job domain.Job, globalDefault int) string {
|
||||
if job.TimeoutSeconds > 0 {
|
||||
return fmt.Sprintf("%d s", job.TimeoutSeconds)
|
||||
}
|
||||
if globalDefault <= 0 {
|
||||
return "no timeout (global default)"
|
||||
}
|
||||
return fmt.Sprintf("%d s (global default)", globalDefault)
|
||||
}
|
||||
|
||||
|
||||
@@ -173,4 +173,7 @@ func TestDisplayTimeout(t *testing.T) {
|
||||
if got, want := DisplayTimeout(inherit, 30), "30 s (global default)"; got != want {
|
||||
t.Errorf("inherited timeout = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := DisplayTimeout(inherit, 0), "no timeout (global default)"; got != want {
|
||||
t.Errorf("inherited infinite timeout = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,8 +393,8 @@ func validateConfig(config domain.Config) error {
|
||||
if config.OverlapPolicy != domain.OverlapPolicySkip && config.OverlapPolicy != domain.OverlapPolicyQueue {
|
||||
return errors.New("overlap policy must be 'skip' or 'queue'")
|
||||
}
|
||||
if config.DefaultTimeoutSeconds <= 0 {
|
||||
return errors.New("default timeout must be a positive number of seconds")
|
||||
if config.DefaultTimeoutSeconds < 0 {
|
||||
return errors.New("default timeout must not be negative (0 means no timeout)")
|
||||
}
|
||||
// Empty Theme is accepted and normalized to the default on load, so older
|
||||
// configs (and hand-built ones) stay valid without an explicit theme.
|
||||
|
||||
@@ -527,7 +527,7 @@ func TestUpdateSettingsRejectsInvalidConfigs(t *testing.T) {
|
||||
{"missing logs dir", func(c *domain.Config) { c.LogsDir = "" }},
|
||||
{"non-positive max files", func(c *domain.Config) { c.MaxLogFiles = 0 }},
|
||||
{"non-positive max age", func(c *domain.Config) { c.MaxLogAgeDays = -1 }},
|
||||
{"non-positive default timeout", func(c *domain.Config) { c.DefaultTimeoutSeconds = 0 }},
|
||||
{"negative default timeout", func(c *domain.Config) { c.DefaultTimeoutSeconds = -1 }},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
+2
-1
@@ -214,7 +214,8 @@ func (s *Service) effectiveOverlapPolicy(job *domain.Job) domain.OverlapPolicy {
|
||||
// job's own TimeoutSeconds when positive, otherwise the global
|
||||
// Config.DefaultTimeoutSeconds. A non-positive Job.TimeoutSeconds means "inherit
|
||||
// the global default", which is why normalizeJob leaves 0 rather than
|
||||
// backfilling the configured value. The caller must hold mu.
|
||||
// backfilling the configured value. A resolved duration of 0 means no timeout;
|
||||
// runner.RunJob treats it as "run without a deadline". The caller must hold mu.
|
||||
func (s *Service) effectiveTimeout(job *domain.Job) time.Duration {
|
||||
secs := job.TimeoutSeconds
|
||||
if secs <= 0 {
|
||||
|
||||
@@ -633,4 +633,9 @@ func TestEffectiveTimeout(t *testing.T) {
|
||||
if got, want := svc.effectiveTimeout(own), 5*time.Second; got != want {
|
||||
t.Errorf("per-job timeout = %s, want %s", got, want)
|
||||
}
|
||||
|
||||
svc.store.Config.DefaultTimeoutSeconds = 0
|
||||
if got, want := svc.effectiveTimeout(inherit), time.Duration(0); got != want {
|
||||
t.Errorf("inherited timeout with no global default = %s, want %s (no timeout)", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user