<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>CSS SQL Server Engineers</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/psssql/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/psssql/feed.xml</link>
    <description>This is the official team Web Log for Microsoft Customer Service and Support (CSS) SQL Support. Posts are provided by the CSS SQL Escalation Services team.</description>
    <lastBuildDate>Tue, 13 Nov 2018 02:33:04 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Understanding Optimizer Timeout and how Complex queries can be Affected in SQL Server</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/understanding-optimizer-timeout-and-how-complex-queries-can-be-affected-in-sql-server</link>
      <pubDate>Fri, 19 Oct 2018 16:13:30 GMT</pubDate>
      <dc:creator><![CDATA[Joseph.Pilov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6525</guid>
      <description><![CDATA[What Is Optimizer Timeout?

SQL Server uses a cost-based query optimizer. Therefore, it selects a...]]></description>
      <content:encoded><![CDATA[<strong>What Is Optimizer Timeout?</strong>

<span>SQL Server uses a cost-based query optimizer. Therefore, it selects a query plan with the lowest cost after it has built and examined multiple query plans. One of the objectives of the SQL Server query optimizer (QO) is to spend a "reasonable time" in query optimization as compared to query execution. Therefore, QO has a built-in threshold of tasks to consider before it stops the optimization process. If this threshold is reached before QO has considered most, if not all, possible plans then it has reached the Optimizer TimeOut limit. An event is reported in the query plan as Time Out under "Reason For Early Termination of Statement Optimization." It's important to understand that this threshold isn't based on clock time but on number of possibilities considered. In current SQL QO versions, over a half million possibilities are considered before a time out is reached.</span>

<span>
Optimizer timeout is designed in Microsoft SQL Server and in many cases encountering it is not a factor affecting query performance. However, in some cases the SQL query plan choice may be affected by optimizer timeout and thus performance could be impacted. When you encounter such issues, if you understand optimizer timeout mechanism and how complex queries can be affected in SQL Server, it can help you to better troubleshoot and improve your performance issue.</span>
<div><strong>What are the Symptoms?</strong></div>
<div></div>
<div><span>Here are some of the factors involved:</span></div>
<ul>
 	<li><span> You have a complex query that involves lots of joined tables (for example, 8 or more tables are joined).</span></li>
 	<li><span>The query may run slowly or slower than when you compare it to another SQL Server version or another system.</span></li>
 	<li><span>The query plan of the query shows the following information in the XML query plan: </span><span style="color: #000000">StatementOptmEarlyAbortReason="TimeOut". </span><span>Or, if you verify the properties of the left-most plan operator in Microsoft SQL Server Management Studio, you notice the value of "Reason For Early Termination of Statement Optimization" is “TimeOut.” </span></li>
</ul>
<div><span>For example, the following is the XML output of a query plan that shows the optimizer timeout:
</span></div>
<pre style="padding-left: 30px"><span>&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.518" Build="13.0.5201.2" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan"&gt;
 &lt;BatchSequence&gt;
  &lt;Batch&gt;
   &lt;Statements&gt;
    &lt;StmtSimple StatementCompId="6" StatementEstRows="419.335" StatementId="1" StatementOptmLevel="FULL" <strong>StatementOptmEarlyAbortReason="TimeOut"</strong> ......&gt;
    ...
   &lt;Statements&gt;
  &lt;Batch&gt;
&lt;BatchSequence&gt; 
</span></pre>
&nbsp;

<span>The following is an example of a graphical representation of a plan, which displays a "TimeOut" value:</span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/10/OptimizerTimeout_en_1.png"><img width="1479" height="443" class="alignleft size-full wp-image-6535" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/10/OptimizerTimeout_en_1.png" /></a>

&nbsp;

&nbsp;

<strong>How does it work, then?</strong>
<p class="ng-scope"><span>There's no simple formula to determine what conditions would cause the optimizer threshold to be reached or exceeded. However, the following are some factors that determine how many plans are explored by QO in the process of looking for a "best plan":</span></p>

<ul class="ng-scope">
 	<li><span>In what order to join tables:</span>
<ul>
 	<li><span>join Table1 with Table2 and the result with Table3</span></li>
 	<li><span>join Table1 with Table3 and the result with Table2</span>
<span><strong>Note</strong> The larger the number of tables, the larger the possibilities are.</span></li>
</ul>
</li>
 	<li><span>What heap or binary tree (HoBT) access structure to use to retrieve the rows from a table:</span>
<ul>
 	<li><span>Nonclustered Index1</span></li>
 	<li><span>Nonclustered Index2</span></li>
 	<li><span>Clustered index, and so on</span></li>
</ul>
</li>
 	<li><span>What access method:</span>
<ul>
 	<li><span>index seek</span></li>
 	<li><span>index scan</span></li>
 	<li><span>table scan</span></li>
</ul>
</li>
 	<li><span>What physical join operator to use :</span>
<ul>
 	<li><span>Nested Loop</span></li>
 	<li><span>Hash Match</span></li>
 	<li><span>Merge Join (NL, HM, MJ)</span></li>
</ul>
</li>
 	<li><span>Should a parallel plan be used or a serial one?</span></li>
</ul>
<p class="ng-scope"><span>To illustrate, take an example of a join between 3 tables (T1, T2 and T3) and each table has a clustered index only. There are two joins involved here and because there are 3 physical join possibilities (NL, HM, MJ), then each of the two joins can be performed in 6 (2 * 3) ways. Also consider the join order: </span></p>

<ul class="ng-scope">
 	<li><span>T1 joined to T2 and then to T3</span></li>
 	<li><span>T1 joined to T3 and then to T2</span></li>
 	<li><span>T2 joined to T3 and then to T1</span></li>
</ul>
<p class="ng-scope"><span>Now multiply 6 ways * 3 join orders and we have a minimum of 18 possible plans to choose from. If you include the possibility of parallelism and other factors like Seek or Scan of the HoBT, then the possible plans increase even more. If you are a math wizard you can figure out that when a query involves for example 10 tables, the possible permutations there are in the millions. Therefore, you can see that a query with lots of joins is more likely to reach the optimizer timeout threshold than one with fewer joins.</span><strong></strong></p>
<p class="ng-scope"><span><strong>Note</strong> The query predicates (filters in the WHERE clause) and existence of constraints will reduce the number of access methods considered and thus the possibilities considered.</span></p>
<p class="ng-scope"><span>The result of reaching the optimizer timeout threshold is that SQL Server has not considered the entire set of possibilities for optimization and it may have missed a set of plans that could produce shorter execution times. QO will stop at the threshold and consider the least-costly query plan at that point, although there may be better unexplored options. This may result in a query execution that's suboptimal.</span></p>
<p class="ng-scope"><span><strong>But I see an Optimizer Timeout with a simpler query?</strong></span></p>
<p class="ng-scope"><span>Nothing with QO is simple (black and white). There are so many possible scenarios, its complexity so high that it is hard to grasp all of the possibilities. The Query Optimizer may dynamically adjust/set timeout threshold based on the cost of the plan found at a certain stage. For example, if a plan that appears relatively "cheap" is found, then the task limit to search for a better plan may be reduced. Therefore, grossly underestimated cardinality estimation may be one example for hitting an optimizer timeout early. In this case, the focus of investigation is cardinality estimation. This is a rarer case than the scenario that's discussed previously about running a complex query, but it is possible.</span></p>
<strong>So, what do I do?</strong>

You may have to do nothing. In many cases the plan you get is quite reasonable and the query you are running is performing well. But in the cases where you find the need to tune and optimize, consider the following options:

First, do this: <span>
</span>
<ul>
 	<li><span>Establish that the query under investigation is slower when you compare it to running on a different build of SQL Server or using a different CE configuration or different system. One of my mottos in performance tuning is "There is no performance problem without a baseline"</span></li>
 	<li><span>Examine your query in detail when you determine its complexity. Upon initial examination,<em> it may not be obvious that the query is complex and involves many joins</em>. This is the common case when views or table-valued functions are involved. For example, on the surface the query may seem to be simple because it joins two views. But when you examine the views, you may find that each view joins 7 tables; When the two views are joined, you end up with a 14-table join. </span></li>
</ul>
<span>The following are different alternatives that you can explore to help improve the performance of the query. Again, be aware that the fact that an optimizer timeout is present in a query plan, does not necessarily mean that it's the reason for query slowness. </span>
<ul class="ng-scope">
 	<li><span>Force a particular plan: If you determine that a particular plan is better for your query through testing, ask QO to select that plan. To do this, check out the following reference articles:</span>
<ul>
 	<li><span><a href="https://docs.microsoft.com/sql/relational-databases/system-stored-procedures/sp-query-store-force-plan-transact-sql?view=sql-server-2017">How to force a plan by using Query Data Store (QDS)</a> </span></li>
 	<li><span><a href="https://docs.microsoft.com/sql/t-sql/queries/hints-transact-sql-query?view=sql-server-2017">Query hints OPTION (USE PLAN &lt;XML PLAN HERE&gt;)</a></span></li>
 	<li><span><a href="https://docs.microsoft.com/sql/relational-databases/performance/plan-guides?view=sql-server-2017">Plan guides</a> <a href="https://docs.microsoft.com/en-us/sql/relational-databases/performance/plan-guides?view=sql-server-2017%20"> </a></span></li>
</ul>
</li>
 	<li><span>Try to reduce the permutations/possibilities that QO needs to consider. This involves testing the query with different options. Note: As is with most decisions with QO, the choices are not always deterministic because there is a large variety of factors considered. </span><span>Therefore, there is no one guaranteed successful strategy. These may improve or worsen the performance of the selected query. For more information, see <a href="https://docs.microsoft.com/sql/t-sql/queries/hints-transact-sql-query?view=sql-server-2017">Query Hints</a>: <a href="https://docs.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-query?view=sql-server-2017%20"> </a></span>
<ul>
 	<li><span>Eliminate the order permutations: OPTION (FORCE ORDER)</span></li>
 	<li><span>Reduce the JOIN possibilities: OPTION (HASH JOIN, MERGE JOIN), OPTION (HASH JOIN, LOOP JOIN) or OPTION (MERGE JOIN)</span><span></span></li>
</ul>
</li>
</ul>
<ul class="ng-scope">
 	<li><span>Change Cardinality Estimation (CE) configuration: You can attempt to change the Cardinality Estimation configuration by switching from Legacy CE to New CE or from New CE to Legacy CE. Changing the Cardinality Estimation configuration can cause the QO to pick a different path when SQL Server evaluates and creates query plans. So even if an optimizer timeout issue occurs, it is possible that you end up with a plan that performs more optimally than the one selected using the alternate CE configuration. For more information, see <a href="https://docs.microsoft.com/en-us/sql/relational-databases/performance/cardinality-estimation-sql-server?view=sql-server-2017&amp;viewFallbackFrom=sql-server-2017%20">how you can assess and choose the best cardinality estimation configuration for your SQL Server system</a>.</span></li>
</ul>
<ul class="ng-scope">
 	<li><span>Optimizer fixes: If you have not enabled QO fixes via T4199 or by using database compatibility levels for SQL Server 2016 and later or ALTER DATABASE SCOPED CONFIGURATION ..QUERY_OPTIMIZER_HOTFIXES = ON, you may consider applying these fixes. This may cause the optimizer to take a different path in plan exploration and therefore possibly end up with a more optimal query plan. For more information, see <a href="https://internal.support.services.microsoft.com/en-us/help/974006" class="managed-link content-anchor-link">SQL Server query optimizer hotfix trace flag 4199 servicing model</a>.</span></li>
</ul>
<ul class="ng-scope">
 	<li><span>Re-write the query: Consider breaking up the single multi-table query into multiple queries by using temporary tables. However, you shouldn't always do this, breaking up the query is just one of the ways to simplify the task for the optimizer. See the following sample:</span><span> </span></li>
</ul>
<pre class="ng-scope" style="padding-left: 60px">select ...
from t1
join t2
on t1.id = t2.id
join t3
on t3.id = t2.id
join t4
on t4.id = t3.id</pre>
<p class="ng-scope"><span>To optimize, try to break down into two queries:</span></p>

<pre class="ng-scope" style="padding-left: 60px">select ...
into #temp1
from t1
join t2
on t1.id = t2.id

select ...
From t3
join #temp1
on t3.id = #temp1.id
join t4
on t4.id = t3.id</pre>
&nbsp;
<p class="ng-scope"><span><strong>Important: </strong>Using Multiple Common Table Expressions (CTEs) is <span style="text-decoration: underline">not</span> an appropriate solution to simplify a query. Multiple CTEs will only increase the complexity of the query. Therefore, it’s counterproductive. CTEs appear to break a query logically, but they are combined into a single query and optimized as a single large join of tables in the end. </span></p>
Enjoy!

Joseph Pilov

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>AGLatency report tool V2.2 introduction</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/aglatency-report-tool-introduction</link>
      <pubDate>Sat, 08 Sep 2018 07:33:14 GMT</pubDate>
      <dc:creator><![CDATA[SimonSu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6505</guid>
      <description><![CDATA[I wrote an article to discuss about data movement latency between AG...]]></description>
      <content:encoded><![CDATA[I wrote an article to discuss about data movement latency between AG groups:

https://blogs.msdn.microsoft.com/psssql/2018/04/05/troubleshooting-data-movement-latency-between-synchronous-commit-always-on-availability-groups/

Now I develop a tool to analyze AG log block movement latency between replicas and create report accordingly (this tool has been updated to Version 2.0).

<a target="_blank" href="https://github.com/suyouquan/AGLatency/blob/master/AGLatency.png" rel="noopener"><img alt="alt text" src="https://github.com/suyouquan/AGLatency/raw/master/AGLatency.png" /></a>

You can download it here: <a href="https://github.com/suyouquan/AGLatency/releases/download/V2.2/AGLatencyV2.2.zip">https://github.com/suyouquan/AGLatency/releases/download/v2.2/AGLatencyV2.2.zip</a>

&nbsp;

Video:https://github.com/suyouquan/AGLatency/blob/master/AGLatency_HowTo.mp4

User Manual:<a href="https://github.com/suyouquan/AGLatency/blob/master/AGLatencyTool_UserManual_V2.pdf">https://github.com/suyouquan/AGLatency/blob/master/AGLatencyTool_UserManual_V2.pdf</a>

You capture log block Xevent trace from both primary and secondary replica for 5-10 minutes and then this tool will generate report about the latency of the log block movement.

<a target="_blank" href="https://github.com/suyouquan/AGLatency/blob/master/AGLatencyReport.png" rel="noopener"><img alt="alt text" src="https://github.com/suyouquan/AGLatency/raw/master/AGLatencyReport.png" /></a>

&nbsp;

Regards,
Simon]]></content:encoded>
    </item>
    <item>
      <title>July 10, 2018 Windows updates cause SQL startup issues due to "TCP port is already in use" errors</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/july-10-2018-windows-updates-cause-sql-startup-issues-due-to-tcp-port-is-already-in-use-errors</link>
      <pubDate>Thu, 26 Jul 2018 18:04:58 GMT</pubDate>
      <dc:creator><![CDATA[Jordon Riel]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6485</guid>
      <description><![CDATA[We have recently become aware of a regression in one of the TCP/IP functions that manages the TCP...]]></description>
      <content:encoded><![CDATA[We have recently become aware of a regression in one of the TCP/IP functions that manages the TCP port pool which was introduced in the July 10, 2018 Windows updates for Windows 7/Server 2008 R2 and Windows 8.1/Server 2012 R2.

This regression may cause the restart of the SQL Server service to fail with the error, “TCP port is already in use”. We have also observed this issue preventing Availability Group listeners from coming online during failover events for both planned and/or unexpected failovers. When this occurs, you may observe errors similar to below in the SQL ERRORLOGs:
<p style="padding-left: 30px"><em><span style="color: #ff0000">Error: 26023, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">Server TCP provider failed to listen on [ &lt;IP ADDRESS&gt; &lt;ipv4&gt; &lt;PORT&gt;]. Tcp port is already in use.</span></em>
<em><span style="color: #ff0000">Error: 17182, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">TDSSNIClient initialization failed with error 0x2740, status code 0xa. Reason: Unable to initialize the TCP/IP listener. Only one usage of each socket address (protocol/network address/port) is normally permitted. </span></em>
<em><span style="color: #ff0000">Error: 17182, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">TDSSNIClient initialization failed with error 0x2740, status code 0x1. Reason: Initialization failed with an infrastructure error. Check for previous errors. Only one usage of each socket address (protocol/network address/port) is normally permitted. </span></em>
<em><span style="color: #ff0000">Error: 17826, Severity: 18, State: 3.</span></em>
<em><span style="color: #ff0000">Could not start the network library because of an internal error in the network library. To determine the cause, review the errors immediately preceding this one in the error log.</span></em>
<em><span style="color: #ff0000">Error: 17120, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">SQL Server could not spawn FRunCommunicationsManager thread. Check the SQL Server error log and the Windows event logs for information about possible related problems.</span></em></p>
If the issue is impacting an Availability Group listener, you may also observe the below error in addition to the above:
<p style="padding-left: 30px"><em><span style="color: #ff0000">Error: 26075, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">Failed to start a listener for virtual network name '&lt;LISTENER NAME&gt;'. Error: 10048.</span></em></p>
Additionally, you may also observe the following errors in the Windows System logs:
<p style="padding-left: 30px"><em><span style="color: #ff0000">The SQL Server (&lt;INSTANCE NAME&gt;) service entered the stopped state. </span></em>
<em><span style="color: #ff0000">The SQL Server (&lt;INSTANCE NAME&gt;) service terminated with the following service-specific error:  Only one usage of each socket address (protocol/network address/port) is normally permitted.</span></em></p>
And if the instance is part of a cluster:
<p style="padding-left: 30px"><em><span style="color: #ff0000">Cluster resource 'SQL Server (&lt;INSTANCE NAME&gt;)' of type 'SQL Server' in clustered role 'SQL Server (&lt;INSTANCE NAME&gt;)' failed. Based on the failure policies for the resource and role, the cluster service may try to bring the resource online on this node or move the group to another node of the cluster and then restart it.  Check the resource and group state using Failover Cluster Manager or the Get-ClusterResource Windows PowerShell cmdlet. </span></em>
<em><span style="color: #ff0000">The Cluster service failed to bring clustered role 'SQL Server (&lt;INSTANCE NAME&gt;)' completely online or offline. One or more resources may be in a failed state. This may impact the availability of the clustered role.</span></em></p>
It is also possible for this issue to impact the creation of a new Availability Group listener. In such scenarios, you may encounter an error like below from SQL Server Management Studio:
<p style="padding-left: 30px"><em><span style="color: #ff0000">The configuration changes to the availability group listener were completed, but the TCP provider of the instance of SQL Server failed to listen on the specified port [&lt;LISTENER NAME&gt;:&lt;PORT&gt;]. This TCP port is already in use. Reconfigure the availability group listener, specifying an available TCP port. For information about altering an availability group listener, see the “ALTER AVAILABILITY GROUP (Transact-SQL)” topic in SQL Server Books Online. (Microsoft SQL Server, Error: 19486)</span></em></p>
For this scenario, you may see errors similar to below in the SQL ERRORLOGs:
<p style="padding-left: 30px"><em><span style="color: #ff0000">Error: 19476, Severity: 16, State: 4.</span></em>
<em><span style="color: #ff0000">The attempt to create the network name and IP address for the listener failed. If this is a WSFC availability group, the WSFC service may not be running or may be inaccessible in its current state, or the values provided for the network name and IP address may be incorrect. Check the state of the WSFC cluster and validate the network name and IP address with the network administrator. Otherwise, contact your primary support provider.</span></em>
<em><span style="color: #ff0000">The Service Broker endpoint is in disabled or stopped state.</span></em>
<em><span style="color: #ff0000">Error: 26023, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">Server TCP provider failed to listen on [ &lt;IP ADDRESS&gt; &lt;PORT&gt;]. Tcp port is already in use.</span></em>
<em><span style="color: #ff0000">Error: 26075, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">Failed to start a listener for virtual network name ‘&lt;LISTENER NAME&gt;:’. Error: 10048.</span></em>
<em><span style="color: #ff0000">Stopped listening on virtual network name ‘&lt;LISTENER NAME&gt;:’. No user action is required.</span></em>
<em><span style="color: #ff0000">Error: 10800, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">The listener for the WSFC resource ‘&lt;RESOURCE GUID&gt;’ failed to start, and returned error code 10048, ‘Only one usage of each socket address (protocol/network address/port) is normally permitted.‘. For more information about this error code, see “System Error Codes” in the Windows Development Documentation.</span></em>
<em><span style="color: #ff0000">Error: 19452, Severity: 16, State: 1.</span></em>
<em><span style="color: #ff0000">The availability group listener (network name) with Windows Server Failover Clustering resource ID ‘&lt;RESOURCE GUID&gt;’, DNS name ‘&lt;LISTENER NAME&gt;’, port &lt;PORT&gt; failed to start with a permanent error: 10048. Verify port numbers, DNS names and other related network configuration, then retry the operation.</span></em></p>
&nbsp;
<h3>Solution:</h3>
The Windows team has already released hotfixes to address this issue and we have had multiple customers already confirm that these hotfixes have resolved issues related to this regression. The below tables list the KB articles for the patches that introduced the regression and the KB articles for their correlating hotfixes.

&nbsp;
<p style="padding-left: 30px"><strong>For Windows 7/Server 2008 R2</strong></p>

<table width="960" style="margin-left: 30px">
<tbody style="padding-left: 30px">
<tr style="padding-left: 30px">
<td width="317" style="padding-left: 30px">
<p style="padding-left: 30px"><strong>KBs that introduced the regression</strong></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="587" style="padding-left: 30px">
<p style="padding-left: 30px"><strong>KBs that fix the regression</strong></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="317" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338818/windows-7-update-kb4338818">July 10, 2018—KB4338818 (Monthly Rollup) </a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="587" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338821/july242018kb4338821osbuildpreviewofmonthlyrollup">July 18, 2018—KB4338821 (Preview of Monthly Rollup) </a></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="319" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338823/windows-7-update-kb4338823">July 10, 2018—KB4338823 (Security-only update) </a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="589" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4345459/stop-error-0xd1-after-a-race-condition-occurs-in-windows-7-service-pac">Improvements and fixes - Windows 7 Service Pack 1 and Windows Server 2008 R2 Service Pack 1 (KB4345459)</a></p>
</td>
</tr>
</tbody>
</table>
<p style="padding-left: 30px"><strong>For Windows Server 2012</strong></p>

<table style="margin-left: 30px">
<tbody style="padding-left: 30px">
<tr style="padding-left: 30px">
<td width="313" style="padding-left: 30px">
<p style="padding-left: 30px"><strong>KBs that introduced the regression</strong></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="365" style="padding-left: 30px">
<p style="padding-left: 30px"><strong>KBs that fix the regression</strong></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="313" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338830/windows-server-2012-update-kb4338830">July 10, 2018—KB4338830 (Monthly Rollup)</a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="368" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338816/july172018kb4338816osbuildpreviewofmonthlyrollup">July 18, 2018—KB4338816 (Preview of Monthly Rollup)</a></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="315" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338820/windows-server-2012-kb4338820">July 10, 2018—KB4338820 (Security-only update)</a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="378" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4345425/improvements-and-fixes-windows-server-2012">Improvements and fixes - Windows Server 2012 (KB4345425)</a></p>
</td>
</tr>
</tbody>
</table>
<p style="padding-left: 30px"><strong>For Windows 8.1/Server 2012 R2</strong></p>

<table style="margin-left: 30px">
<tbody style="padding-left: 30px">
<tr style="padding-left: 30px">
<td width="312" style="padding-left: 30px">
<p style="padding-left: 30px"><strong>KBs that introduced the regression</strong></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="432" style="padding-left: 30px">
<p style="padding-left: 30px"><strong>KBs that fix the regression</strong></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="312" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338815/windows-81-update-kb4338815">July 10, 2018—KB4338815 (Monthly Rollup) </a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="432" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338831/july172018kb4338831osbuildpreviewofmonthlyrollup">July 18, 2018—KB4338831 (Preview <strong>o</strong>f Monthly Rollup) </a></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="314" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338824/windows-81-update-kb4338824">July 10, 2018—KB4338824 (Security-only update) </a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="444" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4345424/improvements-and-fixes-windows-8-1-and-server-2012-r2">Improvements and fixes - Windows 8.1 and Server 2012 R2 (KB4345424)</a></p>
</td>
</tr>
</tbody>
</table>
&nbsp;

You can choose to install either of the applicable KBs that fix the regression in order to resolve issues with SQL service/Availability Group listeners failing to start/come online due to "TCP port is already in use" errors due to this regression. For example, if your system has KB4338815, you can install either KB4338831 or KB4345424 to fix the regression. The difference between the two is that KB4345424 provides only the fix for the regression, whereas KB4338831 includes all of the fixes from KB4338815 as well as some additional quality improvements as a preview of the next Monthly Rollup update (which includes the fix for the regression).

In addition to the monthly rollup/security-only updates mentioned above, this regression was also introduced in updates for specific Windows 10/Server 2016 builds. Please note that the build-specific updates do not have a correlating hotfix-only patch, therefore each build only has one applicable patch to address the regression as noted in the table below.

&nbsp;
<table style="margin-left: 30px">
<tbody style="padding-left: 30px">
<tr style="padding-left: 30px">
<td width="321" style="padding-left: 30px">
<p style="padding-left: 30px"><strong>KB that introduced the regression</strong></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="302" style="padding-left: 30px">
<p style="padding-left: 30px"><strong>KB that fixes the regression</strong></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="321" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338819/windows-10-update-kb4338819">July 10, 2018—KB4338819 (OS Build 17134.165)</a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="305" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-gb/help/4345421/windows-10-update-kb4345421">July 16, 2018—KB4345421 (OS Build 17134.167)</a></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="321" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338825/windows-10-update-kb4338825">July 10, 2018—KB4338825 (OS Build 16299.547)</a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="305" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-gb/help/4345420/windows-10-update-kb4345420">July 16, 2018—KB4345420 (OS Build 16299.551)</a></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="321" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338826/windows-10-update-kb4338826">July 10, 2018—KB4338826 (OS Build 15063.1206)</a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="309" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-gb/help/4345419/windows-10-update-kb4345419">July 16, 2018—KB4345419 (OS Build 15063.1209)</a></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="321" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338814/windows-10-update-kb4338814">July 10, 2018—KB4338814 (OS Build 14393.2363)</a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="309" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4345418/windows-10-update-kb4345418">July 16, 2018—KB4345418 (OS Build 14393.2368)</a></p>
</td>
</tr>
<tr style="padding-left: 30px">
<td width="323" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4338829/windows-10-update-kb4338829">July 10, 2018—KB4338829 (OS Build 10240.17914)</a></p>
</td>
<td width="64" style="padding-left: 30px">
<p style="padding-left: 30px"></p>
</td>
<td width="315" style="padding-left: 30px">
<p style="padding-left: 30px"><a href="https://support.microsoft.com/en-us/help/4345455/windows-10-update-kb4345455">July 16, 2018—KB4345455 (OS Build 10240.17918)</a></p>
</td>
</tr>
</tbody>
</table>
&nbsp;

There can be other causes of the "TCP port is already in use" errors preventing SQL resources from starting/coming online which are not due to the regression mentioned above. If you are encountering similar errors but do not have the July 10, 2018 updates installed on your system, or you already have the fix installed, then you may find our colleague Chris Thompson's blog - <a href="https://blogs.msdn.microsoft.com/sql_pfe_blog/2016/10/05/tcp-port-is-already-in-use/">https://blogs.msdn.microsoft.com/sql_pfe_blog/2016/10/05/tcp-port-is-already-in-use/</a> - useful in identifying whether any other process(es) may be using the port meant for your SQL instance(s).]]></content:encoded>
    </item>
    <item>
      <title>Lesson learned from an Availability Group performance case</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/lesson-learned-from-an-availability-group-performance-case</link>
      <pubDate>Thu, 05 Apr 2018 07:55:47 GMT</pubDate>
      <dc:creator><![CDATA[SimonSu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6325</guid>
      <description><![CDATA[Writer: Simon Su
Technical Reviewer: Pam Lahoud, Sourabh Agarwal, Tejas Shah 

Problem...]]></description>
      <content:encoded><![CDATA[<i><span>Writer: Simon Su</span></i><span>
</span><i><span>Technical Reviewer: Pam </span></i><i><span>Lahoud</span></i><i><span>, Sourabh Agarwal, Tejas Shah</span></i><span> </span>

<b><span>Problem description</span></b><span> </span>

<span>One of my customer</span><span>s</span><span> implemented </span><span>a very high</span><span> workload </span><span>synchronous </span><span>AG</span> <span>(Availability Group)</span><span> solution and he needs 10k transactions/sec in AG databases. </span><span>With the in-memory </span><span>technology,</span><span> this </span><span>10K</span><span>/sec goal</span><span> was</span><span> achieved</span><span> but </span><span>they</span><span> found a very strange behavior </span><span>in</span> <span>transaction processing</span><span> of SQL Server</span><span>. </span><span>During stress testing </span><span>about </span><span>every</span><span> 5~10 minutes the transactions/sec</span> <span>counter (actually it is </span><span>“</span><i><span>SQL Server 2016 XTP </span></i><i><span>Transactions:Transactions</span></i><i><span> Created/sec</span></i><span>”</span> <span>counter) </span><span>could drop to zero suddenly </span><span>and</span><span> quickly resume to normal</span><span> with</span><span>in a second or tens of micro-seco</span><span>nds. Normally you </span><span>will not</span> <span>observe</span><span> this interesting</span><span> thing because the duration of the dip is so short. M</span><span>y customer’s transaction is very time-</span><span>sensitive</span><span> so he has his own transaction/sec calculation formula and he found this s</span><span>hort sharp drop in his monitor log</span><span>. If we observe the </span><span>“</span><i><span>SQL Server 2016 XTP </span></i><i><span>Transactions:Transactions</span></i><i><span> Created/sec</span></i><i><span>”</span></i> <span>in </span><span>his captured </span><span>performance monitor</span><span> log from primary replica</span><span>, </span><span>it looks like below</span><span>:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/transaction_created_sec111.png"><img width="1024" height="670" class="alignnone size-large wp-image-6425" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/transaction_created_sec111-1024x670.png" /></a>

<span>I highlight the </span><span>sharp drop </span><span>with red circle </span><span>in above chart. If we export the performance monitor log to text file, the </span><span>“Transaction Created/sec” </span><span>counter has below values:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/transaction_created_grid.png"><img width="300" height="348" class="alignnone wp-image-6395" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/transaction_created_grid.png" /></a>

<span>You can see that </span><span>the counter </span><span>suddenly dropped to 33 at 37:53.4 as highlighted above. I </span><span>do not</span><span> think this drop is serious since </span><span>SQL server keeps the same high transaction processing speed from next second. However, </span><span>my customer </span><span>is</span> <span>curious</span><span> to</span><span> this little dip and he want to</span><span> find out the root cause</span><span> of </span><span>it. </span><span> </span>

<b><span>How to troubleshoot AG</span></b><b><span> performance delay?</span></b><span> </span>

<span>For AG performance </span><span>troubleshooting,</span><span> we have two </span><span>very good</span> <span>public </span><span>articles:</span><span> </span>

<a href="https://blogs.msdn.microsoft.com/saponsqlserver/2013/04/21/sql-server-2012-alwayson-part-11-performance-aspects-and-performance-monitoring-i/"><span>https://blogs.msdn.microsoft.com/saponsqlserver/2013/04/21/sql-server-2012-alwayson-part-11-performance-aspects-and-performance-monitoring-i/</span></a><span> </span>

<span> </span><a href="https://blogs.msdn.microsoft.com/saponsqlserver/2013/04/24/sql-server-2012-alwayson-part-12-performance-aspects-and-performance-monitoring-ii/"><span>https://blogs.msdn.microsoft.com/saponsqlserver/2013/04/24/sql-server-2012-alwayson-part-12-performance-aspects-and-performance-monitoring-ii/</span></a><span> </span>

<span>If </span><span>you are not familiar with AG performance troubleshooting </span><span>concepts and steps </span><span>please read above two articles first.</span><span> L</span><span>et</span><span> us</span><span> look at</span><span> the</span><span> two key performance counters to check the transaction delay</span><span> in my customer’s synchronous-commit mode replicas</span><span>:</span><span> </span>
<ul>
 	<li><span></span><span>SQL </span><span>Server:Database</span><span> Replica –&gt; Transaction Delay</span></li>
 	<li><span>SQL </span><span>Server:Database</span><span> Replica –&gt; Mirrored Write Transactions/sec</span><span></span></li>
</ul>
<span>In performance </span><span>monitor,</span> <span>these two counters </span><span>look</span><span> like below</span><span>:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/transaction_delay.png"><img width="1024" height="800" class="alignnone size-large wp-image-6405" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/transaction_delay-1024x800.png" /></a>

&nbsp;

<span>The </span><span>“</span><i><span>Transaction Delay</span></i><span>”</span><span> value is an accumulation of the delay of all the current transaction delay in millisecond. You can see that t</span><span>he </span><span>“</span><i><span>Transaction Delay</span></i><span>”</span><span> counter has the same spikes as the sudden drop of the </span><span>“</span><i><span>T</span></i><i><span>ransactions </span></i><i><span>C</span></i><i><span>reated/</span></i><i><span>S</span></i><i><span>ec</span></i><span>”</span><span>. Its spikes indicate</span><span> that at those time points the AG transactions have time delay during commits.  </span><span>This gives us </span><span>a very good</span><span> start point. We can focus on the </span><span>t</span><span>ransaction </span><span>d</span><span>elay in </span><span>our </span><span>AG performance troubleshooting.</span><span> </span>

<span>So</span><span> w</span><span>ho causes the transaction delay? Is it primary replica, secondary replica, or other factors like network traffic?</span><span> </span>

<span>As </span><span>a must</span><span> go-through step for performance troubleshooting w</span><span>e captured performance monitor logs</span><span> to check how the performance behaved on both replicas. </span><span> We want to find out whether there is any performance bottleneck existing in primary</span><span> or </span><span>secondary. For example, </span><span>whether </span><span>CPU usage</span><span> is high when transaction delay spike happens</span><span>,</span><span> whether</span><span> disk </span><span>queue length is long,</span><span> disk latency</span><span> is large, etc.  We expect to find something </span><span>that h</span><span>as the same spike trend as the “</span><i><span>T</span></i><i><span>ransaction </span></i><i><span>C</span></i><i><span>reated/</span></i><i><span>s</span></i><i><span>ec</span></i><span>”</span><span> or </span><span>“</span><i><span>T</span></i><i><span>ransaction </span></i><i><span>Delay</span></i><span>”</span><span>. </span><span>Unfortunately,</span><span> we </span><span>do not</span> <span>anything interesting</span><span>. CPU usage is as low 30%, Disk speed is quite fast. </span><span>No disk queue length at all. </span><span>We </span><span>then </span><span>check</span><span>ed</span> <span>AG related counters, like </span><span>the log send queue and </span><span>the </span><span>recovery queue as the above two links mentioned but </span><span>again we do not find anything helpful</span><span>. </span><span>We</span><span> have below conclusions according to the performance monitor log:</span><span> </span>

<span>--</span><span>There is n</span><span>o overall CPU performance bottleneck</span><span> </span>

<span>--</span><span>There is no</span><span> disk performance bottleneck, especially no disk issue on second replica.</span><span> </span>

<span>--</span><span>There is n</span><span>o network traffic issue.</span><span> </span>

<span>In short</span><span>,</span><span> the performance monitor log </span><span>does not</span><span> tell us much why the transaction delay is </span><span>happening</span><span>. </span><span> </span><span> </span>

<span> </span>

<b><span>Who introduces the transaction delay?</span></b><span> </span>

<span>To investigate the details of the AG transaction performance, we need to study the performance of data movement between </span><span>the two</span><span> synchronous replicas. I </span><span>wrote</span><span> another article discussing the detailed steps to troubleshoot log block movement latency:</span><span> </span>

<i><span>Troubleshooting data movement latency between synchronous-commit </span></i><i><span>AlwaysOn</span></i><i><span> Availability Groups</span></i><span>
</span><a href="https://blogs.msdn.microsoft.com/psssql/2018/04/05/troubleshooting-data-movement-latency-between-synchronous-commit-always-on-availability-groups/"><span>https://blogs.msdn.microsoft.com/psssql/2018/04/05/troubleshooting-data-movement-latency-between-synchronous-commit-always-on-availability-groups/</span></a><span> </span>

<span>I use similar script as above article </span><span>to capture </span><span>Xevent</span><span> traces on both replicas</span><span>. From the </span><span>xevent</span><span> logs</span><span> we </span><span>find out</span><span> that the transaction latency is not caused by below factors:</span><span> </span>

<span>&lt;&gt;Network transfer</span><span> </span>

<span>&lt;&gt;Local log </span><span>harden</span><span> </span>

<span>&lt;&gt;Remote Log </span><span>harden</span><span> </span>

<span>The latency is happening on the primary replica after the primary receives the </span><span>LSN</span><span> harden message from remote node. This is a big milestone because it gives us clear direction where to investigate further. </span><span>We should focus on the primary to know why it cannot commit the transaction in time. </span><span>Below is the figure to tell you where comes the delay:</span><span> </span>

<span> </span><a href="https://msdnshared.blob.core.windows.net/media/2018/04/xevent_AG_commit_grid.png"><img width="1024" height="360" class="alignnone size-large wp-image-6385" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/xevent_AG_commit_grid-1024x360.png" /></a>

<span>From above </span><span>xevent</span> <span>log </span><span>we can see the delay</span><span> (</span><span>about 3.2 seconds gap)</span> <span>occurs</span><span> mainly between </span><span>xevents</span><span> of </span><span>hadr_receive</span><span>_harden</span><span>_lsn_message</span><span> and </span><span>hadr_db_commit_mgr_update_harden</span><span>, i.e. between </span><span>step 13 and step 14 </span><span>in</span> <span>below figure:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/xevent_steps_AG.png"><img width="1024" height="678" class="alignnone size-large wp-image-6375" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/xevent_steps_AG-1024x678.png" /></a>

<span>Normally once </span><span>hadr_receive</span><span>_harden</span><span>_lsn_message</span><span> arrives from remote </span><span>replicas,</span><span> SQL server will process the message and update LSN progress very quickly. Now we see it has delay </span><span>to process</span><span> the messages. </span><span> </span>

<b><span>HADR_LOGPROGRESS_SYNC</span></b> <b><span>Wait</span></b><span> </span>

<span>Now comes the challenge. How to troubleshoot </span><span>this </span><span>further, why step 13-14 produces the latency?  To get the answer of this I use below script</span><span> (</span><span>wait.sql</span><span>)</span><span> to understand the request status </span><span>for </span><span>every second: </span><span> </span>

<span>declare</span><span>  @</span><span>i</span> <span>integer </span><span>=</span><span>0</span><span> </span>

<span style="color: #0000ff">WHILE</span><span>(</span><span>1</span><span>=</span><span>1</span><span>)</span><span> </span>

<span style="color: #0000ff">BEGIN </span>

<span style="color: #0000ff">set</span><span> @</span><span>i</span><span>=</span><span>@i</span><span>+</span><span>1</span><span> </span>

<span><span style="color: #0000ff">RAISERROR</span> </span><span>(</span><span style="color: #ff0000">'-- sys.dm_exec_requests --'</span><span>,</span><span> 0</span><span>,</span><span> 1</span><span>) </span><span style="color: #0000ff">WITH NOWAIT </span>

<span style="color: #0000ff">SELECT</span><span><span style="color: #0000ff"> </span> </span><span>GETDATE</span><span>() </span><span style="color: #ff0000">'runtime'</span><span>, * </span><span>from </span><span style="color: #008000">sys.dm_exec_requests </span><span>where</span> <span>session_id</span> <span>&gt;</span><span>50</span><span> </span>

<span><span style="color: #0000ff">RAISERROR</span> </span><span>(</span><span>'<span style="color: #ff0000">-- </span></span><span style="color: #ff0000">sys.dm_os_waiting_tasks --'</span><span>,</span><span> 0</span><span>,</span><span> 1</span><span>) </span><span style="color: #0000ff">WITH NOWAIT</span><span> </span>

<span style="color: #0000ff">SELECT</span><span><span style="color: #0000ff"> </span> </span><span>getdate</span><span>() </span><span style="color: #ff0000">'runtime'</span><span>,* </span><span>from </span><span style="color: #008000">sys.dm_os_waiting_tasks </span><span style="color: #0000ff">WHERE</span><span><span style="color: #0000ff">   </span> </span><span>session_id</span> <span>&gt;</span><span>50</span><span> </span>

<span style="color: #008000">--Please don’t use so small value in production, it will eat up one core’s usage. </span>

<span style="color: #0000ff">WAITFOR DELAY </span><span>'<span style="color: #ff0000">00:00:01.000</span>'</span><span>;</span><span> </span>

<span style="color: #0000ff">END </span>

<span style="color: #0000ff">GO </span>

<span> </span>

<span>I am lucky that from </span><span>the script</span><span> output I have a big finding. Whenever the transaction sharp drop occurs there are always HADR_LOGPROGRESS_SYNC waits happening</span><span> there</span><span> as well</span><span>:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/hadr_logprogress_sync.png"><img width="1024" height="180" class="alignnone size-large wp-image-6365" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/hadr_logprogress_sync-1024x180.png" /></a>

<span>HADR_LOGPROGRESS_SYNC wait is “Concurrency control wait when updating the log progress status of database replicas”.</span><span> To update log progress for an AG database, like the latest harden LSN from remote replica </span><span>etc</span><span>, a thread </span><span>has to</span><span> acquire the HADR_LOGPROGRESS_SYNC lock first. </span><span>For any given point in </span><span>time,</span><span> only one thread can hold this lock</span><span>, and when this lock is held by someone o</span><span>ther threads </span><span>who want to update the log progress </span><span>ha</span><span>ve</span><span> to</span><span> wait until the </span><span>lock</span><span> release. One example is that thread A holds this </span><span>lock</span><span> to update the latest harden LSN is 1:20:40, after thread A finishes, </span><span>it releases the lock, and then </span><span>thread B holds this </span><span>lock</span><span> and update remote harden LSN to 1:20:44. LSN progress update </span><span>has to</span><span> be serialize</span><span>d</span> <span>to make the log consistent. </span><span> </span>

<span> </span>

<span>Besides HADR</span><span>_LOGPROGRESS_SYNC waits</span><span> in the output, </span><span>there are also lots of HADR_SYNC_COMMIT</span><span> occurring. This is expected</span><span> because we know </span><span>that </span><span>there is latency happening at that time (see the transaction delay spike at the beginning of this article)</span><span>. Here is the screenshot of the HADR_SYNC_COMMIT threads:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/wait_type_hadr_logprogress_sync.png"><img width="388" height="586" class="alignnone wp-image-6355" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/wait_type_hadr_logprogress_sync-678x1024.png" /></a>

<span>W</span><span>hat are the relationship between HADR_LOGPROGRESS_SYNC</span><span> wait</span><span> and HADR_SYNC_COMMIT</span><span> wait</span><span>? It takes me sometime to understand that </span><span>for synchronous replica, log block could contain several log records from different transactions and these transactions </span><span>are</span><span> grouped to commit </span><span>to</span><span> replica, this is the behavior </span><span>of what is called “G</span><span>roup </span><span>C</span><span>ommit</span><span>”</span><span> in </span><span>A</span><span>vailability </span><span>G</span><span>roups. When the log block is hardened on remote replica, it will send the harden LSN to primary (we call this sync progress message</span><span>s</span><span>). The primary receives the harden LSN and then will acquire HADR_LOGPROGRESS_SYNC lock to update the latest harden LSN to the primary database. All those transactions waiting on HADR_SYNC_COMMIT will be signaled that</span><span> the</span><span> remote commit is done if their expected harden LSN is less that the latest harden LSN from rem</span><span>ote replica. </span><span>When local commit and remote commit are both done then the </span><span>use</span><span>r</span> <span>transaction is </span><span>called “</span><span>committed</span><span>”. Note that we are talking synchronous-commit mode replicas here.</span><span> If the thread cannot </span><span>acquire HADR</span><span>_LOGPROGRESS_SYNC lock </span><span>to update the latest LSN then there could be lots of threads being in HADR_SYNC_COMMIT wait </span><span>because they are not able to get signal from the log progress update thread</span><span>.</span><span> </span>

<span> </span>

<span>Now comes to the million dollars question. </span><span>Why is</span><span> there</span><span> long HADR_LOGPROGRESS_SYNC</span><span> wait happening</span><span>? In other words, who owns the HADR_LOGPROGRESS_SYNC lock for that long </span><span>time</span><span>?</span> <span>From the HADR_LOGPROGRESS_SYNC wait figure shown above</span><span>,</span><span> we see</span><span> that</span><span> SPID 438 has </span><span>last</span><span>_</span><span>wait_type</span><span> of HADR_LOGPROGRESS_SYNC, is it possible </span><span>it is the</span><span> owner of the HADR_LOGPROGRESS_SYNC lock? Later investigation </span><span>actually confirms</span><span> that SPID 438 is holding HADR_LOGPROGRESS_SYNC at that </span><span>time. H</span><span>owever why does it hold the lock so long?</span><span> </span>

<span> </span>

<span> </span>

<b><span>Scheduler </span></b><b><span>Y</span></b><b><span>ielding issue</span></b><span> </span>

<span>We check</span><span>ed</span><span> the </span><span>output of </span><span>wait.sql</span><span> to see if we can get the answer why SPID 438 h</span><span>eld </span><span>the lock </span><span>for more than 2 seconds</span><span>. From the output I see </span><span>SPID 438</span><span> status is “background” so I am not able to know whether it is </span><span>“</span><span>running</span><span>”</span><span> or in runnable queue. To </span><span>figure out </span><span>whether this thread is </span><span>really </span><span>running</span><span> or runnable</span><span> we can check the active worker thread of </span><span>its </span><span>scheduler. If the active worker thread </span><span>of the scheduler</span> <span>is </span><span>the same as th</span><span>is</span> <span>thread</span><span> then we know </span><span>this thr</span><span>ead is on the scheduler running. I wrote below article to demonstrate how to troubleshoot thread scheduling and yielding:</span><span> </span>

<span>Troubleshooting SQL Server Scheduling and Yielding</span><span> </span>

<a href="https://blogs.msdn.microsoft.com/psssql/2018/04/05/troubleshooting-sql-server-scheduling-and-yielding/"><span>https://blogs.msdn.microsoft.com/psssql/2018/04/05/troubleshooting-sql-server-scheduling-and-yielding/</span><span> </span></a>

<span> </span>

<span>I use the same technology </span><span>to capture logs. The finding is simple.  The </span><span>HADR_LOGPROGRESS_SYNC thread was in runnable queue for about 100ms-1 second and therefore it </span><span>cause</span><span> lots of HADR_SYNC_COMMIT waits with the same waiting duration, and no doubt it then caused the transaction delay spike as you see in the beginning of this article. Here is the scheduler </span><span>yield_count</span><span> looks like:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/sheduler_non_yield.png"><img width="1024" height="221" class="alignnone size-large wp-image-6345" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/sheduler_non_yield-1024x221.png" /></a>

<span>You can see that </span><span>yield_count</span><span> (</span><span>27076130</span><span>) of scheduler 23</span> <span>does not</span><span> change within 1 second which means someone is actively running on the scheduler without yielding to other threads. You also see runnable task is 7 which means there are 7 threads are waiting in runnable queue.</span><span> </span>

<span>The </span><span>wait_info</span> <span>xevent</span><span> trace also confirms the </span><span>HADR_LOGPROGRESS_SYNC thread is in runnable queue waiting for a while:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/xeven_wait_info_signal.png"><img width="1024" height="272" class="alignnone size-large wp-image-6335" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/xeven_wait_info_signal-1024x272.png" /></a>

<span>You see for SPID 438 the </span><span>signal_duration</span><span> (2407ms)</span><span> is the same as duration </span><span>column. This</span><span> means it has been sitting in runnable queue for about 2407 </span><span>ms.</span><span> </span>

<span> </span>

<b><span>Who is holding the scheduler without yielding</span></b><span> </span>

<span>From above investigation we understand that the thread who owns the </span><span>HADR_LOGPROGRESS_SYNC lock cannot get chance to run on scheduler in time </span><span>and hence</span><span> it causes transaction delay spike (i.e. sharp transaction rate drop). Using the technology described in the article “<a href="https://blogs.msdn.microsoft.com/psssql/2018/04/05/troubleshooting-sql-server-scheduling-and-yielding/">Troubleshooting SQL Server Scheduling and Yielding</a>” we finally find out the “offending” thread is </span><span>running a</span><span> query which will access a big in-memory table. The memory table is big and its index is also very huge, </span><span>and it often</span><span> take</span><span>s</span><span> hundreds of microseconds to run. </span><span> </span><span>In case</span><span> the </span><span>worker thread who picks up the </span><span>HADR_LOGPROGRESS_SYNC message </span><span>to</span><span> process</span><span> is</span><span> on the same scheduler then they have chance to competing for CPU resource at the same time. In this </span><span>case,</span> <span>SQL Server is running the</span><span> query without yielding for about one second</span><span>,</span><span> and</span><span> then this one-second non-yielding scheduler time</span><span> causes the HADR_LOGPROGRESS_SYNC thread to </span><span>wait in runnable queue for one second. </span> <span>Because</span><span> of </span><span>this</span><span>, </span><span>all</span><span> of</span><span> the</span><span> HADR_LOGPROGRESS_SYNC waiter need to wait for 1 second</span><span> for the lock</span><span>,</span><span> which in turn</span> <span>block those</span><span> threads in HADR_SYNC_COMMIT waits </span><span>for one second accordingly.</span><span> </span>

<span>The solution is simple. </span><span> We involved product group to add yielding code when scanning the in-memory table and then the problem is fixed ( in SQL2016 SP1  CU7, see <a href="https://support.microsoft.com/en-us/help/4057280/high-cpu-usage-when-large-index-use-in-query-on-memory-optimized-table">https://support.microsoft.com/en-us/help/4057280/high-cpu-usage-when-large-index-use-in-query-on-memory-optimized-table</a></span><span>).</span>

<span> </span>

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Troubleshooting SQL Server Scheduling and Yielding</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/troubleshooting-sql-server-scheduling-and-yielding</link>
      <pubDate>Thu, 05 Apr 2018 04:32:13 GMT</pubDate>
      <dc:creator><![CDATA[SimonSu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6245</guid>
      <description><![CDATA[Writer: Simon Su
Technical Reviewer: Pam Lahoud, Sourabh Agarwal, Tejas Shah
  

Scheduling and...]]></description>
      <content:encoded><![CDATA[<i><span>Writer: Simon Su</span></i><span>
</span><i><span>Technical Reviewer: Pam </span></i><i><span>Lahoud</span></i><i><span>, Sourabh Agarwal, Tejas Shah</span></i><span>
</span><span> </span><span> </span>

<b><span>Scheduling and </span></b><b><span>Y</span></b><b><span>ielding </span></b><b><span>Knowledge R</span></b><b><span>ecap</span></b><span> </span>

<span>We all know that SQL server is a multi-threads and multi-tasks system and it has its own</span><span> thread scheduling mechanism </span><span>which</span> <span>i</span><span>s</span><span> a small part of job of what </span><span>we call </span><span>SQLOS</span><span>. If you are not familiar with SQLOS you can </span><span>refer to</span><span> below two links for the details:</span><span> </span>

<span>A new platform layer in SQL Server 2005 to exploit new hardware capabilities and their trends</span><span>
</span><a href="https://blogs.msdn.microsoft.com/slavao/2005/07/20/platform-layer-for-sql-server"><span>https://blogs.msdn.microsoft.com/slavao/2005/07/20/platform-layer-for-sql-server</span><span> </span></a>

<span>Inside the SQL Server 2000 User Mode Scheduler</span><span>
</span><a href="https://msdn.microsoft.com/library/aa175393.aspx"><span>https://msdn.microsoft.com/li</span><span>b</span><span>rary/aa175393.aspx</span></a><span> </span>

<span>How To Diagnose and Correct Errors 17883, 17884, 17887, and 17888</span><span>
</span><a href="https://technet.microsoft.com/en-us/library/cc917684.aspx"><span>https://technet.micros</span><span>o</span><span>ft.com/en-us/library/cc917684.aspx</span></a><span> </span>

<span> </span>

<span>Inside SQL server source code, there are </span><span>many</span> <span>voluntary </span><span>yield points to make multi-threads run efficiently and cooperatively. </span><span>If a SQL Server worker thread does not voluntarily yield, it will likely prevent other threads from running</span><span> on the same scheduler. </span><span>When the owner of the scheduler has not yielded within 60 seconds and as a result pending requests (tasks) are stalled, SQL </span><span>Server will log “non-yielding scheduler error in error log like below:</span><span> </span>

<span> </span>

<span>2018-03-10 21:16:35.89 Server      ***********************************************</span><span>
</span><span>2018-03-10 21:16:35.89 Server      *</span><span>
</span><span>2018-03-10 21:16:35.89 Server      * BEGIN STACK DUMP:</span><span>
</span><span>2018-03-10 21:16:35.89 Server      *   03/10/18 21:16:35 </span><span>spid</span><span> 22548</span><span>
</span><span>2018-03-10 21:16:35.89 Server      *</span><span>
</span><span>2018-03-10 21:16:35.89 Server      * Non-yielding Scheduler</span><span>
</span><span>2018-03-10 21:16:35.89 Server      *</span><span>
</span><span>2018-03-10 21:16:35.89 Server      ***********************************************</span><span> </span>

<span> </span>

<span>And a mini dump is generated in the LOG folder. You can contact Microsoft support to understand the details of the mini dump and check whether this non-yielding scheduler warning is a serious issue or </span><span>it </span><span>can be safely ignored. </span><span> </span>

<span> </span>

<b><span>How long a thread </span></b><b><span>has been</span></b><b><span> running </span></b><b><span>on scheduler </span></b><b><span>without yield</span></b><b><span>ing</span></b><span> </span>

<span> </span><span>Regarding SQL server scheduling and yielding all things sound perfect until I got deeply involved in a customer’s case to troubleshoot a sudden transaction drop issue. I will write another post to share the story of that. In that case I need to find out how long a SQL server worker thread </span><span>has been</span> <span>running on</span> <span>a</span><span> scheduler without yielding. </span><span>We know if non-yielding condition exceeds 60 seconds SQL server will lo</span><span>g non-yielding error accordingly. </span><span>However</span><span> how about those threads that ha</span><span>ve</span><span> been running on the schedulers </span><span>without yielding but less than 60 seconds?</span><span> Do we have a way to get the detail </span><span>information of these non-yielding threads</span><span>?</span><span> Note that you cannot use the CPU time column of a statement in profiler trace because the CPU time of a query doesn’t mean</span><span> it has</span><span> been</span><span> exclusively running on the CPU without yielding for that long. Some yields could have occurred among the </span><span>life cycle of a </span><span>query execution and SQL server records </span><span>these</span><span> yield</span><span>s</span><span> with “</span><span>SOS_SCHEDULER_YIELD”</span><span> wait type. </span><span> </span>

<span> </span>

<span>SQL server worker has a quantum target </span><span>of</span><span> 4ms (milliseconds), however due to the complexity of the reality there could be places that the code runs unexpectedly long before it reaches to the voluntary </span><span>yield</span><span> point. Normally this is not a problem because </span><span>that thread will eventually yield without starving the runnable list threads. In case we really need to know the details of this kind of things (like how long a thread has been running on the CPU without yielding) we can </span><span>use</span><span> below approaches. </span><span> </span>

<span>Before talking abou</span><span>t the method to troubleshoot this kind of </span><span>scheduling and yielding issue let’s see how a scheduler and its tasks look like:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/scheduler2.png"><img width="464" height="364" class="alignnone wp-image-6315" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/scheduler2-1024x805.png" /></a>

<span>Here is the logic how scheduling works:</span><span> </span><span> </span>
<ol>
 	<li><span>When the </span><span>task</span> <span>is running, it is in “Running” state and it is the active worker of the scheduler.</span><span> </span></li>
 	<li><span>When the </span><span>t</span><span>ask</span> <span>waits for nothing but CPU to run it is put into “Runnable” queue of a scheduler</span><span> </span></li>
 	<li><span>When the </span><span>task</span> <span>is waiting for something (like </span><span>lock</span><span>,disk</span><span> I/O,</span> <span>etc</span><span>) it is in “Suspended” state.</span><span> </span></li>
 	<li><span>If the suspended </span><span>task</span> <span>finishes the waits</span><span> (wait for nothing)</span><span> and is ready to run, it is put to the end of the Runnable queue.</span><span> </span></li>
 	<li><span>If the running thread voluntarily yields it is put back to end of Runnable queue.</span><span> </span></li>
 	<li><span> If the running thread needs to wait for something it is switched out of the scheduler and </span><span>is </span><span>put into </span><span>s</span><span>uspended status.</span><span> </span></li>
 	<li><span>If the running thread f</span><span>inishes its work then the top thread of</span><span> the runnable queue becomes the “Running” thread.</span><span> </span></li>
</ol>
&nbsp;

<b><span>Resource Wait Time</span></b><span> </span>

<span>For a suspended </span><span>task</span> <span>if it is waiting for something we have lots of way to get the wait </span><span>related information</span><span> like </span><span>wait_time</span><span>, </span><span>wait_resource</span><span> etc. For example, </span><span>both </span><span>DMV</span><span>s</span><span> of </span><span>sys.dm_os_waiting_tasks</span><span> or </span><span>sys.dm_exec_requests</span><span> can tell the detail wait statistics:</span><span> </span>

<span></span>

<span style="color: #0000ff">SELECT</span> session_id,status,wait_time,wait_type,wait_resource,last_wait_type
<span style="color: #0000ff">FROM</span> <span style="color: #008000">sys.dm_exec_requests</span>
<span style="color: #0000ff">WHERE</span> session_id=52

<span>Result:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/sys.dm_exec_requests.png"><img width="1623" height="195" class="alignnone size-full wp-image-6295" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/sys.dm_exec_requests.png" /></a>

&nbsp;

<b><span>Signal </span></b><b><span>W</span></b><b><span>ait </span></b><b><span>T</span></b><b><span>ime</span></b><span> </span>

<span>If you </span><span>query “</span><span>sys.dm_os_wait_stats</span><span>” you will find a column called “</span><span>signal_wait_time_ms</span><span>”. Signal wait time is t</span><span>he time </span><span>a</span><span> thread spent on the scheduler’s “runnable” list waiting to get on the CPU and run again.</span> <span>sys.dm_os_wait_stats</span><span> output gives you an overall picture of </span><span>waits for </span><span>each wait type </span><span>including the </span><span> signal wait time. If you want to get the detailed information of signal wait time of each individual session you can leverage </span><span>Xevent</span> <span>wait_info</span><span> and </span><span>wait_info_external</span><span>. There is an excellent article </span><span>discussing</span><span> how to use </span><span>wait_info</span><span> event to trace REDO latency:</span><span> </span>

<a href="https://blogs.msdn.microsoft.com/alwaysonpro/2015/01/06/troubleshooting-redo-queue-build-up-data-latency-issues-on-alwayson-readable-secondary-replicas-using-the-wait_info-extended-event/"><span>https://blogs.msdn.microsoft.com/alwaysonpro/2015/01/06/troubleshooting-redo-queue-build-up-data-latency-issues-on-alwayson-readable-secondary-replicas-using-the-wait_info-extended-event/</span></a><span> </span>

<span>The same approach applies to all other waits.</span><span> I use below steps to simulate signal waits</span><span>:</span><span> </span>

<span>1. </span><span>Create a table in </span><span>tempdb</span><span> </span>

<span style="color: #0000ff">USE </span><span>tempdb</span><span>
</span><span style="color: #0000ff">CREATE TABLE</span><span> t1</span> <span>(</span><span>c1 </span><span style="color: #0000ff">int</span><span>)</span><span> </span>

<span>2. </span><span>Alter SQL server to use only one CPU (never do this on production server!):</span><span> </span>

<span style="color: #0000ff">EXEC </span><span style="color: #993300">SP_CONFIGURE</span><span>  </span><span style="color: #ff0000">'affinity mask'</span><span>,</span><span>2 </span><span style="color: #008000">--use only the second CPU of the system
</span><span style="color: #0000ff">RECONFIGURE WITH OVERRIDE</span><span> </span>

<span>3. </span><span>Now start the </span><span>xevent</span><span> trace:</span><span> </span>

<span style="color: #0000ff">IF </span><span><span style="color: #808080">EXISTS</span>(</span><span style="color: #0000ff">SELECT </span><span>*</span> <span style="color: #0000ff">FROM </span><span style="color: #008000">sys.server_event_sessions</span> <span style="color: #0000ff">WHERE name</span> <span>LIKE</span> <span style="color: #ff0000">'SignalWaitDemo'</span><span>)</span><span>
</span><span style="color: #0000ff">DROP EVENT SESSION</span><span> [</span><span>SignalWaitDemo</span><span>] </span><span style="color: #0000ff">ON SERVER</span><span> </span><span>
</span><span style="color: #0000ff">GO</span><span><span style="color: #0000ff"> </span>
</span><span style="color: #0000ff">CREATE EVENT SESSION</span><span> [</span><span>SignalWaitDemo</span><span>] </span><span style="color: #0000ff">ON SERVER</span><span> </span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlos</span><span>.</span><span>wait_info</span><span>(</span><span>
</span><span>    </span><span style="color: #0000ff">ACTION</span><span>(</span><span>sqlos</span><span>.</span><span>scheduler_id</span><span>,</span><span>sqlserver</span><span>.</span><span>database_id</span><span>,</span><span>sqlserver</span><span>.</span><span>session_id</span><span>)</span><span>
</span><span>    </span><span style="color: #008000">--Capture End event (opcode=1) only</span><span><span style="color: #008000"> </span>
</span><span><span style="color: #0000ff">WHERE</span> </span><span>(</span><span>[package0]</span><span>.</span><span>[equal_uint64]</span><span>(</span><span>[opcode]</span><span>,(</span><span>1</span><span>))</span><span> </span><span>
</span><span style="color: #008000">--Only capture user sessions (session_id&gt;=50)</span><span><span style="color: #008000"> </span>
</span><span>AND</span><span> [package0]</span><span>.</span><span>[greater_than_equal_uint64]</span><span>(</span><span>[</span><span>sqlserver</span><span>]</span><span>.</span><span>[</span><span>session_id</span><span>]</span><span>,(</span><span>50</span><span>))</span><span>
</span><span style="color: #008000">--You can change duration to bigger value, say, change below 10 ms to 3000ms</span><span><span style="color: #008000"> </span>
</span><span>AND</span><span> [duration]</span><span>&gt;=(</span><span>10</span><span>)))</span><span>
</span><span style="color: #0000ff">ADD TARGET</span><span> package0</span><span>.</span><span>event_file</span><span>(</span><span style="color: #0000ff">SET filename</span><span>=</span><span>N'<span style="color: #ff0000">E:\temp\</span></span><span style="color: #ff0000">Wait_Info_Demo.xel'</span><span>)</span><span>
</span><span><span style="color: #0000ff">WITH</span> </span><span>(</span><span>MAX_MEMORY</span><span>=</span><span>4096 KB</span><span>,</span><span>EVENT_RETENTION_MODE</span><span>=</span><span>ALLOW_SINGLE_EVENT_LOSS</span><span>,</span><span>MAX_DISPATCH_LATENCY</span><span>=</span><span>30 </span><span> </span><span>SECONDS</span><span>,</span><span>MAX_EVENT_SIZE</span><span>=</span><span>0 KB</span><span>,</span><span>MEMORY_PARTITION_MODE</span><span>=</span><span style="color: #0000ff">NONE</span><span>,</span><span>TRACK_CAUSALITY</span><span>=</span><span style="color: #0000ff">OFF</span><span>,</span><span>STARTUP_STATE</span><span>=</span><span style="color: #0000ff">OFF</span><span>)</span><span> </span>

<span style="color: #0000ff">GO </span>

<span> </span><span style="color: #0000ff">ALTER EVENT SESSION</span><span> [</span><span>SignalWaitDemo</span><span>] </span><span style="color: #0000ff">ON SERVER STATE</span><span>=</span><span style="color: #0000ff">START</span><span>;</span><span> </span>

<span> 4. </span><span>Then use ostress.exe tool to simulate </span><span>workload</span> <span>to SQL server:</span><span> </span>

<span>ostress</span><span> -n1000 -r200 -S. </span><span>–</span><span>i</span><span>signal_wait</span><span>.sql</span><span> </span>

<span>In </span><span>signal_wait.sql</span><span>, I have below query inside:</span><span> </span>

<span style="color: #0000ff">SET NOCOUNT ON
USE </span><span>tempdb</span><span>
</span><span style="color: #0000ff">DECLARE</span><span> @</span><span>I</span> <span style="color: #0000ff">int</span><span>=</span><span>0</span><span>,</span><span>@k </span><span style="color: #0000ff">int</span><span>=</span><span>0</span><span> </span><span>
</span>

<span style="color: #0000ff">BEGIN</span><span>
</span><span style="color: #0000ff">IF</span><span>(</span><span>rand</span><span>()&gt;</span><span>0.9</span><span>)</span><span>update</span><span> t1 </span><span style="color: #0000ff">set</span><span> c1</span><span>=</span><span>c1</span><span>+</span><span>10</span><span>*(</span><span>rand</span><span>()-</span><span>0.5</span><span>)</span><span>
</span><span style="color: #0000ff">DECLARE</span><span> @document </span><span style="color: #0000ff">varchar</span><span>(</span><span>64</span><span>);</span><span>  </span><span>
</span><span style="color: #0000ff">SELECT</span><span> @document </span><span>=</span> <span>'<span style="color: #ff0000">Reflectors are vital safety</span>'</span> <span>+</span><span>  </span><span>
</span><span>                   </span><span>' <span style="color: #ff0000">components of your bicycle.</span>'</span><span>;</span><span>  </span><span>
</span><span style="color: #0000ff">DECLARE</span><span> @j </span><span style="color: #0000ff">int</span><span><span style="color: #0000ff"> </span>
</span><span style="color: #0000ff">SET</span><span> @j</span><span>=</span><span style="color: #ff00ff">CHARINDEX</span><span>(</span><span style="color: #ff0000">'bicycle'</span><span>,</span><span> @document</span><span>);</span><span>
</span><span style="color: #0000ff">SET</span><span> @j</span><span>=</span><span style="color: #ff00ff">CHARINDEX</span><span>(</span><span style="color: #ff0000">'are'</span><span>,</span><span> @document</span><span>);</span><span>
</span><span style="color: #0000ff">SET</span><span> @j</span><span>=</span><span style="color: #ff00ff">CHARINDEX</span><span>(</span><span style="color: #ff0000">'vital'</span><span>,</span><span> @document</span><span>);</span><span>
</span><span style="color: #0000ff">SET</span><span> @j</span><span>=</span><span style="color: #ff00ff">CHARINDEX</span><span>(</span><span style="color: #ff0000">'dd'</span><span>,</span><span> @document</span><span>);</span><span>
</span><span style="color: #0000ff">SET</span><span> @j</span><span>=</span><span style="color: #ff00ff">CHARINDEX</span><span>(</span><span style="color: #ff0000">'your'</span><span>,</span><span> @document</span><span>);</span><span>
</span><span style="color: #0000ff">END </span>

<span>When the </span><span>ostress</span><span> tool is running, I query “</span><span style="color: #0000ff">select </span><span>*</span> <span style="color: #0000ff">from </span><span style="color: #008000">sys.dm_exec_requests</span> <span style="color: #0000ff">where </span><span>session_id</span><span>&gt;</span><span>50</span><span>” and I got below output:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/runnable.png"><img width="743" height="488" class="alignnone wp-image-6275" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/runnable.png" /></a>

<span>You notice there are </span><span>lots of runnable threads as well as suspended threads. Those suspended threads are the threads that are waiting for UPDATE lock while </span><span>those </span><span>runnable</span><span> ones</span><span> are waiting for scheduler to run.</span><span> </span>

<span> </span><span>I stop the </span><span>X</span><span>event</span> <span>SignalWaitDemo</span> <span>trace </span><span>and I got below result:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/signal_duration.png"><img width="2200" height="727" class="alignnone size-full wp-image-6285" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/signal_duration.png" /></a>

<span>You see that there are very long </span><span>signal_duration</span><span> sessions</span><span> in the result which means they are in runnable queue for that long time.</span><span> </span>

<span> </span>

<b><span>Non-yielding </span></b><b><span>Scheduler</span></b><b><span> Time</span></b><span> </span>

<span>From above description, we know how to check the resource waits time and signal waits time. Now comes to the </span><span>million</span><span> dollar</span><span>s</span><span> question</span><span>, how to know how long a thread has been running on a given scheduler without yielding (I call it Non-yielding </span><span>Scheduler</span><span> Time)? </span><span> </span>

<span>Note that non-yielding scheduler time means how long </span><span>a</span><span> thread is occupying the scheduler</span><span> without any yielding.</span> <span>I</span><span>t doesn’t always </span><span>means the CPU execution time</span><span>. </span><span> The thread holding the scheduler </span><span>may be held by operation system if other application is using the same CPU at that time point. This is not common since in most cases it is a dedicated server for SQL server and no other heavy application running on the machine.</span><span> </span>

<span> </span><span>The fact is that we </span><span>do not</span><span> have a handy way to track how long a thread is actually </span><span>continuously</span><span> running on a scheduler without yielding. I would expect there is a </span><span>max_non_yielding_scheduler_time_ms</span><span> column somewhere </span><span>in</span> <span>DMV</span><span> but there is no at this moment</span><span>. </span><span> </span>

<span> </span>

<span>The </span><span>good news </span><span>is that we have </span><b><span>yield_count</span></b><span> in DMV of “</span><span>sys.dm_os_schedulers</span><span>”</span><span> like below:</span><span> </span>

<span style="color: #0000ff">select </span><span>yield_count</span><span>,</span> <span>runnable_tasks_count</span><span>,</span> <span>*</span> <span style="color: #0000ff">from </span><span style="color: #008000">sys.dm_os_schedulers</span> <span style="color: #0000ff">where </span><span>scheduler_id</span><span>&lt;</span><span>1024</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/yield_count.png"><img width="2216" height="490" class="alignnone size-full wp-image-6305" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/yield_count.png" /></a>

<span>If the scheduler yields </span><span>the </span><span>yield_count</span><span> will be increased </span><span>by </span><span>one. </span><span>W</span><span>e can query this DMV regularly and get the delta of this column. If </span><span>the </span><span>yield_count</span><span> doesn’t change during the monitoring time delta we know that someone is running on the scheduler for that </span><span>period of</span><span> time. </span><span> </span>

<span>For example:</span><span> </span>

<span>At timestamp1, </span><span>yield_count</span><span> is 33555</span><span> </span>

<span>After 5 seconds, we query it again, and if the </span><span>yield_count</span><span> is still the same, 335</span><span>5</span><span>5, then we know that a thread has holding the scheduler for at least 5 seconds.</span><span> </span>

<span>Once we get the non-yielding scheduler, we can use scheduler’s active worker to inner join </span><span>sys.dm_os_worker</span><span>s</span><span> to get the active task, and use the active task to join the </span><span>sys.dm_exec_requests</span><span> to get the related user session information. A scheduler’s active worker is the worker thread that is current running on the scheduler, which is normally the running session running on the scheduler.</span><span> </span>

<span> </span><span>Here is the script to </span><span>save</span><span> the </span><span>yield_count</span><span> and other related information to a permanent table </span><span>called “</span><span>yields</span><span>”</span><span>. The script will run with every specified interval until you manually stop it: </span><span> </span>

<span style="color: #0000ff">USE </span><span>&lt;</span><span>yourdb</span><span>&gt;</span><span>
</span><span> </span><span style="color: #0000ff">CREATE TABLE</span><span> yields </span><span>
</span><span> </span><span>(</span><span>runtime </span><span style="color: #0000ff">datetime</span><span>,</span> <span>scheduler_id</span> <span style="color: #0000ff">bigint</span><span>,</span><span>yield_count</span> <span style="color: #0000ff">bigint</span><span>,</span><span>runnable</span> <span style="color: #0000ff">int</span><span>,</span> <span>session_id</span> <span style="color: #0000ff">int</span><span>,</span><span>start_time</span> <span style="color: #0000ff">datetime</span><span>,</span><span>command</span> <span style="color: #0000ff">varchar</span><span>(</span><span>200</span><span>),</span><span>database_id</span> <span style="color: #0000ff">int</span><span>)</span><span> </span>

<span> </span><span>GO</span><span> </span><span> </span><span> </span>

<span style="color: #0000ff"> SET NOCOUNT ON</span><span><span style="color: #0000ff"> </span>
</span><span> </span><span style="color: #0000ff">WHILE</span><span>(</span><span>1</span><span>=</span><span>1</span><span>)</span><span>
</span><span> </span><span style="color: #0000ff">BEGIN</span><span><span style="color: #0000ff"> </span>
</span><span> </span><span style="color: #0000ff">INSERT INTO</span><span> yields</span><span>
</span><span> </span><span style="color: #0000ff">SELECT </span><span>getdate</span><span>()</span> <span>'runtime'</span><span>,</span> <span>a</span><span>.</span><span>scheduler_id</span><span>,</span> <span>a</span><span>.</span><span>yield_count</span><span>,</span> <span>runnable_tasks_count</span><span>,</span> <span>session_id</span><span>,</span><span>start_time</span><span>,</span> <span>command</span><span>,</span><span>database_id</span><span> </span><span>
</span><span> </span><span style="color: #0000ff">FROM </span><span style="color: #008000">sys.dm_os_schedulers</span><span> a</span><span>
</span><span style="color: #808080">inner join</span> <span style="color: #008000">sys.dm_os_workers</span><span> b </span><span>on</span> <span>a</span><span>.</span><span>active_worker_address</span><span>=</span><span>b</span><span>.</span><span>worker_address</span><span> </span><span>
</span><span style="color: #808080">left join </span><span style="color: #008000">sys.dm_exec_requests</span><span> c </span><span>on</span> <span>c</span><span>.</span><span>task_address</span><span>=</span><span>b</span><span>.</span><span>task_address</span><span> </span><span>
<span style="color: #008000">--Most system has less than 1024 cores, use this to ignore those HIDDEN schedulers </span>
</span><span style="color: #0000ff"></span><span style="color: #0000ff">WHERE </span><span>a</span><span>.</span><span>scheduler_id</span><span>&lt;</span><span>1024 </span><span>
</span><span> </span><span>--Monitor it every 5 seconds. you can change it to meet your needs</span><span>
</span><span> </span><span style="color: #0000ff">WAITFOR DELAY</span> <span>'<span style="color: #ff0000">00:00:05</span>'</span><span>
</span><span> </span><span style="color: #0000ff">END </span>

<span> </span><span>To get </span><span>interesting </span><span>non-yielding scheduler information out of table yields, I use below script. It is not the perfect one but it can give you the idea how to</span><span> get </span><span> meaningful information from the captured data.</span><span> </span>

<span style="color: #0000ff">DECLARE </span><span>scheduler_cur</span> <span style="color: #0000ff">CURSOR </span><span><span style="color: #0000ff"> </span>
</span><span style="color: #0000ff">FOR SELECT</span> <span>scheduler_id</span> <span>from</span><span> yields </span><span style="color: #0000ff">group by</span> <span>scheduler_id</span> <span style="color: #0000ff">order by</span> <span>scheduler_id</span><span>
</span><span style="color: #0000ff">OPEN </span><span>scheduler_cur</span><span>
</span><span style="color: #0000ff">DECLARE</span><span> @id </span><span>bigint</span><span>
</span><span style="color: #0000ff">FETCH NEXT  FROM</span> <span>scheduler_cur</span> <span style="color: #0000ff">INTO</span><span> @id</span><span>
</span><span><span style="color: #0000ff">WHILE</span> </span><span>(</span><span style="color: #ff00ff">@@FETCH_STATUS</span><span>=</span><span>0</span><span>)</span><span>
</span><span style="color: #0000ff">BEGIN</span><span><span style="color: #0000ff"> </span>
</span><span> </span><span style="color: #0000ff">DECLARE </span><span>delta_cur</span> <span style="color: #0000ff">CURSOR</span><span><span style="color: #0000ff"> </span>
</span><span> </span><span style="color: #0000ff">FOR SELECT</span><span> runtime</span><span>,</span> <span>yield_count</span><span>,</span><span>scheduler_id</span><span>,</span><span>runnable</span><span>,</span><span>session_id</span><span>,</span><span>start_time</span><span>,</span> <span>command</span><span>,</span><span>database_id</span><span> </span><span>
</span><span> </span><span style="color: #0000ff">FROM</span><span> yields </span><span style="color: #0000ff">WHERE </span><span>scheduler_id</span><span>=</span><span>1  </span><span style="color: #0000ff">ORDER BY</span><span> runtime </span><span style="color: #0000ff">ASC</span><span><span style="color: #0000ff"> </span>
</span><span> </span><span style="color: #0000ff">OPEN </span><span>delta_cur</span><span>
</span><span> </span><span style="color: #0000ff">DECLARE</span><span> @</span><span>runtime_previous</span> <span>datetime</span><span>,</span><span>@</span><span>yieldcount_previous</span> <span>bigint</span><span>
</span><span> </span><span style="color: #0000ff">DECLARE</span><span> @runtime </span><span>datetime</span><span>,</span><span>@</span><span>yieldcount</span> <span>bigint</span><span>,</span><span>@</span><span>scheduler_id</span> <span>bigint</span><span>,</span><span>@runnable </span><span>int</span><span>,</span><span>@</span><span>session_id</span> <span>int</span><span>,</span><span>@</span><span>start_time</span> <span>datetime</span><span>,</span><span>@command</span> <span>varchar</span><span>(</span><span>200</span><span>),</span><span>@</span><span>database_id</span> <span>int</span><span>
</span>

<span> </span><span style="color: #0000ff">FETCH NEXT FROM </span><span>delta_cur</span> <span style="color: #0000ff">INTO</span><span><span style="color: #0000ff"> </span> @runtime </span><span>,</span><span>@</span><span>yieldcount</span> <span>,</span><span>@</span><span>scheduler_id</span><span>,</span><span>@runnable</span> <span>,</span><span>@</span><span>session_id</span> <span>,</span><span>@start_time</span><span>,</span><span>@command</span><span>,</span><span>@</span><span>database_id</span><span> </span><span>
</span><span> </span><span style="color: #0000ff">SET</span><span> @</span><span>runtime_previous</span><span>=</span><span>@</span><span>runtime</span><span>;</span><span>SET</span><span> @</span><span>yieldcount_previous</span><span>=</span><span>@</span><span>yieldcount</span><span>
</span><span> </span><span style="color: #0000ff">FETCH NEXT FROM</span> <span>delta_cur</span> <span style="color: #0000ff">INTO</span><span><span style="color: #0000ff"> </span> @runtime </span><span>,</span><span>@</span><span>yieldcount</span> <span>,</span><span>@</span><span>scheduler_id</span> <span>,</span><span>@runnable</span><span>,</span><span>@</span><span>session_id</span> <span>,</span><span>@</span><span>start_time</span><span>,</span><span>@command</span> <span>,</span><span>@</span><span>database_id</span><span> </span><span> </span>

<span> </span><span style="color: #0000ff">WHILE</span><span>(</span><span style="color: #ff00ff">@@FETCH_STATUS</span><span>=</span><span>0</span><span>)</span><span>
</span><span> </span><span style="color: #0000ff">BEGIN</span><span><span style="color: #0000ff"> </span>
</span><span>--We find one non-yielding scheduler during the runtime delta</span><span>
</span><span style="color: #0000ff">IF</span><span>(</span><span>@</span><span>yieldcount</span><span>=</span><span>@</span><span>yieldcount_previous</span><span>)</span><span>
</span><span style="color: #0000ff">BEGIN</span><span><span style="color: #0000ff"> </span>
</span><span style="color: #0000ff">PRINT </span><span>'<span style="color: #ff0000">Non-yielding Scheduler Time delta found!</span>'</span><span>
</span><span>  </span><span style="color: #0000ff">SELECT</span><span> @</span><span>runtime_previous</span> <span>'</span><span style="color: #ff0000">runtime_previous</span><span>'</span><span>,</span><span> @runtime </span><span style="color: #ff0000">'runtime'</span><span>,</span> <span style="color: #ff00ff">datediff</span><span>(</span><span style="color: #ff00ff">second</span><span>,</span><span> @</span><span>runtime_previous</span><span>,</span><span>@runtime</span><span>)</span> <span>'</span><span style="color: #ff0000">non_yielding_scheduler_time_second</span><span>'</span><span>,</span><span> @</span><span>yieldcount_previous</span> <span>'</span><span style="color: #ff0000">yieldcount_previous</span><span>'</span><span>,</span><span>
</span><span>  @</span><span>yieldcount</span> <span style="color: #ff0000">'yieldcount' </span><span>,</span><span>@</span><span>scheduler_id</span> <span>'</span><span><span style="color: #ff0000">scheduler_id</span>'</span><span>,</span><span>@runnable</span> <span>'</span><span>r<span style="color: #ff0000">unnable_tasks</span></span><span>'</span> <span>,</span><span>@</span><span>session_id</span> <span>'</span><span style="color: #ff0000">session_id</span><span>'</span> <span>,</span><span>@</span><span>start_time </span><span>'<span style="color: #ff0000">start_time</span></span><span>'</span><span>,</span><span> </span>

<span>  @command </span><span>'command'</span> <span>,</span><span>@</span><span>database_id</span><span>  </span><span>'</span><span>database_id</span><span>'</span><span>
</span><span style="color: #0000ff">END </span>

<span>-- print @id</span><span>
</span><span style="color: #0000ff">SET</span><span> @</span><span>runtime_previous</span><span>=</span><span>@</span><span>runtime</span><span>;</span><span style="color: #0000ff">SET</span><span> @</span><span>yieldcount_previous</span><span>=</span><span>@</span><span>yieldcount</span><span>
</span><span style="color: #0000ff">FETCH NEXT FROM</span> <span>delta_cur</span> <span style="color: #0000ff">INTO</span><span><span style="color: #0000ff"> </span> @runtime </span><span>,</span><span>@</span><span>yieldcount</span> <span>,</span><span>@</span><span>scheduler_id</span><span>,</span><span>@runnable</span> <span>,</span><span>@</span><span>session_id</span> <span>,</span><span>@</span><span>start_time</span><span>,</span><span>@command</span> <span>,</span><span>@</span><span>database_id</span><span> </span><span> </span><span> </span><span> </span>

<span> </span><span style="color: #0000ff">END</span>

<span> </span><span style="color: #0000ff">CLOSE </span><span>delta_cur</span><span>
</span><span> </span><span style="color: #0000ff">DEALLOCATE </span><span>delta_cur</span><span>
</span><span> </span><span style="color: #0000ff">FETCH NEXT  FROM</span> <span>scheduler_cur</span> <span style="color: #0000ff">INTO</span><span> @id</span><span> </span>

<span style="color: #0000ff">END</span><span><span style="color: #0000ff"> </span>
</span><span style="color: #0000ff">CLOSE </span><span>scheduler_cur</span><span>
</span><span style="color: #0000ff">DEALLOCATE </span><span>scheduler_cur</span><span> </span>

<span>The output looks like below:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/non_yielding_result.png"><img width="2292" height="541" class="alignnone size-full wp-image-6265" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/non_yielding_result.png" /></a>

<span>From above output you can see that scheduler 1 has </span><span>non_yielding_scheduler_time</span><span> for several times. Actually that scheduler 1 is hang because I suspended its active worker thread in debugger.</span><span> </span>

<span> </span>

<span>If you want to capture more information about the user session, like application name, hostname, </span><span>etc</span><span> you can run profiler trace or </span><span>xevent</span><span> at the same time to capture those events, and then you can correlate information with yields table </span><span>to drill down further.</span><span> </span>

&nbsp;

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>Troubleshooting data movement latency between synchronous-commit AlwaysOn Availability Groups</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/troubleshooting-data-movement-latency-between-synchronous-commit-always-on-availability-groups</link>
      <pubDate>Thu, 05 Apr 2018 02:49:32 GMT</pubDate>
      <dc:creator><![CDATA[SimonSu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6155</guid>
      <description><![CDATA[Writer: Simon Su
Technical Reviewer: Pam Lahoud, Sourabh Agarwal, Tejas Shah
Applies to: SQL...]]></description>
      <content:encoded><![CDATA[<em>Writer: Simon Su
Technical Reviewer: Pam Lahoud, Sourabh Agarwal, Tejas Shah
Applies to: SQL Server 2014 SP2, SQL Server 2016 SP1, SQL Server 2017 RTM </em>

<span>In synchronous-commit</span><span> mode</span><span> AG nodes s</span><span>ometimes you may </span><span>observe</span><span> your transactions are pending on </span><span>HADR_SYNC_COMMIT</span><span> waits. HADR_SYNC_COMMIT waits indicate</span><span> that SQL server is waiting for the signal from </span><span>the </span><span>remote </span><span>replicas to commit the transaction.  To understand the transaction commit latency </span><span>you can refer to below a</span><span>rticles</span><span>:</span><span> </span><span> </span>

<span>Troubleshooting High HADR_SYNC_COMMIT wait type with </span><span>AlwaysOn</span><span> Availability Groups</span><span>
</span><a href="https://blogs.msdn.microsoft.com/sql_server_team/troubleshooting-high-hadr_sync_commit-wait-type-with-always-on-availability-groups/"><span>https://blogs.msdn.microsoft.com/sql_server_team/troubleshooting-high-hadr_sync_commit-wait-type-with-always-on-availability-groups/</span></a><span> </span>

<span>SQL Server 2012 </span><span>AlwaysOn</span><span> – Part 12 – Performance Aspects and Performance Monitoring II</span><span>
</span><a href="https://blogs.msdn.microsoft.com/saponsqlserver/2013/04/24/sql-server-2012-alwayson-part-12-performance-aspects-and-performance-monitoring-ii/"><span>https://blogs.msdn.microsoft.com/saponsqlserver/2013/04/24/sql-server-2012-alwayson-part-12-performance-aspects-and-performance-monitoring-ii/</span></a><span> </span>

<span>In above link you will learn </span><span>that </span><span>the transaction delay can be </span><span>evaluated</span><span> by below two performance counters:</span><span> </span>
<ul>
 	<li><span>SQL </span><span>Server:Database</span><span> Replica –&gt; Transaction Delay</span></li>
 	<li><span>SQL </span><span>Server:Database</span><span> Replica –&gt; Mirrored Write Transactions/sec</span><span></span></li>
</ul>
<span>For example, assume there are poor perform</span><span>ing</span><span> AG nodes </span><span>and </span><span>you see “SQL </span><span>Server:Database</span><span> Replica –&gt; Transaction Delay”</span><span> is 1000 </span><span>ms</span> <span>(</span><span>millis</span><span>econds</span><span>)</span><span>, and “SQL </span><span>Server:Database</span><span> Replica –&gt; Mirrored Write Transactions/sec” is </span><span>50</span><span>, then it means on average each transaction has a </span><span>delay of 1000ms/50=20 </span><span>ms</span><span>.</span><span> </span>

<span>G</span><span>iven </span><span>above</span><span> example, can we know </span><span>where</span><span> the delay of 20 </span><span>ms</span><span> is from? What are the factors that cause this latency? To find out </span><span>the </span><span>answers </span><span>of</span><span> this kind of question</span><span>s</span><span> we need to understand how synchronous-commit works:</span><span> </span>

<a href="https://blogs.msdn.microsoft.com/psssql/2011/04/01/alwayson-hadron-learning-series-how-does-alwayson-process-a-synchronous-commit-request/"><span>https://blogs.msdn.microsoft.com/psssql/2011/04/01/alwayson-hadron-learning-series-how-does-alwayson-process-a-synchronous-commit-request/</span></a><span> </span>

<span>To track the data movement between replicas we are lucky we have updated </span><span>xevents</span><span>:</span><span>
</span><a href="https://support.microsoft.com/en-us/help/3173156/update-adds-alwayson-extended-events-and-performance-counters-in-sql-s"><span>https://support.microsoft.com/en-us/help/3173156/update-adds-alwayson-extended-events-and-performance-counters-in-sql-s</span></a><span> </span>

<span>In synchronous-commit mode the basic logic of the log blocks movement is as below:</span>

<span>
In primary</span><span>:</span>

<span>1.1 Log block-&gt;</span><span>LogCache</span><span>-&gt;</span><span>LogFlush</span><span>-&gt;</span><span>LocalHarden</span><span>(LDF)</span><span>
</span><span>1.2 Log block-&gt;</span><span>logPool</span><span>-&gt;</span><span>LogCapture</span><span>-&gt;</span><span>SendToRemote</span><span>
(</span><span>1.1 and 1.2 happens in parallel</span><span> )</span>

<span>In remote synchronous replica:</span><span> </span>

<span>The harden process is similar as primary:</span><span>
</span><span>logBlock</span><span> receive-&gt;</span><span>LogCache</span><span>-&gt;</span><span>LogFlush</span><span>-&gt;</span><span>HardenToLDF</span><span>-&gt;</span><span>AckPrimary</span><span> </span>

&nbsp;

<span>The </span><span>xevents</span><span> (see the first link in this article) happened in different places of log block movement. I have below figure to give you the detailed </span><span>log movement flow</span><span> of each steps and those related </span><span>Xevents</span><span>:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/AG-datamovement.png"><img width="2021" height="1282" class="alignnone size-full wp-image-6165" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/AG-datamovement.png" /></a>

<span>As showed above when the </span><span>Xevent</span><span> trace is captured we can know the precious time point of each step of the </span><span>log block</span><span> movement and you can know exactly </span><span>where</span> <span>the transaction latency is </span><span>from</span><span>. Commonly the delay is from three parts:</span><span> </span>

<span>1. </span><span>The duration of log harden in primary</span><span> </span>

<span>    It equals to the time delta of </span><span>Log_flush_start</span><span>(step </span><span>2</span><span>) and </span><span>Log_flush_complete</span><span> (step </span><span>3</span><span>)</span><span> </span>

<span>2. </span><span>The duration of log harden in remote replica</span><span>  </span>

<span>    It equals to the time delta of </span><span>Log_flush_start</span><span> (step </span><span>10</span><span>) and </span><span>Log_flush_complete</span><span> (step </span><span>11</span><span>)</span><span> </span>

<span>3. </span><span>The duration of network traffic</span><span> </span>

<span>    The sum of time delta</span><span>s</span><span> of </span><span>(</span><span>primary:</span><span>hadr_log_block_send_complete</span><span> -&gt;</span><span>secondary:</span><span>hadr_transport_receive_log_block_message</span><span>, step 6-7</span><span>) and    (</span><span>secondary:</span><span>hadr_lsn_send_complete</span><span>-&gt;</span><span>primary:</span><span>hadr_receive_harden_lsn_message</span><span>,step</span><span> 12-13</span><span>)</span><span> </span>

<span> </span>

<span>I use below script to capture </span><span>xevents</span><span>:</span><span> </span>

<span style="color: #008000">/* Note: this trace could generate very large amount of data very quickly, depends on the actual transaction rate. On a busy server it can grow several GB per minute, so do not run the script too long to avoid the impact to the production server.  */</span>

<span style="color: #0000ff">CREATE EVENT SESSION</span><span> [</span><span>AlwaysOn_Data_Movement_Tracing</span><span>] </span><span style="color: #0000ff">ON SERVER</span><span> </span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>file_write_completed</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>file_write_enqueued</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_apply_log_block</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_apply_vlfheader</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_capture_compressed_log_cache</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_capture_filestream_wait</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_capture_log_block</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_capture_vlfheader</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_db_commit_mgr_harden</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_db_commit_mgr_harden_still_waiting</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_db_commit_mgr_update_harden</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_filestream_processed_block</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_log_block_compression</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_log_block_decompression</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_log_block_group_commit</span> <span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_log_block_send_complete</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_lsn_send_complete</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_receive_harden_lsn_message</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_send_harden_lsn_message</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_transport_flow_control_action</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>hadr_transport_receive_log_block_message</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>log_block_pushed_to_logpool</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT </span><span>sqlserver</span><span>.</span><span>log_flush_complete</span> <span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>log_flush_start</span><span>,</span><span>
</span><span style="color: #0000ff">ADD EVENT</span> <span>sqlserver</span><span>.</span><span>recovery_unit_harden_log_timestamps</span><span> </span><span>
</span><span style="color: #0000ff">ADD TARGET</span><span> package0</span><span>.</span><span>event_file</span><span>(</span><span>SET</span> <span>filename</span><span>=</span><span>N'<span style="color: #ff0000">c</span></span><span style="color: #ff0000">:\mslog\AlwaysOn_Data_Movement_Tracing.xel</span><span>'</span><span>,</span><span>max_file_size</span><span>=(</span><span>500</span><span>),</span><span>max_rollover_files</span><span>=(</span><span>4</span><span>))</span><span>
</span><span><span style="color: #0000ff">WITH</span> </span><span>(</span><span>MAX_MEMORY</span><span>=</span><span>4096 KB</span><span>,</span><span>EVENT_RETENTION_MODE</span><span>=</span><span>ALLOW_SINGLE_EVENT_LOSS</span><span>,</span><span>MAX_DISPATCH_LATENCY</span><span>=</span><span>30 SECONDS</span><span>,</span><span>MAX_EVENT_SIZE</span><span>=</span><span>0 KB</span><span>,</span><span>MEMORY_PARTITION_MODE</span><span>=</span><span style="color: #0000ff">NONE</span><span>,</span><span>TRACK_CAUSALITY</span><span>=</span><span style="color: #0000ff">OFF</span><span>,</span><span>STARTUP_STATE</span><span>=</span><span style="color: #0000ff">ON</span><span>)</span><span> </span>

<span>GO</span><span> </span>

<span>For demo purpose I just run “</span><span>insert</span> <span>into</span><span> [AdventureWorks2014]</span><span>..</span><span>t1 </span><span>values</span><span>(</span><span>1</span><span>)</span><span>”</span><span> and then capture the </span><span>x</span><span>event</span><span> trace on primary and secondary, and below are the screenshot of the captured </span><span>xevents</span><span>:</span><span> </span>

<span> </span><span>P</span><span>rimary:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/Primary.png"><img width="2800" height="1202" class="alignnone size-full wp-image-6175" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/Primary.png" /></a>

<span>In second synchronous replica:</span><span> </span>

<span> <a href="https://msdnshared.blob.core.windows.net/media/2018/04/secondary.png"><img width="2379" height="821" class="alignnone size-full wp-image-6185" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/secondary.png" /></a></span>

<span>Note: You </span><span>may notice that the </span><span>log_block_id</span><span> (</span><span>146028889512</span><span>) of </span><span>hadr_receive_harden_lsn_message</span><span> is not the same as others (</span><span>146028889488</span><span>). This is because the return id is always the next immediate id of the harden log block, we can use </span><span>hadr_db_commit_mgr_update_harden</span> <span>xevent</span><span> to </span><span>correlate the </span><span>xevents</span><span>:</span><span> </span>

<span> <a href="https://msdnshared.blob.core.windows.net/media/2018/04/hadr_db_commit_mgr_update_harden.png"><img width="1622" height="911" class="alignnone size-full wp-image-6195" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/hadr_db_commit_mgr_update_harden.png" /></a></span>

<span>With above </span><span>x</span><span>event</span><span> log</span><span> we now get below detailed time </span><span>latency </span><span>breakdowns</span><span> of the transaction commit:</span><span> </span><span> </span>
<table width="847" border="1">
<tbody>
<tr>
<td><span> </span></td>
<td><span>From</span><span> </span></td>
<td><span>To</span><span> </span></td>
<td><span>latency</span><span> </span></td>
</tr>
<tr>
<td><span>Network:</span><span> </span>

<span>Primary-&gt;Second</span><span> </span></td>
<td><span>Primary:</span><span> </span>

<span>hadr_log_block_send_complete</span><span> </span>

<span>2018-03-06 16:56:28.2174613</span><span> </span>

<span> </span></td>
<td><span>Secondary:</span><span> </span>

<span>hadr_transport_receive_log_block_message</span><span> </span>

<span>2018-03-06 16:56:32.1241242</span><span> </span></td>
<td><span>3.907 seconds</span><span> </span></td>
</tr>
<tr>
<td><span>Network</span><span>:</span><span> </span>

<span>Secondary-&gt;Primary</span><span> </span></td>
<td><span>Secondary:hadr_lsn_send_complete</span><span> </span>

<span>2018-03-06 16:56:32.7863432</span><span> </span></td>
<td><span>Primary:</span><span>hadr_receive_harden_lsn_message</span><span> </span>

<span>2018-03-06 16:56:33.3732126</span><span> </span>

<span> </span></td>
<td><span>0.587 seconds</span><span> </span></td>
</tr>
<tr>
<td><span>LogHarden</span><span>(Primary)</span><span> </span></td>
<td><span>log_flush_start</span><span> </span>

<span>2018-03-06 16:56:28.2168580</span><span> </span></td>
<td><span>log_flush_complete</span><span> </span>

<span>2018-03-06 16:56:28.8785928</span><span> </span>

<span> </span></td>
<td><span>0.663 seconds</span><span> </span></td>
</tr>
<tr>
<td><span>Log Harden(Secondary)</span><span> </span></td>
<td><span>Log_flush_start</span><span> </span>

<span>2018-03-06 16:56:32.1242499</span><span> </span></td>
<td><span>Log_flush_complete</span><span> </span>

<span>2018-03-06 16:56:32.7861231</span><span> </span>

<span> </span></td>
<td><span>0.663 seconds</span><span> </span></td>
</tr>
</tbody>
</table>
<span> </span>

<span>I list the time delta (the latency) </span><span>just </span><span>for network and log harden process there, there could be other latency </span><span>places </span><span>happening as well</span><span>, like log block compressing/decompressing </span><span>etc</span><span>,</span><span> but mainly the latency comes from </span><span>these</span><span> three parts:</span><span> </span>
<ol>
 	<li><span>Network latency between replicas. In above example, it is 3.907+0.587=4.494 seconds</span><span> </span></li>
</ol>
<ol>
 	<li><span>Log harden on Primary=0.663 seconds</span><span> </span></li>
</ol>
<ol>
 	<li><span>Log harden on Secondary=0.663 seconds</span><span> </span></li>
</ol>
<span>To get the total transaction delay</span><span>, we cannot simply sum </span><span>them up </span><span>because Log flush on primary and the network transfer are happening in parallel. Say, the network takes 4.494 seconds, </span><span>but</span><span> primary log harden finished</span><span> (log_flush_complete:2018-03-06 16:56:28.8785928)</span><span> far before the </span><span>primary gets confirmation from</span> <span>replica</span><span> (hadr_receive_harden_lsn_message:2018-03-06 16:56:33.3732126).</span> <span>Luckily, we do not need to manually determine which timestamp to use to calculate the total commit time of </span><span>a</span><span> transaction. We </span><span>can </span><span>use the time delta between the two </span><span>hadr_log_block_group_commit</span><span>  </span><span>xevents</span><span> to know </span><span>the time to commit. For example, in above log</span><span>:</span><span> </span>

<span>Primary:</span> <span>hadr_log_block_group_commit</span><span>:</span><span> 2018-03-06 16:56:28.2167393</span><span> </span>

<span>Primary:</span> <span>hadr_log_block_group_commit</span><span>:</span><span> 2018-03-06 16:56:33.3732847</span><span> </span>

<span>Total</span><span> time to commit=delta of above two timestamps=</span><span> 5</span><span>.157</span><span> seconds</span><span> </span>

<span>This number </span><span>is</span><span> equal to the network </span><span>transfer </span><span>time plus the log harden time on</span><span> the</span><span> secondary. This makes sense because the secondary has to wait for</span><span> the network for</span><span> the log block </span><span>available </span><span>before log harden, it cannot harden the log in parallel like in the primary.</span><span> </span>

<span>If you look at the second </span><span>hadr_log_block_group_commit</span><span> event it has a column of “</span><span>processing_time</span><span>” which is the </span><span>exactly the </span><span>commit time of the transaction that</span><span> we are talking about:</span><span> </span>

<a href="https://msdnshared.blob.core.windows.net/media/2018/04/group_commit_time.png"><img width="2379" height="1032" class="alignnone size-full wp-image-6205" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/04/group_commit_time.png" /></a>

&nbsp;

<span>So now you have an overall picture about the log block movement between synchronous-commit mode replicas, and you know where is the latency</span><span> (if any)</span><span> from, </span><span>replicas, </span><span>network, </span><span>or </span><span>disk (log harden) or others. </span><span> </span>

<span>By the way, y</span><span>ou may notice that there is “</span><span>hadr_db_commit_mgr_harden_still_waiting</span><span>” </span><span>xevent</span><span> happening in primary</span> <span>Xevents</span><span>. This event happens every 2 seconds (</span><span>the 2 seconds is </span><span>hardcoded) when the </span><span>primary is waiting for the </span><span>acknowledge message </span><span>from </span><span>second replica.</span><span> If the ack comes back within 2 seconds you won’t see this </span><span>xevent</span><span>.</span><span> </span>

<span> 2018.9.8 update
=================
I have developed a tool to analyze the trace automatically:</span>

https://blogs.msdn.microsoft.com/psssql/2018/09/08/aglatency-report-tool-introduction/

<b><span>Reference</span></b><span> </span>

<span>New in SSMS – Always </span><span>On</span><span> Availability Group Latency Reports</span><span>
</span><span>https://blogs.msdn.microsoft.com/sql_server_team/new-in-ssms-always-on-availability-group-latency-reports/</span><span> </span>

<span>AlwaysOn</span><span> Availability Groups Troubleshooting and Monitoring Guide</span><span>
</span><span>https://msdn.microsoft.com/library/dn135328</span><span> </span>]]></content:encoded>
    </item>
    <item>
      <title>Installation of SQL Server 2017 failing with 'VS Shell installation has failed with exit code 1638'</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/installation-of-sql-server-2017-failing-with-vs-shell-installation-has-failed-with-exit-code-1638</link>
      <pubDate>Mon, 26 Mar 2018 15:33:02 GMT</pubDate>
      <dc:creator><![CDATA[Guillaume Fourrat [MSFT]]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6145</guid>
      <description><![CDATA[Dear all,

Depending on which products were installed on the server beforehand, a SQL Server 2017...]]></description>
      <content:encoded><![CDATA[Dear all,

Depending on which products were installed on the server beforehand, a SQL Server 2017 setup may fail with the following error :
<pre style="padding-left: 60px">TITLE: Microsoft SQL Server 2017 Setup
------------------------------

The following error has occurred:

VS Shell installation has failed with exit code 1638.</pre>
Note that it could happen even after SQL Server 2017 setup was executed successfully once already. Eg. the following steps would reach the situation : install a first SQL Server 2017 instance (no error), install SSMS 17 from the Web (no error), install a 2nd instance (error 1638).

Anyway, the important thing is that situation is covered by KB <a href="https://support.microsoft.com/en-us/help/4092997/vc-2015-redistributable-install-error-1638-when-newer-version-present">4092997</a> and as indicated in the KB, a simple Repair of 'Microsoft Visual C++ 2015 Redistributable (x64)' followed by a server restart should clear the problem (please check the current KB content though, as it may be updated with further details after this post is published).

We're working on making the interactive link of SQL Server setup's pop-up message a bit more useful than it is today, and the more specific situation where our very own SSMS introduces the problem is under review.

Regards,

Guillaume Fourrat

Escalation Engineer]]></content:encoded>
    </item>
    <item>
      <title>Uniqueifier considerations and error 666</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/uniqueifier-considerations-and-error-666</link>
      <pubDate>Fri, 16 Feb 2018 19:37:08 GMT</pubDate>
      <dc:creator><![CDATA[Luciano Moreira]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6135</guid>
      <description><![CDATA[This post is intended to shed some light around uniqueifiers and table design that rely on its...]]></description>
      <content:encoded><![CDATA[This post is intended to shed some light around uniqueifiers and table design that rely on its usage.

First a quick information about the subject.

A uniqueifier (or uniquifier as reported by SQL Server internal tools) has been used in the engine for a long time (since SQL Server 7.0), and even being known to many, referenced in books and blogs, our documentation clearly states that you will not see it exposed externally in the engine (<a target="_blank" href="https://docs.microsoft.com/en-us/sql/relational-databases/sql-server-index-design-guide" rel="noopener">https://docs.microsoft.com/en-us/sql/relational-databases/sql-server-index-design-guide</a>).
<p style="padding-left: 30px"><em>"If the clustered index is not created with the UNIQUE property, the Database Engine automatically adds a <strong>4-byte uniqueifier </strong>column to the table. When it is required, the Database Engine automatically adds a uniqueifier value to a row to make each key unique. <strong>This column and its values are used internally and cannot be seen or accessed by users</strong>."</em></p>
While it´s unlikely that you will face an issue related with uniqueifiers, we have seen rare cases where customer reaches the uniqueifier limit of 2,147,483,648, generating error 666.
<p style="padding-left: 30px"><em>Msg 666, Level 16, State 2, Line 1</em></p>
<p style="padding-left: 30px"><em>The maximum system-generated unique value for a duplicate group was exceeded for index with partition ID &lt;PARTITIONID&gt;. Dropping and re-creating the index may resolve this; otherwise, use another clustering key.</em></p>
The error message is clear about the actions you should take to resolve the issue.

Drop and recreate the index may resolve the problem if you don´t have 2,147,483,648 records with the same key value. Recreating the index will allow the uniqueifier to be reset, giving you some time to review table design before reaching the limit again.

As of February 2018, the design goal for the storage engine is to not reset uniqueifiers during REBUILDs. As such, rebuild of the index ideally would not reset uniquifiers and issue would continue to occur, while inserting new data with a key value for which the uniquifiers were exhausted. But current engine behavior is different for one specific case, if you use the statement <em>ALTER INDEX ALL ON &lt;TABLE&gt; REBUILD WITH (ONLINE = ON)</em>, it will reset the uniqueifiers (across all version starting SQL Server 2005 to SQL Server 2017).
<p style="padding-left: 30px"><em>Important:</em> <em>This is something that is not documented and can change in future versions, so our recommendation is that you should review table design to avoid relying on it.</em></p>
Related to the table design aspect, based on the second recommendation from error message, our question is: “Is a good design to choose a non-unique clustering key that could have several million/billion duplicate key values?”

The short answer is <strong>probably NO</strong>. Of course, we know that when it comes to table design there is usually not a right or wrong answer, but the <strong>majority of cases should not rely heavily on uniqueifiers.</strong>

While most cases will likely have at most hundreds or thousands of duplicated records for a single value and, is straightforward to solve the issue if you get error 666 (having less duplicated rows than the uniqueifier limit), it can cause some downtime when executing the required steps. Consequently, the best course of action <strong>is to review tables that rely on uniqueifiers and proactively work to improve its design</strong>.

Hope it helps you to have a better understanding about uniqueifiers and to review table design, in order to avoid error 666 in your production environment.

If you want to have more information about uniqueifiers, review post at <em></em> <a target="_blank" href="https://blogs.msdn.microsoft.com/luti/2018/02/16/uniqueifier-details-in-sql-server/" rel="noopener">https://blogs.msdn.microsoft.com/luti/2018/02/16/uniqueifier-details-in-sql-server/</a> and example 03 from companion content for Chapter 6 (Index Internals) of SQL Server 2008 Internals book (<a target="_blank" href="https://www.sqlskills.com/blogs/kimberly/companion-content-for-chapter-6-index-internals-of-sql-server-2008-internals/" rel="noopener">https://www.sqlskills.com/blogs/kimberly/companion-content-for-chapter-6-index-internals-of-sql-server-2008-internals/</a>).]]></content:encoded>
    </item>
    <item>
      <title>SQL Setup ToolSuite Introduction (3) – SQL Registry Viewer Version 2.2</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/sql-setup-toolsuite-introduction-3-sql-registry-viewer</link>
      <pubDate>Wed, 24 Jan 2018 15:03:26 GMT</pubDate>
      <dc:creator><![CDATA[SimonSu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6105</guid>
      <description><![CDATA[You may want to know what registry keys will be added to system for a SQL server installation. If...]]></description>
      <content:encoded><![CDATA[You may want to know what registry keys will be added to system for a SQL server installation. If you use some registry snapshot tool to compare the window registries change of before and after the SQL installation you will find there are 40000~60000 modifications happening. However if you study the modifications carefully you will find that most of them doesn't have much sense, for example, lots of modifications go to "HKLM\DRIVERS\DriverDatabase\DeviceIds\" entry. The most interesting modifications are:

&lt;&gt;Installer related registry keys under
HKEY_CLASSES_ROOT\Installer and
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18

&lt;&gt;COM+ related,Like
Computer\HKEY_CLASSES_ROOT\CLSID
Computer\HKEY_CLASSES_ROOT\Interface
Computer\HKEY_CLASSES_ROOT\TypeLib

&lt;&gt; SQL specific
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server

&lt;&gt;Service, performance log related keys,  and other keys under WOW6432Node

&lt;&gt;Others...

So is it possible to list all these important SQL server related registry keys? My answer is <strong>SQL Registry Viewer</strong> tool in GitHub:
<a href="https://github.com/suyouquan/SQLSetupTools">https://github.com/suyouquan/SQLSetupTools</a>

<a href="https://msdnshared.blob.core.windows.net/media/2018/01/SQLRegistryViewer_V1.0.png"><img width="2099" height="1250" class="alignnone size-full wp-image-6115" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/01/SQLRegistryViewer_V1.0.png" /></a>

This tool pre-reads all SQL server meta data from setup source (the setup media containing those MSI/MSP files). The meta data includes  product code, patch code,package code, files, registry keys will be added to system etc. It will then use this prepared meta data to scan registry and display only SQL server related in the UI. The tool will ask you to specify service pack or CU you ever installed to your system for an accurate report. you can browser/search/export the keys in the UI easily.  Just be attention that this tool is not intended to list all SQL server related  keys. Just those important ones will be displayed.

The binary is here (Version 2.2)

<a href="https://github.com/suyouquan/SQLSetupTools/releases/download/V2.2/SQLRegViewer_v2.2NET3.5.zip">https://github.com/suyouquan/SQLSetupTools/releases/download/V2.2/SQLRegViewer_v2.2NET3.5.zip</a>
<a href="https://github.com/suyouquan/SQLSetupTools/releases/download/V2.2/SQLRegViewer_V2.2NET4.5.zip">https://github.com/suyouquan/SQLSetupTools/releases/download/V2.2/SQLRegViewer_V2.2NET4.5.zip</a>

Enjoy and have fun!

<strong>Update:</strong>

<strong>What is new in Version 2.0</strong>

Comparing to version 1.0, version 2.0 has below advantages:
<ol>
 	<li>Cached meta you scanned</li>
</ol>
If you ever scan your SP/CU folder, their meta will be cached for next run. This can save the scan time significantly.
<ol start="2">
 	<li>Pre-processed the SQL meta files so loading time will be in seconds instead of minutes, and the size of the tool reduces from 40MB to 4MB.</li>
 	<li>Use multi-threading technology to load registry keys asynchronously and in parallel. This help reduce the time to scan the registry keys. With these technologies now the tool runs 2 times faster.</li>
 	<li>Put some scan task to background so you can get the UI sooner to browse the keys.</li>
 	<li>If you export the keys the result detailed text file will have remark about what the keys belongs to. For example, below entry tells you the key is for “SQL Server 2008 R2 Management Studio”:</li>
</ol>
<strong>[SQL Server 2008 R2 Management Studio]</strong>

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\<strong>6E63FE9FCFA1D2244BD1045FE2A00E7F</strong>]

"AB3CB1820BCF65042B6B105D760D8DC8"]]></content:encoded>
    </item>
    <item>
      <title>SQL Setup ToolSuite Introduction (2) – Product Browser</title>
      <link>https://docs.microsoft.com/archive/blogs/psssql/sql-setup-toolsuite-introduction-2-product-browser</link>
      <pubDate>Wed, 24 Jan 2018 14:24:49 GMT</pubDate>
      <dc:creator><![CDATA[SimonSu]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/psssql/?p=6085</guid>
      <description><![CDATA[Oftentimes when I worked on a setup case I hope I have a simple tool to tell me the detailed...]]></description>
      <content:encoded><![CDATA[Oftentimes when I worked on a setup case I hope I have a simple tool to tell me the detailed installed product properties and patch information. In Windows control panel I can find all installed products and its update but it doesn't provide detailed information like the package name of a product, the product code and package code, the cached MSI/MSP name etc. I think this is not a difficult task so I just make it and publish it to GitHub:

<a href="https://github.com/suyouquan/SQLSetupTools">https://github.com/suyouquan/SQLSetupTools</a>

With the tool you can easy browse, search, or export detailed products information to text files.

You can download its latest version from above link or Version 1.2 here:
<a href="https://github.com/suyouquan/SQLSetupTools/releases/download/V2.2/ProductBrowser_V2.2NET45.zip">https://github.com/suyouquan/SQLSetupTools/releases/download/V2.2/ProductBrowser_V2.2NET45.zip</a>
<a href="https://github.com/suyouquan/SQLSetupTools/releases/download/V2.2/ProductBrowser_V2.2NET35.zip">https://github.com/suyouquan/SQLSetupTools/releases/download/V2.2/ProductBrowser_V2.2NET35.zip</a>

&nbsp;

Screenshot for your reference:

<a href="https://msdnshared.blob.core.windows.net/media/2018/01/ProductBrowser_V1.2.png"><img width="1370" height="1006" class="alignnone size-full wp-image-6095" alt="" src="https://msdnshared.blob.core.windows.net/media/2018/01/ProductBrowser_V1.2.png" /></a>]]></content:encoded>
    </item>
  </channel>
</rss>