İki istek aynı cihaz kodunu aynı anda eklemek istesin. İkisi de önce arayıp kayıt bulamasın, ardından INSERT göndersin. Ön kontrol ikisinde de doğru çalıştığı halde tekilliği koruyamadık. Aradaki süreyi kısaltmak bu yarışı ortadan kaldırmıyor. Kuralın bütün yazıcılar için geçerli olmasını istiyorsak SQL Server’da unique index veya constraint ile de tanımlamalıyız. Uygulamanın erken geri bildirimiyle veritabanının son sınırını bu noktada ayırıyoruz.
Örnekte her organizasyon içinde cihaz kodu benzersiz olsun. İki farklı organizasyon aynı kodu kullanabilsin; aynı organizasyonda ikinci kayıt oluşturulamasın. Burada bütün request’in idempotent olmasını değil, belirli bir iş anahtarının tekilliğini ele alıyoruz. Aynı kodla tekrar gelen isteğin nasıl yanıtlanacağı API sözleşmesinin ayrı kararıdır. Bir unique index, dış servise gönderilmiş komutları geriye almaz.
SQL örneği SQL Server 2022 ile uygulanabilecek özellikleri kullanıyor. C# tarafı .NET 8 ve Microsoft.Data.SqlClient üzerinden kurulabilir. TenantId uygulamanın doğrulanmış kullanıcı ve organizasyon bağlamından gelmeli. İstemcinin body içinde yazdığı herhangi bir tenantId’ye doğrudan güvenmek, benzersizlik kuralı doğru olsa bile veri kapsamını bozabilir. Yetki ile tekillik aynı kontrol değildir.
Ön kontrolün arasına başka istek girebiliyor
Tek kullanıcıda “ara, yoksa ekle” sırası düzgün görünür. İki akışta ise ikisi de aynı yok sonucunu görebilir. Kritik olan sorguyla INSERT arasında kaç milisaniye geçtiği değil, bu iki adımın hangi garanti altında yapıldığı. Testten Task.Delay’i kaldırmak da yarışın yok olduğunu göstermez. Sıralama ihtimalini kontrollü kurup ikisinin de ön kontrolden geçtiğini görmek, sorunu rastlantısal hatadan ayırmamızı sağlar.
// Tek başına bu kontrol benzersizlik garantisi vermez.
if (!await CodeExistsAsync(connection, tenantId, code, ct))
{
await InsertAsync(connection, tenantId, code, name, ct);
}
Ön kontrol tamamen gereksiz olmak zorunda değil. Kullanıcı arayüzünde daha erken geri bildirim vermek için kullanılabilir. Ancak son INSERT sırasında veritabanı kuralının ihlal edilebileceği kabul edilmeli. Kontrolün başarılı olması, sonraki yazmanın kesin başarılı olacağı sözü değildir. API hata yolunu buna göre ele almalı. Birden fazla uygulama instance’ı veya farklı bir veri yazıcısı bulunduğunda bu ayrım daha görünür hale gelir.
Process içindeki lock da bütün yazıcıları kapsamaz. Aynı uygulamanın başka instance’ı, bir batch işi veya doğrudan SQL aynı tabloya veri yazabilir. Eğer iş kuralı tablo düzeyinde geçerliyse onun kapsamını yalnızca bir metodun kilidine indirmemeliyiz. Bazı özel senaryolarda transaction veya kilitleme stratejisi gerekebilir; fakat basit benzersizlik ihtiyacında unique constraint veya index daha doğrudan bir temel sağlar.
İş anahtarını doğru kolonlarla tanımlamak
Şemada organizasyon ve normalize edilmiş kod birlikte benzersiz olacak:
CREATE TABLE dbo.DeviceRegistrations
(
Id bigint IDENTITY NOT NULL PRIMARY KEY,
TenantId uniqueidentifier NOT NULL,
NormalizedCode nvarchar(80) NOT NULL,
DisplayName nvarchar(100) NOT NULL
);
CREATE UNIQUE INDEX UX_DeviceRegistrations_Tenant_Code
ON dbo.DeviceRegistrations(TenantId, NormalizedCode);
Yalnızca NormalizedCode üzerinde unique index oluşturursak farklı organizasyonları da birbirine engel yaparız. Yalnızca Id primary key olması ise aynı iş koduyla birden fazla satır eklenmesini engellemez; her satır farklı identity değeri alabilir. Teknik kimlik ile iş anahtarı farklı amaçlara hizmet eder. Hangi kombinasyonun gerçekten benzersiz olması gerektiği ürün kuralından çıkarılmalı.
Kolonların NOT NULL olması da bilinçli. İş kuralı zorunlu kod istiyor. Opsiyonel bir serial number gibi farklı bir alan için null davranışı ayrıca düşünülmeli; SQL Server unique kurallarında null’ların ele alınışı beklentinizle aynı olmayabilir. Gerekirse filtreli unique index farklı bir sözleşme sunabilir. Bir alanın nullable olması yalnızca C# property’sinin seçimi değil, veritabanı tekillik kuralının da parçasıdır.
Normalize etmek neyi değiştirecek?
Kullanıcı kodun başına boşluk koyduğunda veya küçük harfle yazdığında aynı cihaz kodunu mu kastetmiş sayacağız? Bu bir iş kararıdır. Uygulama Trim ve ToUpperInvariant uygulayabilir; fakat veritabanının collation davranışı da karşılaştırmayı etkiler. Unicode, büyük-küçük harf ve boşluk kuralları birlikte değerlendirilmelidir. Alanın adını NormalizedCode koymak bütün bu kuralların kendiliğinden doğru uygulandığını söylemez.
public static string NormalizeCode(string? raw)
{
if (string.IsNullOrWhiteSpace(raw))
throw new ArgumentException("Cihaz kodu zorunlu.");
var value = raw.Trim().ToUpperInvariant();
if (value.Length > 80)
throw new ArgumentException("Cihaz kodu çok uzun.");
return value;
}
Bu, örneğin normalizasyon seçimidir; bütün dillerde ve bütün kod sistemlerinde doğru olduğu iddia edilmiyor. Ürün yalnızca ASCII cihaz kodlarına izin veriyorsa bu karakter kümesi ayrıca doğrulanabilir. Diğer yazıcılar aynı normalizasyonu uygulamıyorsa farklı biçimler tabloya girebilir. Bu durumda veritabanında hangi karşılaştırmanın korunduğu ve uygulamanın hangi biçimi ürettiği açıkça test edilmelidir. Tekillik ile gösterim tercihi birbirinden ayrılmalı.
Normalizasyon değişikliği mevcut veriye de etki eder. Daha önce farklı kabul edilen iki kod yeni kuralla aynı değere dönüşebilir. Sadece yeni metodu deployment’a almak geçmiş veriyi düzeltmez. Unique index kurulmadan önce çakışmaların bulunması ve ürünün kabul ettiği biçimde çözülmesi gerekebilir. Bu kararı otomatik olarak ilk satırı bırakıp diğerini sil şeklinde vermemeliyiz. Kayıtların bağlı verisi ve iş anlamı olabilir.
INSERT sonucu ve çakışma yolu
Örnekte tek INSERT’in sonucunu alıyoruz. Başarılıysa oluşan kimliği döndürüyoruz; bilinen duplicate key hata sınıfında aynı tenant ve kodun varlığını yeniden denetliyoruz. Genel bir SQL hatasını duplicate saymıyoruz. Metot, gösterilen küçük şema ve onun bilinen tekillik sözleşmesi için yazıldı. Daha fazla unique kural veya trigger eklendiğinde hata eşlemesi yeniden incelenmelidir.
public sealed record RegistrationResult(bool Created, long? Id);
public static async Task<RegistrationResult> RegisterAsync(
SqlConnection connection,
Guid tenantId,
string code,
string name,
CancellationToken ct)
{
await using var command = connection.CreateCommand();
command.CommandText = """
INSERT INTO dbo.DeviceRegistrations
(TenantId, NormalizedCode, DisplayName)
OUTPUT INSERTED.Id
VALUES (@Tenant, @Code, @Name);
""";
command.Parameters.Add("@Tenant", SqlDbType.UniqueIdentifier)
.Value = tenantId;
command.Parameters.Add("@Code", SqlDbType.NVarChar, 80)
.Value = code;
command.Parameters.Add("@Name", SqlDbType.NVarChar, 100)
.Value = name;
try
{
var id = (long)(await command.ExecuteScalarAsync(ct))!;
return new RegistrationResult(true, id);
}
catch (SqlException error)
when (error.Number is 2601 or 2627)
{
if (!await CodeExistsAsync(connection, tenantId, code, ct))
throw;
return new RegistrationResult(false, null);
}
}
Bu metoda açık bir bağlantı, normalize edilmiş ve doğrulanmış kod ile doğrulanmış ad verilmelidir. Örnekte çağrı tek INSERT’in autocommit davranışıyla ele alınıyor. Başka bir transaction içine taşındığında hata sonrası transaction durumu ve yeniden sorgu yapılabilirliği ayrıca incelenmeli. Mevcut işlem sınırını değiştirmeden kodu her yere kopyalamak doğru olmaz. Bir yardımcı metodun bağımlılıkları yalnızca parametre tiplerinden ibaret değildir.
Kodu kontrol eden metot da parametre kullanmalı:
public static async Task<bool> CodeExistsAsync(
SqlConnection connection,
Guid tenantId,
string code,
CancellationToken ct)
{
await using var command = connection.CreateCommand();
command.CommandText = """
SELECT TOP (1) 1
FROM dbo.DeviceRegistrations
WHERE TenantId = @Tenant
AND NormalizedCode = @Code;
""";
command.Parameters.Add("@Tenant", SqlDbType.UniqueIdentifier)
.Value = tenantId;
command.Parameters.Add("@Code", SqlDbType.NVarChar, 80)
.Value = code;
return await command.ExecuteScalarAsync(ct) is not null;
}
Çakışmadan sonra kayıt hemen başka bir işlem tarafından silinmiş olabilir. Kontrol artık kayıt bulamazsa asıl hatayı tekrar yükseltiyoruz; olmayan bir kaydı var diye göstermiyoruz. Daha güçlü bir ürün sözleşmesi gerekiyorsa bu yarış ayrıca ele alınabilir. Burada amaç her eşzamanlı durumu tek bir başarı sonucuna çevirmek değil, bilinen çakışmayı kontrollü biçimde ayırmak. Veri değişmeye devam ederken bütün gözlemlerin aynı anda geçerli kalacağını varsayamayız.
Çakışma başarı mı, hata mı?
Aynı kod bulunduğunda API 409 Conflict verebilir veya var olan kayda yönlendiren bir akış sunabilir. Bu seçimle, aynı HTTP isteğinin daha önce işlendiğini kanıtlamış olmuyoruz. İki kullanıcı aynı koda farklı adlar ve ayarlar göndermiş olabilir. Unique index anahtar kombinasyonunu korur; bu iki komutun eşdeğer olduğunu söylemez. Yanıtı seçerken mevcut kaydı göstermeye yetkimiz olup olmadığını ve istemcinin neyi başarmak istediğini ayrıca değerlendirmeliyiz.
Yanıtta mevcut kaydın kimliğini göstermek de kaynak yetkisiyle uyumlu olmalı. Kodun varlığını bilen herkesin başka kaydın ayrıntılarını öğrenmesi istenmeyebilir. Örnekte duplicate sonucunda kimlik döndürmüyoruz. Kullanıcı var olan kayda erişebiliyorsa ayrı yetkili sorgu yolu kullanılabilir. Hata mesajının ne kadar bilgi vereceği ürün ve güvenlik sınırlarıyla birlikte seçilmeli. Veritabanı exception metnini doğrudan HTTP response’a taşımak gerekmiyor.
Soft delete ve kodun yeniden kullanılması
Silinmiş sayılan bir cihazın kodu yeni bir kayıtta kullanılabilecek mi? Tabloda IsDeleted alanı bulunması bu soruyu otomatik yanıtlamaz. Mevcut unique index bütün satırları kapsıyorsa mantıksal olarak silinmiş kayıt da yeni eklemeyi engelleyebilir. Ürün bunu isteyebilir; örneğin kodun tarihsel kimliğini korumak önemlidir. Yeniden kullanıma izin verilecekse farklı bir veri ve indeks kuralı gerekebilir.
Filtreli unique index, belirli aktif kayıt kapsamını korumak için bir seçenek olabilir. Ancak bunun da geri yükleme ve eşzamanlı durum değişikliğiyle ilişkisi vardır. Eski kayıt tekrar aktif yapılırken aynı kodu taşıyan yeni kayıt bulunabilir. Böyle bir çatışmayı yalnızca teknik hata olarak görmek yerine ürünün nasıl çözeceği belirlenmeli. Silme ve geri alma yaşam döngüsü, benzersizlik kuralının dışında bırakılmamalıdır.
Mevcut tabloya index eklerken
Canlı tabloda duplicate değerler varsa unique index kurulamaz. Önce hangi grupların çakıştığı incelenebilir. Bu inceleme, kayıtları otomatik birleştirmek veya silmek için tek başına yetki vermez. Aynı kodu taşıyan kayıtlar farklı işlere bağlı olabilir. Düzeltme planı, bağlı veriyi ve kullanıcı beklentisini korumalı. Teknik tekillik kuralı ile veri temizliği kararı ayrı adımlardır.
İndeks oluşturmanın operasyonel maliyeti de düşünülmeli. Büyük tabloda zaman, disk ve eşzamanlı yazma etkileri ortaya çıkabilir. Kullanılabilecek oluşturma seçenekleri SQL Server sürümü ve ortam özellikleriyle doğrulanmalı. Bu yazıdaki kısa CREATE INDEX satırı her üretim ortamı için hazır deployment komutu sayılmaz. Önce test verisinde kuralın doğru olduğu, sonra gerçek veri ve kurulum koşullarında uygulanabilirliği değerlendirilmelidir.
Yarışı testte gerçekten oluşturmak
İki ayrı bağlantı açıp aynı tenant ve kod için INSERT denemek anlamlı bir entegrasyon testidir. Beklenti, tek satır oluşması ve diğer isteğin bilinen çakışma yoluna girmesidir. Sonra aynı kodun farklı tenant’ta kabul edildiği kontrol edilir. Böylece indeksin yanlışlıkla global tekillik kurmadığı görülür. Tek bağlantıda sırayla iki INSERT çalıştırmak çakışma kuralını gösterir; eşzamanlı davranışın bütününü göstermez.
Ön kontrolün yetersizliğini göstermek için iki iş de var mı sorgusundan sonra kontrollü bir bariyerde bekletilebilir. İkisi de yok sonucunu aldıktan sonra INSERT’ler bırakılır. Unique index varsa veritabanı temel sınırı korur. Bu deneme, zamanlamaya bağlı rastgele bir hata beklemekten daha tekrarlanabilir bir kanıt sağlar. Test yine ayrılmış veritabanında yapılmalı; canlı cihaz tablosunda sahte kayıtlar üretilmemeli.
Normalizasyon ve collation için ayrıca örnekler seçilmeli. Başında boşluk bulunan kod, küçük-büyük harf farkı ve ürünün desteklediği özel karakterler nasıl karşılaştırılıyor? Testte yalnızca A-1 ve A-1 kullanmak bütün iş kuralını doğrulamaz. Özellikle farklı yazıcılar varsa aynı girdinin aynı anahtara dönüştüğünü görmek gerekir. Uygulama ve veritabanı farklı eşitlik anlamları taşıyorsa beklenmeyen çakışmalar veya farklı kayıtlar oluşabilir.
Kuralın son sahibi
Arayüzdeki ön kontrol kullanıcı deneyimine yardım edebilir; uygulamadaki normalizasyon tutarlı girdi üretebilir; transaction işin daha büyük sınırını koruyabilir. Fakat tablodaki belirli anahtarın bütün yazıcılar arasında benzersiz olmasını istiyorsak bunun veritabanında da karşılığı olmalı. Bu katmanları birbirinin alternatifi gibi görmek gerekmiyor. Her biri farklı bir sorumluluğu üstlendiğinde hata yolları daha açık hale gelir.
İkinci isteğin gelmeyeceğine güvenmek yerine geldiğinde ne olacağını belirledik. Veritabanı aynı tenant ve kod için ikinci satırı reddediyor; API de bilinen çakışmayı kendi sözleşmesine çeviriyor. Ön kontrol kullanıcıya erken bilgi vermeye devam edebilir. Böylece tekillik, iki çağrının şans eseri farklı zamanlarda çalışmasına bağlı kalmaz. Yeni bir entegrasyon veya başka uygulama instance’ı eklendiğinde de aynı veritabanı kuralı uygulanır.
İş anahtarıyla teknik kimliği karıştırmamak
Unique anahtardaki kolonların null kabulü de açık olmalıdır. Eksik cihaz kodunu boş bir karşılaştırma anahtarına çevirmek, geçersiz girdiyi bir benzersizlik sorununa dönüştürebilir. Örnekte zorunlu alanlar NOT NULL tutuluyor ve giriş önceden doğrulanıyor. Böylece constraint beklenen iş tekrarını korurken, eksik veriye ilişkin kullanıcı mesajı ayrı kalıyor. Veritabanı kuralı ile giriş doğrulamasının birlikte bulunması bu iki hata türünü daha anlaşılır hale getirir.
İndeksin adı da bakım sırasında yararlı bir işaret olabilir. İsmin koruduğu kuralı açıkça anlatması migration incelemesini ve şema kontrolünü kolaylaştırır. Ancak uygulamanın ham hata metnine bağımlı olacağı bir sözleşme gibi değerlendirilmemelidir. Gözlemlenebilirlik için anlamlı isim kullanmak ile exception mesajını parse ederek iş kararı vermek farklı tercihlerdir.
Tablodaki Id alanı satırın teknik kimliğidir. Aynı cihazın aynı tenant içinde iki kez kaydedilemeyeceği kuralını ise başka kolonlar taşır. Her INSERT yeni Id ürettiği için primary key ihlali olmadan yinelenen iş kaydı oluşabilir. Bu yüzden benzersizliğin hangi alan birleşimine ait olduğunu açıkça yazıyoruz. TenantId ve NormalizedCode birlikte seçilince aynı kodun başka tenant’ta kullanılmasına izin veren ürün kuralı da şemada görünür oluyor.
Bu alanların gerçekten iş anahtarı olup olmadığı paydaşlarla doğrulanmalı. Cihaz kodu zaman içinde başka cihaza devredilebiliyor mu, aynı fiziksel cihaz farklı lokasyonda başka kod alabiliyor mu ve eski kayıtlar korunacak mı? Yanlış iş anahtarı üzerine kurulan unique index doğru çalışsa bile istenen işi engelleyebilir. Constraint’in güçlü olması, seçilen kuralın ürün açısından doğru olduğunu göstermez. Önce hangi tekrarın yasak olduğunu örneklerle tanımlamak gerekir.
Birden fazla entegrasyon kaydı yazıyorsa bütün yazıcıların aynı normalizasyonu uyguladığı düşünülmemeli. SQL script’i, veri aktarımı veya başka dilde yazılmış servis farklı davranabilir. Unique index veritabanındaki gerçek kolon değerlerine göre çalışır. Normalizasyonun uygulama dışında da korunması gerekiyorsa şema veya veri yazma sınırında uygun bir çözüm değerlendirilmelidir. Yalnızca C# helper metodunun varlığı bütün sisteme ait bir güvence değildir.
Büyük küçük harf kuralını örnekle belirlemek
Kodların AB-12 ve ab-12 biçimleri aynı cihazı mı temsil ediyor? Bu karar verildikten sonra uygulamanın normalizasyonu ve SQL Server collation davranışı birlikte kontrol edilir. Unicode karakterler, aksanlar ve kültüre bağlı dönüşümler sonucu değiştirebilir. Basit ASCII cihaz kodu sözleşmesi varsa bunu girişte daraltmak işi sadeleştirebilir. Serbest insan adı gibi bir alanı aynı kuralla dönüştürmek ise başka bir konudur. Normalizasyon kuralını veri alanının gerçek sınırlarına göre seçmek gerekir.
Trim kullanmak da bilinçli bir ürün kararı olmalıdır. Başta veya sonda boşluk taşıyan giriş kullanıcı hatası sayılıp reddedilebilir ya da normalize edilerek kabul edilebilir. Hangisi seçilirse API yanıtında ve saklanan gösterimde anlaşılır olmalı. Kullanıcı yazdığı koddan farklı bir değer görüyorsa bu değişimin nedeni açık olmalıdır. Teknik kolaylık için yapılan sessiz dönüşümler destek incelemesini zorlaştırabilir. Özgün gösterim değeri ile karşılaştırma anahtarının ayrı tutulması bazı durumlarda daha kullanışlıdır.
Normalizasyon sürümü değişirse geçmiş verinin yeni kurala göre çakışıp çakışmadığı araştırılmalı. Daha önce ayrı kabul edilen iki kod yeni kuralla aynı değere dönüşebilir. Yeni helper’ı dağıtıp yalnızca yeni kayıtları dönüştürmek iki farklı karşılaştırma anlamını aynı tabloda bırakabilir. Böyle bir değişiklik veri migration’ı olarak ele alınmalıdır. Çakışma analizi, düzeltme politikası ve geri dönüş planı olmadan yapılan toplu dönüşüm mevcut kayıtların anlamını bozabilir.
İhlali doğru API sonucuna çevirmek
2601 ve 2627 hata numaraları benzersizlik ihlaline ilişkin sinyal verir. Ancak büyük bir tabloda birden fazla unique kural bulunabilir. Her böyle hatayı cihaz zaten var yanıtına çevirmek yanlış constraint’i saklayabilir. Örnekteki dar şema ve bilinen kayıt yolu bu sınıflandırmanın sınırını belirliyor. Gerçek uygulamada hangi kuralın ihlal edildiğini güvenilir biçimde ayıran yaklaşım seçilmelidir. Ham ve yerelleştirilebilen hata metnini kırılgan string kontrolleriyle çözümlemek bakım riski taşıyabilir.
İhlal sonrasında aynı iş anahtarıyla kayıt aranıyorsa bu arama yetki kapsamını korumalı. Başka tenant’a ait satırı bulup kullanıcıya göstermek, benzersizlik kontrolünü veri sızıntısına çevirebilir. Aynı anahtarın varlığını bildirmek ile o kaydın bütün alanlarını döndürmek farklı kararlardır. API’nin hangi bilgiyi paylaşacağı baştan belirlenmelidir. Hata yanıtı için gereken küçük bilgi, tam entity’yi dışarı taşımayı gerektirmeyebilir.
İki istemci aynı kod için farklı açıklamalar gönderdiğinde de sonuç açıktır: unique index yalnızca bir satır olmasını sağlar. Kaybeden isteğin açıklaması otomatik uygulanmaz. Kullanıcı bunu güncelleme bekleyerek yaptıysa ayrı bir iş komutu gerekir. INSERT çakışmasını sessizce UPDATE’e çevirmek, mevcut kaydın kim tarafından hangi yetkiyle değiştirilebileceğini etkiler. Bir veritabanı hatasını ürün davranışına dönüştürürken bu yan etkiler düşünülmelidir.
Tekrar gelen komut ile farklı isteğin çakışması
Aynı HTTP isteğinin ağ sorunu nedeniyle tekrar gönderilmesi ile iki farklı kullanıcının aynı cihaz kodunu seçmesi aynı olay değildir. Unique index her iki durumda da ikinci satırı engelleyebilir; fakat istemciye verilecek anlam farklı olabilir. İlk durumda önceki komutun sonucunu yeniden sunmak istenebilir. İkinci durumda gerçek bir iş çakışması vardır. Bu ayrımı korumak için işlem anahtarı gibi ek sözleşmeler gerekebilir; unique iş anahtarı tek başına komut kimliği değildir.
İşlem anahtarı kullanılacaksa aynı anahtarla farklı içerik geldiğinde ne yapılacağı belirlenmeli. Aksi halde istemci yanlışlıkla eski bir sonucu yeni komutun başarısı sanabilir. Ayrıca sonucun ne kadar süre saklandığı ve yetki kapsamı da önemlidir. Burada amaç bütün sisteme idempotency katmanı eklemek değil, benzersizlikten beklenen güvenceyi doğru sınırlamaktır. Tek satır korunması ile bütün isteğin bir kez yürütülmesi aynı iddia değildir.
Commit yanıtı ulaşmadığında da aynı belirsizlik ortaya çıkar. Kayıt veritabanına yazılmış olabilir. Kullanıcı yeniden denediğinde unique ihlali alması ilk isteğin kesin başarısız olduğunu göstermez. Uygulama iş anahtarıyla güvenli bir sonuç sorgusu sunabilir; fakat bulunan kaydın gerçekten aynı niyete ait olup olmadığı değerlendirilmelidir. Sadece kodun eşitliği bazı işlerde yeterlidir, bazılarında değildir. Ürün akışı bu kararı açıkça taşımalıdır.
Var olan tabloda constraint kurulmadan önce
Yeni tabloya unique index koymak ile yıllardır veri alan tabloya eklemek farklı işlemlerdir. Önce seçilen anahtara göre yinelenen gruplar bulunmalı. Her grupta hangi satır korunacak, ilişkili kayıtlar nasıl taşınacak ve yanlış birleşme nasıl önlenecek? En küçük Id kalsın gibi otomatik kural her veri için doğru olmaz. Yinelenen kayıtlar bazen gerçekten aynı cihazı, bazen yanlış tanımlanmış iş anahtarını gösterebilir. Temizleme kararı bu ayrımı yapmalıdır.
İndeks oluşturma sırasında uygulamanın yeni tekrar üretmeye devam etmesi de ele alınmalı. Veri temizliği ile constraint’in devreye girdiği an arasında boşluk bulunursa işlem yine başarısız olabilir. Dağıtım planı yazıcıların davranışı ve veritabanının sağladığı kilit seçenekleriyle birlikte hazırlanır. Yoğun tabloda oluşturma süresi ve kaynak maliyeti test edilmelidir. Bu yazıdaki küçük şema örneği büyük üretim tablosuna kesintisiz migration garantisi vermez.
Yarış testinin kabul şartı
Yarış testinde iki bağlantıyı da “kayıt yok” sonucundan sonra bekletebiliriz. İkisini serbest bıraktığımızda bir INSERT başarılı olmalı, diğeri bilinen çakışma yoluna girmeli ve tabloda tek satır kalmalı. Ardından aynı kodun başka tenant’ta kabul edildiğini kontrol ederiz. Bu ikinci kontrol de önemli: tekrarları engellerken farklı organizasyonları gereksiz yere birbirine bağlamadığımızı gösteriyor. Rastgele zamanlama beklemek yerine kritik sırayı testin kendisi kuruyor.
Hata numarasını görmek son kontrol değildir. İşlem sonrası kayıt sayısı, saklanan anahtar ve bağımsız kaydın korunması okunur. Başarısız işlem açık transaction veya kullanılmaz bağlantı bırakmış mı diye de bakılabilir. Bu kanıtlar birlikte kuralın veritabanında korunduğunu gösterir. Uygulamadaki ön kontrol daha anlaşılır kullanıcı deneyimi sunabilir; fakat eşzamanlı yazıcılar karşısındaki son güvence seçilen unique constraint’tir. İkisini farklı amaçlarla kullanmak tasarımı sade tutar.
İlişkili işlem sınırları için Unit of Work yazısı okunabilir. SQL Server kuralları unique index belgesinde, kapsamlı hata eşlemesi için numaralar 2601 hata başvurusunda ve 2627 hata başvurusunda incelenebilir.
