domain/runtime, app/run: add execution-time statistics aggregate (T2.4)
Add RunCount, FailCount, LastDurationMS, AvgDurationMS, MaxDurationMS to JobRuntime and fold each completed RunRecord into them via updateStats called from executeRun. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -88,10 +88,10 @@ Done first because both share a compact, single-line record formatter.
|
|||||||
- [x] T1.4 — formatter tests
|
- [x] T1.4 — formatter tests
|
||||||
|
|
||||||
### Phase 2 — Execution-time statistics
|
### Phase 2 — Execution-time statistics
|
||||||
- [ ] T2.1 — `DurationMS` on `RunRecord`
|
- [x] T2.1 — `DurationMS` on `RunRecord`
|
||||||
- [ ] T2.2 — measure duration in runner
|
- [x] T2.2 — measure duration in runner
|
||||||
- [ ] T2.3 — `duration` log header
|
- [x] T2.3 — `duration` log header
|
||||||
- [ ] T2.4 — runtime aggregate + `executeRun` update
|
- [x] T2.4 — runtime aggregate + `executeRun` update
|
||||||
- [ ] T2.5 — seed stats from log files
|
- [ ] T2.5 — seed stats from log files
|
||||||
- [ ] T2.6 — `DisplayStats` + Statistics row
|
- [ ] T2.6 — `DisplayStats` + Statistics row
|
||||||
- [ ] T2.7 — stats tests
|
- [ ] T2.7 — stats tests
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ func (s *Service) executeRun(ctx context.Context, jobCopy domain.Job, trigger st
|
|||||||
runtime.LastState = record.State
|
runtime.LastState = record.State
|
||||||
runtime.Output = record.Output
|
runtime.Output = record.Output
|
||||||
prependLog(runtime, record)
|
prependLog(runtime, record)
|
||||||
|
updateStats(runtime, record)
|
||||||
rerun := runtime.Pending && current.Enabled && !s.paused
|
rerun := runtime.Pending && current.Enabled && !s.paused
|
||||||
runtime.Pending = false
|
runtime.Pending = false
|
||||||
if rerun {
|
if rerun {
|
||||||
@@ -190,6 +191,20 @@ func (s *Service) advanceNextDueLocked(job *domain.Job, runtime *domain.JobRunti
|
|||||||
runtime.NextDue = sched.Next(from)
|
runtime.NextDue = sched.Next(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateStats folds one completed RunRecord into the runtime's aggregate
|
||||||
|
// execution-time statistics. Called under mu inside executeRun.
|
||||||
|
func updateStats(rt *domain.JobRuntime, r domain.RunRecord) {
|
||||||
|
rt.RunCount++
|
||||||
|
if r.State == "Failed" {
|
||||||
|
rt.FailCount++
|
||||||
|
}
|
||||||
|
rt.LastDurationMS = r.DurationMS
|
||||||
|
if r.DurationMS > rt.MaxDurationMS {
|
||||||
|
rt.MaxDurationMS = r.DurationMS
|
||||||
|
}
|
||||||
|
rt.AvgDurationMS = (rt.AvgDurationMS*int64(rt.RunCount-1) + r.DurationMS) / int64(rt.RunCount)
|
||||||
|
}
|
||||||
|
|
||||||
// runningOutput is the placeholder output shown while a job is running, before
|
// runningOutput is the placeholder output shown while a job is running, before
|
||||||
// the real command output replaces it.
|
// the real command output replaces it.
|
||||||
func runningOutput(job domain.Job, trigger string, started time.Time) string {
|
func runningOutput(job domain.Job, trigger string, started time.Time) string {
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ type JobRuntime struct {
|
|||||||
// Pending is set when a run was skipped due to the overlap policy being
|
// Pending is set when a run was skipped due to the overlap policy being
|
||||||
// "queue". The scheduler will start this job as soon as the current run ends.
|
// "queue". The scheduler will start this job as soon as the current run ends.
|
||||||
Pending bool
|
Pending bool
|
||||||
|
|
||||||
|
// Execution-time statistics accumulated since the last process start.
|
||||||
|
// Seeded from log files on startup by T2.5; zero until then.
|
||||||
|
RunCount int
|
||||||
|
FailCount int
|
||||||
|
LastDurationMS int64
|
||||||
|
AvgDurationMS int64
|
||||||
|
MaxDurationMS int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRuntime builds the initial runtime state for a freshly loaded or created
|
// NewRuntime builds the initial runtime state for a freshly loaded or created
|
||||||
|
|||||||
Reference in New Issue
Block a user