agent runner: drop status-event spam from log.stream

restic --json emits a status frame ~every 16ms during a backup.
The runner was forwarding every line to log.stream verbatim, which
flooded the live log pane with duplicate status JSON for any
short-running backup (visible immediately on a 1000-file, ~4MB
test set: ~14 identical 'percent_done: 1' lines in 220ms).

The progress widget already covers the same information at a sane
sample rate (one per second via job.progress), so the raw status
lines in log.stream are double-bookkeeping. Skip them and forward
only non-status lines (file names, errors, summary).

Throttling logic for job.progress is unchanged.
This commit is contained in:
2026-05-03 13:35:18 +01:00
parent e45f75598f
commit 5f2845c331
+10 -3
View File
@@ -74,7 +74,14 @@ func (r *Runner) RunBackup(ctx context.Context, jobID string, paths, excludes, t
lastProgress := time.Now() lastProgress := time.Now()
handle := func(stream string, line string, ev any) { handle := func(stream string, line string, ev any) {
// Forward every line to the server as log.stream. // Throttled progress events come from restic's `status` JSON.
// We deliberately do NOT forward the raw status line to
// log.stream — it's emitted ~every 16ms by restic --json and
// would drown the live log in dupes for any short backup. The
// progress widget already covers the same information at a
// sane sample rate.
status, isStatus := ev.(restic.BackupStatus)
if !isStatus {
now := time.Now().UTC() now := time.Now().UTC()
logEnv, _ := api.Marshal(api.MsgLogStream, "", api.LogStreamLine{ logEnv, _ := api.Marshal(api.MsgLogStream, "", api.LogStreamLine{
JobID: jobID, JobID: jobID,
@@ -84,9 +91,9 @@ func (r *Runner) RunBackup(ctx context.Context, jobID string, paths, excludes, t
Payload: line, Payload: line,
}) })
_ = r.tx.Send(logEnv) _ = r.tx.Send(logEnv)
}
// Throttled progress events. if isStatus {
if status, ok := ev.(restic.BackupStatus); ok {
if time.Since(lastProgress) < r.progressMinPeriod { if time.Since(lastProgress) < r.progressMinPeriod {
return return
} }