are you using artisan serve? Its single threaded.
Maybe you have queues configured to sync
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
I'm working on setting up a Job to import movies from the TMDB API (they can take some time and I don't want to interrupt the user). I can successfully dispatch the job, warn the user if they try to start a job with the same ID, but while the Job is in "RUNNING" status, I literally cannot do anything on 127.0.0.1 ðŸ˜
The code that is causing the issue:
<?php
namespace App\Jobs;
use App\Models\Movie;
use App\Services\CreditsService;
use App\Services\TmdbApiService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class ImportMovieJob implements ShouldQueue, ShouldBeUnique
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private string $type;
private string $id;
public function __construct(
// These can be used in `handle` and are created with the Job
string $type,
string $id
) {
$this->type = $type;
$this->id = $id;
}
/* @noinspection PhpUnused */
public function uniqueId(): string
{
return $this->type.':'.$this->id;
}
public function uniqueFor(): int
{
return 120; // Lock for 120 seconds
}
/**
* @param TmdbApiService $tmdb
* @param CreditsService $creditsService
* @return void
*/
public function handle(TmdbApiService $tmdb, CreditsService $creditsService): void
{
// TODO: What happens if $id is invalid?
$movieDetails = $tmdb->movieDetails($this->id);
$credits = $tmdb->movieCredits($this->id);
$genres = $movieDetails['genres'];
// Create the movie record in the movies table
// Use a DB::transaction due to the complexity, and we want to have Eloquent automatically rollback
// if something goes wrong
DB::transaction(function () use ($tmdb, $creditsService, $movieDetails, $credits, $genres) {
$movie = Movie::firstOrCreate([
'tmdb_id' => $movieDetails['id'],
], [
'title' => $movieDetails['title'],
'overview' => $movieDetails['overview'],
'poster_path' => $tmdb->posterUrl($movieDetails['poster_path']),
'backdrop_path' => $tmdb->backdropUrl($movieDetails['backdrop_path']),
'release_date' => $movieDetails['release_date'],
'runtime' => $movieDetails['runtime'],
'tagline' => $movieDetails['tagline'],
'tmdb_id' => $movieDetails['id'],
]);
// Loop over the genres array
// and add the records to the genres table
foreach ($genres as $genre) {
$movie->genres()->updateOrCreate([
'name' => $genre['name'],
'tmdb_id' => $genre['id'],
]);
}
// Store the cast members and their related person data
$creditsService->storeCastMembers($credits['cast'], $movie);
// Store the crew members and their related person data
$creditsService->storeCrewMembers($credits['crew'], $movie);
});
Cache::forget('movie_import_'.$this->id);
}
}
And the Controller responsible for handling the action
<?php
namespace App\Http\Controllers;
use App\Jobs\ImportMovieJob;
use App\Services\TmdbApiService;
use Cache;
use Illuminate\Http\RedirectResponse;
class TmdbController extends Controller
{
/**
* Finds or creates a movie record from the TMDb API.
*
* This method queues an ImportMovieJob to fetch extended details and store them
* in the database in the background.
*
* The method will redirect the user to the `/movies` page.
*
* @param string $type
* @param string $id
* @return RedirectResponse
* @uses ImportMovieJob
*
*/
public function findOrCreate(TmdbApiService $tmdb, string $type, string $id):
RedirectResponse {
// Check if the type is 'movie'
if ($type === 'movie') {
// Validate that the given ID is actually valid
$movieDetails = $tmdb->movieDetails($id);
if (!$movieDetails) {
// If the ID is invalid, don't enqueue the job
return redirect()->route('movies.index')->with('error', 'Movie not found.');
}
$lock = Cache::lock('movie_import_lock'.$id, 10); // Lock for 10 seconds
if ($lock->get()) {
// Check if the job requested is already in the jobs queue to be run
if (Cache::has('movie_import_'.$id)) {
$lock->release();
return redirect()->route('movies.index')->with('warning',
'Movie import job already in progress with this job ID. Please wait for the import to complete.');
}
Cache::put('movie_import_'.$id, true, 600); // Cache for 10 minutes
// Dispatch the job
ImportMovieJob::dispatch($type, $id);
// Release the lock
$lock->release();
// Return a quick response
return redirect()->route('movies.index')->with('status', 'Movie added to queue. Check back shortly!');
} else {
// If the lock cannot be acquired, return a warning
return redirect()->route('movies.index')->with('error',
'Unable to process request. Please try again later.');
}
}
}
}
My understanding of queues and locks and this stuff is rudimentary at best. I understand the concept, but can't put it into practice really ever. I need some help understanding what is causing this complete deadlock here. Thanks!
Please or to participate in this conversation.