@@ -2664,15 +2664,165 @@ If one of them is a lead blocker, consider killing that query.'' AS HowToStopit,
26642664
26652665 IF @max_worker_threads > 0
26662666 BEGIN
2667- INSERT INTO #BlitzFirstResults (CheckID, Priority, FindingsGroup, Finding, URL , Details)
2668- SELECT 49 AS CheckID,
2669- 210 AS Priority,
2670- ' Potential Upcoming Problems' AS FindingGroup,
2671- ' High Number of Connections' AS Finding,
2672- ' https://www.brentozar.com/archive/2014/05/connections-slow-sql-server-threadpool/' AS URL ,
2673- ' There are ' + CAST (SUM (1 ) AS VARCHAR (20 )) + ' open connections, which would lead to ' + @LineFeed + ' worker thread exhaustion and THREADPOOL waits' + @LineFeed + ' if they all ran queries at the same time.' AS Details
2674- FROM sys .dm_exec_connections c
2675- HAVING SUM (1 ) > @max_worker_threads;
2667+ /* Count connections first so we only build the Top 5 breakdowns when the
2668+ alert actually fires. See issue #3903 for the feature request. */
2669+ DECLARE @TotalConnections INT ;
2670+ SET @TotalConnections = (SELECT COUNT (* ) FROM sys .dm_exec_connections );
2671+
2672+ IF @TotalConnections > @max_worker_threads
2673+ BEGIN
2674+ /* Load connection + session attributes into a table variable once so we can
2675+ aggregate it three different ways (by host, login, and app) without
2676+ re-reading the DMVs. sys.dm_exec_sessions.last_request_end_time defaults
2677+ to 1900-01-01 for sessions that have never run a request - treat that
2678+ sentinel as NULL so it renders as "unknown" rather than "45000 days ago". */
2679+ DECLARE @ConnSessions TABLE
2680+ (
2681+ HostName NVARCHAR (128 ) NOT NULL ,
2682+ LoginName NVARCHAR (128 ) NOT NULL ,
2683+ AppName NVARCHAR (128 ) NOT NULL ,
2684+ LastFinish DATETIME NULL
2685+ );
2686+
2687+ INSERT INTO @ConnSessions (HostName, LoginName, AppName, LastFinish)
2688+ SELECT
2689+ COALESCE (NULLIF (s .host_name , N ' ' ), N ' (unknown host)' ),
2690+ COALESCE (NULLIF (s .login_name , N ' ' ), N ' (unknown login)' ),
2691+ COALESCE (NULLIF (s .program_name , N ' ' ), N ' (unknown app)' ),
2692+ NULLIF (s .last_request_end_time , CONVERT (DATETIME , ' 1900-01-01' ))
2693+ FROM sys .dm_exec_connections c
2694+ LEFT JOIN sys .dm_exec_sessions s ON s .session_id = c .session_id ;
2695+
2696+ DECLARE @TopServers NVARCHAR (MAX ),
2697+ @TopLogins NVARCHAR (MAX ),
2698+ @TopApps NVARCHAR (MAX );
2699+
2700+ SELECT @TopServers = STUFF ((
2701+ SELECT @LineFeed
2702+ + g .ConnectionGroup + ' - ' + CAST (g .ConnectionCount AS VARCHAR (20 )) + ' connections'
2703+ + ' , most recent query finished '
2704+ + CASE
2705+ WHEN g .MostRecentSec IS NULL THEN ' unknown'
2706+ WHEN g .MostRecentSec < 60 THEN CAST (g .MostRecentSec AS VARCHAR (10 )) + ' seconds ago'
2707+ WHEN g .MostRecentSec < 3600 THEN CAST (g .MostRecentSec / 60 AS VARCHAR (10 )) + ' minutes ago'
2708+ WHEN g .MostRecentSec < 86400 THEN CAST (g .MostRecentSec / 3600 AS VARCHAR (10 )) + ' hours '
2709+ + CAST ((g .MostRecentSec % 3600 ) / 60 AS VARCHAR (10 )) + ' minutes ago'
2710+ ELSE CAST (g .MostRecentSec / 86400 AS VARCHAR (10 )) + ' days '
2711+ + CAST ((g .MostRecentSec % 86400 ) / 3600 AS VARCHAR (10 )) + ' hours ago'
2712+ END
2713+ + ' , oldest query finished '
2714+ + CASE
2715+ WHEN g .OldestSec IS NULL THEN ' unknown'
2716+ WHEN g .OldestSec < 60 THEN CAST (g .OldestSec AS VARCHAR (10 )) + ' seconds ago'
2717+ WHEN g .OldestSec < 3600 THEN CAST (g .OldestSec / 60 AS VARCHAR (10 )) + ' minutes ago'
2718+ WHEN g .OldestSec < 86400 THEN CAST (g .OldestSec / 3600 AS VARCHAR (10 )) + ' hours '
2719+ + CAST ((g .OldestSec % 3600 ) / 60 AS VARCHAR (10 )) + ' minutes ago'
2720+ ELSE CAST (g .OldestSec / 86400 AS VARCHAR (10 )) + ' days '
2721+ + CAST ((g .OldestSec % 86400 ) / 3600 AS VARCHAR (10 )) + ' hours ago'
2722+ END
2723+ FROM (
2724+ SELECT TOP (5 )
2725+ ConnectionGroup = HostName,
2726+ ConnectionCount = COUNT (* ),
2727+ MostRecentSec = DATEDIFF (SECOND, MAX (LastFinish), GETDATE ()),
2728+ OldestSec = DATEDIFF (SECOND, MIN (LastFinish), GETDATE ())
2729+ FROM @ConnSessions
2730+ GROUP BY HostName
2731+ ORDER BY COUNT (* ) DESC , HostName
2732+ ) g
2733+ ORDER BY g .ConnectionCount DESC , g .ConnectionGroup
2734+ FOR XML PATH (' ' ), TYPE
2735+ ).value (N ' .[1]' , N ' NVARCHAR(MAX)' ), 1 , LEN (@LineFeed), N ' ' );
2736+
2737+ SELECT @TopLogins = STUFF ((
2738+ SELECT @LineFeed
2739+ + g .ConnectionGroup + ' - ' + CAST (g .ConnectionCount AS VARCHAR (20 )) + ' connections'
2740+ + ' , most recent query finished '
2741+ + CASE
2742+ WHEN g .MostRecentSec IS NULL THEN ' unknown'
2743+ WHEN g .MostRecentSec < 60 THEN CAST (g .MostRecentSec AS VARCHAR (10 )) + ' seconds ago'
2744+ WHEN g .MostRecentSec < 3600 THEN CAST (g .MostRecentSec / 60 AS VARCHAR (10 )) + ' minutes ago'
2745+ WHEN g .MostRecentSec < 86400 THEN CAST (g .MostRecentSec / 3600 AS VARCHAR (10 )) + ' hours '
2746+ + CAST ((g .MostRecentSec % 3600 ) / 60 AS VARCHAR (10 )) + ' minutes ago'
2747+ ELSE CAST (g .MostRecentSec / 86400 AS VARCHAR (10 )) + ' days '
2748+ + CAST ((g .MostRecentSec % 86400 ) / 3600 AS VARCHAR (10 )) + ' hours ago'
2749+ END
2750+ + ' , oldest query finished '
2751+ + CASE
2752+ WHEN g .OldestSec IS NULL THEN ' unknown'
2753+ WHEN g .OldestSec < 60 THEN CAST (g .OldestSec AS VARCHAR (10 )) + ' seconds ago'
2754+ WHEN g .OldestSec < 3600 THEN CAST (g .OldestSec / 60 AS VARCHAR (10 )) + ' minutes ago'
2755+ WHEN g .OldestSec < 86400 THEN CAST (g .OldestSec / 3600 AS VARCHAR (10 )) + ' hours '
2756+ + CAST ((g .OldestSec % 3600 ) / 60 AS VARCHAR (10 )) + ' minutes ago'
2757+ ELSE CAST (g .OldestSec / 86400 AS VARCHAR (10 )) + ' days '
2758+ + CAST ((g .OldestSec % 86400 ) / 3600 AS VARCHAR (10 )) + ' hours ago'
2759+ END
2760+ FROM (
2761+ SELECT TOP (5 )
2762+ ConnectionGroup = LoginName,
2763+ ConnectionCount = COUNT (* ),
2764+ MostRecentSec = DATEDIFF (SECOND, MAX (LastFinish), GETDATE ()),
2765+ OldestSec = DATEDIFF (SECOND, MIN (LastFinish), GETDATE ())
2766+ FROM @ConnSessions
2767+ GROUP BY LoginName
2768+ ORDER BY COUNT (* ) DESC , LoginName
2769+ ) g
2770+ ORDER BY g .ConnectionCount DESC , g .ConnectionGroup
2771+ FOR XML PATH (' ' ), TYPE
2772+ ).value (N ' .[1]' , N ' NVARCHAR(MAX)' ), 1 , LEN (@LineFeed), N ' ' );
2773+
2774+ SELECT @TopApps = STUFF ((
2775+ SELECT @LineFeed
2776+ + g .ConnectionGroup + ' - ' + CAST (g .ConnectionCount AS VARCHAR (20 )) + ' connections'
2777+ + ' , most recent query finished '
2778+ + CASE
2779+ WHEN g .MostRecentSec IS NULL THEN ' unknown'
2780+ WHEN g .MostRecentSec < 60 THEN CAST (g .MostRecentSec AS VARCHAR (10 )) + ' seconds ago'
2781+ WHEN g .MostRecentSec < 3600 THEN CAST (g .MostRecentSec / 60 AS VARCHAR (10 )) + ' minutes ago'
2782+ WHEN g .MostRecentSec < 86400 THEN CAST (g .MostRecentSec / 3600 AS VARCHAR (10 )) + ' hours '
2783+ + CAST ((g .MostRecentSec % 3600 ) / 60 AS VARCHAR (10 )) + ' minutes ago'
2784+ ELSE CAST (g .MostRecentSec / 86400 AS VARCHAR (10 )) + ' days '
2785+ + CAST ((g .MostRecentSec % 86400 ) / 3600 AS VARCHAR (10 )) + ' hours ago'
2786+ END
2787+ + ' , oldest query finished '
2788+ + CASE
2789+ WHEN g .OldestSec IS NULL THEN ' unknown'
2790+ WHEN g .OldestSec < 60 THEN CAST (g .OldestSec AS VARCHAR (10 )) + ' seconds ago'
2791+ WHEN g .OldestSec < 3600 THEN CAST (g .OldestSec / 60 AS VARCHAR (10 )) + ' minutes ago'
2792+ WHEN g .OldestSec < 86400 THEN CAST (g .OldestSec / 3600 AS VARCHAR (10 )) + ' hours '
2793+ + CAST ((g .OldestSec % 3600 ) / 60 AS VARCHAR (10 )) + ' minutes ago'
2794+ ELSE CAST (g .OldestSec / 86400 AS VARCHAR (10 )) + ' days '
2795+ + CAST ((g .OldestSec % 86400 ) / 3600 AS VARCHAR (10 )) + ' hours ago'
2796+ END
2797+ FROM (
2798+ SELECT TOP (5 )
2799+ ConnectionGroup = AppName,
2800+ ConnectionCount = COUNT (* ),
2801+ MostRecentSec = DATEDIFF (SECOND, MAX (LastFinish), GETDATE ()),
2802+ OldestSec = DATEDIFF (SECOND, MIN (LastFinish), GETDATE ())
2803+ FROM @ConnSessions
2804+ GROUP BY AppName
2805+ ORDER BY COUNT (* ) DESC , AppName
2806+ ) g
2807+ ORDER BY g .ConnectionCount DESC , g .ConnectionGroup
2808+ FOR XML PATH (' ' ), TYPE
2809+ ).value (N ' .[1]' , N ' NVARCHAR(MAX)' ), 1 , LEN (@LineFeed), N ' ' );
2810+
2811+ INSERT INTO #BlitzFirstResults (CheckID, Priority, FindingsGroup, Finding, URL , Details)
2812+ VALUES (
2813+ 49 ,
2814+ 210 ,
2815+ ' Potential Upcoming Problems' ,
2816+ ' High Number of Connections' ,
2817+ ' https://www.brentozar.com/archive/2014/05/connections-slow-sql-server-threadpool/' ,
2818+ ' There are ' + CAST (@TotalConnections AS VARCHAR (20 )) + ' open connections, which would lead to ' + @LineFeed
2819+ + ' worker thread exhaustion and THREADPOOL waits' + @LineFeed
2820+ + ' if they all ran queries at the same time.'
2821+ + @LineFeed + @LineFeed + ' Top 5 Servers:' + @LineFeed + ISNULL (@TopServers, ' (none)' )
2822+ + @LineFeed + @LineFeed + ' Top 5 Logins:' + @LineFeed + ISNULL (@TopLogins, ' (none)' )
2823+ + @LineFeed + @LineFeed + ' Top 5 Apps:' + @LineFeed + ISNULL (@TopApps, ' (none)' )
2824+ );
2825+ END
26762826 END
26772827 END
26782828
0 commit comments