Kayıp Güncelleme Problemi
<p>Birden fazla kiracıya sahip bir B2B SaaS platformu geliştirirken, eş zamanlı istekler arasında veri doğruluğu hayati önem taşımaktadır. İki kiracının tam aynı anda aynı veritabanı kaynağını değiştirmeye çalışması durumunda klasik bir veri bütünlüğü zayıflığı olan Kayıp Güncelleme yarış durumu meydana gelir.</p>
<p>Destek Temsilcisi A, bir faturayı açıp bir not eklemek için işlem yapıyor. Bir saniye sonra, Destek Temsilcisi B, tam aynı faturayı açıp fatura durumunu değiştirmek için işlem yapıyor. Temsilci B, durumu güncelleyerek kaydet butonuna tıklıyor. Bir süre sonra Temsilci A kaydet butonuna bastığında, tarayıcısı faturanın daha eski bir anlık görüntüsünü tutuyordu, bu nedenle kaydetme işlemi tüm satırı üzerine yazarak Temsilci B'nin yaptığı durum güncellemesini tamamen silmiş olur. Temsilci B'nin değişiklikleri kalıcı olarak kaybolur ve veritabanında hiçbir iz bırakmaz. Bunun çözümü, satır versiyonlaması uygulamaktır.</p>
<h2>İhtiyatlı ve İyimser Kilitlenme</h2>
<p>Yarış durumlarını çözmek için iki felsefeyi kullanabiliriz:</p>
<ul>
<li>
<strong>İhtiyatlı Kilitlenme (<code>lockForUpdate()</code>):</strong> Veritabanı, Temsilci A'nın okumaya başladığı an itibariyle satırı açıkça kilitler ve Temsilci B'nin bir sırada beklemesini zorunlu kılar. Bu yöntem güvenli ancak yüksek okuma hacimlerinde performansı düşürür ve sık sık veritabanı deadlock'larına yol açar.</li>
<li>
<strong>İyimser Kilitlenme:</strong> Okuma sırasında satırı kilitlemeyiz. Bunun yerine, satıra ardışık bir <code>version</code> tamsayı sütunu atanır. Güncelleme yaparken, versiyonun okuduğumuz anlık görüntü ile uyumlu olup olmadığını kontrol ederiz. Eğer uyumluysa, veriyi kaydederiz ve versiyonu artırırız. Uyumlu değilse güncelleme işlemini durdururuz ve veri bozulmasını önleriz.</li>
</ul>
<h3>Adım 1: Veritabanı Migration’ı</h3>
<p>İyimser kilitlenme uygulamak için, her eş zamanlı hassas tabloda bir tamsayı <code>version</code> sütunu eklenmeli ve varsayılan olarak <code>1</code> olarak ayarlanmalıdır.</p>
<pre><code>use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddVersionToInvoicesTable extends Migration
{
public function up(): void
{
Schema::table(‘invoices’, function (Blueprint $table) {
// Satır anlık görüntüsünün artan versiyonunu takip et
$table->unsignedInteger(‘version’)->default(1);
});
}
}
<h3>Adım 2: Laravel’de Versiyon Kontrolünün Uygulanması</h3>
<p>Standart model kaydetme işlemlerini atlayarak, ham veritabanı koşulları veya açık işlem sorguları aracılığıyla katı bir atomik doğrulama güncellemesi uygularız.</p>
<pre><code>namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Invoice;
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\ModelNotFoundException;
class ConcurrentInvoiceController extends Controller
{
public function update(Request $request, string $id)
{
// 1. Fatura kaydını al ve mevcut versiyon damgasını kaydet
$invoice = Invoice::findOrFail($id);
$currentVersion = $invoice->version;
// 2. İş mantığı atamaları gerçekleştir
$invoice->amount = $request->input('amount');
$invoice->status = $request->input('status');
// 3. Atomik bir kontrol-ve-kaydet güncelleme durumu gerçekleştir
// Güncellemeyi yalnızca veritabanındaki sürüm $currentVersion ile EŞLEŞİYORSA gerçekleştir
$updatedRows = Invoice::where('id', $id)
->where('version', $currentVersion)
->update([
'amount' => $invoice->amount,
'status' => $invoice->status,
'version' => $currentVersion + 1, // Versiyonu atomik olarak artır
'updated_at' => now(),
]);
// 4. Eğer sıfır satır güncellenmişse, başkası bu arada veriyi değiştirmiş demektir!
if ($updatedRows === 0) {
return response()->json([
'error' => 'Çatışma tespit edildi.',
'message' => 'Bu kaynak başka bir kullanıcı tarafından güncellendi. Lütfen yenileyin ve tekrar deneyin.'
], 409); // 409 Çatışma durum kodu döndür
}
return response()->json(['success' => true, 'version' => $currentVersion + 1]);
}}
<h2>Mühendislik ROI'si</h2>
<p>İyimser kilitlenme, eş zamanlı güncellemeler nedeniyle oluşan veri bozulmasını tamamen ortadan kaldırırken, fiziksel veritabanı düzeyindeki satır kilitlenmelerinin performans cezasını üstlenmeden çalışır. Okuma yollarınızı tamamen bloke etmeden tutar, işlem deadlock'ları olasılığını ortadan kaldırır ve ön uç uygulama katmanlarınız için net, programatik olarak yönetilebilir durum çatışma çözümü stratejileri sunar.</p>Kaynak: Orijinal Makale


