fix(engine): dispatch run continuation after releasing the lock

Prevents a second worker from consuming the follow-up job and bailing on the
still-held run lock, which would stall the run until the next scheduler tick.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/portal-design
nexxo 2026-07-25 11:09:24 +02:00
parent 294c9aef2f
commit c7fb1ce56d
1 changed files with 12 additions and 1 deletions

View File

@ -18,8 +18,12 @@ class RunRunner
{
public function __construct(private PipelineRegistry $registry) {}
private ?string $pendingContinuation = null;
public function advance(ProvisioningRun $run): void
{
$this->pendingContinuation = null;
// Lock must outlive the longest step so a duplicate job (dispatched by
// the minutely tick while this one still runs) can't execute concurrently.
$lock = Cache::lock('run:'.$run->uuid, 2100);
@ -33,6 +37,13 @@ class RunRunner
} finally {
$lock->release();
}
// Dispatch the continuation only AFTER releasing the lock, so a second
// worker can't consume it and then bail on the still-held lock.
if ($this->pendingContinuation !== null) {
AdvanceRunJob::dispatch($this->pendingContinuation);
$this->pendingContinuation = null;
}
}
private function runLocked(ProvisioningRun $run): void
@ -116,7 +127,7 @@ class RunRunner
$run->save();
$this->record($run, $step->key(), 'advanced', null);
AdvanceRunJob::dispatch($run->uuid);
$this->pendingContinuation = $run->uuid; // dispatched after the lock releases
}
private function onRetry(ProvisioningRun $run, ProvisioningStep $step, StepResult $result): void