Transaksi terdistribusi untuk database heterogen di MS .NET

Baru-baru ini, dalam sebuah wawancara saya ditanya apakah saya bekerja dengan transaksi terdistribusi, dalam arti bahwa saya harus melakukan penyisipan / memperbarui catatan tersebut, dengan ketentuan:

  1. Transaksi tunggal.
  2. Ini bisa berupa beberapa basis data seperti Oracle, MS SQL Server dan PostgreSQL.
  3. Respons terhadap operasi CRUD bisa signifikan.
  4. Urutan penyisipan tidak penting.

Apa yang dicapai oleh rekan kerja dengan mengajukan pertanyaan ini? Periksa pengalaman saya atau dapatkan solusi yang sudah jadi? Sebenarnya itu tidak penting bagi saya, tetapi yang penting - masalah dalam teori pertanyaan itu tampak menarik bagi saya, dan saya memutuskan untuk menulis artikel tentang bagaimana masalah ini dapat diselesaikan.

Sebelum saya menjelaskan solusi saya, mari kita ingat bagaimana transaksi terdistribusi yang khas diimplementasikan menggunakan platform Microsoft sebagai contoh.

Opsi nomor 1. Aplikasi C ++ dan driver ODBC (dimiliki oleh Microsoft Distributed Transaction Coordinator (MSDTC) untuk SQL Server)

Microsoft Distributed Transaction Coordinator (MSDTC) memungkinkan aplikasi untuk memperluas atau mendistribusikan transaksi di dua contoh atau lebih SQL Server. Transaksi terdistribusi berfungsi bahkan jika dua instance terletak di komputer yang berbeda.

MSDTC hanya berfungsi secara lokal untuk Microsoft SQL Server, dan tidak tersedia untuk layanan basis data Microsoft Azure SQL cloud.

MSDTC disebut oleh SQL Server Native Client Driver untuk Open Database Connectivity (ODBC) ketika program C ++ Anda mengelola transaksi terdistribusi. Pengandar ODBC klien asli memiliki manajer transaksi yang sesuai dengan XA Open Distributed Transaction Processing (DTP). Kepatuhan ini diperlukan oleh MSDTC. Biasanya, semua perintah manajemen transaksi dikirim melalui driver ODBC ini untuk klien asli. Urutannya adalah sebagai berikut:

Aplikasi ODBC untuk klien C ++ asli memulai transaksi dengan memanggil SQLSetConnectAttr dengan komitmen otomatis dinonaktifkan.
Aplikasi memperbarui beberapa data di SQL Server X di komputer A. Aplikasi memperbarui beberapa data di SQL Server X
di komputer B.
Jika pembaruan gagal pada SQL Server Y, semua pembaruan yang tidak dikomit di kedua contoh SQL Server dibatalkan.
Akhirnya, aplikasi menyelesaikan transaksi dengan memanggil SQLEndTran (1) dengan opsi SQL_COMMIT atau SQL_ROLLBACK.

(1) MSDTC dapat dipanggil tanpa ODBC. Dalam hal ini, MSDTC menjadi manajer transaksi, dan aplikasi tidak lagi menggunakan SQLEndTran.

Hanya satu transaksi yang didistribusikan.

Misalkan aplikasi ODBC Anda untuk klien C ++ asli terdaftar dalam transaksi terdistribusi. Kemudian aplikasi dikreditkan ke transaksi terdistribusi kedua. Dalam hal ini, pengandar ODBC SQL Server Native Client meninggalkan transaksi asli didistribusikan dan termasuk dalam transaksi terdistribusi baru.

Baca lebih lanjut tentang MSDTC di sini..

Opsi nomor 2. Aplikasi C #, sebagai alternatif untuk database SQL di cloud Azur,

MSDTC tidak didukung untuk Azure SQL Database atau Azure SQL Data Warehouse.

Namun, Anda dapat membuat transaksi terdistribusi untuk database SQL jika program C # Anda menggunakan kelas .NET System.Transactions.TransactionScope.

Contoh berikut menunjukkan cara menggunakan kelas TransactionScope untuk menentukan blok kode untuk berpartisipasi dalam transaksi.

static public int CreateTransactionScope(
    string connectString1, string connectString2,
    string commandText1, string commandText2)
{
    //        StringWriter   
   // .
    int returnValue = 0;
    System.IO.StringWriter writer = new System.IO.StringWriter();

    try
    {
        //  TransactionScope   , 
        //            
        //  .
        using (TransactionScope scope = new TransactionScope())
        {
            using (SqlConnection connection1 = new SqlConnection(connectString1))
            {
                //      
                // TransactionScope   .
                connection1.Open();

                //   SqlCommand    .
                SqlCommand command1 = new SqlCommand(commandText1, connection1);
                returnValue = command1.ExecuteNonQuery();
                writer.WriteLine("   command1: {0}", returnValue);

                //      ,  ,  
                //  command1  .  
                //    using  connection2   connection1, 
                //         connection2 
                //      .   
                using (SqlConnection connection2 = new SqlConnection(connectString2))
                {
                    //       
                    //   connection2 .
                    connection2.Open();

                    //      .
                    returnValue = 0;
                    SqlCommand command2 = new SqlCommand(commandText2, connection2);
                    returnValue = command2.ExecuteNonQuery();
                    writer.WriteLine("    command2: {0}", returnValue);
                }
            } 
            scope.Complete();
        }
    }
    catch (TransactionAbortedException ex)
    {
        writer.WriteLine("   : {0}", ex.Message);
    }

    Console.WriteLine(writer.ToString());

    return returnValue;
}

