Özet: Laravel 11, kullanıcının HTTP yanıtını aldıktan sonra çalışacak defer() özelliğini tanıttı. Kuyruk, görev sınıfları veya işçi gerekmeksizin, sadece fire-and-forget mantığındaki kod parçacıklarınızı defer() içine almanız yeterli. Bu sayede API’niz anında daha hızlı hale geliyor.
Geçen yıl, sipariş API son noktamızın neden 1.2 saniye yanıt süresine sahip olduğunu anlamak için iki gün harcadım. Siparişin kendisi yaklaşık 80 milisaniyede oluşturuluyordu. Peki geri kalan zaman nereye gidiyordu?
Anlaşılan, bir onay e-postası gönderiyorduk, analiz olayını takip ediyorduk, üçüncü taraf bir hizmetle envanter senkronizasyonu yapıyorduk ve bir önbellek anahtarını temizliyorduk. Bütün bunlar, yanıt kullanıcıya gönderilmeden senkron olarak gerçekleşiyordu.
Kullanıcının, siparişini onaylamadan önce bu işlemlerin tamamlanmasını umursamadığını anlamıştım. Sadece siparişinin geçip gitmesini istiyordu.
Eski Yol: Her Şey İçin Kuyruk
Eski Yol: Her Şey İçin Kuyruk
Tipik tavsiye, bu görevleri bir kuyruğa itmek. Bir görev sınıfı oluştur, ilet, bir kuyruk işçisi çalıştır, izleme kur, başarısız görevleri yönet. Karmaşık arka plan işlemesi olan büyük uygulamalar için bu mantıklı.
Ancak e-posta göndermek veya bir olay takip etmek gibi basit fire-and-forget görevler için? Bu, basit olan bir şey için oldukça fazla altyapı. Uygulamamızda 14 farklı görev sınıfımız vardı. Bunların sekizi, sadece küçük bir şeyi gerçekleştiren tek yöntemli sınıflardı. Her biri kendi dosyasına, testine ve başarısız görevler tablosunda kendi girişine sahipti. Bu aşırı göründü.
defer() Özelliği
defer() Özelliği
Laravel 11, defer() özelliğini ekledi ve arka plan görevleri hakkında düşünme şeklimi değiştirdi. İşte nasıl çalıştığı:
Route::post('/order', function () {
$order = Order::create($data);
defer(fn() => Mail::send(new OrderConfirmation($order)));
defer(fn() => Analytics::track('order_placed', $order));
defer(fn() => InventorySync::push($order));
defer(fn() => Cache::forget("user:{$order->user_id}:cart"));
return response()->json($order);
});Peki yanıt, sipariş oluşturulduktan hemen sonra kullanıcıya geri döner. Ardından Laravel, yanıt gönderildikten sonra tüm defer edilmiş geri çağırmaları çalıştırır. Kullanıcı bunlar için beklemez.
Hiçbir görev sınıfı yok. Hiçbir kuyruk işçisi yok. Hiçbir Redis veya veritabanı kuyruk sürücüsü yok. Sadece yanıt sonrası çalıştırılan bir closure.
defer() ve Kuyrukları Ne Zaman Kullanmalı?
defer() ve Kuyrukları Ne Zaman Kullanmalı?
Bu kısım, pek çok makalenin yanlış anladığı yerdir. “Her şey için defer kullanın” veya “her zaman kuyrukları kullanın” diyorlar. Gerçek daha nuanslıdır.
Basit bir görev ise ve tekrar denemeye ihtiyaç duymuyorsa defer() kullanın. Bildirim e-postası göndermek, bir analiz olayını takip etmek, bir önbelleği temizlemek, bir etkinliği kaydetmek gibi. Eğer başarısız olursa otomatik olarak tekrar denemeye ihtiyacınız yoktur.
Karmaşık bir görev ise veya güvenilirlik garantilerine ihtiyaç duyuyorsa kuyrukları kullanın. Ödeme işlemek, büyük bir PDF oluşturmak, binlerce kaydı bir harici API ile senkronize etmek gibi. Eğer başarısız olursa bunu bilmeniz ve tekrar denemeniz gerekir.
Şirket içindeki başlangıçta webhook teslimatı için defer() kullanma hatasını yaptım. Webhook hedefi güvenilmezdi ve teslimatların yaklaşık %10’u sessizce başarısız oldu. Hiçbir tekrar mekanizması, başarısız görev kaydı yoktu; bunu fark etmenin bir yolu yoktu. Bunu tekrar üç kez denemeyle kuyruğa aldım ve sorun çözüldü.
Gerçek Sayılar
Gerçek Sayılar
Kurumsal sipariş son noktamızı, kritik olmayan görevler için defer() kullanarak yeniden yapılandırdıktan sonra:
- Yanıt süresi 1.2 saniyeden 280 milisaniyeye düştü
- Kullanıcı algılanan performansı dramatik şekilde iyileşti
- 8 adet tek yöntemli görev sınıfını sildik
- Kuyruk işçisi yükü, daha az işin yönlendirilmesi nedeniyle azaldı
E-posta hala gönderiliyor. Analiz olayı hala takip ediliyor. Önbellek hala temizleniyor. Kullanıcı sadece bunlar için beklemiyor.
Her Projede Kullanılan Bir Desen
Her Projede Kullanılan Bir Desen
Yaygın istek düzeyindeki görevleri defer eden basit bir middleware oluşturdum:
class DeferCommonTasks
{
public function handle($request, Closure $next)
{
$response = $next($request);
defer(fn() => ActivityLog::record($request));
defer(fn() => MetricsCollector::trackRequest($request, $response));
return $response;
}
}Her istek otomatik olarak etkinliği günlüğe kaydeder ve yanıt gecikmesi olmadan metrikleri takip eder. Middleware bir kez çalışır ve her uç nokta bundan faydalanır.
Farklı Yapabileceklerim
Farklı Yapabileceklerim
Başlangıçta, açık bir kural koyardım: eğer görev tekrar deneme gerekmiyorsa ve 5 saniyeden az sürüyorsa ve hızlı bir yanıt istiyorsanız defer() kullanmalısınız. Eğer tekrar deneme gerekiyorsa veya uzun sürüyorsa kuyruk kullanmalısınız. Bu kuralın erken belirlenmesi, sonunda elimizde olan 14 gereksiz görev sınıfına sahip olmamızı önlerdi.
Ayrıca, defer() işlemini veritabanı işlemleri içinde kullanmam. Eğer işlem geri alınırsa, defer edilen geri çağırma hala çalışır çünkü yanıt sonrası gerçekleşir. Bu, siparişlerin kaydedilmediği durumlarda sipariş onay e-postalarını göndermemize neden olan bir hata yarattı. Bunu zor yoldan öğrendim.
Nihai Sonuç
Nihai Sonuç
defer() kuyrukların yerini almaz. Yerini, yalnızca yanıt sonrası “çalışması gereken” bir şey olduğundan mevcut olan küçük görev sınıflarının kadar alır.
Bir satır kod. Hiçbir altyapı değişikliği yok. Ölçülebilir şekilde daha hızlı yanıtlar.
Laravel 11 veya daha yenisiniz ve hala defer() kullanmıyorsanız, muhtemelen kullanıcılarınızı bekletiyorsunuz. Laravel uygulamalarınızdaki en basit performans kazanımı ne oldu?
Kaynak: Orijinal Makale


