İki sekme aynı kaydı açtı. A başlığı değiştirip kaydetti, B eski ekranından farklı metin gönderdi. Son yazanın sessizce kazanması uygulamayı basit gösterebilir ama A'nın çalışmasını kaybettirir. Portfolio'da kullandığım revizyon yaklaşımının özü, “hangi sürümü düzenledin?” bilgisini yazma koşulunun parçası yapmaktır.
Okuduğum sürüm hâlâ geçerli mi?
Aşağıdaki PostgreSQL örneği geçici tablo kullanır; uygulamanın tablolarına dokunmaz. payload alanı yalnız gösterim için jsonb. Portfolio'nun gerçek kalıcı içeriği ilişkisel tablolardadır; aynı revizyon fikri birden fazla tabloyu kapsayan kayıt işleminde de kullanılabilir.
CREATE TEMP TABLE revision_demo(
id integer PRIMARY KEY, revision integer NOT NULL, payload jsonb NOT NULL);
INSERT INTO revision_demo VALUES(1,1,'{"title":"first"}');
UPDATE revision_demo SET payload='{"title":"tab-a"}', revision=revision+1
WHERE id=1 AND revision=1 RETURNING revision;
UPDATE revision_demo SET payload='{"title":"tab-b"}', revision=revision+1
WHERE id=1 AND revision=1 RETURNING revision;
SELECT payload->>'title',revision FROM revision_demo;
İlk UPDATE revision=1 koşulunu sağlar, başlığı tab-a yapar ve sürümü 2'ye çıkarır. İkinci UPDATE hâlâ 1 beklediği için hiçbir satır güncellemez. Son SELECT tab-a ve 2 döndürür. İkinci adımın sıfır satır değiştirmesi bir SQL sözdizimi hatası değil, uygulamanın anlamlandırması gereken çakışma sonucudur.
Kontrol ile yazmayı aynı SQL kararına koymak
Önce SELECT ile sürümü okuyup sonra yalnız Id ile UPDATE yapmak arada yarış penceresi bırakır. Revizyon koşulu doğrudan UPDATE içinde olduğunda değiştirme kararı atomik hale gelir. Aynı kayda gelen güncellemelerde PostgreSQL'in kilitleme ve izolasyon davranışı bu koşulla birlikte değerlendirilir; uygulama kendi SELECT sonucuna güvenerek yarışı ortadan kaldırmış olmaz.
RETURNING, başarılı yazmanın yeni sürümünü geri verir. İstemci sonraki değişiklikte onu göndermeli. Sunucu başarısız bir koşulda eski veriyi “kaydedildi” diye geri döndürürse görünürde düzgün çalışan ama kalıcı olmayan seçimler oluşabilir. Başarı bildirimi gerçek commit sonucuna bağlı olmalı.
Sürümün kapsamını veri sözleşmesi seçer
Her kayıt ayrı revizyon taşıyabilir veya tek bir düzenleme belgesinin bütününe revizyon verilebilir. Portfolio yönetim paneli profil, proje ve yazı değişikliklerini tek Kaydet ile gönderdiği için belge seviyesinde koruma kullanıyor. Bunun bedeli, iki kişinin farklı bölümleri düzenlese de çakışabilmesi. Faydası, bütün düzenleme setinin birlikte kabul veya ret edilmesidir.
Bir kayıt birden çok tabloya dağılıyorsa revizyon artışıyla ilişkili yazmalar aynı transaction içinde tamamlanmalı. Sürüm güncellenip alt tablo yazısı başarısız olursa yeni sürüm başarıyı temsil etmez. Veritabanı commit'i ile istemciye verilen yanıtın birlikte düşünülmesi gerekir.
409 sonrası kullanıcının yazdığını korumak
Eski sürümle gelen isteği otomatik olarak en yeni sürüme çevirip yeniden göndermem. Bu, korumayı atlayıp yine son yazan kazansın demek olabilir. İstemci taslağı saklar, güncel kaydı ayrı okuyabilir ve kullanıcıya yeniden karşılaştırma imkânı verir. Bağlantı hatasında da düzenlenen alanları temizlemek doğru değildir.
İstek sürerken yeni yazılan metinler ayrı bir ayrıntı. A isteği başladıktan sonra kullanıcı aynı editörde B metnini yazarsa A'nın yanıtı geldiğinde B'yi silmemeliyim. Kaydedilen snapshot ile o anki taslak ayrılır; yalnız aynı düzenleme sürümü için taslak temiz kabul edilir.
Kimlik, yetki ve sürüm birbirinin yerine geçmez
Doğru revision değerini bilmek düzenleme izni değildir. Önce endpoint kimliği ve kaydın kapsamı doğrulanır. Silinmiş kayıt ile eski sürüm ayrımı uygulamanın bilgi sızdırmama politikasına göre yapılır. ETag/If-Match benzer bir HTTP sözleşmesi kurabilir, ancak hangi yöntemin seçildiği istemci ve sunucuda aynı olmalıdır.
Testi aynı başlangıç sürümünden iki düzenlemeyle yaparım: yalnız biri kalıcı olmalı, diğeri çakışma görmeli. Başarılı işlemden sonra tekrar GET, yayın seçimleri ve kaynak tarihleri de kontrol edilir. Otomatik yeniden deneme ancak gerçekten yeniden yürütülebilir işlem sınırında uygulanmalıdır.
PostgreSQL UPDATE ve RETURNING; PostgreSQL izolasyon davranışı.