﻿{"id":471,"date":"2019-03-20T20:14:02","date_gmt":"2019-03-20T19:14:02","guid":{"rendered":"https:\/\/blog.pjsen.eu\/?p=471"},"modified":"2022-02-22T11:39:13","modified_gmt":"2022-02-22T10:39:13","slug":"things-ive-learned-about-sql-server-the-hard-way","status":"publish","type":"post","link":"https:\/\/blog.pjsen.eu\/?p=471","title":{"rendered":"Things I&#8217;ve learned about SQL Server the hard way"},"content":{"rendered":"<p>In this post I am presenting a couple of things I&#8217;ve learned from the analysis of a problem, that manifested itself in an occasional HTTP 500 errors in production instance of an ASP.NET application. This time I don&#8217;t aim at exhaustively explaining every single point, because each of them could be a subject of a dedicated blog post.<\/p>\n<p>The story begins with SQL error: <em>SQLEXCEPTION: Transaction was deadlocked on lock resources with another process and has been chosen as the deadlock victim.<\/em><\/p>\n<ol>\n<li>\nIn any reasonably modern version of <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/ssms\/download-sql-server-management-studio-ssms\">SQL Server Management Studio<\/a> there is an <code>XEvent<\/code> session <code>system_health<\/code> under <i>Management<\/i> \u2192 <i>Extended Events<\/i>. It allows for viewing some important server logs, among which <code>xml_deadlock_report<\/code> in particularly interesting. It is very important to have an access to the production instance of database server in order to be able to watch the logs.\n<\/li>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"628\" height=\"378\" src=\"https:\/\/blog.pjsen.eu\/wp-content\/uploads\/2019\/03\/systemhealth.png\" alt=\"\" class=\"wp-image-472\" srcset=\"https:\/\/blog.pjsen.eu\/wp-content\/uploads\/2019\/03\/systemhealth.png 628w, https:\/\/blog.pjsen.eu\/wp-content\/uploads\/2019\/03\/systemhealth-300x181.png 300w\" sizes=\"auto, (max-width: 628px) 100vw, 628px\" \/><figcaption>System health XEvent session<\/figcaption><\/figure>\n<\/div>\n<li>\nIn this particular case, these <code>xml_deadlock_reports<\/code> contained one suspicious attribute: <i>isolationlevel = Serializable (4)<\/i> and the SQL code was a <code>SELECT<\/code>. I would not expect my <code>SELECT<\/code>s running with <i>Serializable<\/i> isolation level.\n<\/li>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.pjsen.eu\/wp-content\/uploads\/2019\/03\/deadlocklog.png\" alt=\"\" class=\"wp-image-472\"\/><figcaption>Details of a deadlock<\/figcaption><\/figure>\n<\/div>\n<li>\nThe isolation level is an attribute of a connection between a client and the database server. A connection is called <i>session<\/i> in SQL Server terminology. An explicit <code>BEGIN TRAN<\/code> is not necessary for the isolation level to be applied. Every SQL statement runs in its own statement-wide transaction. However, for such narrow-scoped transactions, in practice it may not make any difference whether you raise the isolation level or not. The difference can be observed when a transaction is explicit and spans multiple SQL statements.\n<\/li>\n<li>\nThe cause of setting the serialization level to <i>Serializable<\/i> was the behaviour of the <code>TransactionScope<\/code> <a href=\"#ref1\">[1]<\/a>. If you use it, it raises the isolation level. It is just a peculiarity of this very API of the .NET framework. It is good to know this.\n<\/li>\n<li>SQL Server, at last in 2012 and some (I am not sure exactly which ones) later versions, does not reset the isolation level when ADO.NET disposes of a connection. A connection returns back to the connection pool <a href=\"#ref2\">[2]<\/a> and is reused by subsequent <code>SqlConnection<\/code> objects unless they have different connection string.\n<\/li>\n<li>The connection pool size, if the connection pooling is active, poses the limit of how many concurrent connections to a database server a .NET application can make. If there are no free connections in the pool, an exception is thrown <a href=\"#ref3\">[3]<\/a>.\n<\/li>\n<li>\nEliminating the usage of <code>TransactionScope<\/code> did not solve the issue. Even if you run <code>SELECT<\/code>s under the default <i>Read Committed<\/i> isolation level, these still issues <i>Shared locks<\/i> which may deadlock with <i>Exclusive locks<\/i> of <code>UPDATE<\/code>s. In any reasonably high production data traffic, where <code>SELECT<\/code>s span multiple tables, which are also very frequently updated, it is highly probable, that a deadlock will occur.\n<\/li>\n<li>\nThe difference between running <code>SELECT<\/code> under <i>Serializable<\/i> isolation level and <i>Read Committed<\/i> level is that in the former, the locks are kept from the moment of executing the <code>SELECT<\/code> until the transaction ends. You can observe it by manually beginning a <i>Serializable<\/i> transaction, running any <code>SELECT<\/code> and observing <code>dm_tran_locks<\/code> <i>DMV<\/i> and only then committing (or rolling back, whatever) the transaction. With <i>Read Committed<\/i> level locks are <strong>not<\/strong> kept until an explicit transaction ends, they are released immediately after execution of a <code>SELECT<\/code> finishes. These are the same kind of locks, <i>Shared locks<\/i>. This implies one cannot observe the difference between executing a <code>SELECT<\/code> under <i>Serializable<\/i> and <i>Read Committed<\/i>, when there is no explicit transaction and thus, there is only a statement-wide transaction which releases locks immediately after the results are returned.<\/li>\n<li>Setting isolation level of <i>Read Uncommitted<\/i> is practically equivalent to running a <code>SELECT WITH(NOLOCK)<\/code> hint, even if you don&#8217;t explicitly open a transaction.\n<\/li>\n<li>\nIn Entity Framework a <code>SqlConnection<\/code> is opened for every materialization of the query, the results are returned, and the connection is immediately closed and returned back to the pool <a href=\"#ref5\">[5]<\/a>. <strong>The connection lifetime is by no means related to the scope of <code>DbContext<\/code> object<\/strong>. I can see a kind of similarity between how Entity Framework uses <code>SqlConnection<\/code>s and how ASP.NET makes use of threads when executing <code>async<\/code> methods. A thread is released on every <code>await<\/code> and can be used for doing something more valuable than waiting. Similarly, a <code>SqlConnection<\/code> is released right after materialization and can be used for executing different command, in different request (in case of ASP.NET) even before <code>DbContext<\/code> is disposed of.\n<\/li>\n<li>It is not that obvious how to reset the isolation level of the connection. You see, every time your C# code using Entity Framework results in sending a SQL to the SQL Server, it can take different connection from the pool (if anyone knows if there is any ordering applied when retrieving connections from the pool, please feel free to comment). It may or may not be the same connection you used previously. Consequently, it is not easy to &#8216;catch&#8217; the underlying connection using Entity Framework. You can call <code>BeginTransaction<\/code> every time you use <code>DbContext<\/code>, and then you are guaranteed to own the connection for all your SQL commands. But that way you are forcing opening transaction when you don&#8217;t really need one.  What I recommend is to handle <code>StateChange<\/code> event of <code>DbConnection<\/code> object as described in <a href=\"#ref4\">[4]<\/a>. You can do it either on opening the connection or on closing it.<\/li>\n<li>\nIn SQL Server you can monitor open sessions with the following query:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/przemsen\/97eaf5028e91b9111fae417055eb9c3e.js?file=blog-2019-03-20-sessions.sql\"><\/script><\/p>\n<\/li>\n<\/ol>\n<p>References:<\/p>\n<p style=\"text-align:left\">\n<a id=\"ref1\">[1]<\/a> &nbsp;&nbsp; <a href=\"https:\/\/stackoverflow.com\/questions\/11292763\/why-is-system-transactions-transactionscope-default-isolationlevel-serializable\">https:\/\/stackoverflow.com\/questions\/11292763\/why-is-system-transactions-transactionscope-default-isolationlevel-serializable<\/a><br \/>\n<br \/>\n<a id=\"ref2\">[2]<\/a> &nbsp;&nbsp; <a href=\"https:\/\/stackoverflow.com\/questions\/9851415\/sql-server-isolation-level-leaks-across-pooled-connections\">https:\/\/stackoverflow.com\/questions\/9851415\/sql-server-isolation-level-leaks-across-pooled-connections<\/a><br \/>\n<br \/>\n<a id=\"ref3\">[3]<\/a> &nbsp;&nbsp; <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/data\/adonet\/sql-server-connection-pooling\">https:\/\/docs.microsoft.com\/en-us\/dotnet\/framework\/data\/adonet\/sql-server-connection-pooling<\/a><br \/>\n<br \/>\n<a id=\"ref4\">[4]<\/a> &nbsp;&nbsp; <a href=\"https:\/\/stackoverflow.com\/questions\/28442558\/entity-framework-and-transactionscope-doesnt-revert-the-isolation-level-after-d\">https:\/\/stackoverflow.com\/questions\/28442558\/entity-framework-and-transactionscope-doesnt-revert-the-isolation-level-after-d<\/a><br \/>\n<br \/>\n<a id=\"ref5\">[5]<\/a> &nbsp;&nbsp; <a href=\"https:\/\/docs.microsoft.com\/en-us\/previous-versions\/dotnet\/netframework-4.0\/bb896325(v=vs.100)#connections-and-the-entity-framework\">https:\/\/docs.microsoft.com\/en-us\/previous-versions\/dotnet\/netframework-4.0\/bb896325(v=vs.100)#connections-and-the-entity-framework<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post I am presenting a couple of things I&#8217;ve learned from the analysis of a problem, that manifested itself in an occasional HTTP 500 errors in production instance of an ASP.NET application. This time I don&#8217;t aim at exhaustively explaining every single point, because each of them could be a subject of a<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,8],"tags":[],"class_list":["post-471","post","type-post","status-publish","format-standard","hentry","category-net","category-asp-net"],"_links":{"self":[{"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=\/wp\/v2\/posts\/471","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=471"}],"version-history":[{"count":0,"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=\/wp\/v2\/posts\/471\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=471"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=471"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.pjsen.eu\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=471"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}