Makale Özeti

Önceki makalede transaction kavramının ne olduğundan kısaca söz etmiş ve ADO .NET kütüphanesindeki ilgili sınıflar kullanılarak manuel transaction ların nasıl yapılandırıldığı ve yönetildiği üzerinde durmuştuk. Birincisinin devamı niteliğindeki bu makalede ise; transaction kavramını detaylandıracak ve bağlantısız veriler üzerinde yapılan kilitleme işlemlerindeki kullanımını örnekleyeceğiz.

Makale

Transaction - 2

ADO .NET TE TRANSACTION TABANLI İŞLEMLER - 2

Giriş

       Önceki makalede transaction kavramının ne olduğundan kısaca söz etmiş ve ADO .NET kütüphanesindeki ilgili sınıflar kullanılarak manuel transaction ların nasıl yapılandırıldığı ve yönetildiği üzerinde durmuştuk. Birincisinin devamı niteliğindeki bu makalede ise; transaction kavramını detaylandıracak ve bağlantısız veriler üzerinde yapılan kilitleme işlemlerindeki kullanımını örnekleyeceğiz.

ACID Kavramı

         Transaction kavramı “ACID teorisi” ile başlar. ACID kelimesinin açılımı; “Atomicity, Consistency, Isolation ve Durability” kelimelerinin baş harfleridir. Bir transaction ‘ının mantıksal tasarımında bu dört kelimenin ifade ettiği ilkelere baş vurulur. İyi tasarlanmış bir transaction bu dört ilkeye uygun olmalıdır. Aşağıdaki paragraflarda söz konusu ilkeler kısaca açıklanmaktadır;

Atomicity : Bir transaction ‘ının başarılı olabilmesi için; tanımlı alt işlemlerinin eksiksiz başarılı olması gerekir. Burada ana ilke; ya hep ya da hiçtir. Buna gerçek hayattan bir örnek vermek gerekirse; üç adet seri anahtar ile çalışması kontrol edilen bir elektrik devresinde, lambanın yanması için, üç anahtarın da kapalı olması gerekir. Anahtarlardan biri dahi açık kalsa lambanın çalışması imkansız hale gelir.

Atomicity İlkesi

Consistency (Tutarlılık) : Transaction ürettiği sonuçlar bakımından tutarlı davranmalıdır. Bunun anlamı transaction ‘ının üzerinde çalıştığı verilerin tutarlılığını koruyor olması gerektiğidir. Örneğin banka hesabınızın bakiyesi başlangıçta 1000 dolar ise, ve siz hesabınızdan 150 dolar çekmişseniz, bu işlem sonucunda bakiyenizin 850 dolar olması gerekir. Bu işlem sonucunda bakiye 850 değilde, 650 ya da 900 dolar ise tutarlılıktan söz edilemez.

Isolation (Yalıtım) : Herhangi bir t anında, veri üzerinde değişiklik yapan bir transaction, aynı anda ve aynı veriler üzerinden çalışan diğer olası transaction ‘lardan izole çalışmalıdır. Bunun anlamı aynı veri üzerinde değişiklik yapmak üzere tasarlanmış transaction ‘lardan herhangi birisinin çalışması sonlanmadan diğerinin söz konusu veriler üzerinde işlem yapamamasıdır.

Durability : Transaction ‘ının başarısız olması halinde, üzerinde çalışılan sistemin transaction çalışmadan önceki durumuna döndürülebilmesi yeteneğidir. Örneğin disk üzerindeki bir dosyanın silinmesini ele alırsak; bu iki aşamada gerçekleşen bir işlemdir. İlk aşama söz konusu dosyaya ilişkin root girişindeki ilk byte ‘a özel bir karakterin atanması, ikinci aşama ise; dosyanın fat zincirinin sıfırlanmasıdır. Bu işlemi bir transaction olarak düşünelim ve ilk aşamanın gerçekleşmesinin ardından, ikinci aşamada bir hata oluştuğunu ve fat zincirinin sıfırlanamadan silme işleminin yarıda kaldığını farz edelim. Bu durumda dosya sistemi üzerinde bir tutarsızlık oluşur. Bu tutarsızlığın oluşmaması için ilk aşamada yapılan işlemin de iptal edilerek sistemin ilk haline döndürülebilmesi gerekir.

