Dört Saniye Bekleyen Bir Spinner
Dört Saniye Bekleyen Bir Spinner
Kullanıcı kayıt formunu doldurup “Hesap Oluştur” butonuna tıkladığında… bekler. Bir. İki. Üç. Dört saniye, buton yükleniyor.
Burada, veritabanına yapılan INSERT işlemi 12 milisaniye sürüyor. Geriye kalan süre, e-posta sunucusunun yanıt vermesiyle geçiyor. Sonra bir CRM çağrısı yapılıyor ve hoş geldin PDF’si üretiliyor.
Kullanıcı bunun hiçbirini bilmiyor. Tek bildiği, sitenin yavaş olduğu. Eğer bağlantı bu sırada koparsa, butona tekrar basıyor — ve şimdi iki kayıt elde ediyorsun.
Yani, şu soru önemli: Kullanıcının gerçekten beklemesi gerekiyor mu?
Çoğu durumda, hayır.
Pratikte Sorun
Pratikte Sorun
Bu controller muhtemelen projenizde başka bir isimle var:
public function store(CriarContaRequest $request)
{
$user = User::create($request->validated());
// 2s (eğer SMTP iyi bir ruh halinde ise)
Mail::to($user)->send(new BoasVindas($user));
// 1,5s ve sizin olmayan bir API'ye bağlı
$this->crm->sincronizar($user);
// daha 1s, kimse bu PDF'yi açmayacak
$this->gerarPdfDeBoasVindas($user);
return redirect()->route('dashboard');
}
Dört işlem. Tek acil olan birincisi.
Ve bir detay daha var: Eğer CRM çevrimdışıysa, kayıt tamamen sorun çıkartır. Kullanıcı, veritabanında zaten oluşturulmuş bir hesapla karşılaşır ve 500 hatası alır. Başarıyı, üçüncü bir sistemin kullanılabilirliğine bağlamış oluyorsunuz.
Kavram: Şimdi mi, Sonra mı?
Kavram: Şimdi mi, Sonra mı?
Queue, bazı işlemlerin bekletildiği bir görev listesi (veritabanı, Redis, SQS) içerir; bu görevler, ayrı bir worker tarafından alınıp yürütülür.
Hatırlamanız gereken kural şudur: Eğer kullanıcı, sonucu bir sonraki ekranda görmek zorunda değilse, o iş kuyruğa gidebilir.
E-posta, bildirim, harici sistemle senkronizasyon, rapor oluşturma, resim yeniden boyutlandırma, webhook işlemleri — bunların hepsi “sonra” yapılması gerekenler. O anda kullanıcıya gösterilecek kaydı oluşturmak “şimdi” yapılması gereken.
Çözüm: Her Görev İçin Bir Job
Çözüm: Her Görev İçin Bir Job
php artisan make:job EnviarEmailDeBoasVindas
namespace App\Jobs;
use App\Models\User;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class EnviarEmailDeBoasVindas implements ShouldQueue
{
use Queueable;
// 3 kez denemek için
public int $tries = 3;
// 10sn, sonra 30sn, sonra 60sn bekle
public array $backoff = [10, 30, 60];
public function __construct(public User $user) {}
public function handle(): void
{
Mail::to($this->user)->send(new BoasVindas($this->user));
}
}
implements ShouldQueue bu işin anahtarıdır. Onu kullanmazsan, iş hemen koşar, bu durumda sorun giderilmez.
Control geri hızlanır:
public function store(CriarContaRequest $request)
{
$user = User::create($request->validated());
EnviarEmailDeBoasVindas::dispatch($user);
SincronizarComCrm::dispatch($user);
GerarPdfDeBoasVindas::dispatch($user);
return redirect()->route();
}
Üç dispatch sadece birkaç milisaniye sürüyor — bu, yalnızca joblar tablosuna bir INSERT. Kullanıcı neredeyse anında dashboard’a yönlendiriliyor ve ağır işler arka planda gerçekleşiyor.
Daha da iyi: Eğer CRM çevrimdışı olursa, sadece o job başarısız olur. Kayıt sağlam durmaya devam eder.
Çalıştırma
Çalıştırma
Başlamak için, database sürücüsü iyi bir çözüm sağlar (yük arttığında Redis’e geçiş yapabilirsiniz). .env dosyasında:
QUEUE_CONNECTION=database
Job tablosu, Laravel’ın standart migration dosyasında gelir. Eğer projeniz eskiyse ve yoksa:
php artisan make:queue-table
php artisan migrate
Ve ardından worker’ı başlatıyorsunuz:
php artisan queue:work
Üretimde, bu komutun birinin onu izlediğinden emin olmalısınız — Supervisor, systemd veya kullanıyorsanız Horizon. İşçi kapanırsa ve kimse yeniden başlatmazsa, kuyruğun sessizce dolması kaçınılmazdır.
Tuzaklar (Her biri En Az Bir Kez Pahalıdır)
Tuzaklar (Her biri En Az Bir Kez Pahalıdır)
1. Job, nesneyi değil, ID’yi saklar. Queueable trait’i, modelin yalnızca birincil anahtarını seri hale getirir ve çalıştırma anında tekrar veritabanında getirilir. Bu harika — veri asla eski olmaz — ama yan etkisi var: dispatch ile yürütme arasında kayıt silinirse, job ModelNotFoundException hatası verir. Bu nedenle standart olarak model geçmek daha iyidir; veriyi ücretsiz olarak yeni elde edersiniz.
2. Deploy işlemi queue:restart olmadan hiçbir değişiklik yapmaz. Worker, kodunuzu sunucuya yüklediğinizde bellekte saklar ve çalışmaya devam eder. Job’da bir hatayı düzelttiniz, üretime yüklediniz ama hala eski davranış sergilemekte? İşte bu. Deploy script’inizin sonuna şunu ekleyin:
php artisan queue:restart
Herkes bunu yaşar. Bir kez.
3. İş başarısız oldu ve kimse görmedi. Deneme sayısı bittiğinde, job failed_jobs tablosuna gider ve hayatınızdan çıkar. Eğer bu tabloya hiç bakmadıysanız, hiçbir e-posta gönderilmiyor ve bunu bilmiyorsunuz. Hataları yönetin:
public function failed(\Throwable $e): void
{
Log::error("Kullanıcıya hoş geldin e-postası gönderilemedi: {$this->user->id}", [
'hata' => $e->getMessage(),
]);
}
Eğer tablonuz yoksa: php artisan make:queue-failed-table. Hataları düzelttikten sonra yeniden işlemek için: php artisan queue:retry all.
4. Transaction içinde Dispatch. Eğer job’u commit‘ten önce dispatch ederseniz, worker çok hızlı çalışabilir ve henüz var olmayan bir kaydı almaya çalışır. afterCommit() ile düzeltebilirsiniz:
EnviarEmailDeBoasVindas::dispatch($user)->afterCommit();
Ya da 'after_commit' => true parametresini config/queue.php içindeki bağlantıya verebilirsiniz.
Bonus: Eğer Bir Worker Tutmak İstemiyorsam?
Bonus: Eğer Bir Worker Tutmak İstemiyorsam?
Her proje için Supervisor üzerinde çalışacak bir altyapı yok. Bu tür durumlar için, Laravel 12, işleri kolaylaştıran iki bağlantı sundu:
RegistrarAcesso::dispatch($user)->onConnection();
deferred job’u aynı işlemde çalıştırır, ama HTTP yanıtı tarayıcıya gönderdikten sonra. background ise ayrı bir PHP sürecini başlatır. Her iki durumda da kullanıcı beklemez — ve hiçbir işçiye ihtiyaç duymazsınız.
Gerçek bir kuyruk sistemi ile değiştiremez (güçlü bir yeniden deneme mekanizması yok, izleme yok, hacmi kaldırmaz), ama sadece e-postayı request’in yolundan çıkarmak isteyen küçük bir proje için oldukça kullanışlıdır.
Sekmeyi Kapatmadan Önce
Sekmeyi Kapatmadan Önce
Projenizdeki en yavaş controller’ı açın ve her satırı okuyun; “Kullanıcı bunu sonraki ekranda görmek zorunda mı?” diye sorun.
Her “hayır” bir dispatch olarak bekliyor. Ve muhtemelen butonda yaklaşık 2 saniye daha az bekleme süresi. ⚡
Peki, bugüne kadar queue:restart olmadan deploy yapmayı denediniz mi yoksa hala bu zevki yaşamak mı üzeresiniz? Yorumlarda yazın — ve eğer projenizde failed_jobs biriken tozlanmışsa, şimdi oraya bir göz atmanın tam zamanı.
Kaynak: Orijinal Makale


