spatie/laravel-long-running-tasks
Monitor and manage external long-running tasks (e.g., AWS Rekognition jobs) in Laravel by polling for status. Define a task with a check() method, store metadata, and keep checking at a configurable interval until completion.
Installation:
composer require spatie/laravel-long-running-tasks
php artisan vendor:publish --tag="long-running-tasks-migrations"
php artisan migrate
php artisan vendor:publish --tag="long-running-tasks-config"
Define a Task Class:
Create a class extending Spatie\LongRunningTasks\LongRunningTask with a check() method:
use Spatie\LongRunningTasks\LongRunningTask;
use Spatie\LongRunningTasks\Enums\TaskResult;
class ProcessVideoTask extends LongRunningTask
{
public function check($logItem): TaskResult
{
$status = $this->pollExternalService($logItem->meta['video_id']);
return $status === 'completed' ? TaskResult::StopChecking : TaskResult::ContinueChecking;
}
}
Start the Task:
ProcessVideoTask::make()->meta(['video_id' => 123])->start();
Monitor an external API (e.g., AWS Rekognition) that requires polling for task completion. Use the check() method to poll the API and return TaskResult::StopChecking when done.
Task Definition:
Extend LongRunningTask and implement check(). Use $logItem->meta to access task-specific data.
Task Execution:
// Start with metadata
MyTask::make()->meta(['key' => 'value'])->start();
// Or inline
MyTask::make()->start(['key' => 'value']);
Polling Logic:
public function check($logItem): TaskResult
{
$response = $this->callExternalService($logItem->meta);
if ($response->isComplete()) {
return TaskResult::StopChecking;
}
return TaskResult::ContinueChecking;
}
Queue Management:
Use onQueue() to route tasks to specific queues (e.g., high-priority queues for time-sensitive tasks):
MyTask::make()->onQueue('high')->start();
Dynamic Check Intervals:
Override checkFrequencyInSeconds for tasks needing variable polling:
class MyTask extends LongRunningTask
{
public int $checkFrequencyInSeconds = 30; // 30-second interval
}
Backoff Strategies:
Use StandardBackoffCheckStrategy for exponential delays:
MyTask::make()
->checkStrategy(\Spatie\LongRunningTasks\Strategies\StandardBackoffCheckStrategy::class)
->start();
Event-Driven Extensions:
Listen to LongRunningTaskStarted, LongRunningTaskCompleted, or LongRunningTaskFailed events for logging/alerts:
use Spatie\LongRunningTasks\Events\LongRunningTaskCompleted;
LongRunningTaskCompleted::listen(function ($logItem) {
Log::info("Task {$logItem->task} completed");
});
Task Metadata:
Use meta to pass structured data (e.g., IDs, configs) to the task:
MyTask::make()->meta(['user_id' => 1, 'options' => ['resize' => true]])->start();
Error Handling:
Implement onFail() to retry or log failures:
public function onFail($logItem, Exception $e): ?TaskResult
{
Log::error("Task failed: " . $e->getMessage());
return TaskResult::ContinueChecking; // Retry
}
Custom Models:
Extend LongRunningTaskLogItem for additional fields:
class CustomTaskLog extends LongRunningTaskLogItem
{
protected $casts = ['custom_field' => 'string'];
}
Update config/long-running-tasks.php:
'log_model' => App\Models\CustomTaskLog::class,
Queue Workers:
php artisan queue:work) will halt task polling.Infinite Loops:
running state due to misconfigured keepCheckingForInSeconds (default: 300s).60 * 60 for 1 hour) or override per task:
MyTask::make()->keepCheckingForInseconds(60 * 60)->start();
Meta Data Serialization:
meta may not serialize correctly.->meta(['data' => json_encode($complexObject)])
Race Conditions:
meta or state.check() for critical operations.Strategy Misconfiguration:
ExponentialBackoffCheckStrategy) may cause excessive delays.php artisan queue:work --once.LongRunningTaskLogItem::where('status', 'running')->get() to inspect stuck tasks.check() to test onFail():
throw new \Exception("Simulated failure");
php artisan queue:failed and retry manually if needed.Custom Jobs:
Extend RunLongRunningTaskJob to add pre/post-processing:
namespace App\Jobs;
use Spatie\LongRunningTasks\Jobs\RunLongRunningTaskJob as BaseJob;
class CustomTaskJob extends BaseJob
{
protected function handle()
{
// Custom logic before/after task execution
parent::handle();
}
}
Update config:
'task_job' => App\Jobs\CustomTaskJob::class,
Webhooks:
Combine with spatie/laravel-webhooks to trigger tasks via external events:
Webhook::route('video-processed', function (Payload $payload) {
ProcessVideoTask::make()->meta($payload->data)->start();
});
Rate Limiting:
Use spatie/laravel-rate-limiting to throttle polling:
RateLimiter::for('task-polling')->allow(10)->every(60)->response(function () {
return TaskResult::StopChecking;
});
default in config) exists in .env:
QUEUE_CONNECTION=database
last_check_started_at/ended_at use the app’s timezone. Set explicitly in config/app.php if needed:
'timezone' => 'UTC',
long_running_task_log_items for large-scale use:
Schema::table('long_running_task_log_items', function (Blueprint $table) {
$table->index('status');
$table->index('task');
});
How can I help you explore Laravel packages today?