TransactionScope secara otomatis meningkat ke MSDTC pada beberapa mesin?

284

Dalam proyek kami, kami menggunakan TransactionScope untuk memastikan lapisan akses data kami melakukan tindakannya dalam transaksi. Kami bertujuan untuk tidak mengharuskan layanan MSDTC diaktifkan di mesin pengguna akhir kami.

Masalahnya adalah, pada setengah dari mesin pengembang kami, kami dapat berjalan dengan MSDTC dinonaktifkan. Setengah lainnya harus diaktifkan atau mereka mendapatkan pesan kesalahan "MSDTC pada [SERVER] tidak tersedia" .

Ini benar-benar membuat saya menggaruk-garuk kepala dan membuat saya serius mempertimbangkan untuk kembali ke solusi seperti TransactionScope home-spun berdasarkan objek transaksi ADO.NET. Ini tampaknya gila - kode yang sama bahwa karya (dan tidak meningkat) pada setengah dari pengembang kami yang melakukan Meningkat pada pengembang lain.

Saya berharap untuk jawaban yang lebih baik untuk Trace mengapa transaksi meningkat ke DTC tetapi sayangnya tidak.

Berikut adalah sedikit contoh kode yang akan menyebabkan masalah, pada mesin yang mencoba untuk meningkatkan, ia mencoba untuk meningkat pada koneksi kedua. Buka () (dan ya, tidak ada koneksi lain yang terbuka pada saat itu.)

using (TransactionScope transactionScope = new TransactionScope() {
   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();
         using (SqlDataReader reader = command.ExecuteReader()) {
            // use the reader
            connection.Close();
         }
      }
   }

   // Do other stuff here that may or may not involve enlisting 
   // in the ambient transaction

   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();  // Throws "MSDTC on [SERVER] is unavailable" on some...

         // gets here on only half of the developer machines.
      }
      connection.Close();
   }

   transactionScope.Complete();
}

Kami benar-benar menggali dan mencoba mencari tahu ini. Berikut ini beberapa info tentang mesin yang bekerja:

  • Pengembang 1: Windows 7 x64 SQL2008
  • Pengembang 2: Windows 7 x86 SQL2008
  • Pengembang 3: Windows 7 x64 SQL2005 SQL2008

Pengembang yang tidak berfungsi:

  • Dev 4: Windows 7 x64, SQL2008 SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • PC Rumah Saya: Windows Vista Home Premium, x86, SQL2005

Saya harus menambahkan bahwa semua mesin, dalam upaya untuk mencari masalah, telah sepenuhnya ditambal dengan semua yang tersedia dari Pembaruan Microsoft.

Pembaruan 1:

Halaman eskalasi transaksi MSDN itu menyatakan bahwa kondisi berikut ini akan menyebabkan transaksi meningkat ke DTC:

  1. Setidaknya satu sumber daya tahan lama yang tidak mendukung pemberitahuan satu fase terdaftar dalam transaksi.
  2. Setidaknya dua sumber daya tahan lama yang mendukung notifikasi satu fase terdaftar dalam transaksi. Misalnya, mendaftar satu koneksi dengan tidak menyebabkan transaksi dipromosikan. Namun, setiap kali Anda membuka koneksi kedua ke database yang menyebabkan database terdaftar, infrastruktur System.Transactions mendeteksi bahwa itu adalah sumber daya tahan lama kedua dalam transaksi, dan meningkatkannya ke transaksi MSDTC.
  3. Permintaan untuk "marshal" transaksi ke domain aplikasi yang berbeda atau proses yang berbeda dipanggil. Misalnya, serialisasi objek transaksi melintasi batas domain aplikasi. Objek transaksi adalah marshaled-by-value, yang berarti bahwa setiap upaya untuk melewati batas domain aplikasi (bahkan dalam proses yang sama) menghasilkan serialisasi objek transaksi. Anda dapat melewati objek transaksi dengan melakukan panggilan pada metode jarak jauh yang mengambil Transaksi sebagai parameter atau Anda dapat mencoba mengakses komponen layanan transaksional jarak jauh. Ini membuat serial objek objek dan menghasilkan eskalasi, seperti ketika transaksi serial di seluruh domain aplikasi. Ini didistribusikan dan manajer transaksi lokal tidak lagi memadai.

