# Task System RSpade provides a unified task execution system supporting immediate CLI execution, queued async tasks, and scheduled cron-based tasks. ## Creating Task Services Task services must extend `Rsx_Service_Abstract` and use attributes to define tasks: ```php use App\RSpade\Core\Service\Rsx_Service_Abstract; use App\RSpade\Core\Task\Task_Instance; class My_Service extends Rsx_Service_Abstract { // Simple task (can be run via CLI or dispatched to queue) #[Task('Description of what this task does')] public static function my_task(Task_Instance $task, array $params = []) { $task->info('Task is running'); // Task logic here return ['result' => 'success']; } // Scheduled task (runs automatically via cron) #[Task('Cleanup old records')] #[Schedule('0 2 * * *')] // Daily at 2 AM public static function cleanup(Task_Instance $task, array $params = []) { $task->info('Cleanup running'); // Cleanup logic here } } ``` ## Requirements - Class MUST extend `Rsx_Service_Abstract` - Method MUST be `public static` - Method MUST have signature: `(Task_Instance $task, array $params = [])` - Method MUST have `#[Task('description')]` attribute - Scheduled tasks also need `#[Schedule('cron_expression')]` attribute ## Important Notes on Attributes - Attributes work via reflection - NO backing PHP classes needed - The linter will remove `use` statements for attributes - this is INTENTIONAL and CORRECT - Use `#[Task]` and `#[Schedule]`, NOT `#[Task_Attribute]` or any other variation - Never create PHP classes for these attributes ## Task_Instance Methods The `$task` parameter provides logging and progress tracking: ```php public static function process_items(Task_Instance $task, array $params = []) { $items = Item_Model::all(); $total = count($items); $task->info("Processing {$total} items"); foreach ($items as $index => $item) { // Update progress $task->progress($index + 1, $total); // Log info $task->info("Processing item {$item->id}"); // Log warnings if ($item->status === 'pending') { $task->warning("Item {$item->id} is still pending"); } // Process item $item->process(); } $task->info("Completed processing {$total} items"); return ['processed' => $total]; } ``` ## Running Tasks ### CLI Execution ```bash # List all available tasks php artisan rsx:task:list # Run task immediately from CLI (synchronous) php artisan rsx:task:run Service_Class method_name # Run with parameters php artisan rsx:task:run Service_Class method_name '{"param":"value"}' ``` ### Programmatic Dispatch ```php // Dispatch task to queue (async) use App\RSpade\Core\Task\Task; Task::dispatch('Service_Class', 'method_name', ['param' => 'value']); // Dispatch with delay Task::dispatch('Service_Class', 'method_name', ['param' => 'value'], 60); // 60 second delay ``` ### Scheduled Execution Tasks with `#[Schedule]` attribute run automatically via cron. Add to crontab: ```cron * * * * * cd /var/www/html && php artisan rsx:task:process ``` This runs every minute and: 1. Processes any queued tasks 2. Runs scheduled tasks that are due ## Cron Expression Syntax ``` ┌───────────── minute (0 - 59) │ ┌───────────── hour (0 - 23) │ │ ┌───────────── day of month (1 - 31) │ │ │ ┌───────────── month (1 - 12) │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday) │ │ │ │ │ │ │ │ │ │ * * * * * ``` Common patterns: - `*/5 * * * *` - Every 5 minutes - `*/30 * * * *` - Every 30 minutes - `0 * * * *` - Every hour - `0 2 * * *` - Daily at 2 AM - `0 */6 * * *` - Every 6 hours - `0 0 * * 0` - Weekly on Sunday at midnight - `0 0 1 * *` - Monthly on the 1st at midnight - `30 2 * * 1-5` - Weekdays at 2:30 AM ## Task Queue Tasks dispatched via `Task::dispatch()` are stored in the database queue: ```sql task_queue ├── id (bigint) ├── service_class (varchar) ├── method_name (varchar) ├── parameters (json) ├── status (enum: pending, processing, completed, failed) ├── attempts (int) ├── run_at (timestamp) ├── started_at (timestamp) ├── completed_at (timestamp) ├── error_message (text) └── timestamps ``` ## Error Handling Tasks that throw exceptions: 1. Are marked as `failed` in the queue 2. Error message is logged 3. Can be retried manually via CLI 4. Maximum 3 automatic retry attempts ```php public static function risky_task(Task_Instance $task, array $params = []) { try { // Risky operation $result = External_API::call(); } catch (Exception $e) { $task->error("API call failed: " . $e->getMessage()); throw $e; // Re-throw to mark task as failed } return $result; } ``` ## Best Practices 1. **Keep tasks idempotent** - Safe to run multiple times 2. **Log progress** for long-running tasks 3. **Return meaningful data** for debugging 4. **Use transactions** for database operations 5. **Set appropriate schedules** to avoid overlap 6. **Handle exceptions gracefully** 7. **Keep tasks focused** - one task, one purpose ## Common Task Patterns ### Cleanup Task ```php #[Task('Clean old logs')] #[Schedule('0 3 * * *')] // Daily at 3 AM public static function cleanup_logs(Task_Instance $task, array $params = []) { $cutoff = now()->subDays(30); $deleted = Log_Model::where('created_at', '<', $cutoff)->delete(); $task->info("Deleted {$deleted} old log entries"); return ['deleted' => $deleted]; } ``` ### Import Task ```php #[Task('Import data from CSV')] public static function import_csv(Task_Instance $task, array $params = []) { $file_path = $params['file_path'] ?? null; if (!$file_path || !file_exists($file_path)) { throw new Exception("File not found: {$file_path}"); } $handle = fopen($file_path, 'r'); $row_count = 0; while (($data = fgetcsv($handle)) !== false) { $row_count++; $task->progress($row_count); // Process row Model::create([ 'field1' => $data[0], 'field2' => $data[1], ]); } fclose($handle); $task->info("Imported {$row_count} rows"); return ['imported' => $row_count]; } ``` ### Report Generation ```php #[Task('Generate monthly report')] #[Schedule('0 0 1 * *')] // First day of month at midnight public static function monthly_report(Task_Instance $task, array $params = []) { $start = now()->subMonth()->startOfMonth(); $end = now()->subMonth()->endOfMonth(); $task->info("Generating report for {$start->format('F Y')}"); $data = Sales_Model::whereBetween('created_at', [$start, $end]) ->get(); $report = Report_Generator::create($data); $report->save(storage_path("reports/monthly-{$start->format('Y-m')}.pdf")); $task->info("Report generated successfully"); return [ 'period' => $start->format('F Y'), 'records' => count($data), ]; } ``` ## Monitoring Tasks View task history and status: ```bash # View pending tasks php artisan rsx:task:pending # View failed tasks php artisan rsx:task:failed # Retry failed task php artisan rsx:task:retry {task_id} # Clear completed tasks older than 30 days php artisan rsx:task:clear --days=30 ``` ## Service Discovery The manifest system automatically discovers all services extending `Rsx_Service_Abstract` in directories configured for scanning. Ensure your service directory is included in the manifest's `scan_directories` configuration.