Bağlantısız Veriler Üzerinde Kilitleme Yapmak

          Bağlantı tabanlı mimari esas alınarak tasarlanmış olan, ADO veri tabanı erişim teknolojisinde, çeşitli tür ve düzeylerde kilitleme yapmak mümkündü. Örneğin; aşağıdaki kod parçacığında, oRs isimli Recordset nesnesinin LockType property ‘sine atanan adLockPessimistic değeri, Recordset ‘in üzerinde çalıştığı verilerin, Recordset kapatılana kadar, başka bir Recordset nesnesi tarafından kullanılamamasını sağlar.

Dim oRs As New ADODB.Recordset
oRs.Open
With oRs
    .Source = "Select * From Employees"
    .ActiveConnection = oCnn
    .CursorLocation = adUseServer
    .LockType = adLockPessimistic
End With

             Ancak veri tabanından bağlantısız çalışma özelliği ön planda olan ADO .NET ‘te ise; kilitleme işlemleri ADO ‘da olduğu kadar kolay değildir. Çünkü ADO .NET ‘te veri tabanından bağımsız çalışmayı sağlayan DataSet, DataTable gibi sınıfların, Recordset sınıfında olduğu gibi kilitleme yapabilmeyi sağlayan property ya da metotları yoktur. Bu durum ADO .NET ‘teki bir eksiklik olarak değil, bağlantısız çalışma modelinin bir özelliği olarak algılanmalıdır. Ancak ADO .NET ‘te bağlantısız veriler üzerinde kilitleme yapmak, (ADO ‘daki kadar kolay olmasa da) mümkündür.

          ADO .NET ‘te bağlantısız verilerin nasıl kilitleneceğini incelemeye başlamadan önce, kilitleme işlemlerine neden gerek duyulduğunu örnek bir senaryo üzerinden giderek anlatmaya çalışalım. Örneğin bir kullanıcı (User1), açık bir bağlantı üzerinden elde ettiği verileri, bir DataSet nesnesi ile temsil edilen bellek bölgesine almış olsun. Bu noktada başka bir kullanıcı da (User2) veri tabanından aynı verileri alabilir, hatta üzerinde bir takım değişiklikler yaparak yeniden veri tabanına gönderebilir. Bu durumda ilk kullanıcının elinde bulunan veriler, ikinci kullanıcının veri tabanını güncellemiş olması nedeni ile, (veri tabanına göre) güncelliğini yitirmiş durumdadır. Buraya kadar hiç bir sorun yokmuş gibi gözükmek ‘sine rağmen, ilk kullanıcının elinde bulunan verileri (ister değiştirmiş isterse değiştirmemiş olsun) veri tabanına göndermek istemesi durumunda, ikinci kullanıcının yaptığı değişiklikler zarar görecektir. Buradaki sorun ilk kullanıcının işi bitinceye kadar üzerinde çalıştığı verileri, başka kullanıcıların da kullanabiliyor olmasıdır. Eğer amaç, bir kullanıcının işi bitinceye kadar üzerinde çalıştığı verileri, başkalarının kullanmasını önlemek ise; kilitleme yapmaya ihtiyaç vardır.