Kami tidak mengalami # 3. # 2 tidak terjadi karena hanya ada satu koneksi pada satu waktu, dan itu juga untuk satu 'sumber daya tahan lama'. Apakah ada cara # 1 bisa terjadi? Beberapa konfigurasi SQL2005 / 8 yang menyebabkannya tidak mendukung pemberitahuan satu fase?

Pembaruan 2:

Diselidiki ulang, secara pribadi, semua versi SQL Server - "Dev 3" sebenarnya memiliki SQL2008, dan "Dev 4" sebenarnya adalah SQL2005. Itu akan mengajarkan saya untuk tidak pernah mempercayai rekan kerja saya lagi. ;) Karena perubahan data ini, saya cukup yakin kami telah menemukan masalah kami. Pengembang SQL2008 kami tidak mengalami masalah karena SQL2008 memiliki jumlah yang sangat banyak termasuk yang tidak dimiliki SQL2005.

Ini juga memberi tahu saya bahwa karena kita akan mendukung SQL2005 sehingga kita tidak dapat menggunakan TransactionScope seperti sebelumnya, dan jika kita ingin menggunakan TransactionScope kita harus melewati objek SqlConnection tunggal di sekitar ... yang tampaknya bermasalah dalam situasi di mana SqlConnection tidak dapat dengan mudah diteruskan ... hanya berbau instance global-SqlConnection. Bangku gereja!

Perbarui 3

Hanya untuk mengklarifikasi di sini dalam pertanyaan:

SQL2008:

  • Mengizinkan beberapa koneksi dalam satu TransactionScope tunggal (seperti yang ditunjukkan dalam kode contoh di atas.)
  • Peringatan # 1: Jika beberapa SqlConnections bersarang, yaitu, dua atau lebih SqlConnections dibuka pada saat yang sama, TransactionScope akan segera meningkat ke DTC.
  • Peringatan # 2: Jika SqlConnection tambahan dibuka untuk 'sumber daya tahan lama' yang berbeda (yaitu: SQL Server yang berbeda,) itu akan segera meningkat ke DTC

SQL2005:

  • Tidak mengizinkan banyak koneksi dalam satu TransactionScope, titik. Ini akan meningkat ketika / jika SqlConnection kedua dibuka.

Perbarui 4

Dalam kepentingan membuat pertanyaan ini bahkan lebih berantakan berguna, dan hanya demi kejelasan lebih lanjut, berikut adalah bagaimana Anda bisa mendapatkan SQL2005 meningkat ke DTC dengan tunggal SqlConnection :

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}

Ini sepertinya rusak bagi saya, tapi saya rasa saya bisa mengerti jika setiap panggilan masuk SqlConnection.Open()diambil dari pool koneksi.

"Tapi mengapa ini bisa terjadi?" Nah, jika Anda menggunakan SqlTableAdapter terhadap koneksi itu sebelum dibuka, SqlTableAdapter akan membuka dan menutup koneksi, secara efektif menyelesaikan transaksi untuk Anda karena Anda sekarang tidak dapat membukanya kembali.

Jadi, pada dasarnya, agar berhasil menggunakan TransactionScope dengan SQL2005 Anda harus memiliki semacam objek koneksi global yang tetap terbuka dari titik TransactionScope pertama instantiated sampai tidak lagi diperlukan. Selain bau kode dari objek koneksi global, membuka koneksi terlebih dahulu dan menutupnya terakhir bertentangan dengan logika membuka koneksi selambat mungkin dan menutupnya sesegera mungkin.

Yoopergeek
sumber
Dapatkah Anda memperluas "Lakukan hal-hal lain di sini yang mungkin terlibat atau tidak dalam transaksi ambient". Tentunya apa yang ada di sana sangat mempengaruhi bagaimana kode itu berperilaku?
RichardOD
2
"# 2 tidak terjadi karena hanya ada satu koneksi pada satu waktu" - # 2 tidak mengatakan bahwa koneksi kedua harus terbuka pada saat yang sama, hanya saja perlu terdaftar dalam transaksi yang sama.
Joe
3
Terima kasih banyak telah melaporkan kembali dengan Pembaruan 4 yang menunjukkan bagaimana eskalasi dapat terjadi hanya dengan satu SqlConnection. Ini adalah persis apa yang saya temui meskipun hati-hati memastikan bahwa hanya SqlConnection tunggal digunakan. Sangat menyenangkan mengetahui bahwa itu komputer yang gila dan bukan aku. :-)
Oran Dennison
Dalam hal penyatuan koneksi, jika kita memiliki banyak koneksi (dan bersarang jika perlu) jika kita membuka dan menutup satu per satu apakah kita menggunakan 1 sumber daya koneksi nyata atau 1 per koneksi, saya mencoba merasionalisasi ini untuk menentukan apakah atau tidak memiliki cakupan yang sesuai "kononeksi" (yang ingin saya hindari)
brumScouse
1
Koneksi bersarang di bawah cakupan transaksi yang sama akan mempromosikan ke transaksi terdistribusi. Dari SQL server 2008 dan di atas beberapa (tidak bersarang) koneksi di bawah lingkup transaksi yang sama tidak akan mempromosikan ke transaksi yang didistribusikan.
PreguntonCojoneroCabrón

