Salah satu kebuntuan yang kurang umum adalah di mana ada satu pengguna dan mereka menemui jalan buntu pada beberapa sumber daya sistem. Yang baru-baru ini saya temui adalah membuat tipe alias, lalu mendeklarasikan variabel tipe itu, di dalam transaksi yang sama. Bayangkan Anda mencoba menjalankan pengujian unit atau pengujian pra-penerapan, memeriksa kegagalan, dan melakukan rollback dalam kasus apa pun sehingga Anda tidak meninggalkan jejak apa pun dari apa yang telah Anda lakukan. Polanya mungkin terlihat seperti ini:
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO DECLARE @x TABLE (e EmailAddress); GO ROLLBACK TRANSACTION;
Atau, lebih mungkin, sedikit lebih rumit:
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = N'whatever'; GO ROLLBACK TRANSACTION;
Tempat pertama saya mencoba kode ini adalah SQL Server 2012, dan kedua contoh gagal dengan kesalahan berikut:
Msg 1205, Level 13, State 55, Line 14Transaksi (Process ID 57) menemui jalan buntu pada sumber daya kunci dengan proses lain dan telah dipilih sebagai korban kebuntuan. Jalankan kembali transaksi.
Dan tidak banyak yang bisa dipelajari dari grafik kebuntuan:
Melangkah mundur beberapa tahun, saya ingat ketika saya pertama kali belajar tentang tipe alias, di SQL Server 2000 (ketika mereka disebut Tipe Data Buatan Pengguna). Pada saat itu, kebuntuan yang saya temui baru-baru ini tidak akan terjadi (tetapi ini setidaknya sebagian karena Anda tidak dapat mendeklarasikan variabel tabel dengan tipe alias – lihat di sini dan di sini). Saya menjalankan kode berikut pada SQL Server 2000 RTM (8.0.194) dan SQL Server 2000 SP4 (8.0.2039), dan itu berjalan dengan baik:
BEGIN TRANSACTION; GO EXEC sp_addtype @typename = N'EmailAddress', @phystype = N'VARCHAR(320)'; GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; SELECT @param; END GO EXEC dbo.foo @param = N'whatever'; GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = @x; GO ROLLBACK TRANSACTION;
Tentu saja, skenario ini tidak tersebar luas pada saat itu karena, bagaimanapun, tidak banyak orang yang menggunakan tipe alias. Meskipun mereka dapat membuat metadata Anda lebih mendokumentasikan diri sendiri dan seperti definisi data, mereka sangat merepotkan jika Anda ingin mengubahnya, yang mungkin menjadi topik untuk posting lain.
SQL Server 2005 muncul, dan memperkenalkan sintaks DDL baru untuk membuat tipe alias:CREATE TYPE
. Ini tidak benar-benar menyelesaikan masalah dengan mengubah tipe, itu hanya membuat sintaksnya sedikit lebih bersih. Di RTM, semua contoh kode di atas berfungsi dengan baik tanpa kebuntuan. Di SP4, bagaimanapun, mereka semua akan menemui jalan buntu. Oleh karena itu, di suatu tempat antara RTM dan SP4, mereka mengubah penanganan internal untuk transaksi yang melibatkan variabel tabel menggunakan tipe alias.
Maju cepat beberapa tahun ke SQL Server 2008, di mana parameter bernilai tabel ditambahkan (lihat kasus penggunaan yang bagus di sini). Hal ini membuat penggunaan jenis ini menjadi lebih umum, dan memperkenalkan kasus lain di mana transaksi yang mencoba membuat dan menggunakan jenis seperti itu akan menemui jalan buntu:
BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO DECLARE @r dbo.Items; GO ROLLBACK TRANSACTION;
Saya memeriksa Connect, dan menemukan beberapa item terkait, salah satunya mengklaim bahwa masalah ini telah diperbaiki di SQL Server 2008 SP2 dan 2008 R2 SP1:
Connect #365876 :Deadlock terjadi saat membuat tipe data yang ditentukan pengguna dan objek yang menggunakannya
Apa yang sebenarnya dirujuk adalah skenario berikut, di mana hanya membuat prosedur tersimpan yang mereferensikan tipe dalam variabel tabel akan menemui jalan buntu di SQL Server 2008 RTM (10.0.1600) dan SQL Server 2008 R2 RTM (10.50.1600):
BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO ROLLBACK TRANSACTION;
Namun, ini tidak menemui jalan buntu di SQL Server 2008 SP3 (10.0.5846) atau 2008 R2 SP2 (10.50.4295). Jadi saya cenderung percaya komentar pada item Connect – bahwa bagian dari bug ini telah diperbaiki pada 2008 SP2 dan 2008 R2 SP1, dan tidak pernah menjadi masalah di versi yang lebih modern.
Tapi ini masih menghilangkan kemampuan untuk benar-benar menempatkan tipe alias melalui segala jenis pengujian yang sebenarnya. Jadi pengujian unit saya akan berhasil selama yang ingin saya lakukan hanyalah menguji bahwa saya dapat membuat prosedur – lupakan tentang mendeklarasikan tipe sebagai variabel lokal atau sebagai kolom dalam variabel tabel lokal.
Satu-satunya cara untuk menyelesaikannya adalah dengan membuat tipe tabel sebelum memulai transaksi, dan secara eksplisit menjatuhkannya sesudahnya (atau memecahnya menjadi beberapa transaksi). Ini bisa menjadi sangat rumit, atau bahkan tidak mungkin, untuk sering mengotomatiskan kerangka kerja pengujian dan memanfaatkan sepenuhnya mengubah cara mereka beroperasi untuk memperhitungkan batasan ini.
Jadi saya memutuskan untuk menjalani beberapa tes di build awal dan terbaru dari semua versi utama:SQL Server 2005 RTM, 2005 SP4, 2008 RTM, 2008 SP3, 2008 R2 RTM, 2008 R2 SP2, 2012 RTM, 2012 SP1, dan 2014 CTP2 (dan ya, saya sudah menginstal semuanya). Saya telah meninjau beberapa item Connect dan berbagai komentar yang membuat saya bertanya-tanya kasus penggunaan mana yang didukung dan di mana, dan saya memiliki dorongan aneh untuk mencari tahu aspek mana dari masalah ini yang sebenarnya telah diperbaiki. Saya menguji berbagai skenario kebuntuan potensial yang melibatkan tipe alias, variabel tabel, dan parameter bernilai tabel terhadap semua build ini; kodenya sebagai berikut:
/* alias type - declare in local table variable always deadlocks on 2005 SP4 -> 2014, except in 2005 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320) GO DECLARE @r TABLE(e EmailAddress); GO ROLLBACK TRANSACTION; /* alias type - create procedure with param & table var sometimes deadlocks - 2005 SP4, 2008 RTM & SP1, 2008 R2 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO ROLLBACK TRANSACTION; /* alias type - create procedure, declare & exec always deadlocks on 2005 SP4 -> 2014, except on 2005 RTM */ BEGIN TRANSACTION; GO CREATE TYPE EmailAddress FROM VARCHAR(320); GO CREATE PROCEDURE dbo.foo @param EmailAddress AS BEGIN SET NOCOUNT ON; DECLARE @x TABLE (e EmailAddress); INSERT @x SELECT @param; END GO DECLARE @x EmailAddress; SET @x = N'whatever'; EXEC dbo.foo @param = N'whatever'; GO ROLLBACK TRANSACTION; /* obviously did not run these on SQL Server 2005 builds */ /* table type - create & declare local variable always deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO DECLARE @r dbo.Items; GO ROLLBACK TRANSACTION; /* table type - create procedure with param and SELECT never deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO CREATE PROCEDURE dbo.foo @param dbo.Items READONLY AS BEGIN SET NOCOUNT ON; SELECT Item FROM @param; END GO ROLLBACK TRANSACTION; /* table type - create procedure, declare & exec always deadlocks on 2008 -> 2014 */ BEGIN TRANSACTION; GO CREATE TYPE dbo.Items AS TABLE(Item INT); GO CREATE PROCEDURE dbo.foo @param dbo.Items READONLY AS BEGIN SET NOCOUNT ON; SELECT Item FROM @param; END GO DECLARE @x dbo.Items; EXEC dbo.foo @param = @x; GO ROLLBACK TRANSACTION;
Dan hasilnya mencerminkan cerita saya di atas:SQL Server 2005 RTM tidak menemui jalan buntu pada salah satu skenario, tetapi pada saat SP4 bergulir, semuanya menemui jalan buntu. Ini telah diperbaiki untuk skenario "buat tipe dan buat prosedur", tetapi tidak satu pun yang lain, di 2008 SP2 dan 2008 R2 SP1. Berikut adalah tabel yang menunjukkan semua hasil:
Versi SQL Server / Build # | ||||||||||
SQL 2005 | SQL 2008 | SQL 2008 R2 | SQL 2012 | SQL 2014 | ||||||
RTM 9.0.1399 | SP4 9.0.5324 | RTM 10.0.1600 | SP3 10.0.5846 | RTM 10.50.1600 | SP2 10.50.4295 | RTM 11.0.2100 | SP1 11.0.3381 | CTP2 12.0.1524 | ||
deklarasikan dalam tabel var | ||||||||||
buat prosedur | ||||||||||
buat &jalankan proc | ||||||||||
deklarasikan var lokal | T/A | |||||||||
buat prosedur | ||||||||||
buat &jalankan proc |
Kesimpulan
Jadi, moral dari ceritanya adalah, masih belum ada perbaikan untuk use case yang dijelaskan di atas, di mana Anda ingin membuat tipe tabel, membuat prosedur atau fungsi yang menggunakan tipe, mendeklarasikan tipe, menguji modul, dan menggulung semuanya kembali. Bagaimanapun, berikut adalah item Connect lainnya untuk Anda lihat; semoga Anda dapat memilih mereka dan meninggalkan komentar yang menjelaskan bagaimana skenario kebuntuan ini memengaruhi bisnis Anda secara langsung:
Saya sepenuhnya mengharapkan beberapa klarifikasi akan ditambahkan ke item Connect ini dalam waktu dekat, meskipun saya tidak tahu persis kapan mereka akan didorong.