Laravel Queue Sistemi: Job, Worker ve Hata Yönetimi
Laravel Queue sistemi, kullanıcıyı bekletmemesi gereken e-posta, dosya işleme, rapor üretme ve dış API çağrısı gibi işleri arka planda yürütür. Ancak yalnızca bir job oluşturup dispatch() çağırmak üretim ortamı için yeterli değildir. Bağlantı sürücüsü, worker süreci, tekrar deneme sınırı, idempotency, başarısız iş kaydı ve deployment adımı birlikte tasarlanmalıdır.
Bu rehber, Laravel’in resmi Queue belgelerindeki güncel davranışları temel alarak veritabanı veya Redis bağlantısından job sınıfına, Supervisor yapılandırmasından hata incelemeye kadar uygulanabilir bir yol sunar. Örneklerde gerçek müşteri verisi ya da doğrulanmamış performans iddiası kullanılmaz; her ayarın neden gerektiği ve hangi riski azalttığı açıklanır.
Laravel Queue sistemi nasıl çalışır?
Bir HTTP isteği içindeki ağır işi doğrudan çalıştırmak, yanıt süresini uzatır ve uzak servis geçici olarak durduğunda kullanıcı işlemini başarısız gösterebilir. Queue kullanıldığında uygulama önce yapılacak işi bir mesaj olarak seçilen kuyruğa yazar. Ayrı çalışan worker bu mesajı alır, job sınıfının handle() metodunu yürütür ve sonuç başarılıysa işi kuyruktan siler.

Connection, queue ve worker farkı
Connection, işlerin nerede tutulacağını belirleyen database, Redis, SQS veya sync gibi bağlantıdır. Queue, aynı bağlantı içindeki default, emails ya da reports gibi mantıksal kanaldır. Worker ise belirli bağlantı ve kuyrukları dinleyen uzun ömürlü PHP sürecidir. Bu üç kavramı ayırmak, “job kuyruğa yazıldı ama çalışmadı” sorununu teşhis etmeyi kolaylaştırır.
sync bağlantısı işi gerçekten arka plana taşımaz; job aynı PHP isteğinde çalışır. Yerel geliştirmede hızlı doğrulama için yararlı olsa da üretim davranışını temsil etmez. Gerçek asenkron akışı test etmek için database, Redis veya kullanılan bulut kuyruğu ile çalışan bir worker gerekir.
Queue bağlantısını ve veri tabanını hazırlama
Küçük bir projede ek servis kurmadan başlamak için database bağlantısı anlaşılır bir seçenektir. Yüksek iş hacmi, gelişmiş kuyruk önceliği veya Horizon izleme ekranı gerekiyorsa Redis daha uygun olabilir. Horizon yalnızca Redis tabanlı kuyruklarla çalışır. Seçimi “hangisi daha hızlı?” sorusuyla değil; iş hacmi, operasyon ekibinin yetkinliği, yedekleme ve gözlemleme ihtiyacıyla yapın.
QUEUE_CONNECTION=database
php artisan make:queue-table
php artisan migrate
php artisan make:queue-failed-table
php artisan migrate
Yeni Laravel kurulumlarında gerekli migration dosyaları zaten bulunabilir. Komutu çalıştırmadan önce database/migrations dizinini kontrol etmek, aynı tablo için ikinci migration üretme riskini önler. Yapılandırma önbelleği kullanılıyorsa .env değişikliğinden sonra php artisan config:clear veya deployment akışındaki uygun config cache komutu uygulanmalıdır.
Job’a aktarılacak veriyi kuyruğa göndermeden önce doğrulamak gerekir. Form Request kullanımı ve yalnızca doğrulanmış alanların seçilmesi için Laravel validation rehberine bakabilirsiniz. Model ilişkilerini gereksiz büyüklükte serialize etmemek için de Eloquent ilişkileri rehberi yardımcı olur.
Job oluşturma ve güvenli biçimde dispatch etme
Job sınıfını Artisan ile oluşturun ve sınıfın ShouldQueue sözleşmesini uyguladığını doğrulayın. İş mantığını controller içine veya job constructor’ına yığmak yerine handle() içinde, gerektiğinde ayrı bir service sınıfı üzerinden çalıştırın. Constructor, kuyruğa yazılacak kimlikleri ve küçük değer nesnelerini taşımak için uygundur.
php artisan make:job GenerateMonthlyReport
<?php
namespace App\Jobs;
use App\Services\ReportService;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class GenerateMonthlyReport implements ShouldQueue
{
use Queueable;
public int $tries = 3;
public int $timeout = 120;
public function __construct(
public readonly int $accountId,
public readonly string $period
) {
$this->onQueue('reports');
}
public function backoff(): array
{
return [10, 60, 300];
}
public function handle(ReportService $reports): void
{
$reports->generate($this->accountId, $this->period);
}
}
Job, service container üzerinden bağımlılık alabilir. Böylece rapor üretme kuralı job sınıfından ayrılır ve HTTP isteği, CLI komutu ya da test tarafından tekrar kullanılabilir. Dispatch işlemi ise doğrulama ve yetki kontrolü tamamlandıktan sonra yapılmalıdır.
GenerateMonthlyReport::dispatch(
accountId: $account->id,
period: $request->validated('period')
)->onConnection('database')
->onQueue('reports');