Jawaban:

71

SQL Server 2008 dapat menggunakan banyak SQLConnections dalam satu TransactionScopetanpa eskalasi, asalkan koneksi tidak terbuka pada saat yang sama, yang akan menghasilkan beberapa koneksi TCP "fisik" dan karenanya memerlukan eskalasi.

Saya melihat beberapa pengembang Anda memiliki SQL Server 2005 dan yang lain memiliki SQL Server 2008. Apakah Anda yakin telah mengidentifikasi dengan benar mana yang meningkat dan mana yang tidak?

Penjelasan yang paling jelas adalah bahwa pengembang dengan SQL Server 2008 adalah orang-orang yang tidak meningkat.

Joe
sumber
Ya, detailnya benar, dan adakah yang melihat kode itu? Ada dua koneksi dalam ruang lingkup transaksi, namun, hanya ada satu koneksi yang dipasang dan dibuka pada satu saat. Juga, tidak, DTC tidak berjalan pada mesin yang berfungsi.
Yoopergeek
1
"Namun, hanya ada satu koneksi yang dipasang dan dibuka pada satu saat" - mengapa itu relevan? Dengan SQL2005, jika Anda membuka lebih dari satu koneksi dalam ruang lingkup transaksi, Anda akan meningkatkan apakah mereka tetap terbuka secara bersamaan atau tidak. Yang logis jika Anda memikirkannya.
Joe
Anda dan penipu sekarang membuat saya menebak-nebak dan saya ingin mulai bekerja pada hari Senin dan memeriksa mesin individual mereka lebih dekat dan memastikan versi SQL Server seperti yang dilaporkan sebelumnya.
Yoopergeek
19
Anda dan penipu benar. Saya memiliki telur di seluruh wajah saya. Terima kasih telah memukul saya dengan tongkat petunjuk. :) Karena Anda yang pertama, Anda mendapatkan jawabannya. Saya ingin menambahkan satu titik klarifikasi, - SQL2008 memungkinkan beberapa koneksi dibuka, tetapi tidak pada saat yang bersamaan. Masih ada satu koneksi tunggal yang terbuka pada waktu tertentu atau TransactionScope akan meningkat ke DTC.
Yoopergeek
@Yoopergeek, saya dapat memverifikasi bahwa "tidak pada saat yang sama" Anda adalah penting dan telah mengedit jawaban @ Jo. Pemantauan koneksi TCP sementara pengujian menunjukkan bahwa koneksi TCP lama akan dapat digunakan kembali ketika koneksi tidak digunakan pada saat yang sama, dan dengan demikian TransactionScopedapat dilakukan dengan satu COMMITdi sisi server, yang akan membuat eskalasi berlebihan.
Eugene Beresovsky
58

Hasil penelitian saya pada topik:

masukkan deskripsi gambar di sini

Lihat Menghindari Eskalasi yang tidak diinginkan ke Transaksi Terdistribusi

Saya masih menyelidiki perilaku eskalasi Oracle: Apakah transaksi yang mencakup banyak koneksi ke DB yang sama meningkat ke DTC?

Peter Meinl
sumber
1
Terima kasih telah berbagi penelitian Anda. Ini sangat membantu. Satu lagi permintaan cepat. Apa perbedaan antara TransactionScope () dan sqlConnection.BeginTransaction ()?
Baig
Menurut permintaan fitur ini , ODAC 12C sekarang seharusnya berperilaku sebagai SQL 2008, tidak mempromosikan untuk didistribusikan ketika menggunakan koneksi berturut-turut ke sumber data yang sama.
Frédéric
31

Kode itu akan menyebabkan peningkatan saat menyambung ke 2005.

Periksa dokumentasi di MSDN - http://msdn.microsoft.com/en-us/library/ms172070.aspx

