Problem
Problem
Arka planda bazı verileri işlerken, ilgili verileri okumak için başka bir istek yapılmış olabilir. Bu durumda ya tarihsel bir veri sağlıyorsunuz ya da hatalı bilgi sunuyorsunuz.
Çözüm
Çözüm
İşlem tamamlanana kadar isteği bekletmek, en basit çözüm olabilir. Bu benim tek çözüm önerim değil, fakat en basiti.
Bazı Senaryolar
Bazı Senaryolar
Olası senaryolar hakkında konuşalım.
Rezervasyon İşlemi
Rezervasyon İşlemi
Kullanıcı, iki belirli tarih arasında bir kaynağı rezerve etmek istediğinde, bunun bir iş aracılığıyla yapıldığını varsayalım. Üretim yükü dolayısıyla bu işlem bir süre alabilir. Bu esnada, başka bir istek kullanıcının rezervasyon verilerini sorguluyorsa, ne olmalı?
Dosya Yükleme İşlemi
Dosya Yükleme İşlemi
Kullanıcı, bir CSV veya XML dosyası yüklediğinde, dosyayı kabul ettiniz fakat işlemenin de yapılması gerekiyor, bu bir işte gerçekleşmeli. Kullanıcı, başka bir istekte CSV kaynağının durumunu sorarsa ne olacak?
Paket
Paket
aihimel/laravel-waiting-request tam olarak bunu çözen küçük bir Laravel paketidir —bu paket, bir isteğin, başka bir işlem (bir iş, senkronizasyon veya uzun süreli kontrolör eylemi) kaynağın okunmaya hazır olduğunu belirttiği ana kadar beklemesine olanak tanır.
Kurulum
Kurulum
composer require aihimel/laravel-waiting-request
Opsiyonel olarak konfigürasyonu yayınlayın:
php artisan vendor:publish --tag="waiting-request-config"
Nasıl Çalışır
Nasıl Çalışır
Paket, dört fikir etrafında küçük bir API sunar: block, wait, check, resolve. Temelde Laravel önbelleği ile çalışır — fazla bir altyapıya veya kuyruk sistemine ihtiyaç duymaz.
Bir engelleyici, bir sınıf yolu ve bir kaynak id ile tanımlanır. Bu çift, benzersiz bir önbellek anahtarı oluşturur, böylece engelleyiciler kaynak başına (rezervasyon 42 diğer rezervasyon 43 ile çakışmaz) olur.
use Aihimel\LaravelWaitingRequest\Facades\LWRequest;
// 1. Block — arka plan işinin başladığı yerde çağırın
LWRequest::addBlocker(Booking::class, $booking->id);
// 2. Wait — kaynağı okumak isteyen istekte çağırın
$resolved = LWRequest::whenResolved(Booking::class, $booking->id);
if ($resolved) {
return BookingResource::make($booking->fresh());
}
return response()->json(['message' => 'Hala işleniyor, tekrar deneyin'], 202);
// 3. Resolve — arka plan işi bittiğinde çağırın
LWRequest::resolveBlocker(Booking::class, $booking->id);
Ayrıca beklemeden göz atabilirsiniz:
if (LWRequest::isBlocked(Booking::class, $booking->id)) {
// kaynak işleniyor
}
Senaryolara Uygulanması
Senaryolara Uygulanması
Rezervasyon işlemi. Rezervasyonu kabul eden kontrolör, addBlocker(Booking::class, $id) çağırarak işi başlatır. İş, handle() içinde resolveBlocker(...) çağrısını yapar. Bu süreçte GET /bookings/{id} isteğini yapan herhangi bir okuyucu ilk olarak whenResolved(...) çağrısını yapar ve yalnızca yazıcı işlemi tamamlandıktan sonra modeli okur.
Dosya yükleme işlemi. Aynı şekilde: yükleme kabul edildiğinde addBlocker(Import::class, $import->id) çağrılır, ayrıştırıcı tamamlandığında resolveBlocker(...) çağrılır (başarı veya başarısızlık — her ikisi de serbest kalmalıdır). Durum uç noktası whenResolved(...) çağrısını yapar, böylece istemci yarım kalmış bir görüntü yerine belirlenmiş bir cevap alır.
Ayarlanabilir Mantıklı Varsayılan Değerler
Ayarlanabilir Mantıklı Varsayılan Değerler
Her ayar config/waiting-request.php dosyasında bulunur ve .env dosyası üzerinden değiştirilebilir:
| Konfigürasyon | Env | Varsayılan | Açıklama |
|---|---|---|---|
cache_prefix | LW_REQUEST_CACHE_PREFIX | lw_request_ | Önbellek anahtarları için ad alanı |
timeout | LW_REQUEST_MAX_WAITING_TIME | 5 | whenResolved()‘ın pes etmeden önce beklediği süre (saniye) |
check_interval | LW_REQUEST_CHECK_INTERVAL | 250 | whenResolved() içindeki anlık kontrol süresi (milisaniye) |
max_blocking_time | LW_REQUEST_MAX_BLOCKING_TIME | 10 | Bir engelleyicinin otomatik olarak süresinin dolması için maksimum süre (saniye) |
addBlocker() isteğe bağlı bir üçüncü argüman alır, böylece belirli bir işin daha uzun sürdüğü zaman TTL’yi artırabilirsiniz:
LWRequest::addBlocker(Import::class, $import->id, 120); // 2 dakika
Neden Engelleyicinin Bir Süresi Var?
Neden Engelleyicinin Bir Süresi Var?
Bir iş resolveBlocker() çağırmadan çöküyorsa, okuyucuların sonsuza kadar beklemesini istemezsiniz. Versiyon v2.x’den itibaren her engelleyici bir Unix zaman damgası taşır. O zaman damgasından sonraki isBlocked() / whenResolved() çağrısı:
- Önbellek girdisini unutacaktır, ve
Log::warning('Waiting-request blocker expired without being resolved', [...])kaydını çıkaracaktır.
Bu nedenle, işiniz çökse bile, trafik kendiliğinden toparlanır ve bunun olduğu konusunda bir kayıt alırsınız.
Yapılması ve Yapılmaması Gerekenler
Yapılması ve Yapılmaması Gerekenler
Yapılacaklar
Yapılacaklar
- Engelleyiciyi
finallyiçinde serbest bırakın. İş gövdesini sarmalayın, böylece fırlatılan bir istisnaresolveBlocker()‘a ulaşır. Otomatik süresi dolması bir yedek, hoş bir yol değildir. -
max_blocking_time‘ı en kötü senaryo iş sürenizi aşacak şekilde ayarlayın. Eğer yüklemeniz ortalama 8sn sürüyorsa ve en kötü durumda 25sn alıyorsa, 10sn’lik bir varsayılan süre işin hala devam ederken otomatik olarak serbest bırakılacaktır — bu durumu geçersiz kılar. -
timeout‘ı kullanıcı deneyimi bütçenize uygun şekilde ayarlayın. Eğer bir istemci senkronize cevap için 2sn beklemeyi kabul ediyorsa,timeout=2ayarlayın;whenResolved()‘ın bir HTTP işçisini 30sn bağlamasına izin vermeyin. - 1.x’den 2.x’e güncellerken önbelleği temizleyin. Önceki değerler
trueolarak saklandığında, bunlar1olarak okunacak ve zaten süresi dolmuş olarak işlenecek, bu nedenle bir kerelik uyarı günlükleri üretecektir. - Bir
falsedöndüğündewhenResolved()‘i “henüz bekliyor” olarak değerlendirin. Bununla birlikte202 Accepted(veya benzeri) olarak yanıt verin ve istemcinin sorgulamasına izin verin — verilerin hazır olduğunu iddia etmeyin.
Yapılmaması Gerekenler
Yapılmaması Gerekenler
- Beklenmeyen
isBlocked()çağrılarını yüksek, yalnızca okuma yollarında yapmayın. Bu, süresi dolmuş öğeleri geçersiz kılar ve bir günlük kaydı oluşturur. Bu kasıtlıdır, ancak bilinmesi gerekir. - Yazmalar için dağıtılmış bir mutex olarak kullanmayın. Bu paket, yazıcılara bağlı okuyucular için en iyi çaba ile hazırlanmıştır. İki yazar aynı anda sahneye çıkarsa,
Cache::add()ikinciaddBlocker()çağrısını reddedecektir (bufalsedöner), ancak paket kuyruklama, adalet veya katı karşılıklı hariç tutma sağlamaz. - İlgisiz kaynaklar arasında tek bir engelleyici paylaşmayın. Gerçek kaynakla anahtarlandırın (
Booking::class + $id), kullanıcı kimliği gibi kaba bir yapı ile değil, yoksa hiçbir ilgisi olmayan istekleri engelleyebilirsiniz. - Önbellek sürücüsünün önemli olduğunu unutmayın.
arrayveyafilesürücüleri süreçler arasında çalışmaz. Üretimde, hem engelleyiciyi çözen işçi hem de bekleyen web işleminin aynı önbelleği paylaşması içinredis/memcachedkullanın. - Bir kuyruk işçisinden
whenResolved()ile beklemeye güvenmeyin. Bir işçi içinde anket yapmak, bir işçi slotunu harcar. İşçiler, engelleyicileri çözmeli, bunlar üzerinde beklememelidir.
Paketin tamamı — birkaç facade çağrısı, her kaynak için bir önbellek anahtarı ve kimsenin sıkışmasına neden olmayacak mantıklı bir süresi ile birlikte. Eğer daha önce bir ?retry=true hilesi ya da bir kontrolörde bekleyip dua ettiyseniz, bu, onun daha temiz versiyonudur.
Kaynak & sorunlar: github.com/aihimel/laravel-waiting-request
Kaynak: Orijinal Makale


