Yüksek Trafikli SaaS’teki Sessiz Hata
<p>Kurumsal düzeyde B2B platformları geliştiren bir tam yığın geliştirici olarak karşılaşabileceğiniz en tehlikeli hatalardan biri yarış koşulu (race condition) hatasıdır. Bu hata nadiren yerel geliştirme sırasında ortaya çıkar, ancak platformunuz ölçeklendikçe ve çoklu kullanıcılar aynı verilerle etkileşime girmeye başladığında sorunlar ortaya çıkar.</p>
<p>Bir envanter yönetim sistemi veya SaaS uygulamasındaki ortak bir ekip cüzdanını düşünün. Kullanıcı A ve Kullanıcı B, cüzdanda yalnızca 50 $ kaldığında her ikisi de aynı milisaniyede 50 $ düşürmeye çalışırsa, kontrolcünüz muhtemelen her iki istekte de 50 $ bakiyesini okuyacak, her iki isteği de onaylayacak ve veritabanınızı negatif, bozulmuş bir durumda bırakacaktır. Finansal veya endüstriyel uygulamalarda bu felaketicidir.</p>
<h2>Çözüm: Veritabanı Düzeyinde Kilitleme</h2>
<p>Standart Eloquent güncellemeleri, eşzamanlı yazım anormalliklerini önlemek için yeterli değildir. Veritabanı düzeyinde sıkı veri bütünlüğünü sağlamak için <strong>Pessimistic Locking</strong> tekniğini uygulamalıyız. Bu teknik, PostgreSQL (veya MySQL) veritabanı satırını okuma işlemi tamamlanana kadar fiziksel olarak kilitlemesini söyler. O satırı okumaya veya güncellemeye çalışan başka bir isteğe kuyrukta beklemeye zorlanır.</p>
<h3>Laravel'de `lockForUpdate()` Uygulaması</h3>
<p>Laravel, pesimist kilitleme için oldukça zarif bir sarıcı sunar. Bunun için <code>lockForUpdate()</code> yöntemini kullanmalısınız. Ancak, bu yöntemi yalnızca bir veritabanı işleminde kullanmalısınız.</p>
<pre><code>namespace App\Services;
use App\Models\TenantWallet;
use Illuminate\Support\Facades\DB;
use Exception;
class BillingService
{
/
Eşzamanlı yarış koşullarını önleyerek fonları güvenli bir şekilde düşürün.
*/
public function chargeWallet(int $tenantId, float $amountToDeduct)
{
// 1. Bunu bir işlem içinde sarmalamalıyız. Kilit, işlem tamamlandığında serbest bırakılır.
return DB::transaction(function () use ($tenantId, $amountToDeduct) {// 2. Cüzdanı al ve PostgreSQL'de satırı kilitle. // Bu satıra mevcut olan diğer istekler burada DURACAK. $wallet = TenantWallet::where('tenant_id', $tenantId)->lockForUpdate()->first(); if (!$wallet) { throw new Exception("Cüzdan bulunamadı."); } // 3. İş mantığımızı güvenli bir şekilde gerçekleştirin $wallet->balance -= $amountToDeduct; $wallet->save(); return $wallet;}); // Kilit burada otomatik olarak serbest bırakılır.
}
}
<h2>Kilit Zaman Aşımının Yönetimi</h2>
<p>İşlem çok uzun sürerse (<code>lockForUpdate()</code> güçlüdür, ancak örneğin, dışarıda bir API araması yapılıyorsa), diğer istekler kuyrukta bekleyecek ve sonunda zaman aşımına uğrayarak bir darboğaza neden olacaktır. Pesimist kilitlemenin altın kuralı, işlemi mümkün olduğunca hızlı tutmaktır. Dış HTTP isteklerini veya ağır işleme süreçlerini kilitli bir veritabanı işlemine koymaktan kaçının. Mantığı önceden hesaplayın, satırı kilitleyin, güncellemeyi uygulayın ve serbest bırakın.</p>
<h2>Sonuç</h2>
<p>Para, envanter veya paylaşılan endüstriyel varlıkları yöneten yazılımlar geliştirirken, PHP'nin eşzamanlılığı yönetmesini bekleyemezsiniz. Sistemlerinizi, veritabanı motorunuzun sağlam kilitleme mekanizmalarına dayandırmalısınız. Laravel’in pesimist kilitlemesini kullanmak, B2B SaaS'inizin her zaman verilerin bütünlüğünü korumasını sağlar, trafiğin yükü ne olursa olsun.</p>Kaynak: Orijinal Makale