Transaksi yang Dapat Dipromosikan dalam SQL Server 2008

Dalam versi 2.0 dari .NET Framework dan SQL Server 2005, membuka koneksi kedua di dalam TransactionScope akan secara otomatis mempromosikan transaksi menjadi transaksi terdistribusi penuh, bahkan jika kedua koneksi menggunakan string koneksi yang identik. Dalam hal ini, transaksi yang didistribusikan menambah overhead yang tidak perlu yang menurunkan kinerja.

Dimulai dengan SQL Server 2008 dan versi 3.5 dari .NET Framework, transaksi lokal tidak lagi dipromosikan menjadi transaksi terdistribusi jika koneksi lain dibuka dalam transaksi setelah transaksi sebelumnya ditutup. Ini tidak memerlukan perubahan pada kode Anda jika Anda sudah menggunakan pooling koneksi dan mendaftar dalam transaksi.

Saya tidak bisa menjelaskan mengapa Dev 3: Windows 7 x64, SQL2005 berhasil dan Dev 4: Windows 7 x64 gagal. Apakah Anda yakin itu tidak sebaliknya?

penambang
sumber
10

Saya tidak tahu mengapa jawaban ini dihapus tetapi ini sepertinya memiliki beberapa informasi yang relevan.

jawab 4 Agustus 10 pada 17:42 Eduardo

  1. Tetapkan Daftar = palsu pada string koneksi untuk menghindari pendaftaran otomatis pada transaksi.

  2. Secara manual mendaftar koneksi sebagai peserta dalam ruang lingkup transaksi. [ artikel asli kedaluwarsa] atau lakukan ini: Cara mencegah promosi MSDTC otomatis [archive.is]

Chris Marisic
sumber
msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx tidak ditemukan, Visual Studio 2005 Dokumentasi yang sudah pensiun
Kiquenet
2

Saya tidak terlalu yakin apakah koneksi bersarang adalah masalahnya. Saya memanggil contoh lokal dari server SQL dan tidak menghasilkan DTC ??

    public void DoWork2()
    {
        using (TransactionScope ts2 = new TransactionScope())
        {
            using (SqlConnection conn1 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;"))
            {
                SqlCommand cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                cmd.Connection = conn1;
                cmd.Connection.Open();
                cmd.ExecuteNonQuery();

                using (SqlConnection conn2 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;Connection Timeout=100"))
                {
                    cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                    cmd.Connection = conn2;
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }

            ts2.Complete();
        }
    }
Iftikhar Ali
sumber
SQL Server edisi apa yang Anda gunakan? Saya ingin tahu apakah jawaban @Peter Meinl perlu diperbarui untuk mencerminkan setiap perubahan yang dibuat pada 2008R2, dan / atau Denali.
Yoopergeek
Saya menggunakan SQL Server 2008 R2.
Iftikhar Ali
Saya ingin tahu apakah 2008 R2 berperilaku lebih baik? @hwiechers menjawab juga membuat saya bertanya-tanya apakah versi Framework yang Anda kompilasi mencegah eskalasi. Akhirnya, saya bertanya-tanya apakah itu menjadi contoh R2 lokal membuat perbedaan. Saya berharap saya punya waktu / sumber daya untuk menyelidiki bagaimana ini telah berubah dengan rilis 2008 R2 dan SQL Server 2012.
Yoopergeek
Tidak yakin apakah koneksi bersarang adalah masalahnya? lol ... mekar menghapusnya kalau begitu!, mengapa orang-orang bersarang menggunakan pernyataan ketika tidak benar-benar diperlukan, saya tidak akan pernah tahu.
Paul Zahra
1

TransactionScope selalu meningkat ke transaksi DTC, jika Anda menggunakan akses lebih dari 1 koneksi di dalamnya. Satu-satunya cara kode di atas dapat bekerja dengan DTC dinonaktifkan adalah jika secara kebetulan Anda mendapatkan koneksi yang sama dari kumpulan koneksi dua kali.

"Masalahnya adalah, pada setengah dari mesin pengembang kami, kami dapat berjalan dengan MSDTC dinonaktifkan." Apakah Anda yakin itu dinonaktifkan;)

amatir
sumber
0

Pastikan connectionString Anda tidak mengatur pooling ke false. Ini akan menghasilkan koneksi baru untuk setiap SqlConnection baru di TransactionScope dan meningkatkannya ke DTC.

FreddyV
sumber