Veri tabanı işlemi tamamlandıktan sonra dispatch
Job bir transaction içinden gönderilirse worker, transaction commit edilmeden önce çalışabilir. Bu durumda henüz veri tabanında görünmeyen bir modeli arayıp hata verebilir. Bağlantının after_commit ayarını kullanmak veya job üzerinde afterCommit() çağırmak, işin başarılı commit sonrasında kuyruğa bırakılmasını sağlar.
GenerateMonthlyReport::dispatch($account->id, $period)
->afterCommit();
Tekrarlanan işleri idempotent ve benzersiz tasarlama
Queue sistemleri “bir job kesinlikle yalnızca bir kez çalışır” varsayımıyla tasarlanmamalıdır. Worker timeout olduğunda, bağlantı kesildiğinde veya retry_after süresi yanlış ayarlandığında aynı iş yeniden alınabilir. Job ikinci kez çalıştığında ödeme, e-posta ya da rapor kaydını çoğaltmıyorsa idempotent kabul edilir.
Veri tabanında benzersiz anahtar kullanmak, işlem sonucunu iş anahtarıyla kaydetmek ve dış API destekliyorsa idempotency key göndermek sağlam yöntemlerdir. Yalnızca cache kilidine güvenmek yerine kalıcı veri kuralını da uygulayın. Aynı hesap ve dönem için birden fazla rapor job’ının kuyruğa girmesini önlemek gerekiyorsa ShouldBeUnique kullanılabilir.
use Illuminate\Contracts\Queue\ShouldBeUnique;
class GenerateMonthlyReport implements ShouldQueue, ShouldBeUnique
{
public int $uniqueFor = 3600;
public function uniqueId(): string
{
return $this->accountId.':'.$this->period;
}
}
Benzersiz job kilitleri cache altyapısını kullanır ve batch içindeki job’lara uygulanmaz. Birden fazla uygulama sunucusu varsa hepsinin aynı merkezi cache’i kullandığını doğrulayın. Dağıtık servis sınırlarında tekrar işleme riskini ayrıca ele almak için mikroservis mimarisine geçiş rehberindeki veri sahipliği ve mesajlaşma ilkeleriyle birlikte değerlendirme yapın.
Retry, backoff ve timeout ayarlarını birlikte kurma
$tries toplam deneme sayısını, backoff() denemeler arasındaki gecikmeyi, $timeout ise tek bir çalışmanın en uzun süresini sınırlar. Ağ hatasında hemen art arda yeniden denemek uzak servisi daha fazla zorlayabilir. Artan gecikme dizisi, geçici sorunlara toparlanma alanı verir.
retry_after ve timeout sıralaması
config/queue.php içindeki retry_after, işin başka bir worker tarafından yeniden alınmadan önce ne kadar süre görünmez kalacağını belirler. Worker veya job timeout değeri, retry_after süresinden birkaç saniye kısa olmalıdır. Tersi durumda ilk worker hâlâ çalışırken aynı job ikinci kez işlenebilir. Supervisor’ın stopwaitsecs değeri de en uzun job’ın tamamlanmasına izin verecek kadar büyük olmalıdır.
Süreye bağlı deneme sınırı
Sabit deneme sayısı yerine belirli bir zamana kadar yeniden denemek gerekiyorsa retryUntil() metodu kullanılabilir. Kalıcı doğrulama hatalarını tekrar denemeyin; bunları açıkça başarısız bırakın. Geçici bağlantı hataları ve rate limit yanıtları ise kontrollü backoff ile yeniden denenebilir.
public function retryUntil(): \DateTime
{
return now()->addMinutes(20);
}
Başarısız job kayıtlarını inceleme ve tekrar çalıştırma
Deneme sınırı dolan işler failed_jobs tablosuna yazılabilir. Bu tabloyu yalnızca hata arşivi olarak bırakmayın; exception türü, başarısızlık zamanı, bağlantı ve queue adı için izleme oluşturun. Job sınıfındaki failed(?Throwable $exception) metodu bildirim veya temizlik başlatabilir. Laravel bu metodu çağırmadan önce job’ın yeni bir örneğini oluşturduğu için handle() sırasında bellekte değiştirilen özelliklere güvenilmemelidir.
php artisan queue:failed
php artisan queue:retry 7
php artisan queue:retry all
php artisan queue:forget 7
php artisan queue:flush
queue:retry all veya queue:flush komutlarını otomatik refleks olarak çalıştırmayın. Önce kök nedeni düzeltin; ardından job’ın idempotent olduğunu ve eski payload’ın hâlâ geçerli olduğunu doğrulayın. Toplu silme geri alınamaz ve hata inceleme kanıtını ortadan kaldırır.
Worker’ı Supervisor ile sürekli çalıştırma
php artisan queue:work terminal kapandığında sona ererse üretim kuyruğu durur. Linux sunucuda Supervisor veya işletim sistemine uygun başka bir process monitor, worker’ı başlatmalı ve beklenmedik kapanmada yeniden çalıştırmalıdır. Aşağıdaki örnekte kullanıcı adı, PHP yolu, proje yolu ve log konumu sunucunuza göre değiştirilmelidir.
[program:laravel-reports]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/example/artisan queue:work database --queue=reports --sleep=3 --tries=3 --timeout=120
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-reports.log
stopwaitsecs=180
Yapılandırma eklendikten sonra supervisorctl reread, supervisorctl update ve ilgili program için supervisorctl start komutları uygulanır. Worker sayısını yalnızca kuyrukta bekleyen iş sayısına bakarak artırmayın; veri tabanı bağlantı limiti, uzak API rate limit’i, CPU ve bellek tüketimi de sınır oluşturur.