ADO .NET te Transaction Tabanlı Kilitleme

          ADO .NET ‘te pesimistik kilitleme, yalıtım düzeyi RepeatableRead ya da Serializable olarak belirlenmiş transaction ‘lar kullanılarak sağlanır. Çünkü, daha önce de söylediğimiz gibi, veri tabanından bağımsız çalışmayı sağlayan DataSet, DataTable gibi sınıfların, Recordset sınıfında olduğu gibi kilitleme yapabilmeyi sağlayan property ya da metotları yoktur. Transaction tabanlı kilitlemede temel ilke; kilitlenmek istenen verilerin, ya da bu veriler üzerinde çalışan işlemlerin, yalıtılmış bir transaction çerçevesine alınması ve transaction tamamlanıncaya kadar, verilerin başka kullanıcılar tarafından değiştirilmesinin önlenmesidir.

            Bildiğiniz gibi ADO .NET ‘te manuel transaction ‘ların başlatılmasından ve yönetilmelerini sağlayan XxxTransaction sınıfı türündeki nesnelerin yaratılmasından; XxxConnection sınıflarına ait BeginTransaction() metotları sorumludur. BeginTransaction() metodunun aşağıda prototipleri verilen versiyonu, parametre olarak aldığı IsolationLevel sembolik sabiti türündeki değeri, temsil ettiği transaction ‘ının yalıtım düzeyini belirlemekte kullanır.

Public Function BeginTransaction(ByVal isolationLevel as IsolationLevel) As OleDbTransaction

Public Function BeginTransaction(ByVal isolationLevel as IsolationLevel) As SqlTransaction

            Yalıtım düzeyi RepeatableRead ya da Serializable olarak belirlenmiş bir XxxTransaction nesnesi, temsil ettiği transaction tamamlanıncaya kadar, üzerinde çalışılan verilerin, diğer kullanıcılar tarafından değiştirilmesini önler.

Senaryo

             Şekilde görülen User1 isimli kullanıcının yaptığı işlemler, bir transaction çerçevesinde gerçekleşmektedir. Bu nedenle transaction tamamlanıncaya kadar, User2 aynı verilere sadece okuma amacı ile erişebilir.  

Örnek 1 : Aşağıdaki uygulamada transaction tabanlı kilitleme örneklenmiştir. User1 ‘e ait işlemler yalıtılmış bir transaction çerçevesinde gerçekleşmektedir. Bu sayede User1 ‘in üzerinde çalıştığı verilerin (söz konusu transaction tamamlanıncaya kadar) başka bir kullanıcı tarafından değiştirilmesi önlenmektedir.

Sub Main()

     Dim cnnStr As String

     cnnStr = "Datasource=localhost;Database=northwind;"

     cnnStr &= "integrated security=sspi"

     Dim cnnLocked As New SqlConnection()

     Dim cnnUnLocked As New SqlConnection()

     cnnLocked.ConnectionString = cnnStr

     cnnUnLocked.ConnectionString = cnnStr

     cnnLocked.Open()

     cnnUnLocked.Open()

     Dim oTrs As SqlTransaction

     oTrs = cnnLocked.BeginTransaction(IsolationLevel.Serializable)

     Dim sSQL As String

     sSQL = "Select * From Employees"

     Dim oCmd As New SqlCommand()

     With oCmd

        .CommandText = sSQL
 
       .Connection = cnnLocked
 
       .Transaction = oTrs

     End With

     Dim adpLocked As SqlDataAdapter

     adpLocked = New SqlDataAdapter(sSQL, cnnLocked)

     adpLocked.SelectCommand = oCmd

     Dim adpUnLocked As SqlDataAdapter

     adpUnLocked = New SqlDataAdapter(sSQL, cnnUnLocked)

     Dim dsLocked As New DataSet()

     Dim dsUnLocked As New DataSet()

     adpLocked.Fill(dsLocked)

     adpUnLocked.Fill(dsUnLocked)

     Dim cmbUsr2 As New SqlCommandBuilder(adpUnLocked)

     dsUnLocked.Tables(0).Rows(1)(2) = "User2"

     Try

         adpUnLocked.Update(dsUnLocked)

         Console.WriteLine("Kayıt User2 tarafından güncellendi")

     Catch exc As SqlException

         Console.WriteLine(exc.Message)

     End Try

     oTrs.Commit()

     cnnLocked.Close()

     cnnUnLocked.Close()

  End Sub

         Bu örnekte, User2 tarafından yapılmak istenen güncelleme işlemi, transaction tamamlanmadan önce (oTrs.Commit() satırı çalışmadan önce) yapıldığı için başarısız olmaktadır. Güncelleme işlemini yapmayı sağlayan komut, Try bloğu içerisinde çalıştırılmakta, ancak işlem yapılamadığı için, bir CommandTimeout hatası oluşmaktadır. Bu hata Catch kısmında yakalanarak kullanıcıya aşağıdaki şekilde olduğu gibi gösterilmektedir.

         Ancak User2 güncelleme işlemini, User1 ‘in işlemlerini içeren transaction ‘ının tamamlanmasının ardından yapmış olsaydı, başarılı olacaktı. Bu durum ise, Örnek 2 ‘de gösterilmiştir;

