Laravel’de Farklı Arkaplan Görevlerinin, İşledikleri Veriye Bağlı Olarak Eş Zamanlılığının Yönetimi

Emincan Özcan
Huawei Developers - Türkiye
3 min readDec 1, 2022
Fotograf Link

Giriş

Arkaplan işleri pek çok web projesinin ortak ihtiyacıdır. Laravel bu konuda geliştiricilere müthiş bir deneyim sağlar. İş kurallarınızın özel gereksinimleri yoksa bunları arkaplanda çalışan kuyruk görevlerine çevirmek için genellikle birkaç dakika yeterlidir.

Lakin kimi zaman iş kurallarımızın özel gereksinimleri olur ve bu gereksinimleri karşılamak için biraz daha detaylı çalışmak gerekebilir. Yakın zamanda karşılaştığım bir senaryo, 3 farklı kuyruk sınıfının, aynı model üzerinde eş zamanlı olarak çalışmamasını gerektiriyordu. Bu içerikte, bu senaryonun implementasyonu için takip ettiğim adımları paylaşacağım.

Temel Mantık

Bu tarz bir iş kuralını implemente ederken, kuyrukta T anında çalışan işlere dair kaydı bir yerde saklamamız gerekir. Bunun için Redis veya benzer bir araçtan faydalanabiliriz.

Örneğin, “process-product:$productId” gibi bir key kullanabiliriz. Herhangi bir iş çalışmaya başladığında, öncelikle bu key değerinin ilgili araçta bulunup bulunmadığını kontrol etmeli ve buna bağlı aksiyonları şu şekilde almalıdır:

  1. Eğer bulunuyorsa: İlgili iş, kendisini aynı parametrelerle tekrardan kuyruğa göndermelidir.
  2. Eğer bulunmuyorsa: İlgili iş öncelikle bu değeri yaratmalı, sonrasında işlemlerini gerçekleştirmeli ve son olarak bu değeri silmelidir.

Bu temel mantık, kullanılan programlama dili ve araçlardan bağımsız olarak koda dökülebilir. Ekstradan lock mekanizmasının ne kadar sağlıklı çalıştığı, bir iş çalışırken hata vermesi durumunda takip eden işlerin sonsuza kadar bloklanmaması için belli bir sürenin sonunda otomatik invalidasyonun sağlanması vb. detaylar ihmal edilmemelidir.

Laravel ile “Biraz Kirli” Implementasyon

Laravel’in sağlamış olduğu Redis facade’ında, throttle adlı statik bir metod bulunur. Bu metot, arkaplanda DurationLimiter sınıfının bir instance’ını yaratarak Redis üzerinde gerekli operasyonları Lua Scriptlerinden de yararlanarak gerçekleştirir. Çeşitli yerlerde ihtiyaç duyulabilen expiry date gibi ek özellikleri de içerdiği için, özellikle Redis’e de ilginiz varsa, buradaki implementasyonlara göz atmanızı öneririm. Buraya tıklayarak ilgili kod bloğuna ulaşabilirsiniz.

Lakin bu yöntemi kullanarak her iş sınıfına tek tek ilgili kodları yazmak kodun biraz kirli hale gelmesine yol açabilir. İlgili sınıfta, iş kurallarının yazılması gereken handle metodunda, işin ne zaman çalışacağına dair ciddi bir kod kalabalığı yaratmak ve bu durumun diğer sınıflarda da tekrarlanması kötü bir pratik olacaktır. Bunun için daha temiz bir implementasyonu nasıl yapabiliriz ona bir bakalım.

Laravel ile Temiz İmplementasyon

Burada Laravel, job middleware kavramıyla imdadımıza yetişiyor. Job middleware’ları, tıpkı Http requestlerinde kullandığımız middlewarelar gibi, asıl lojik çalışmadan önce çalışır ve çeşitli aksiyonlar almamızı sağlarlar. Job middleware kavramı hakkında daha detaylı bilgi için, bu adrese bir göz atabilirsiniz.

Job middleware’larından faydalanmak, ayrı bir middleware sınıfı yaratarak gerekli iş sınıflarımızda kullanabilmemizi ve bunu asıl lojikten bağımsız bir metot içerisinde saklamamızı sağlar. Bu durum yazdığımız kodu hem daha temiz hem de daha kolay test edilebilir hale getirir.

Ayrıca job overlapping’i engelleyen bir middleware, Laravel içerisinde halihazırda bulunur. İlgili dokümantasyona ulaşmak için buraya tıklayınız.

Bu middleware, varsayılan olarak job class ismini ve bir key değerini uygulamanın cache’inde saklar ve bu key üzerinden kontrolleri gerçekleştirir. Sorun şu ki; job class’ı, key’in bir parçası haline geldiğinde farklı job classları paralelde çalışmaya devam edecektir. Biz aynı model üzerinde işlem yapan 3 farklı iş sınıfının eş zamanlılığını yönetmek istiyorduk. Hedefimize ulaşmak için takip edeceğimiz yol, kullanılan Laravel sürümüne göre 2'ye ayrılıyor.

Laravel 9.32 ve sonrası:

11 Eylül 2022 tarihindeki şu committen yararlanabiliriz.

Tek yapmamız gereken, ilgili middleware’ı kullanırken shared metodunu da çağırmak:

Laravel 9.32 öncesi:

Önceki sürümlerde WithoutOverlapping middleware’i gelse de, shared metodu tanımlanmamış durumdadır. Bu sebeple farklı bir çözüm gerekir. Yeni bir middleware sınıfı yaratarak, WithoutOverlapping sınıfındaki getLockKey metodunu aşağıdaki şekilde override etmek bizi istediğimiz noktaya ulaştırır.

Yeni middleware sınıfı:

İş sınıfına eklenecek middleware metodu:

Sonuç

Laravel’de arkaplan işlemlerini yürütmek için sıklıkla faydalandığımız kuyruk görevleri, oldukça spesifik bir takım ihtiyaçlara da yanıt verebilmektedir. Bu içerikte paylaşılan yöntemlerden faydalanarak, birden fazla kuyruk görevinin, üzerinde çalıştığı nesneye bağlı olarak eşzamanlılığının nasıl yönetilebileceği paylaşılmıştır.

Referanslar

--

--