Deployment sırasında worker’ları güvenle yenileme
Queue worker uzun ömürlü bir süreçtir; başladıktan sonra yüklenen uygulama kodunu bellekte tutar. Yeni kod yayınlandığında worker kendiliğinden değişikliği görmez. Deployment akışında migration ve cache adımlarının uygun noktasında php artisan queue:restart çalıştırılmalıdır. Process monitor, mevcut job tamamlandıktan sonra çıkan worker’ı yeniden başlatır.
php artisan migrate --force
php artisan config:cache
php artisan queue:restart
Restart sinyali cache üzerinden iletildiği için uygulamanın cache sürücüsü ve izinleri çalışır durumda olmalıdır. Otomatik yayın akışının genel yapısı için GitHub Actions ile hosting deploy rehberindeki doğrulama ve rollback adımlarını Queue kontrolüyle genişletebilirsiniz.
Zamanlanmış komutların job dispatch etmesi gerekiyorsa cron ile worker’ın farklı süreçler olduğunu unutmayın. Cron yalnızca zamanlayıcıyı tetikler; kuyruğu tüketmez. Bu ayrım Laravel Schedule kurulum rehberinde ayrıntılı olarak açıklanır.
Horizon, loglama ve güvenlik kontrolleri
Redis kullanan projelerde Laravel Horizon; iş hacmi, çalışma süresi ve başarısızlıkları izlemek için bir panel ve kod tabanlı worker yapılandırması sağlar. Horizon panelini internete açık bırakmayın. Üretim erişimini authorization gate ile sınırlandırın, çalışan sayısını bağlantı kapasitelerine göre belirleyin ve deployment sırasında php artisan horizon:terminate kullanın.
Job payload’ına parola, API anahtarı, tam ödeme verisi veya gereksiz kişisel veri koymayın. Model serialize edilse bile payload kuyruk altyapısında ve hata kayıtlarında görülebilir. Yalnızca gereken kimlikleri taşıyın; iş sırasında yetki ve kayıt durumunu yeniden doğrulayın. Secret yönetimi, hata mesajları ve log erişimi için Laravel güvenlik kontrol listesini uygulayın.
Resmi davranışların ayrıntıları için Laravel Queue dokümantasyonunu; Redis tabanlı panel ve worker dengelemesi için Laravel Horizon dokümantasyonunu kaynak olarak kullanın. Projenizin kurulu Laravel ana sürümü farklıysa aynı sürüme ait belgeyi seçin.
Laravel Queue sorun giderme kontrol listesi
QUEUE_CONNECTIONdeğeri ile worker komutundaki connection aynı mı?- Worker doğru queue adını dinliyor mu?
- Yapılandırma önbelleği eski
.envdeğerini tutuyor mu? - Job, transaction commit edilmeden önce mi çalışıyor?
timeoutdeğeriretry_aftersüresinden kısa mı?- Process monitor worker’ı gerçekten ayakta tutuyor mu?
- Job ikinci kez çalıştığında aynı sonucu çoğaltıyor mu?
failed_jobs, uygulama logu ve Supervisor logu birlikte incelendi mi?- Deployment sonrasında
queue:restartveya Horizon içinhorizon:terminateçalıştı mı?
Seeder ve factory ile kontrollü test verisi üretip job davranışını gerçek müşteri kaydı kullanmadan sınamak için Laravel Seeder ve Factory rehberinden yararlanabilirsiniz. Testte aynı job’ı iki kez dispatch ederek idempotency kuralını ayrıca doğrulayın.
Sonuç
Sağlam bir Laravel Queue sistemi; job sınıfından daha fazlasıdır. Doğru connection ve queue seçimi, küçük payload, commit sonrası dispatch, idempotency, ölçülü retry/backoff, uyumlu timeout değerleri, sürekli çalışan worker ve gözlemlenebilir hata akışı tek bir bütün oluşturur. Bu katmanlar birlikte kurulduğunda arka plan işleri kullanıcı isteğinden ayrılırken operasyon ekibi de başarısızlığı sessizce birikmeden görebilir.
Sık Sorulan Sorular
Laravel Queue için database mi Redis mi kullanılmalı?
Database sürücüsü küçük ve orta ölçekli başlangıçlar için ek altyapı gerektirmeden çalışabilir. Yüksek hacim, daha gelişmiş kuyruk yönetimi ve Horizon ihtiyacında Redis değerlendirilebilir. Karar iş hacmi, operasyon bilgisi ve mevcut sunucu kapasitesiyle verilmelidir.
queue:work ile queue:listen arasındaki fark nedir?
queue:work uygulamayı belleğe yükleyen uzun ömürlü bir süreçtir ve üretimde process monitor ile yaygın olarak kullanılır. queue:listen her iş için uygulamayı yeniden başlattığından kod değişikliklerini görür, fakat daha düşük verimlidir. Uzun ömürlü worker’da deployment sonrası restart gerekir.
Job kuyruğa yazılıyor ama neden çalışmıyor?
En sık nedenler worker’ın çalışmaması, yanlış connection veya queue adını dinlemesi, eski config cache ve process monitor hatasıdır. Önce job’ın kuyruğa yazıldığını, sonra worker komutunu, uygulama logunu ve Supervisor logunu aynı zaman aralığında kontrol edin.
Başarısız Laravel job nasıl tekrar çalıştırılır?
php artisan queue:failed ile kayıtları inceleyip kök nedeni düzelttikten sonra php artisan queue:retry ID kullanılabilir. Tüm işleri tekrar çalıştırmadan önce payload geçerliliği ve job’ın idempotent olup olmadığı doğrulanmalıdır.
Laravel Queue worker kod değişikliğini otomatik görür mü?
Hayır. queue:work uzun ömürlü olduğu için başladığında yüklediği kodu kullanır. Deployment sırasında php artisan queue:restart çalıştırın ve worker’ın process monitor tarafından yeniden başlatıldığını doğrulayın. Horizon kullanılıyorsa karşılığı php artisan horizon:terminate komutudur.