Opsi nomor 3. Aplikasi C # dan Entity Framework Core untuk transaksi terdistribusi.

Secara default, jika penyedia database mendukung transaksi, semua perubahan dalam satu panggilan ke SaveChanges () diterapkan pada transaksi. Jika ada perubahan yang tidak dilakukan, transaksi dibatalkan, dan tidak ada perubahan yang diterapkan ke database. Ini berarti bahwa SaveChanges () dijamin akan berhasil diselesaikan atau membiarkan database tidak berubah jika terjadi kesalahan.

Untuk sebagian besar aplikasi, perilaku default ini cukup. Anda harus mengontrol transaksi secara manual hanya jika persyaratan aplikasi Anda anggap perlu.

Anda dapat menggunakan DbContext.Database APIuntuk memulai, melakukan, dan memutar kembali transaksi. Contoh berikut menunjukkan dua operasi SaveChanges () dan kueri LINQ dieksekusi dalam satu transaksi.

Tidak semua penyedia basis data mendukung transaksi. Beberapa penyedia mungkin atau mungkin tidak mengeluarkan transaksi saat menggunakan API transaksi.

using (var context = new BloggingContext())
{
    using (var transaction = context.Database.BeginTransaction())
    {
        try
        {
            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
            context.SaveChanges();

            context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
            context.SaveChanges();

            var blogs = context.Blogs
                .OrderBy(b => b.Url)
                .ToList();

           transaction.Commit();
        }
        catch (Exception)
        {
             
        }
    }
}

Jika Anda menggunakan EF Core dalam .NET Framework , implementasi System.Transaksi di sana mendukung transaksi terdistribusi.

Seperti yang dapat Anda lihat dari dokumentasi dalam kerangka platform dan lini produk Microsoft, transaksi terdistribusi bukan masalah: MSDTC, cloud itu sendiri toleran terhadap kesalahan, sekelompok ADO .NET + EF Core untuk kombinasi eksotis (baca di sini ).

Bagaimana ulasan transaksi pada platform Microsoft membantu kami memecahkan masalah? Sangat sederhana, ada pemahaman yang jelas bahwa tidak ada toolkit standar untuk mengimplementasikan transaksi terdistribusi di seluruh basis data heterogen dalam aplikasi .NET.

Dan jika demikian, maka kita membuat koordinator transaksi distribusi terdistribusi buatan kita sendiri.

Salah satu opsi implementasi adalah transaksi terdistribusi berdasarkan layanan mikro (lihat di sini ). Opsi ini tidak buruk, tetapi membutuhkan penyempurnaan serius Web API untuk semua sistem yang terlibat dalam transaksi.

Karena itu, kita memerlukan mekanisme yang sedikit berbeda. Berikut ini adalah pendekatan umum untuk mengembangkan MCT.



Seperti yang Anda lihat dari diagram, ide utamanya adalah membuat dan menyimpan dengan status yang diperbarui dari catatan transaksi yang disimpan dalam database induk (atau tabel). Model CRT ini memungkinkan Anda untuk menerapkan transaksi terdistribusi yang memenuhi persyaratan:

  • atomitas
  • koherensi
  • isolasi
  • umur panjang

- dapat menjadi bagian dari aplikasi di mana pengguna telah membuat catatan, atau mungkin aplikasi yang benar-benar terpisah dalam bentuk layanan sistem. dapat berisi subproses khusus yang menghasilkan transaksi dalam kumpulan untuk dieksekusi ketika pemadaman listrik terjadi (tidak ditunjukkan dalam diagram). Aturan bisnis untuk mengkonversi (memetakan) sumber data yang dihasilkan oleh pengguna untuk database terkait dapat ditambahkan dan dikonfigurasi secara dinamis melalui format XML / JSON dan disimpan dalam folder aplikasi lokal atau dalam catatan transaksi (sebagai opsi). Oleh karena itu, untuk kasus ini, disarankan untuk menyatukan konversi ke database terkait di tingkat kode dengan menerapkan modularitas konverter dalam bentuk DLL. (Dan ya, SRT menyiratkan akses langsung ke database tanpa partisipasi Web API.)

Dengan demikian, SRT dalam bentuk sederhana dapat berhasil diimplementasikan sebagai bagian dari aplikasi, atau sebagai solusi terpisah, dapat disesuaikan dan independen (yang lebih baik).

Sekali lagi, saya akan mengklarifikasi bahwa transaksi terdistribusi adalah, menurut definisi, penyimpanan informasi satu kali tanpa mengubahnya, tetapi dengan kemampuan untuk memetakan berbagai basis data ke dalam skema data. Jadi, jika Anda perlu mencatat data di pangkalan lain, misalnya, Kementerian Dalam Negeri, FSB dan perusahaan asuransi, ketika merekam insiden di ruang gawat darurat rumah sakit (luka tembak), maka pendekatan ini pasti akan membantu Anda. Mekanisme yang sama dapat bekerja dengan sempurna di lembaga keuangan.

Saya harap pendekatan ini tampak menarik bagi Anda, tuliskan pendapat Anda tentang hal itu, kolega dan teman!

All Articles