diff --git a/app/Jobs/ProcessFleetArrival.php b/app/Jobs/ProcessFleetArrival.php index 3e271359f..cedb837c2 100644 --- a/app/Jobs/ProcessFleetArrival.php +++ b/app/Jobs/ProcessFleetArrival.php @@ -19,9 +19,18 @@ class ProcessFleetArrival implements ShouldQueue use Queueable; use SerializesModels; - public int $tries = 3; - - public int $timeout = 120; + /** + * Allow several lock-contention retries while a large battle holds the + * destination lock (release delay is 30s; see handle()). + */ + public int $tries = 10; + + /** + * Must be >= the destination Cache::lock TTL (600s) so a long battle cannot + * be killed by the queue worker while still holding the lock. + * Issue #363 noted multi-million-unit battles can take 10s+; leave headroom. + */ + public int $timeout = 600; public function __construct(public int $missionId) { diff --git a/app/Services/FleetMissionService.php b/app/Services/FleetMissionService.php index 8cd567d53..2637e9f46 100644 --- a/app/Services/FleetMissionService.php +++ b/app/Services/FleetMissionService.php @@ -713,10 +713,9 @@ public function processDueMissionEventsForMission(FleetMission $mission): void { $lockKey = $this->getMissionDestinationLockKey($mission); - // Lock TTL of 180s provides headroom for long-running battles. - // A TTL shorter than the actual processing time risks expiring mid-transaction, - // allowing another worker to enter and observe uncommitted writes. - Cache::lock($lockKey, 180)->block(10, function () use ($mission) { + // Lock TTL must stay >= ProcessFleetArrival::$timeout so a long battle cannot + // outlive the lock and let another worker observe uncommitted writes. + Cache::lock($lockKey, 600)->block(10, function () use ($mission) { DB::transaction(function () use ($mission) { $currentTime = (int) Date::now()->timestamp; diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 410df2ad1..b7ee4a866 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -27,7 +27,9 @@ if [ "$role" = "scheduler" ]; then sleep 60 done elif [ "$role" = "queue" ]; then - php /var/www/artisan queue:work --queue=fleet-arrivals,default --verbose --no-interaction + # --sleep=0.5 keeps delayed fleet arrivals snappy (default sleep is 3s). + # --timeout=600 matches ProcessFleetArrival::$timeout for large battles. + php /var/www/artisan queue:work --queue=fleet-arrivals,default --sleep=0.5 --timeout=600 --verbose --no-interaction elif [ "$role" = "reverb" ]; then php /var/www/artisan reverb:start --host="${REVERB_SERVER_HOST:-0.0.0.0}" --port="${REVERB_SERVER_PORT:-8090}" elif [ "$role" = "app" ]; then