diff --git a/config/search.php b/config/search.php index 49b0ffdef5..4292708fa7 100644 --- a/config/search.php +++ b/config/search.php @@ -85,7 +85,7 @@ |-------------------------------------------------------------------------- | | Here you may configure the queue name and connection used when indexing - | documents. + | documents, along with a timeout (in seconds) for each indexing job. | */ @@ -93,6 +93,8 @@ 'queue_connection' => env('STATAMIC_SEARCH_QUEUE_CONNECTION'), + 'queue_timeout' => env('STATAMIC_SEARCH_QUEUE_TIMEOUT'), + /* |-------------------------------------------------------------------------- | Chunk Size diff --git a/src/Search/InsertMultipleJob.php b/src/Search/InsertMultipleJob.php index f723d68874..91e806e97c 100644 --- a/src/Search/InsertMultipleJob.php +++ b/src/Search/InsertMultipleJob.php @@ -15,6 +15,16 @@ class InsertMultipleJob implements ShouldQueue { use Queueable; + /** + * The number of seconds the job can run before timing out. + * + * This needs to stay a property. Queue::createObjectPayload() reads it through + * getAttributeValue($job, Timeout::class, 'timeout'), which - unlike maxTries + * and backoff - has no method_exists() fallback, so a timeout() method here + * would be silently ignored. + */ + public ?int $timeout = null; + /** * Create a new job instance. */ @@ -25,6 +35,10 @@ public function __construct( ) { $this->onConnection($connection = config('statamic.search.queue_connection', config('queue.default'))); $this->onQueue(config('statamic.search.queue', config("queue.connections.{$connection}.queue"))); + + if (filled($timeout = config('statamic.search.queue_timeout'))) { + $this->timeout = (int) $timeout; + } } /** diff --git a/src/View/Cascade.php b/src/View/Cascade.php index 36149fe477..13df29aaa7 100644 --- a/src/View/Cascade.php +++ b/src/View/Cascade.php @@ -317,6 +317,7 @@ public static function config(): array 'statamic.search.defaults', 'statamic.search.queue', 'statamic.search.queue_connection', + 'statamic.search.queue_timeout', 'statamic.search.chunk_size', 'statamic.stache.watcher', 'statamic.stache.cache_store', diff --git a/tests/Search/InsertMultipleJobTest.php b/tests/Search/InsertMultipleJobTest.php new file mode 100644 index 0000000000..b45aeac008 --- /dev/null +++ b/tests/Search/InsertMultipleJobTest.php @@ -0,0 +1,68 @@ + $configured]); + + $job = new InsertMultipleJob('test', null, collect(['entry::123'])); + + $this->assertSame($expected, $job->timeout); + } + + public static function timeoutProvider() + { + return [ + 'not configured' => [null, null], + 'empty env var' => ['', null], + 'integer' => [300, 300], + 'string from env' => ['300', 300], + ]; + } + + #[Test, DataProvider('queueConnectionsProvider')] + public function it_uses_the_configured_queue_and_connection( + $configuredQueue, + $configuredConnection, + $defaultConnection, + $expectedJobQueue, + $expectedJobConnection + ) { + config([ + 'statamic.search.queue' => $configuredQueue, + 'statamic.search.queue_connection' => $configuredConnection, + 'queue.default' => $defaultConnection, + ]); + + $job = new InsertMultipleJob('test', null, collect(['entry::123'])); + + $this->assertSame($expectedJobQueue, $job->queue); + $this->assertSame($expectedJobConnection, $job->connection); + } + + /** + * When the config keys are null the job leaves connection/queue null rather than + * resolving them to the framework defaults, since config() only falls back to its + * second argument when a key is absent, and these keys always ship in the config + * file. A null connection/queue already means "use the default", so the outcome is + * the same - but the properties stay null. + */ + public static function queueConnectionsProvider() + { + return [ + [null, null, 'redis', null, null], + ['indexing', null, 'redis', 'indexing', null], + [null, 'sqs', 'redis', null, 'sqs'], + ['indexing', 'sqs', 'redis', 'indexing', 'sqs'], + ]; + } +}