heartbeatRoutine periodically sends updates on the job, which keeps coder server from assuming the job is stalled, and allows the runner to learn if the job has been canceled by the user.
(ctx context.Context)
| 471 | // from assuming the job is stalled, and allows the runner to learn if the job |
| 472 | // has been canceled by the user. |
| 473 | func (r *Runner) heartbeatRoutine(ctx context.Context) { |
| 474 | ctx, span := r.startTrace(ctx, tracing.FuncName()) |
| 475 | defer span.End() |
| 476 | |
| 477 | ticker := time.NewTicker(r.updateInterval) |
| 478 | defer ticker.Stop() |
| 479 | |
| 480 | for { |
| 481 | select { |
| 482 | case <-r.notCanceled.Done(): |
| 483 | return |
| 484 | case <-ticker.C: |
| 485 | } |
| 486 | |
| 487 | resp, err := r.sendHeartbeat(ctx) |
| 488 | if err != nil { |
| 489 | // Calling Fail starts cancellation so the process will exit. |
| 490 | err = r.Fail(ctx, r.failedJobf("send periodic update: %s", err)) |
| 491 | if err != nil { |
| 492 | r.logger.Error(ctx, "failed to call FailJob", slog.Error(err)) |
| 493 | } |
| 494 | return |
| 495 | } |
| 496 | if !resp.Canceled { |
| 497 | ticker.Reset(r.updateInterval) |
| 498 | continue |
| 499 | } |
| 500 | r.logger.Info(ctx, "attempting graceful cancellation") |
| 501 | r.Cancel() |
| 502 | // Mark the job as failed after a minute of pending cancellation. |
| 503 | timer := time.NewTimer(r.forceCancelInterval) |
| 504 | select { |
| 505 | case <-timer.C: |
| 506 | r.logger.Debug(ctx, "cancel timed out") |
| 507 | err := r.Fail(ctx, r.failedJobf("Cancel timed out")) |
| 508 | if err != nil { |
| 509 | r.logger.Warn(ctx, "failed to call FailJob", slog.Error(err)) |
| 510 | } |
| 511 | return |
| 512 | case <-r.Done(): |
| 513 | timer.Stop() |
| 514 | return |
| 515 | case <-r.notStopped.Done(): |
| 516 | timer.Stop() |
| 517 | return |
| 518 | } |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *proto.FailedJob) { |
| 523 | ctx, span := r.startTrace(ctx, tracing.FuncName()) |
no test coverage detected