Örnek 2 : Aşağıdaki uygulamada User2 ‘nin yaptığı güncelleme işlemi, User1 ‘in yaptığı işlemleri içeren transaction ‘ının tamamlanmasının ardından yapılmış olduğu için, (yani oTrs.Commit() satırından sonra) başarılı olmaktadır.

Sub Main()

     Dim cnnStr As String

     cnnStr = "Datasource=localhost;Database=northwind;"

     cnnStr &= "integrated security=sspi"

     Dim cnnLocked As New SqlConnection()

     Dim cnnUnLocked As New SqlConnection()

     cnnLocked.ConnectionString = cnnStr

     cnnUnLocked.ConnectionString = cnnStr

     cnnLocked.Open()

     cnnUnLocked.Open()

     Dim oTrs As SqlTransaction

     oTrs = cnnLocked.BeginTransaction(IsolationLevel.Serializable)

     Dim sSQL As String

     sSQL = "Select * From Employees"

     Dim oCmd As New SqlCommand()

     With oCmd

        .CommandText = sSQL
 
       .Connection = cnnLocked
 
       .Transaction = oTrs

     End With

     Dim adpLocked As SqlDataAdapter

     adpLocked = New SqlDataAdapter(sSQL, cnnLocked)

     adpLocked.SelectCommand = oCmd

     Dim adpUnLocked As SqlDataAdapter

     adpUnLocked = New SqlDataAdapter(sSQL, cnnUnLocked)

     Dim dsLocked As New DataSet()

     Dim dsUnLocked As New DataSet()

     adpLocked.Fill(dsLocked)

     adpUnLocked.Fill(dsUnLocked)

     Dim cmbUsr2 As New SqlCommandBuilder(adpUnLocked)

     dsUnLocked.Tables(0).Rows(1)(2) = "User2"

     oTrs.Commit()

     Try

         adpUnLocked.Update(dsUnLocked)

         Console.WriteLine("Kayıt User2 tarafından güncellendi")

     Catch exc As SqlException

         Console.WriteLine(exc.Message)

     End Try

     cnnLocked.Close()

     cnnUnLocked.Close()

  End Sub

        Bu durumda, Employees tablosundaki ilgili alan, User2 tarafından başarı ile güncellenmekte ve aşağıdaki şekilde gösterilen sonuç elde edilmektedir.

Sonuç : Transaction tabanlı kilitleme işlemi, özellikle fazla sayıda kullanıcıya hizmet vermesi düşünülen uygulamalarda çok dikkat edilerek kullanılması, hatta mümkünse kaçınılması gereken bir yöntemdir. Tasarımcı, kilitleme işleminin kullanılıp kullanılmayacağına uygulama mimarisi, kullanıcı sayısı ya da güncellenme sıklığı gibi faktörleri göz önüne alarak karar vermelidir. Çünkü kilitleme (uygulandığı süreyle orantılı olarak) uygulamanın ölçeklenebilirliğini olumsuz yönde etkileyen bir işlemdir. Eğer bir kullanıcının elde ettiği veriler üzerinde uzun süreli bir kilitleme yapılıyorsa, bu; diğer kullanıcıların bu süre zarfında o verileri kullanamayacağı (değiştiremeyeceği) anlamına gelir. Sorun bu sürenin ne kadar uzun olduğudur. Bu sorunun çözümü ise; başka bir makalede ele alacağımız; "Optimistik Kilitleme" işlemidir.


Aykut TAŞDELEN

aykutt@csdos.org