fix(engine): don't reset started_at on poll so step deadlines accumulate
RunRunner::onPoll no longer resets started_at, so a poll step's own deadline (WaitForGuestAgent 270s, ConfigureDnsAndTls cert 840s) actually fires instead of resetting every poll. maxDuration raised above each own-deadline so the step's fail wins over the generic timeout. Shared core fix (A + B). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/portal-design
parent
fffcecb152
commit
c56aa14f9d
|
|
@ -147,12 +147,15 @@ class RunRunner
|
|||
$this->record($run, $step->key(), 'retry', $result->reason);
|
||||
}
|
||||
|
||||
/** Poll: wait and re-run without consuming the retry budget (step owns its deadline). */
|
||||
/**
|
||||
* Poll: wait and re-run without consuming the retry budget. started_at is NOT
|
||||
* reset here, so a step's own deadline (measured from started_at) actually
|
||||
* accumulates across polls instead of resetting every time.
|
||||
*/
|
||||
private function onPoll(ProvisioningRun $run, ProvisioningStep $step, StepResult $result): void
|
||||
{
|
||||
$run->status = ProvisioningRun::STATUS_WAITING;
|
||||
$run->next_attempt_at = now()->addSeconds($result->afterSeconds);
|
||||
$run->started_at = now();
|
||||
$run->save();
|
||||
$this->record($run, $step->key(), 'info', $result->reason);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ class ConfigureDnsAndTls extends CustomerStep
|
|||
|
||||
public function maxDuration(): int
|
||||
{
|
||||
return 900;
|
||||
// Above the 840s cert own-deadline below, so the step's own fail fires first.
|
||||
return 960;
|
||||
}
|
||||
|
||||
public function execute(ProvisioningRun $run): StepResult
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ class WaitForGuestAgent extends CustomerStep
|
|||
|
||||
public function maxDuration(): int
|
||||
{
|
||||
return 300;
|
||||
// Above the 270s own-deadline below, so the step's own fail fires first.
|
||||
return 360;
|
||||
}
|
||||
|
||||
public function execute(ProvisioningRun $run): StepResult
|
||||
|
|
|
|||
|
|
@ -93,6 +93,18 @@ it('polls without consuming the retry budget', function () {
|
|||
expect($run->fresh()->status)->toBe('waiting')->and($run->fresh()->attempt)->toBe(0);
|
||||
});
|
||||
|
||||
it('keeps started_at stable across polls so step deadlines can accumulate', function () {
|
||||
bindPipeline(['test' => [FakePollStep::class]]);
|
||||
$run = ProvisioningRun::factory()->create([
|
||||
'pipeline' => 'test', 'status' => 'running', 'started_at' => now()->subMinutes(5),
|
||||
]);
|
||||
$original = $run->started_at->timestamp;
|
||||
|
||||
app(RunRunner::class)->advance($run);
|
||||
|
||||
expect($run->fresh()->started_at->timestamp)->toBe($original);
|
||||
});
|
||||
|
||||
it('marks a Host subject as error when the run fails', function () {
|
||||
bindPipeline(['test' => [FakeFailStep::class]]);
|
||||
$host = Host::factory()->create(['status' => 'onboarding']);
|
||||
|
|
|
|||
Loading…
Reference in New Issue