clock->now(); $cloud = $this->settings->load()->cloud(); if ( ! $cloud->isConfigured() ) { return ScheduledLinkHealthTickResult::skipped( 'cloud_not_configured' ); } $state = $this->loadState(); if ( ! $state->canRun( $now ) ) { return ScheduledLinkHealthTickResult::skipped( 'backoff', $state->attempts(), $state->nextAvailableAt() ); } $batch = $this->collector->collect( $state->nextPage(), $this->scanPageSize, $this->submitLimit, $now, $this->freshTtlSeconds ); if ( $batch->isEmpty() ) { $this->saveState( $state->recordSuccess( $batch->nextPage() ) ); return ScheduledLinkHealthTickResult::success( $batch->scannedProducts(), 0, 0 ); } try { $results = $this->client->check( $cloud, $batch->urls(), $now->getTimestamp(), bin2hex( random_bytes( 12 ) ), $batch->contexts() ); $persisted = $this->statusStore->persistResults( $results, $batch->contexts(), $now ); $this->saveState( $state->recordSuccess( $batch->nextPage() ) ); return ScheduledLinkHealthTickResult::success( $batch->scannedProducts(), count( $batch->urls() ), $persisted ); } catch ( Throwable $e ) { $newAttempts = $state->attempts() + 1; $storedAttempts = $newAttempts; if ( $this->retry->hasNextAttempt( $newAttempts ) ) { $waitSeconds = $this->retry->backoffSeconds( $newAttempts ); } else { $waitSeconds = $this->terminalCooldownSeconds; $storedAttempts = 0; } $nextAvailableAt = $now->setTimestamp( $now->getTimestamp() + $waitSeconds ); $nextState = $state->recordRetry( $storedAttempts, $nextAvailableAt, $e->getMessage() ); $this->saveState( $nextState ); return ScheduledLinkHealthTickResult::retryScheduled( $nextState->attempts(), $nextAvailableAt ); } } /** * Load persisted runner state. */ private function loadState(): ScheduledLinkHealthState { $stored = $this->options->get( self::OPTION_STATE, null ); if ( ! is_array( $stored ) ) { return ScheduledLinkHealthState::defaults(); } return ScheduledLinkHealthState::fromArray( $stored ); } /** * Persist runner state. * * @param ScheduledLinkHealthState $state State. */ private function saveState( ScheduledLinkHealthState $state ): void { $this->options->update( self::OPTION_STATE, $state->toArray() ); } }