Preventing Command Conflicts with Laravel's Isolatable Interface
Last updated on by Harris Raftopoulos
Laravel's Isolatable interface ensures exclusive command execution, preventing multiple instances from running simultaneously and protecting against resource conflicts or data corruption during critical operations.
The Isolatable interface automatically manages command locking, making it ideal for long-running processes, data synchronization, or system maintenance tasks:
<?php namespace App\Console\Commands; use Illuminate\Console\Command;use Illuminate\Contracts\Console\Isolatable; class SyncInventoryCommand extends Command implements Isolatable{ protected $signature = 'inventory:sync'; protected $description = 'Synchronize inventory with external systems'; public function handle() { $this->info('Synchronizing inventory data...'); }}
Laravel automatically adds the --isolated option when you implement this interface, enabling exclusive execution mode.
php artisan inventory:sync --isolated
Here's a comprehensive backup system that demonstrates isolation with progress tracking and error handling:
<?php namespace App\Console\Commands; use Illuminate\Console\Command;use Illuminate\Contracts\Console\Isolatable;use App\Services\DatabaseBackupService;use App\Services\FileBackupService;use App\Services\NotificationService;use Exception; class SystemBackupCommand extends Command implements Isolatable{ protected $signature = 'backup:system {--type=full : Backup type (full, incremental, database)} {--compress : Compress backup files} {--verify : Verify backup integrity}'; protected $description = 'Create comprehensive system backup'; protected $databaseService; protected $fileService; protected $notificationService; public function __construct( DatabaseBackupService $databaseService, FileBackupService $fileService, NotificationService $notificationService ) { parent::__construct(); $this->databaseService = $databaseService; $this->fileService = $fileService; $this->notificationService = $notificationService; } public function handle() { $startTime = now(); $backupType = $this->option('type'); $shouldCompress = $this->option('compress'); $shouldVerify = $this->option('verify'); $this->info("Starting {$backupType} backup at {$startTime->format('Y-m-d H:i:s')}"); try { $backupResults = $this->performBackup($backupType, $shouldCompress); if ($shouldVerify) { $this->info('Verifying backup integrity...'); $this->verifyBackupIntegrity($backupResults); } $duration = now()->diffInMinutes($startTime); $this->info("Backup completed successfully in {$duration} minutes"); $this->notificationService->sendBackupSuccess($backupResults, $duration); return Command::SUCCESS; } catch (Exception $e) { $this->error("Backup failed: {$e->getMessage()}"); $this->notificationService->sendBackupFailure($e); return Command::FAILURE; } } private function performBackup($type, $compress) { $results = []; if (in_array($type, ['full', 'database'])) { $this->info('Backing up databases...'); $results['database'] = $this->databaseService->createBackup([ 'compress' => $compress, 'include_structure' => true, 'include_data' => true ]); $this->info('Database backup completed'); } if (in_array($type, ['full', 'incremental'])) { $this->info('Backing up application files...'); $fileOptions = [ 'compress' => $compress, 'incremental' => $type === 'incremental', 'exclude_patterns' => [ 'storage/logs/*', 'storage/cache/*', 'node_modules/*', '.git/*' ] ]; $results['files'] = $this->fileService->createBackup($fileOptions); $this->info('File backup completed'); } $this->info('Cleaning up old backups...'); $this->cleanupOldBackups(); return $results; } private function verifyBackupIntegrity($backupResults) { foreach ($backupResults as $backupType => $result) { $this->line("Verifying {$backupType} backup..."); $isValid = match($backupType) { 'database' => $this->databaseService->verifyBackup($result['path']), 'files' => $this->fileService->verifyBackup($result['path']), default => false }; if ($isValid) { $this->info("✓ {$backupType} backup verification passed"); } else { throw new Exception("Backup verification failed for {$backupType}"); } } } private function cleanupOldBackups() { $retentionDays = config('backup.retention_days', 30); $cutoffDate = now()->subDays($retentionDays); $deletedCount = $this->fileService->cleanupOldBackups($cutoffDate); $this->info("Cleaned up {$deletedCount} old backup files"); }} class ReportGenerationCommand extends Command implements Isolatable{ protected $signature = 'reports:generate {type : Report type to generate} {--period=monthly : Time period for the report} {--format=pdf : Output format (pdf, excel, csv)} {--email= : Email address to send the report}'; protected $description = 'Generate comprehensive business reports'; public function handle() { $reportType = $this->argument('type'); $period = $this->option('period'); $format = $this->option('format'); $emailRecipient = $this->option('email'); $this->info("Generating {$reportType} report for {$period} period..."); $reportData = $this->gatherReportData($reportType, $period); $reportPath = $this->generateReport($reportData, $format); $this->info("Report generated: {$reportPath}"); if ($emailRecipient) { $this->emailReport($reportPath, $emailRecipient); $this->info("Report emailed to {$emailRecipient}"); } return Command::SUCCESS; } private function gatherReportData($type, $period) { $this->info('Collecting report data...'); return match($type) { 'sales' => $this->getSalesData($period), 'user-activity' => $this->getUserActivityData($period), 'financial' => $this->getFinancialData($period), default => throw new Exception("Unknown report type: {$type}") }; } private function generateReport($data, $format) { $this->info("Formatting report as {$format}..."); $reportGenerator = app("App\\Services\\ReportGenerators\\{$format}Generator"); return $reportGenerator->generate($data); } private function emailReport($reportPath, $recipient) { $this->info('Sending report via email...'); Mail::to($recipient)->send(new ReportGeneratedMail($reportPath)); }}
Custom exit codes provide specific feedback when isolation prevents execution:
php artisan backup:system --isolated=42
The Isolatable interface ensures command safety in production environments, preventing resource conflicts and maintaining data integrity during critical operations.